@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
@@ -11,6 +11,11 @@ import {
11
11
  normalizeOAuthReturnPath,
12
12
  shouldRetryAuthSessionProbe,
13
13
  } from "@agent-native/core/client/auth/auth-page-helpers";
14
+ import {
15
+ acceptBuilderSignupAssignment,
16
+ getBuilderSignupAssignment,
17
+ type BuilderSignupAssignment,
18
+ } from "@agent-native/core/client/auth/builder-signup";
14
19
  import { whenConsentDecided } from "@agent-native/core/client/consent";
15
20
  import { openOAuthPopup } from "@agent-native/core/client/oauth-popup";
16
21
  import { navigateForSession } from "@agent-native/core/client/use-session";
@@ -260,13 +265,15 @@ async function requestJson(
260
265
  return { response, data, readable };
261
266
  }
262
267
 
268
+ let builderSignupTelemetry: Record<string, unknown> = {};
269
+
263
270
  function trackAuth(
264
271
  app: string,
265
272
  name: string,
266
273
  properties: Record<string, unknown> = {},
267
- email: string,
274
+ email?: string,
268
275
  ): void {
269
- if (!isValidEmail(email) || isTestIdentityEmail(email)) return;
276
+ if (email && (!isValidEmail(email) || isTestIdentityEmail(email))) return;
270
277
  if (
271
278
  isSyntheticTrafficValue(
272
279
  (
@@ -295,7 +302,11 @@ function trackAuth(
295
302
  "https://analytics.agent-native.com/track",
296
303
  window.location.hostname,
297
304
  );
298
- const legacyProperties = { app, ...properties };
305
+ const legacyProperties = {
306
+ app,
307
+ ...(app === "design" ? builderSignupTelemetry : {}),
308
+ ...properties,
309
+ };
299
310
  const events: Array<{
300
311
  name: string;
301
312
  properties: Record<string, unknown>;
@@ -656,12 +667,63 @@ export function AuthPage(props: AuthPageProps) {
656
667
  googleAuthMode,
657
668
  builderPreviewLocalDevEnabled,
658
669
  } = props;
670
+ const [builderSignup, setBuilderSignup] =
671
+ React.useState<BuilderSignupAssignment | null>(
672
+ trackingApp === "design" ? null : { status: "disabled" },
673
+ );
674
+ const builderSignupRequest =
675
+ React.useRef<Promise<BuilderSignupAssignment> | null>(null);
676
+ React.useEffect(() => {
677
+ if (trackingApp !== "design") return;
678
+ builderSignupTelemetry = {};
679
+ let disposed = false;
680
+ builderSignupRequest.current ??= getBuilderSignupAssignment();
681
+ void builderSignupRequest.current.then(
682
+ (assignment) => {
683
+ if (disposed) return;
684
+ setBuilderSignup(assignment);
685
+ },
686
+ () => {
687
+ if (!disposed) {
688
+ builderSignupTelemetry = {};
689
+ setBuilderSignup({ status: "unavailable" });
690
+ trackAuth(trackingApp, "auth.builder_signup_experiment_unavailable", {
691
+ stage: "assignment",
692
+ });
693
+ }
694
+ },
695
+ );
696
+ return () => {
697
+ disposed = true;
698
+ };
699
+ }, [trackingApp]);
659
700
  const [localePreference, setLocalePreference] = React.useState("system");
660
701
  const [locale, setLocale] = React.useState(defaultLocale);
661
702
  const [browserLocationReady, setBrowserLocationReady] = React.useState(false);
662
703
  React.useEffect(() => setBrowserLocationReady(true), []);
663
704
  const [localeMenuOpen, setLocaleMenuOpen] = React.useState(false);
664
705
  const [view, setView] = React.useState<AuthView>(props.initialView);
706
+ const builderSignupExposure = React.useRef<string | null>(null);
707
+ React.useEffect(() => {
708
+ if (
709
+ builderSignup?.status !== "assigned" ||
710
+ !["signup", "magicLink", "googleOnly"].includes(view) ||
711
+ builderSignupExposure.current === builderSignup.id
712
+ )
713
+ return;
714
+ builderSignupExposure.current = builderSignup.id;
715
+ builderSignupTelemetry = {
716
+ experiment_id: "design_builder_signup_v1",
717
+ experiment_variant: builderSignup.variant,
718
+ assignment_id: builderSignup.id,
719
+ };
720
+ trackAuth(
721
+ trackingApp,
722
+ "auth.builder_signup_experiment_exposed",
723
+ builderSignupTelemetry,
724
+ );
725
+ }, [builderSignup, trackingApp, view]);
726
+
665
727
  const [messages, setMessages] = React.useState<Record<string, Notice>>({});
666
728
  const [submitting, setSubmitting] = React.useState<string | null>(null);
667
729
  const [localDevAvailable, setLocalDevAvailable] = React.useState(false);
@@ -1428,10 +1490,52 @@ export function AuthPage(props: AuthPageProps) {
1428
1490
  [publicOAuthOrigin, runtimeAppBasePath],
1429
1491
  );
1430
1492
 
1493
+ const prepareBuilderSignup = React.useCallback(
1494
+ async (
1495
+ method: "password" | "magic-link" | "google",
1496
+ email?: string,
1497
+ flowId = createFlowId(),
1498
+ ): Promise<string | undefined> => {
1499
+ if (
1500
+ builderSignup?.status !== "assigned" ||
1501
+ builderSignup.variant !== "automatic" ||
1502
+ view === "login"
1503
+ )
1504
+ return undefined;
1505
+ try {
1506
+ await acceptBuilderSignupAssignment(
1507
+ builderSignup.id,
1508
+ flowId,
1509
+ method,
1510
+ email,
1511
+ );
1512
+ return flowId;
1513
+ } catch {
1514
+ trackAuth(
1515
+ trackingApp,
1516
+ "auth.builder_signup_experiment_unavailable",
1517
+ { stage: "acceptance" },
1518
+ email,
1519
+ );
1520
+ return undefined;
1521
+ }
1522
+ },
1523
+ [builderSignup, trackingApp, view],
1524
+ );
1525
+
1431
1526
  const startGoogle = React.useCallback(async () => {
1432
- if (!showGoogle || googleBusy) return;
1527
+ if (!showGoogle || googleBusy || !builderSignup) return;
1433
1528
  setGoogleBusy(true);
1434
1529
  setNotice("google", null);
1530
+ const flowId = createFlowId();
1531
+ const redirectAfterAcceptance = async (url: string) => {
1532
+ const attemptId = await prepareBuilderSignup("google", undefined, flowId);
1533
+ const targetUrl = new URL(url, window.location.origin);
1534
+ if (attemptId)
1535
+ targetUrl.searchParams.set("builder_signup_attempt", attemptId);
1536
+ targetUrl.searchParams.set("flow_id", flowId);
1537
+ window.location.replace(targetUrl.toString());
1538
+ };
1435
1539
  try {
1436
1540
  window.sessionStorage.setItem("__an_signin", "1");
1437
1541
  } catch {
@@ -1447,10 +1551,9 @@ export function AuthPage(props: AuthPageProps) {
1447
1551
  })
1448
1552
  ) {
1449
1553
  const params = new URLSearchParams({ return: target });
1450
- window.location.replace(`${identityHref}?${params.toString()}`);
1554
+ await redirectAfterAcceptance(`${identityHref}?${params.toString()}`);
1451
1555
  return;
1452
1556
  }
1453
- const flowId = createFlowId();
1454
1557
  oauthFlowId.current = flowId;
1455
1558
  const flow = resolveGoogleFlow();
1456
1559
  const nativeDesktop = flow === "redirect" && isAgentNativeDesktop();
@@ -1473,7 +1576,7 @@ export function AuthPage(props: AuthPageProps) {
1473
1576
  return: oauthTarget,
1474
1577
  redirect: "1",
1475
1578
  });
1476
- window.location.replace(`${authUrl}?${params.toString()}`);
1579
+ await redirectAfterAcceptance(`${authUrl}?${params.toString()}`);
1477
1580
  return;
1478
1581
  }
1479
1582
  const verifier = createVerifier();
@@ -1507,7 +1610,7 @@ export function AuthPage(props: AuthPageProps) {
1507
1610
  return: oauthTarget,
1508
1611
  redirect: "1",
1509
1612
  });
1510
- window.location.replace(`${authUrl}?${params.toString()}`);
1613
+ await redirectAfterAcceptance(`${authUrl}?${params.toString()}`);
1511
1614
  return;
1512
1615
  }
1513
1616
  try {
@@ -1528,15 +1631,17 @@ export function AuthPage(props: AuthPageProps) {
1528
1631
  return: oauthTarget,
1529
1632
  redirect: "1",
1530
1633
  });
1531
- window.location.replace(`${authUrl}?${params.toString()}`);
1634
+ await redirectAfterAcceptance(`${authUrl}?${params.toString()}`);
1532
1635
  return;
1533
1636
  }
1534
1637
  }
