@agent-native/toolkit 0.203.2-nightly-20261007213109 → 0.204.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 (51) hide show
  1. package/dist/app/chat/AgentKitAssistantChat.js +2 -2
  2. package/dist/app/chat/AgentKitAssistantChat.js.map +1 -1
  3. package/dist/app/i18n/catalogs/ar-SA.js +1 -1
  4. package/dist/app/i18n/catalogs/ar-SA.js.map +1 -1
  5. package/dist/app/i18n/catalogs/de-DE.js +1 -1
  6. package/dist/app/i18n/catalogs/de-DE.js.map +1 -1
  7. package/dist/app/i18n/catalogs/en-US.d.ts +1 -1
  8. package/dist/app/i18n/catalogs/en-US.d.ts.map +1 -1
  9. package/dist/app/i18n/catalogs/en-US.js +1 -1
  10. package/dist/app/i18n/catalogs/en-US.js.map +1 -1
  11. package/dist/app/i18n/catalogs/es-ES.js +1 -1
  12. package/dist/app/i18n/catalogs/es-ES.js.map +1 -1
  13. package/dist/app/i18n/catalogs/fr-FR.js +1 -1
  14. package/dist/app/i18n/catalogs/fr-FR.js.map +1 -1
  15. package/dist/app/i18n/catalogs/hi-IN.js +1 -1
  16. package/dist/app/i18n/catalogs/hi-IN.js.map +1 -1
  17. package/dist/app/i18n/catalogs/ja-JP.js +1 -1
  18. package/dist/app/i18n/catalogs/ja-JP.js.map +1 -1
  19. package/dist/app/i18n/catalogs/ko-KR.js +1 -1
  20. package/dist/app/i18n/catalogs/ko-KR.js.map +1 -1
  21. package/dist/app/i18n/catalogs/pt-BR.js +1 -1
  22. package/dist/app/i18n/catalogs/pt-BR.js.map +1 -1
  23. package/dist/app/i18n/catalogs/zh-CN.js +1 -1
  24. package/dist/app/i18n/catalogs/zh-CN.js.map +1 -1
  25. package/dist/app/i18n/catalogs/zh-TW.js +1 -1
  26. package/dist/app/i18n/catalogs/zh-TW.js.map +1 -1
  27. package/dist/app/onboarding/FirstRunOnboarding.d.ts.map +1 -1
  28. package/dist/app/onboarding/FirstRunOnboarding.js +12 -4
  29. package/dist/app/onboarding/FirstRunOnboarding.js.map +1 -1
  30. package/dist/app/settings/useBuilderStatus.d.ts +4 -0
  31. package/dist/app/settings/useBuilderStatus.d.ts.map +1 -1
  32. package/dist/app/settings/useBuilderStatus.js +56 -16
  33. package/dist/app/settings/useBuilderStatus.js.map +1 -1
  34. package/package.json +4 -4
  35. package/src/app/chat/AgentKitAssistantChat.spec.tsx +7 -0
  36. package/src/app/chat/AgentKitAssistantChat.tsx +3 -3
  37. package/src/app/i18n/catalogs/ar-SA.ts +1 -1
  38. package/src/app/i18n/catalogs/de-DE.ts +1 -1
  39. package/src/app/i18n/catalogs/en-US.ts +1 -1
  40. package/src/app/i18n/catalogs/es-ES.ts +1 -1
  41. package/src/app/i18n/catalogs/fr-FR.ts +1 -1
  42. package/src/app/i18n/catalogs/hi-IN.ts +1 -1
  43. package/src/app/i18n/catalogs/ja-JP.ts +1 -1
  44. package/src/app/i18n/catalogs/ko-KR.ts +1 -1
  45. package/src/app/i18n/catalogs/pt-BR.ts +1 -1
  46. package/src/app/i18n/catalogs/zh-CN.ts +1 -1
  47. package/src/app/i18n/catalogs/zh-TW.ts +1 -1
  48. package/src/app/onboarding/FirstRunOnboarding.spec.tsx +163 -27
  49. package/src/app/onboarding/FirstRunOnboarding.tsx +39 -22
  50. package/src/app/settings/useBuilderStatus.spec.tsx +24 -2
  51. package/src/app/settings/useBuilderStatus.ts +57 -17
@@ -587,6 +587,159 @@ describe("FirstRunOnboarding", () => {
587
587
  expect(flow.cancel).toHaveBeenCalledOnce();
588
588
  });