1535
1638
  try {
1639
+ const attemptId = await prepareBuilderSignup("google", undefined, flowId);
1536
1640
  const params = new URLSearchParams({
1537
1641
  return: oauthTarget,
1538
1642
  desktop: "1",
1539
1643
  flow_id: flowId,
1644
+ ...(attemptId ? { builder_signup_attempt: attemptId } : {}),
1540
1645
  });
1541
1646
  const { response, data } = await requestJson(
1542
1647
  `${authUrl}?${params.toString()}`,
@@ -1589,6 +1694,8 @@ export function AuthPage(props: AuthPageProps) {
1589
1694
  apiPath,
1590
1695
  googleAuthUrlPath,
1591
1696
  googleBusy,
1697
+ builderSignup,
1698
+ prepareBuilderSignup,
1592
1699
  identityHref,
1593
1700
  identityBootstrapHref,
1594
1701
  googleViaIdentitySso,
@@ -1819,6 +1926,10 @@ export function AuthPage(props: AuthPageProps) {
1819
1926
  email,
1820
1927
  );
1821
1928
  try {
1929
+ const builderSignupAttemptId = await prepareBuilderSignup(
1930
+ "password",
1931
+ email,
1932
+ );
1822
1933
  const { response, data } = await requestJson(
1823
1934
  apiPath("/_agent-native/auth/register"),
1824
1935
  {
@@ -1826,6 +1937,7 @@ export function AuthPage(props: AuthPageProps) {
1826
1937
  headers: { "Content-Type": "application/json" },
1827
1938
  body: JSON.stringify({
1828
1939
  email,
1940
+ ...(builderSignupAttemptId ? { builderSignupAttemptId } : {}),
1829
1941
  password: signupPassword,
1830
1942
  callbackURL: identityBootstrapHref(resumeHref()),
1831
1943
  }),
@@ -1888,6 +2000,7 @@ export function AuthPage(props: AuthPageProps) {
1888
2000
  signupEmail,
1889
2001
  signupPassword,
1890
2002
  signupPasswordConfirmation,
2003
+ prepareBuilderSignup,
1891
2004
  t,
1892
2005
  trackingApp,
1893
2006
  view,
@@ -2070,6 +2183,10 @@ export function AuthPage(props: AuthPageProps) {
2070
2183
  );
2071
2184
  const desktop = isAgentNativeDesktop();
2072
2185
  try {
2186
+ const builderSignupAttemptId = await prepareBuilderSignup(
2187
+ "magic-link",
2188
+ email,
2189
+ );
2073
2190
  const { response, data } = await requestJson(
2074
2191
  apiPath("/_agent-native/auth/magic-link"),
2075
2192
  {
@@ -2077,6 +2194,7 @@ export function AuthPage(props: AuthPageProps) {
2077
2194
  headers: { "Content-Type": "application/json" },
2078
2195
  body: JSON.stringify({
2079
2196
  email,
2197
+ ...(builderSignupAttemptId ? { builderSignupAttemptId } : {}),
2080
2198
  callbackURL: desktop
2081
2199
  ? apiPath("/_agent-native/auth/magic-link/desktop-callback")
2082
2200
  : identityBootstrapHref(resumeHref()),
@@ -2122,6 +2240,7 @@ export function AuthPage(props: AuthPageProps) {
2122
2240
  apiPath,
2123
2241
  identityBootstrapHref,
2124
2242
  magicLinkEmail,
2243
+ prepareBuilderSignup,
2125
2244
  resumeHref,
2126
2245
  setNotice,
2127
2246
  startOAuthExchange,
@@ -2312,6 +2431,29 @@ export function AuthPage(props: AuthPageProps) {
2312
2431
  </button>
2313
2432
  </div>
2314
2433
  ) : null;
2434
+ const builderLegalNote =
2435
+ builderSignup?.status === "assigned" &&
2436
+ builderSignup.variant === "automatic" ? (
2437
+ <p className="legal-note">
2438
+ {t("builderSignupDisclosure")}{" "}
2439
+ <a
2440
+ href="https://www.builder.io/legal/terms"
2441
+ target="_blank"
2442
+ rel="noopener noreferrer"
2443
+ >
2444
+ {t("legalTerms")}
2445
+ </a>{" "}
2446
+ {t("legalConnector")}{" "}
2447
+ <a
2448
+ href="https://www.builder.io/legal/privacy"
2449
+ target="_blank"
2450
+ rel="noopener noreferrer"
2451
+ >
2452
+ {t("legalPrivacy")}
2453
+ </a>
2454
+ {t("legalSuffix")}
2455
+ </p>
2456
+ ) : null;
2315
2457
  const legalNote = signupLegalNotice ? (
2316
2458
  <p className="legal-note">
2317
2459
  <span data-i18n="legalPrefix">
@@ -2401,11 +2543,12 @@ export function AuthPage(props: AuthPageProps) {
2401
2543
  submitLabel={t("createAccount")}
2402
2544
  submitProps={{
2403
2545
  "data-i18n": "createAccount",
2404
- disabled: submitting === "signup",
2546
+ disabled: submitting === "signup" || !builderSignup,
2405
2547
  }}
2406
2548
  footer={
2407
2549
  <>
2408
2550
  {legalNote}
2551
+
2409
2552
  {localModeNote}
2410
2553
  </>
2411
2554
  }
@@ -2541,13 +2684,14 @@ export function AuthPage(props: AuthPageProps) {
2541
2684
  {t("googleNotConfigured")}
2542
2685
  </p>
2543
2686
  ) : null}
2687
+ {view !== "login" && builderLegalNote}
2544
2688
  {showGoogle ? (
2545
2689
  <div className="google-signin" id="google-signin">
2546
2690
  <button
2547
2691
  className={`btn-google ${view === "magicLink" && isValidEmail(magicLinkEmail) ? "magic-link-secondary" : ""}`}
2548
2692
  id="google-btn"
2549
2693
  type="button"
2550
- disabled={googleBusy}
2694
+ disabled={googleBusy || !builderSignup}
2551
2695
  aria-busy={googleBusy}
2552
2696
  onClick={() => void startGoogle()}
2553
2697
  >
@@ -2583,7 +2727,7 @@ export function AuthPage(props: AuthPageProps) {
2583
2727
  <button
2584
2728
  type="submit"
2585
2729
  id="organization-sso-submit"
2586
- disabled={ssoBusy || !isValidEmail(ssoEmail)}
2730
+ disabled={!builderSignup || ssoBusy || !isValidEmail(ssoEmail)}
2587
2731
  data-i18n="ssoButton"
2588
2732
  >
2589
2733
  {ssoBusy ? t("checking") : t("ssoButton")}
@@ -2618,7 +2762,9 @@ export function AuthPage(props: AuthPageProps) {
2618
2762
  type="submit"
2619
2763
  id="magic-link-submit"
2620
2764
  className="magic-link-submit"
2621
- disabled={magicLinkBusy || !isValidEmail(magicLinkEmail)}
2765
+ disabled={
2766
+ !builderSignup || magicLinkBusy || !isValidEmail(magicLinkEmail)
2767
+ }
2622
2768
  data-i18n="sendMagicLink"
2623
2769
  >
2624
2770
  {magicLinkBusy ? t("sending") : t("sendMagicLink")}
@@ -1332,6 +1332,24 @@ describe("AgentKitAssistantChat host behavior", () => {
1332
1332
  ]);
1333
1333
  });
1334
1334
 
1335
+ it("renders a menu-only provider without optional context items", async () => {
1336
+ const Provider = ({
1337
+ children,
1338
+ }: AssistantChatComposerContextProviderProps) =>
1339
+ children({
1340
+ menuItems: [
1341
+ {
1342
+ id: "native-reference",
1343
+ label: "Native reference",
1344
+ onSelect: vi.fn(),
1345
+ },
1346
+ ],
1347
+ });
1348
+ const ref = createRef<AssistantChatHandle>();
1349
+ await mount(baseProps({ composerContextProvider: Provider }), ref);
1350
+ expect(ref.current).not.toBeNull();
1351
+ });
1352
+
1335
1353
  it("counts provider-owned context against the prefill capacity", async () => {
1336
1354
  const providerItem = {
1337
1355
  key: "provider-reference",
@@ -5174,7 +5192,15 @@ describe("AgentKitAssistantChat host behavior", () => {
5174
5192
  try {
5175
5193
  await expect(
5176
5194
  chatMocks.rootProps.clientOptions.upload(
5177
- { uploadId: "upload-1", method: "POST", url: "/uploads" },
5195
+ await chatMocks.transportOptions.operations.createUpload({
5196
+ threadId: "test-thread",
5197
+ descriptor: {
5198
+ name: "reference.png",
5199
+ mediaType: "image/png",
5200
+ size: 4,
5201
+ purpose: "message",
5202
+ },
5203
+ }),
5178
5204
  {
5179
5205
  name: "reference.pdf",
5180
5206
  mediaType: "application/pdf",
@@ -5350,7 +5376,15 @@ describe("AgentKitAssistantChat host behavior", () => {
5350
5376
  try {
5351
5377
  const error = await chatMocks.rootProps.clientOptions
5352
5378
  .upload(
5353
- { uploadId: "upload-1", method: "POST", url: "/uploads" },
5379
+ await chatMocks.transportOptions.operations.createUpload({
5380
+ threadId: "test-thread",
5381
+ descriptor: {
5382
+ name: "reference.png",
5383
+ mediaType: "image/png",
5384
+ size: 4,
5385
+ purpose: "message",
5386
+ },
5387
+ }),
5354
5388
  {
5355
5389
  name: "reference.png",
5356
5390
  mediaType: "image/png",
@@ -5381,7 +5415,15 @@ describe("AgentKitAssistantChat host behavior", () => {
5381
5415
  try {
5382
5416
  await expect(
5383
5417
  chatMocks.rootProps.clientOptions.upload(
5384
- { uploadId: "upload-1", method: "POST", url: "/uploads" },
5418
+ await chatMocks.transportOptions.operations.createUpload({
5419
+ threadId: "test-thread",
5420
+ descriptor: {
5421
+ name: "reference.png",
5422
+ mediaType: "image/png",
5423
+ size: 4,
5424
+ purpose: "message",
5425
+ },
5426
+ }),
5385
5427
  {
5386
5428
  name: "reference.png",
5387
5429
  mediaType: "image/png",
@@ -6811,6 +6853,16 @@ describe("AgentKitAssistantChat host behavior", () => {
6811
6853
  await ref.current!.sendMessage("Sent by the app", undefined, {
6812
6854
  submitMessageId: "submit-app",
6813
6855
  surface: "deck-outline",
6856
+ references: [
6857
+ {
6858
+ type: "mention",
6859
+ path: "github",
6860
+ name: "GitHub",
6861
+ source: "workspace-integration",
6862
+ refType: "integration",
6863
+ refId: "github",
6864
+ },
6865
+ ],
6814
6866
  });
6815
6867
  });
6816
6868
  await act(async () => {
@@ -6832,6 +6884,16 @@ describe("AgentKitAssistantChat host behavior", () => {
6832
6884
  expect(metadata[1]).toMatchObject({
6833
6885
  submitMessageId: "submit-app",
6834
6886
  agentNativeSurface: "deck-outline",
6887
+ references: [
6888
+ {
6889
+ type: "mention",
6890
+ path: "github",
6891
+ name: "GitHub",
6892
+ source: "workspace-integration",
6893
+ refType: "integration",
6894
+ refId: "github",
6895
+ },
6896
+ ],
6835
6897
  });
6836
6898
  });
6837
6899
 
@@ -26,7 +26,6 @@ import type {
26
26
  AgentTransport,
27
27
  AgentThreadSnapshot,
28
28
  AgentToolCall,
29
- AgentUploadTarget,
30
29
  FilePart,
31
30
  } from "@agent-native/agentkit/protocol";
32
31
  import type { AgentChatAttachment } from "@agent-native/core";
@@ -70,6 +69,10 @@ import {
70
69
  useAgentEngineConfigured,
71
70
  type AgentEngineConfiguredState,
72
71
  } from "@agent-native/core/client/agent-chat";
72
+ import {
73
+ createAgentNativeUploadRuntime,
74
+ fileUploadUrlForChatApi,
75
+ } from "@agent-native/core/client/agentkit-chat/transport";
73
76
  import { agentNativePath } from "@agent-native/core/client/api-path";
74
77
  import {
75
78
  compareAndSetClientAppState,
@@ -1948,55 +1951,54 @@ export const AgentKitAssistantChat = forwardRef<
1948
1951
  }),
1949
1952
  [formatDate, props.composerPlaceholder, t],
1950
1953
  );
1951
- const uploadedFilesRef = useRef(new Map<string, FilePart>());
1954
+ const { session: uploadSession } = useSession();
1955
+ const uploadScopeKey = JSON.stringify([
1956
+ uploadSession?.email ?? null,
1957
+ uploadSession?.orgId ?? null,
1958
+ uploadSession?.authUserId ?? null,
1959
+ ]);
1960
+ const uploadRuntime = useMemo(
1961
+ () =>
1962
+ createAgentNativeUploadRuntime({
1963
+ url: fileUploadUrlForChatApi(
1964
+ props.apiUrl ?? agentNativePath("/_agent-native/agent-chat"),
1965
+ ),
1966
+ }),
1967
+ [props.apiUrl],
1968
+ );
1969
+ useEffect(
1970
+ () => () => uploadRuntime.dispose(),
1971
+ [uploadRuntime, uploadScopeKey],
1972
+ );
1952
1973
  const upload: AgentKitUploadDriver = useCallback(
1953
- async (target, file, context) => {
1954
- if (file.size > AGENT_CHAT_UPLOAD_MAX_FILE_BYTES) {
1955
- throw attachmentTooLargeError(
1956
- uploadTooLargeMessage(translatorRef.current),
1957
- );
1958
- }
1959
- const form = new FormData();
1960
- for (const [key, value] of Object.entries(target.fields ?? {})) {
1961
- form.set(key, value);
1962
- }
1963
- form.set("file", file.body, file.name);
1964
- let response: Response;
1974
+ async (...args) => {
1965
1975
  try {
1966
- response = await fetch(target.url, {
1967
- method: target.method,
1968
- headers: target.headers,
1969
- body: form,
1970
- signal: context?.signal,
1971
- });
1972
- } catch (error) {
1973
- if (context?.signal?.aborted) throw error;
1976
+ await uploadRuntime.upload(...args);
1977
+ } catch (cause) {
1978
+ if (args[2]?.signal?.aborted) throw cause;
1979
+ const status =
1980
+ cause && typeof cause === "object" && "status" in cause
1981
+ ? cause.status
1982
+ : undefined;
1983
+ if (typeof status === "number")
1984
+ throw uploadHttpError(status, translatorRef.current);
1985
+ const code =
1986
+ cause && typeof cause === "object" && "code" in cause
1987
+ ? cause.code
1988
+ : undefined;
1974
1989
  throw Object.assign(
1975
- new Error(translatorRef.current("agentChat.composer.uploadOffline")),
1976
- { code: "upload_network_error", retryable: true, cause: error },
1977
- );
1978
- }
1979
- // The server's error body is never shown: its text is untranslated.
1980
- if (!response.ok) {
1981
- throw uploadHttpError(response.status, translatorRef.current);
1982
- }
1983
- const result: unknown = await response.json();
1984
- const uploaded = asRecord(result);
1985
- if (typeof uploaded?.url !== "string" || !uploaded.url) {
1986
- throw Object.assign(
1987
- new Error(translatorRef.current("agentChat.composer.uploadFailed")),
1988
- { code: "upload_invalid_response", retryable: false },
1990
+ new Error(
1991
+ translatorRef.current(
1992
+ code === "upload_network_error"
1993
+ ? "agentChat.composer.uploadOffline"
1994
+ : "agentChat.composer.uploadFailed",
1995
+ ),
1996
+ ),
1997
+ { code, cause, retryable: code === "upload_network_error" },
1989
1998
  );
1990
1999
  }
1991
- uploadedFilesRef.current.set(target.uploadId, {
1992
- type: "file",
1993
- name: file.name,
1994
- mediaType: file.mediaType,
1995
- url: uploaded.url,
1996
- ...(typeof uploaded.id === "string" ? { fileId: uploaded.id } : {}),
1997
- });
1998
2000
  },
1999
- [],
2001
+ [uploadRuntime],
2000
2002
  );
2001
2003
  const transportThreadIdRef = useRef(threadId);
2002
2004
  const tabIdRef = useRef(props.tabId);
@@ -2036,21 +2038,7 @@ export const AgentKitAssistantChat = forwardRef<
2036
2038
  const operations: NonNullable<
2037
2039
  CreateAgentNativeAgentKitTransportOptions["operations"]
2038
2040
  > = {
2039
- createUpload: async () =>
2040
- ({
2041
- uploadId: createAgentUploadId(),
2042
- method: "POST",
2043
- url: agentNativePath("/_agent-native/file-upload"),
2044
- fields: {},
2045
- }) satisfies AgentUploadTarget,
2046
- completeUpload: async ({ uploadId }) => {
2047
- const uploaded = uploadedFilesRef.current.get(uploadId);
2048
- if (!uploaded) {
2049
- throw new Error(`Upload ${uploadId} did not complete.`);
2050
- }
2051
- uploadedFilesRef.current.delete(uploadId);
2052
- return uploaded;
2053
- },
2041
+ ...uploadRuntime.operations,
2054
2042
  invokeAction: async ({ invocation }) => {
2055
2043
  try {
2056
2044
  const result = await callAction(
@@ -3700,10 +3688,13 @@ const AgentKitAssistantChatBody = forwardRef<
3700
3688
  ? { agentNativeInternalContinuation: true }
3701
3689
  : {}),
3702
3690
  ...(contextScope ? { chatScope: contextScope } : {}),
3703
- ...(references.length || options.recoveryReferences?.length
3691
+ ...(references.length ||
3692
+ options.references?.length ||
3693
+ options.recoveryReferences?.length
3704
3694
  ? {
3705
3695
  references: [
3706
3696
  ...references,
3697
+ ...(options.references ?? []),
3707
3698
  ...(options.recoveryReferences ?? []),
3708
3699
  ],
3709
3700
  }
@@ -5811,12 +5802,11 @@ function AgentKitComposerSurface({
5811
5802
  mounted.current = false;
5812
5803
  };
5813
5804
  }, []);
5805
+ const visibleProviderContextItems = composerContext?.contextItems ?? [];
5814
5806
  const providerContextKeys = new Set(
5815
- composerContext?.contextItems.map((item) => item.key),
5807
+ visibleProviderContextItems.map((item) => item.key),
5816
5808
  );
5817
- const visibleContextItems = composerContext
5818
- ? [...contextItems, ...composerContext.contextItems]
5819
- : contextItems;
5809
+ const visibleContextItems = [...contextItems, ...visibleProviderContextItems];
5820
5810
  const submissionScope = JSON.stringify([
5821
5811
  threadId,
5822
5812
  props.tabId,
@@ -6273,7 +6263,9 @@ function AgentKitComposerSurface({
6273
6263
  props.availableModels === undefined
6274
6264
  }
6275
6265
  attachmentsEnabled={fileStorageConfigured}
6276
- onAttachmentRequest={requestFileStorage}
6266
+ {...(!fileStorageConfigured
6267
+ ? { onAttachmentRequest: requestFileStorage }
6268
+ : {})}
6277
6269
  contextButtonTooltipDisabled={fileStoragePromptOpen}
6278
6270
  onAttachmentError={setComposerError}
6279
6271
  onSubmit={submitComposer}
@@ -5345,6 +5345,27 @@ describe("MultiTabAssistantChat postMessage bridge", () => {
5345
5345
  );
5346
5346
  });
5347
5347
 
5348
+ it("forwards serialized workspace integration references through the app handoff", () => {
5349
+ const references = [
5350
+ {
5351
+ type: "mention" as const,
5352
+ path: "github",
5353
+ name: "GitHub",
5354
+ source: "workspace-integration",
5355
+ refType: "integration",
5356
+ refId: "github",
5357
+ },
5358
+ ];
5359
+ act(() => {
5360
+ dispatchSubmitChat({ message: "Use this integration", references });
5361
+ });
5362
+ expect(chatHandleMocks.sendMessage).toHaveBeenCalledWith(
5363
+ "Use this integration",
5364
+ undefined,
5365
+ { references },
5366
+ );
5367
+ });
5368
+
5348
5369
  it("forwards approval keys as a hidden protocol continuation", () => {
5349
5370
  act(() => {
5350
5371
  dispatchSubmitChat({
@@ -139,6 +139,7 @@ interface PendingSend {
139
139
  actionScope?: AgentActionScope;
140
140
  /** See `AgentChatMessage.approvedToolCalls`. */
141
141
  approvedToolCalls?: string[];
142
+ references?: AgentChatMessage["references"];
142
143
  /** See `AssistantChatSendOptions.surface`. */
143
144
  surface?: string;
144
145
  }
@@ -270,6 +271,7 @@ function deliverPendingSend(
270
271
  ...(send.trackInRunsTray ? { trackInRunsTray: true } : {}),
271
272
  ...(send.requestMode ? { requestMode: send.requestMode } : {}),
272
273
  ...(send.attachments ? { attachments: send.attachments } : {}),
274
+ ...(send.references ? { references: send.references } : {}),
273
275
  ...(send.submitMessageId ? { submitMessageId: send.submitMessageId } : {}),
274
276
  ...(send.submissionExpiresAt !== undefined
275
277
  ? { submissionExpiresAt: send.submissionExpiresAt }
@@ -2554,6 +2556,7 @@ export function MultiTabAssistantChat({
2554
2556
  submit,
2555
2557
  images,
2556
2558
  attachments,
2559
+ references,
2557
2560
  submitMessageId,
2558
2561
  usageLabel,
2559
2562
  actionScope,
@@ -2608,6 +2611,7 @@ export function MultiTabAssistantChat({
2608
2611
  ...(prefillContext ? { prefillContext } : {}),
2609
2612
  images,
2610
2613
  attachments,
2614
+ references,
2611
2615
  submit,
2612
2616
  ...(background ? { trackInRunsTray: true } : {}),
2613
2617
  ...(requestMode ? { requestMode } : {}),