589
589
 
590
+ it("keeps Cancel during a failed status poll without offering a fake retry", () => {
591
+ const flow = {
592
+ hasFetchedStatus: true,
593
+ statusResolved: false,
594
+ configured: false,
595
+ agentNativeProvisioningEnabled: true,
596
+ connecting: false,
597
+ statusUnavailable: true,
598
+ terminalError: null,
599
+ error: "Couldn't check the Builder.io connection.",
600
+ start: vi.fn(),
601
+ cancel: vi.fn(),
602
+ retry: vi.fn(),
603
+ };
604
+ flow.start.mockImplementation(() => {
605
+ flow.connecting = true;
606
+ });
607
+ mocks.useBuilderConnectFlow.mockReturnValue(flow);
608
+
609
+ act(() => {
610
+ root.render(
611
+ <TooltipProvider>
612
+ <FirstRunOnboarding />
613
+ </TooltipProvider>,
614
+ );
615
+ });
616
+ act(() => {
617
+ document.body
618
+ .querySelector("[data-testid='first-run-role-skip']")
619
+ ?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
620
+ });
621
+ act(() => {
622
+ document.body
623
+ .querySelector("[data-testid='first-run-builder-create-account']")
624
+ ?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
625
+ });
626
+
627
+ expect(document.body.textContent).toContain(
628
+ "Couldn't check the Builder.io connection.",
629
+ );
630
+ expect(
631
+ document.body.querySelector('[data-testid="first-run-cancel-builder"]'),
632
+ ).not.toBeNull();
633
+ expect(
634
+ [...document.body.querySelectorAll("button")].some(
635
+ (button) => button.textContent === "Try again",
636
+ ),
637
+ ).toBe(false);
638
+
639
+ act(() => {
640
+ document.body
641
+ .querySelector<HTMLButtonElement>(
642
+ '[data-testid="first-run-cancel-builder"]',
643
+ )
644
+ ?.click();
645
+ });
646
+ expect(flow.cancel).toHaveBeenCalledOnce();
647
+ });
648
+
649
+ it.each([
650
+ {
651
+ mode: "provision",
652
+ choiceId: "first-run-builder-create-account",
653
+ provisionAccount: true,
654
+ failure: "transient status read failure",
655
+ statusUnavailable: true,
656
+ terminalError: null,
657
+ error: "Couldn't check the Builder.io connection.",
658
+ },
659
+ {
660
+ mode: "existing",
661
+ choiceId: "first-run-builder-sign-in",
662
+ provisionAccount: false,
663
+ failure: "transient status read failure",
664
+ statusUnavailable: true,
665
+ terminalError: null,
666
+ error: "Couldn't check the Builder.io connection.",
667
+ },
668
+ {
669
+ mode: "provision",
670
+ choiceId: "first-run-builder-create-account",
671
+ provisionAccount: true,
672
+ failure: "terminal connection error",
673
+ statusUnavailable: true,
674
+ terminalError: "Builder connection failed.",
675
+ error: "Builder connection failed.",
676
+ },
677
+ {
678
+ mode: "existing",
679
+ choiceId: "first-run-builder-sign-in",
680
+ provisionAccount: false,
681
+ failure: "terminal connection error",
682
+ statusUnavailable: true,
683
+ terminalError: "Builder connection failed.",
684
+ error: "Builder connection failed.",
685
+ },
686
+ ])(
687
+ "restarts the prior $mode mode after a $failure",
688
+ ({
689
+ choiceId,
690
+ provisionAccount,
691
+ statusUnavailable,
692
+ terminalError,
693
+ error,
694
+ }) => {
695
+ const start = vi.fn();
696
+ mocks.useBuilderConnectFlow.mockReturnValue({
697
+ hasFetchedStatus: true,
698
+ statusResolved: true,
699
+ configured: false,
700
+ agentNativeProvisioningEnabled: true,
701
+ connecting: false,
702
+ statusUnavailable,
703
+ terminalError,
704
+ error,
705
+ start,
706
+ cancel: vi.fn(),
707
+ retry: vi.fn(),
708
+ });
709
+
710
+ act(() => {
711
+ root.render(
712
+ <TooltipProvider>
713
+ <FirstRunOnboarding />
714
+ </TooltipProvider>,
715
+ );
716
+ });
717
+ act(() => {
718
+ document.body
719
+ .querySelector("[data-testid='first-run-role-skip']")
720
+ ?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
721
+ });
722
+ act(() => {
723
+ document.body
724
+ .querySelector(`[data-testid='${choiceId}']`)
725
+ ?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
726
+ });
727
+
728
+ expect(start).toHaveBeenCalledOnce();
729
+ expect(document.body.textContent).toContain(error);
730
+ act(() => {
731
+ [...document.body.querySelectorAll("button")]
732
+ .find((button) => button.textContent === "Try again")
733
+ ?.click();
734
+ });
735
+
736
+ expect(start).toHaveBeenCalledTimes(2);
737
+ expect(start).toHaveBeenLastCalledWith(
738
+ expect.objectContaining({ provisionAccount }),
739
+ );
740
+ },
741
+ );
742
+
590
743
  it("creates a Builder account from the primary button and shows its loading state", () => {
591
744
  const flow = {
592
745
  hasFetchedStatus: true,
@@ -760,7 +913,7 @@ describe("FirstRunOnboarding", () => {
760
913
  expect(JSON.stringify(outcome)).not.toContain("Error:");
761
914
  });
762
915
 
763
- it("does not attach a stale Builder error to a later manual setup attempt", async () => {
916
+ it("does not attach a status-read failure to a manual setup attempt", async () => {
764
917
  const flow = {
765
918
  hasFetchedStatus: true,
766
919
  statusResolved: true,
@@ -768,8 +921,9 @@ describe("FirstRunOnboarding", () => {
768
921
  agentNativeProvisioningEnabled: true,
769
922
  accountExists: false,
770
923
  connecting: false,
771
- error: null as string | null,
772
- start: vi.fn(),
924
+ statusUnavailable: true,
925
+ terminalError: null,
926
+ error: "Couldn't check the Builder.io connection.",
773
927
  };
774
928
  let resolveCompletion: (() => void) | undefined;
775
929
  mocks.completeFirstRun.mockImplementation(
@@ -792,25 +946,6 @@ describe("FirstRunOnboarding", () => {
792
946
  .querySelector("[data-testid='first-run-role-skip']")
793
947
  ?.click();
794
948
  });
795
- act(() => {
796
- document.body
797
- .querySelector("[data-testid='first-run-builder-create-account']")
798
- ?.click();
799
- });
800
-
801
- flow.error = "stale Builder error";
802
- act(() => {
803
- root.render(
804
- <TooltipProvider>
805
- <FirstRunOnboarding />
806
- </TooltipProvider>,
807
- );
808
- });
809
- act(() => {
810
- [...document.body.querySelectorAll("button")]
811
- .find((button) => button.textContent === "Try again")
812
- ?.click();
813
- });
814
949
 
815
950
  await act(async () => {
816
951
  document.body
@@ -818,7 +953,7 @@ describe("FirstRunOnboarding", () => {
818
953
  ?.click();
819
954
  await Promise.resolve();
820
955
  });
821
- flow.error = "stale Builder error changed";
956
+ flow.error = "Builder status still unavailable.";
822
957
  act(() => {
823
958
  root.render(
824
959
  <TooltipProvider>
@@ -1534,7 +1669,7 @@ describe("FirstRunOnboarding", () => {
1534
1669
  expect(document.body.textContent).not.toMatch(/\bProduct\b/);
1535
1670
  });
1536
1671
 
1537
- it("keeps the create-account CTA actionable after a failed status read", () => {
1672
+ it("keeps setup choices clear and actionable after a failed status read", () => {
1538
1673
  const start = vi.fn();
1539
1674
  const retry = vi.fn();
1540
1675
  mocks.useBuilderConnectFlow.mockReturnValue({
@@ -1544,7 +1679,8 @@ describe("FirstRunOnboarding", () => {
1544
1679
  agentNativeProvisioningEnabled: true,
1545
1680
  accountExists: false,
1546
1681
  connecting: false,
1547
- error: "Couldn't reach Builder to check your account. Retrying.",
1682
+ statusUnavailable: true,
1683
+ error: "Couldn't check the Builder.io connection.",
1548
1684
  retry,
1549
1685
  start,
1550
1686
  });
@@ -1565,8 +1701,8 @@ describe("FirstRunOnboarding", () => {
1565
1701
  expect(
1566
1702
  document.body.querySelector(
1567
1703
  '[data-testid="first-run-builder-status-error"]',
1568
- )?.textContent,
1569
- ).toContain("Couldn't reach Builder");
1704
+ ),
1705
+ ).toBeNull();
1570
1706
 
1571
1707
  const cta = document.body.querySelector(
1572
1708
  '[data-testid="first-run-builder-create-account"]',
@@ -464,10 +464,14 @@ export function FirstRunOnboarding({
464
464
  trackFirstRunSetupOutcome(attempt, "failed", "account_exists");
465
465
  return;
466
466
  }
467
- if (connectFlow.error) {
467
+ if (connectFlow.terminalError) {
468
468
  trackFirstRunSetupOutcome(attempt, "failed", "connection_error");
469
469
  }
470
- }, [connectFlow.accountExists, connectFlow.connecting, connectFlow.error]);
470
+ }, [
471
+ connectFlow.accountExists,
472
+ connectFlow.connecting,
473
+ connectFlow.terminalError,
474
+ ]);
471
475
  const canActivateBuilderFreeCredits =
472
476
  connectFlow.agentNativeProvisioningEnabled;
473
477
  const retryOnboardingCompletion = useCallback(() => {
@@ -746,15 +750,6 @@ export function FirstRunOnboarding({
746
750
  {t("agentChat.onboarding.builderSignInWithAccount")}
747
751
  </button>
748
752
  </div>
749
- {connectFlow.error && !connectFlow.statusResolved && (
750
- <p
751
- role="status"
752
- data-testid="first-run-builder-status-error"
753
- className="text-center text-xs text-destructive"
754
- >
755
- {connectFlow.error}
756
- </p>
757
- )}
758
753
  </section>
759
754
 
760
755
  <section className="flex flex-col gap-5 rounded-2xl border border-border bg-card p-6">
@@ -996,18 +991,40 @@ export function FirstRunOnboarding({
996
991
  {t("common.cancel")}
997
992
  </button>
998
993
  )}
999
- {connectFlow.error && (
1000
- <div className="mt-4 flex flex-col items-center gap-2">
1001
- <p className="text-xs text-destructive">{connectFlow.error}</p>
1002
- <button
1003
- type="button"
1004
- className={secondaryButtonClass}
1005
- onClick={() => setScreen("choice")}
1006
- >
1007
- Try again
1008
- </button>
1009
- </div>
994
+ {connectFlow.statusUnavailable &&
995
+ connectFlow.connecting &&
996
+ !connectFlow.terminalError && (
997
+ <p className="mt-4 text-xs text-destructive" role="alert">
998
+ {t(
999
+ "agentChat.settingsShell.integrations.builderStatusFailed",
1000
+ )}
1001
+ </p>
1002
+ )}
1003
+ {connectFlow.terminalError && connectFlow.connecting && (
1004
+ <p className="mt-4 text-xs text-destructive" role="alert">
1005
+ {connectFlow.terminalError}
1006
+ </p>
1010
1007
  )}
1008
+ {!connectFlow.connecting &&
1009
+ (connectFlow.statusUnavailable || connectFlow.terminalError) && (
1010
+ <div className="mt-4 flex flex-col items-center gap-2">
1011
+ <p className="text-xs text-destructive" role="alert">
1012
+ {connectFlow.terminalError ??
1013
+ t(
1014
+ "agentChat.settingsShell.integrations.builderStatusFailed",
1015
+ )}
1016
+ </p>
1017
+ <button
1018
+ type="button"
1019
+ className={secondaryButtonClass}
1020
+ onClick={() =>
1021
+ handleBuilder(builderConnectionMode === "provision")
1022
+ }
1023
+ >
1024
+ Try again
1025
+ </button>
1026
+ </div>
1027
+ )}
1011
1028
  </>
1012
1029
  )}
1013
1030
  </div>
@@ -103,6 +103,12 @@ function BuilderConnectProbeContent({
103
103
  <output data-testid="credential-source">
104
104
  {flow.credentialSource ?? "none"}
105
105
  </output>
106
+ <output data-testid="status-unavailable">
107
+ {flow.statusUnavailable ? "unavailable" : "available"}
108
+ </output>
109
+ <output data-testid="terminal-error">
110
+ {flow.terminalError ?? "none"}
111
+ </output>
106
112
  <output>{flow.error ?? ""}</output>
107
113
  </div>
108
114
  );
@@ -1644,8 +1650,15 @@ describe("useBuilderConnectFlow", () => {
1644
1650
  await flushAfterPaint();
1645
1651
 
1646
1652
  expect(container.textContent).toContain("not-configured idle unresolved");
1653
+ expect(
1654
+ container.querySelector('[data-testid="status-unavailable"]')
1655
+ ?.textContent,
1656
+ ).toBe("unavailable");
1657
+ expect(
1658
+ container.querySelector('[data-testid="terminal-error"]')?.textContent,
1659
+ ).toBe("none");
1647
1660
  expect(container.textContent).toContain(
1648
- "Couldn't reach Builder to check your account.",
1661
+ "Couldn't check the Builder.io connection.",
1649
1662
  );
1650
1663
 
1651
1664
  await act(async () => {
@@ -1655,7 +1668,16 @@ describe("useBuilderConnectFlow", () => {
1655
1668
  });
1656
1669
 
1657
1670
  expect(container.textContent).toContain("not-configured idle resolved");
1658
- expect(container.textContent).not.toContain("Couldn't reach Builder");
1671
+ expect(
1672
+ container.querySelector('[data-testid="status-unavailable"]')
1673
+ ?.textContent,
1674
+ ).toBe("available");
1675
+ expect(
1676
+ container.querySelector('[data-testid="terminal-error"]')?.textContent,
1677
+ ).toBe("none");
1678
+ expect(container.textContent).not.toContain(
1679
+ "Couldn't check the Builder.io connection.",
1680
+ );
1659
1681
  });
1660
1682
 
1661
1683
  it("shows the chooser without navigating when status cannot be resolved", async () => {
@@ -268,6 +268,10 @@ export interface BuilderConnectFlow {
268
268
  orgName: string | null;
269
269
  connecting: boolean;
270
270
  error: string | null;
271
+ /** The most recent terminal Builder connection attempt error, if any. */
272
+ terminalError: string | null;
273
+ /** The most recent Builder connection-status read failed. */
274
+ statusUnavailable: boolean;
271
275
  accountExists: boolean;
272
276
  hasFetchedStatus: boolean;
273
277
  start: (options?: BuilderConnectStartOptions) => void;
@@ -281,8 +285,6 @@ const POLL_TIMEOUT_MS = 5 * 60 * 1000;
281
285
  export const POPUP_CLOSED_CONFIRMATION_GRACE_MS = 20_000;
282
286
  const POPUP_LOAD_TIMEOUT_MS = 20_000;
283
287
  const STATUS_FETCH_ABORT_MS = 10_000;
284
- const BUILDER_STATUS_UNAVAILABLE_MESSAGE =
285
- "Couldn't reach Builder to check your account. Retrying.";
286
288
  const CALLBACK_SUCCESS_STATUS_RETRY_MS = 500;
287
289
  const CALLBACK_SUCCESS_STATUS_RETRIES = 10;
288
290
  const BUILDER_CONNECT_PARAM = "_an_connect";
@@ -841,6 +843,7 @@ export function useBuilderConnectFlow(
841
843
  const [orgName, setOrgName] = useState<string | null>(null);
842
844
  const [connecting, setConnecting] = useState(false);
843
845
  const [error, setError] = useState<string | null>(null);
846
+ const [statusUnavailable, setStatusUnavailable] = useState(false);
844
847
  const [accountExists, setAccountExists] = useState(false);
845
848
  const [hasFetchedStatus, setHasFetchedStatus] = useState(false);
846
849
  const [statusResolved, setStatusResolved] = useState(false);
@@ -868,6 +871,14 @@ export function useBuilderConnectFlow(
868
871
  } | null>(null);
869
872
  const retryStatusRef = useRef<() => boolean>(() => false);
870
873
  const statusUnavailableRef = useRef(false);
874
+ const markStatusUnavailable = useCallback(() => {
875
+ statusUnavailableRef.current = true;
876
+ setStatusUnavailable(true);
877
+ }, []);
878
+ const markStatusAvailable = useCallback(() => {
879
+ statusUnavailableRef.current = false;
880
+ setStatusUnavailable(false);
881
+ }, []);
871
882
  const statusPollFailuresRef = useRef(0);
872
883
  const mountedRef = useRef(true);
873
884
  const notifiedConnectedRef = useRef(false);
@@ -1018,6 +1029,7 @@ export function useBuilderConnectFlow(
1018
1029
  setOrgName(null);
1019
1030
  setConnecting(false);
1020
1031
  setError(null);
1032
+ markStatusAvailable();
1021
1033
  setAccountExists(false);
1022
1034
  setHasFetchedStatus(false);
1023
1035
  setStatusResolved(false);
@@ -1039,13 +1051,11 @@ export function useBuilderConnectFlow(
1039
1051
  setHasFetchedStatus(true);
1040
1052
  setStatusReadSettledCount((count) => count + 1);
1041
1053
  if (!s) {
1042
- statusUnavailableRef.current = true;
1043
- setError(BUILDER_STATUS_UNAVAILABLE_MESSAGE);
1054
+ markStatusUnavailable();
1044
1055
  return;
1045
1056
  }
1046
1057
  if (statusUnavailableRef.current) {
1047
- statusUnavailableRef.current = false;
1048
- setError(null);
1058
+ markStatusAvailable();
1049
1059
  }
1050
1060
  setStatusResolved(true);
1051
1061
  setConfigured(!!s.configured);
@@ -1123,7 +1133,13 @@ export function useBuilderConnectFlow(
1123
1133
  document.removeEventListener("visibilitychange", onVisible);
1124
1134
  window.removeEventListener("agent-engine:configured-changed", refreshNow);
1125
1135
  };
1126
- }, [enabled, fetchStatus, notifyProvisionedAccount]);
1136
+ }, [
1137
+ enabled,
1138
+ fetchStatus,
1139
+ markStatusAvailable,
1140
+ markStatusUnavailable,
1141
+ notifyProvisionedAccount,
1142
+ ]);
1127
1143
 
1128
1144
  const retry = useCallback(() => retryStatusRef.current(), []);
1129
1145
  const cancel = useCallback(() => {
@@ -1254,6 +1270,11 @@ export function useBuilderConnectFlow(
1254
1270
  const freshProvisioningCredentials = async () => {
1255
1271
  // coercion-ok: no fresh token is sent as none and refused as provision_token_invalid
1256
1272
  const status = await fetchStatus();
1273
+ if (!status) {
1274
+ markStatusUnavailable();
1275
+ return null;
1276
+ }
1277
+ markStatusAvailable();
1257
1278
  if (!status?.agentNativeProvisioningEnabled) return null;
1258
1279
  const freshUrl = status?.connectUrl ?? null;
1259
1280
  const freshConnectToken = freshUrl
@@ -1283,7 +1304,9 @@ export function useBuilderConnectFlow(
1283
1304
  if (!credentials) {
1284
1305
  connectStartedAtRef.current = null;
1285
1306
  setConnecting(false);
1286
- setError(t("agentChat.settingsShell.builder.setupStartFailed"));
1307
+ if (!statusUnavailableRef.current) {
1308
+ setError(t("agentChat.settingsShell.builder.setupStartFailed"));
1309
+ }
1287
1310
  return;
1288
1311
  }
1289
1312
  let result = await activate(credentials);
@@ -1317,6 +1340,8 @@ export function useBuilderConnectFlow(
1317
1340
  // attempt with a retryable error.
1318
1341
  const status = await fetchStatus();
1319
1342
  if (!isCurrentAttempt()) return;
1343
+ if (status) markStatusAvailable();
1344
+ else markStatusUnavailable();
1320
1345
  if (
1321
1346
  status &&
1322
1347
  isBuilderConnectComplete(status, connectTargetRef.current)
@@ -1393,11 +1418,12 @@ export function useBuilderConnectFlow(
1393
1418
  cancelledConnectAttemptIdRef.current !== connectAttemptId;
1394
1419
  if (!isCurrentAttempt()) return;
1395
1420
  if (!status) {
1421
+ markStatusUnavailable();
1396
1422
  connectStartedAtRef.current = null;
1397
1423
  setConnecting(false);
1398
- setError(BUILDER_STATUS_UNAVAILABLE_MESSAGE);
1399
1424
  return;
1400
1425
  }
1426
+ markStatusAvailable();
1401
1427
  setHasFetchedStatus(true);
1402
1428
  setStatusResolved(true);
1403
1429
  setConfigured(!!status.configured);
@@ -1432,7 +1458,7 @@ export function useBuilderConnectFlow(
1432
1458
  ) {
1433
1459
  connectStartedAtRef.current = null;
1434
1460
  setConnecting(false);
1435
- setError(BUILDER_STATUS_UNAVAILABLE_MESSAGE);
1461
+ setError(t("agentChat.settingsShell.builder.setupStartFailed"));
1436
1462
  return;
1437
1463
  }
1438
1464
  const result = await openDesktopConnectUrl({
@@ -1445,7 +1471,10 @@ export function useBuilderConnectFlow(
1445
1471
  if (!result?.ok) {
1446
1472
  connectStartedAtRef.current = null;
1447
1473
  setConnecting(false);
1448
- setError(result?.error ?? BUILDER_STATUS_UNAVAILABLE_MESSAGE);
1474
+ setError(
1475
+ result?.error ??
1476
+ t("agentChat.settingsShell.builder.setupStartFailed"),
1477
+ );
1449
1478
  }
1450
1479
  })();
1451
1480
  } else {
@@ -1474,6 +1503,8 @@ export function useBuilderConnectFlow(
1474
1503
  ) {
1475
1504
  return;
1476
1505
  }
1506
+ if (s) markStatusAvailable();
1507
+ else markStatusUnavailable();
1477
1508
  if (s) {
1478
1509
  setHasFetchedStatus(true);
1479
1510
  setStatusResolved(true);
@@ -1537,6 +1568,8 @@ export function useBuilderConnectFlow(
1537
1568
  }
1538
1569
  return;
1539
1570
  }
1571
+ if (s) markStatusAvailable();
1572
+ else markStatusUnavailable();
1540
1573
  if (s) {
1541
1574
  setHasFetchedStatus(true);
1542
1575
  setStatusResolved(true);
@@ -1619,6 +1652,8 @@ export function useBuilderConnectFlow(
1619
1652
  enabled,
1620
1653
  t,
1621
1654
  fetchStatus,
1655
+ markStatusAvailable,
1656
+ markStatusUnavailable,
1622
1657
  transport,
1623
1658
  agentNativeProvisioningEnabled,
1624
1659
  agentNativeProvisioningToken,
@@ -1645,14 +1680,12 @@ export function useBuilderConnectFlow(
1645
1680
  statusPollFailuresRef.current = 0;
1646
1681
  } else {
1647
1682
  statusPollFailuresRef.current += 1;
1648
- statusUnavailableRef.current = true;
1649
- setError(BUILDER_STATUS_UNAVAILABLE_MESSAGE);
1683
+ markStatusUnavailable();
1650
1684
  }
1651
1685
  const orgName = s?.orgName ?? null;
1652
1686
  if (s) {
1653
1687
  if (statusUnavailableRef.current) {
1654
- statusUnavailableRef.current = false;
1655
- setError(null);
1688
+ markStatusAvailable();
1656
1689
  }
1657
1690
  setHasFetchedStatus(true);
1658
1691
  setStatusResolved(true);
@@ -1862,6 +1895,7 @@ export function useBuilderConnectFlow(
1862
1895
  return;
1863
1896
  }
1864
1897
  if (!s) return;
1898
+ markStatusAvailable();
1865
1899
  if (!isBuilderConnectComplete(s, connectTargetRef.current)) {
1866
1900
  const connectError = isCurrentConnectError(s?.connectError, started)
1867
1901
  ? s?.connectError
@@ -1981,7 +2015,7 @@ export function useBuilderConnectFlow(
1981
2015
  channel?.close();
1982
2016
  window.removeEventListener("message", handler);
1983
2017
  };
1984
- }, [fetchStatus, notifyProvisionedAccount]);
2018
+ }, [fetchStatus, markStatusAvailable, notifyProvisionedAccount]);
1985
2019
 
1986
2020
  return {
1987
2021
  configured,
@@ -1999,7 +2033,13 @@ export function useBuilderConnectFlow(
1999
2033
  builderEnabled,
2000
2034
  orgName,
2001
2035
  connecting,
2002
- error,
2036
+ terminalError: error,
2037
+ error:
2038
+ error ??
2039
+ (statusUnavailable
2040
+ ? t("agentChat.settingsShell.integrations.builderStatusFailed")
2041
+ : null),
2042
+ statusUnavailable,
2003
2043
  accountExists,
2004
2044
  hasFetchedStatus,
2005
2045
  start,