@agent-native/toolkit 0.216.0 → 0.218.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 (261) hide show
  1. package/agent-native.eject.json +26 -0
  2. package/dist/app/agent-page/AgentContextTab.d.ts.map +1 -1
  3. package/dist/app/agent-page/AgentContextTab.js +1 -0
  4. package/dist/app/agent-page/AgentContextTab.js.map +1 -1
  5. package/dist/app/agentkit/react/components.d.ts.map +1 -1
  6. package/dist/app/agentkit/react/components.js +1 -1
  7. package/dist/app/agentkit/react/components.js.map +1 -1
  8. package/dist/app/agentkit/react/context.d.ts +4 -1
  9. package/dist/app/agentkit/react/context.d.ts.map +1 -1
  10. package/dist/app/agentkit/react/context.js +3 -1
  11. package/dist/app/agentkit/react/context.js.map +1 -1
  12. package/dist/app/agentkit/react/data-chart-layout.d.ts +30 -0
  13. package/dist/app/agentkit/react/data-chart-layout.d.ts.map +1 -0
  14. package/dist/app/agentkit/react/data-chart-layout.js +2 -0
  15. package/dist/app/agentkit/react/data-chart-layout.js.map +1 -0
  16. package/dist/app/agentkit/react/index.d.ts +1 -0
  17. package/dist/app/agentkit/react/index.d.ts.map +1 -1
  18. package/dist/app/agentkit/react/index.js +1 -0
  19. package/dist/app/agentkit/react/index.js.map +1 -1
  20. package/dist/app/agentkit/react/root.d.ts +3 -1
  21. package/dist/app/agentkit/react/root.d.ts.map +1 -1
  22. package/dist/app/agentkit/react/root.js +2 -2
  23. package/dist/app/agentkit/react/root.js.map +1 -1
  24. package/dist/app/agentkit/react/styles.css +18 -0
  25. package/dist/app/agentkit/react/transcript-layout.d.ts +2 -0
  26. package/dist/app/agentkit/react/transcript-layout.d.ts.map +1 -1
  27. package/dist/app/agentkit/react/transcript-layout.js.map +1 -1
  28. package/dist/app/auth/AuthPage.d.ts.map +1 -1
  29. package/dist/app/auth/AuthPage.js +33 -44
  30. package/dist/app/auth/AuthPage.js.map +1 -1
  31. package/dist/app/chat/AgentKitAssistantChat.d.ts.map +1 -1
  32. package/dist/app/chat/AgentKitAssistantChat.js +2 -2
  33. package/dist/app/chat/AgentKitAssistantChat.js.map +1 -1
  34. package/dist/app/chat/MultiTabAssistantChat.d.ts.map +1 -1
  35. package/dist/app/chat/MultiTabAssistantChat.js +51 -93
  36. package/dist/app/chat/MultiTabAssistantChat.js.map +1 -1
  37. package/dist/app/chat/agentkit-chat/action-widget.d.ts.map +1 -1
  38. package/dist/app/chat/agentkit-chat/action-widget.js +3 -1
  39. package/dist/app/chat/agentkit-chat/action-widget.js.map +1 -1
  40. package/dist/app/chat/agentkit-chat/rail.d.ts +1 -1
  41. package/dist/app/chat/agentkit-chat/rail.d.ts.map +1 -1
  42. package/dist/app/chat/agentkit-chat/rail.js +1 -1
  43. package/dist/app/chat/agentkit-chat/rail.js.map +1 -1
  44. package/dist/app/chat/chat/index.d.ts +2 -1
  45. package/dist/app/chat/chat/index.d.ts.map +1 -1
  46. package/dist/app/chat/chat/index.js +1 -1
  47. package/dist/app/chat/chat/index.js.map +1 -1
  48. package/dist/app/chat/chat/surface-types.d.ts +3 -0
  49. package/dist/app/chat/chat/surface-types.d.ts.map +1 -1
  50. package/dist/app/chat/chat/surface-types.js.map +1 -1
  51. package/dist/app/chat/chat/tool-call-display.d.ts +3 -1
  52. package/dist/app/chat/chat/tool-call-display.d.ts.map +1 -1
  53. package/dist/app/chat/chat/tool-call-display.js +4 -3
  54. package/dist/app/chat/chat/tool-call-display.js.map +1 -1
  55. package/dist/app/chat/chat/tool-render-registry.d.ts +2 -0
  56. package/dist/app/chat/chat/tool-render-registry.d.ts.map +1 -1
  57. package/dist/app/chat/chat/tool-render-registry.js.map +1 -1
  58. package/dist/app/chat/chat/widgets/DataChartRenderer.d.ts +3 -1
  59. package/dist/app/chat/chat/widgets/DataChartRenderer.d.ts.map +1 -1
  60. package/dist/app/chat/chat/widgets/DataChartRenderer.js +116 -26
  61. package/dist/app/chat/chat/widgets/DataChartRenderer.js.map +1 -1
  62. package/dist/app/chat/chat/widgets/DataChartWidget.d.ts +3 -1
  63. package/dist/app/chat/chat/widgets/DataChartWidget.d.ts.map +1 -1
  64. package/dist/app/chat/chat/widgets/DataChartWidget.js +21 -2
  65. package/dist/app/chat/chat/widgets/DataChartWidget.js.map +1 -1
  66. package/dist/app/chat/chat/widgets/DataInsightsWidget.d.ts +3 -1
  67. package/dist/app/chat/chat/widgets/DataInsightsWidget.d.ts.map +1 -1
  68. package/dist/app/chat/chat/widgets/DataInsightsWidget.js +2 -2
  69. package/dist/app/chat/chat/widgets/DataInsightsWidget.js.map +1 -1
  70. package/dist/app/chat/chat/widgets/DataWidgetDisplayControls.d.ts +1 -1
  71. package/dist/app/chat/chat/widgets/DataWidgetDisplayControls.d.ts.map +1 -1
  72. package/dist/app/chat/chat/widgets/DataWidgetDisplayControls.js +3 -1
  73. package/dist/app/chat/chat/widgets/DataWidgetDisplayControls.js.map +1 -1
  74. package/dist/app/chat/chat/widgets/builtin-tool-renderers.d.ts.map +1 -1
  75. package/dist/app/chat/chat/widgets/builtin-tool-renderers.js +2 -2
  76. package/dist/app/chat/chat/widgets/builtin-tool-renderers.js.map +1 -1
  77. package/dist/app/chat/chat-first/chat-history.d.ts +13 -2
  78. package/dist/app/chat/chat-first/chat-history.d.ts.map +1 -1
  79. package/dist/app/chat/chat-first/chat-history.js +24 -3
  80. package/dist/app/chat/chat-first/chat-history.js.map +1 -1
  81. package/dist/app/chat/ownership-history/ChatOwnerAvatar.d.ts +6 -0
  82. package/dist/app/chat/ownership-history/ChatOwnerAvatar.d.ts.map +1 -0
  83. package/dist/app/chat/ownership-history/ChatOwnerAvatar.js +9 -0
  84. package/dist/app/chat/ownership-history/ChatOwnerAvatar.js.map +1 -0
  85. package/dist/app/chat/ownership-history/index.d.ts +11 -0
  86. package/dist/app/chat/ownership-history/index.d.ts.map +1 -0
  87. package/dist/app/chat/ownership-history/index.js +22 -0
  88. package/dist/app/chat/ownership-history/index.js.map +1 -0
  89. package/dist/app/chat/ownership-history/rows.d.ts +27 -0
  90. package/dist/app/chat/ownership-history/rows.d.ts.map +1 -0
  91. package/dist/app/chat/ownership-history/rows.js +101 -0
  92. package/dist/app/chat/ownership-history/rows.js.map +1 -0
  93. package/dist/app/chat/ownership-history/useOwnershipChatHistory.d.ts +52 -0
  94. package/dist/app/chat/ownership-history/useOwnershipChatHistory.d.ts.map +1 -0
  95. package/dist/app/chat/ownership-history/useOwnershipChatHistory.js +497 -0
  96. package/dist/app/chat/ownership-history/useOwnershipChatHistory.js.map +1 -0
  97. package/dist/app/i18n/catalogs/ar-SA.d.ts.map +1 -1
  98. package/dist/app/i18n/catalogs/ar-SA.js +3 -0
  99. package/dist/app/i18n/catalogs/ar-SA.js.map +1 -1
  100. package/dist/app/i18n/catalogs/de-DE.d.ts.map +1 -1
  101. package/dist/app/i18n/catalogs/de-DE.js +3 -0
  102. package/dist/app/i18n/catalogs/de-DE.js.map +1 -1
  103. package/dist/app/i18n/catalogs/en-US.d.ts +3 -0
  104. package/dist/app/i18n/catalogs/en-US.d.ts.map +1 -1
  105. package/dist/app/i18n/catalogs/en-US.js +3 -0
  106. package/dist/app/i18n/catalogs/en-US.js.map +1 -1
  107. package/dist/app/i18n/catalogs/es-ES.d.ts.map +1 -1
  108. package/dist/app/i18n/catalogs/es-ES.js +3 -0
  109. package/dist/app/i18n/catalogs/es-ES.js.map +1 -1
  110. package/dist/app/i18n/catalogs/fr-FR.d.ts.map +1 -1
  111. package/dist/app/i18n/catalogs/fr-FR.js +3 -0
  112. package/dist/app/i18n/catalogs/fr-FR.js.map +1 -1
  113. package/dist/app/i18n/catalogs/hi-IN.d.ts.map +1 -1
  114. package/dist/app/i18n/catalogs/hi-IN.js +3 -0
  115. package/dist/app/i18n/catalogs/hi-IN.js.map +1 -1
  116. package/dist/app/i18n/catalogs/ja-JP.d.ts.map +1 -1
  117. package/dist/app/i18n/catalogs/ja-JP.js +3 -0
  118. package/dist/app/i18n/catalogs/ja-JP.js.map +1 -1
  119. package/dist/app/i18n/catalogs/ko-KR.d.ts.map +1 -1
  120. package/dist/app/i18n/catalogs/ko-KR.js +3 -0
  121. package/dist/app/i18n/catalogs/ko-KR.js.map +1 -1
  122. package/dist/app/i18n/catalogs/pt-BR.d.ts.map +1 -1
  123. package/dist/app/i18n/catalogs/pt-BR.js +3 -0
  124. package/dist/app/i18n/catalogs/pt-BR.js.map +1 -1
  125. package/dist/app/i18n/catalogs/zh-CN.d.ts.map +1 -1
  126. package/dist/app/i18n/catalogs/zh-CN.js +3 -0
  127. package/dist/app/i18n/catalogs/zh-CN.js.map +1 -1
  128. package/dist/app/i18n/catalogs/zh-TW.d.ts.map +1 -1
  129. package/dist/app/i18n/catalogs/zh-TW.js +3 -0
  130. package/dist/app/i18n/catalogs/zh-TW.js.map +1 -1
  131. package/dist/app/onboarding/GettingStartedCard.d.ts +35 -0
  132. package/dist/app/onboarding/GettingStartedCard.d.ts.map +1 -0
  133. package/dist/app/onboarding/GettingStartedCard.js +38 -0
  134. package/dist/app/onboarding/GettingStartedCard.js.map +1 -0
  135. package/dist/app/onboarding/index.d.ts +1 -0
  136. package/dist/app/onboarding/index.d.ts.map +1 -1
  137. package/dist/app/onboarding/index.js +1 -0
  138. package/dist/app/onboarding/index.js.map +1 -1
  139. package/dist/app/providers/AppProviders.d.ts.map +1 -1
  140. package/dist/app/providers/AppProviders.js +3 -1
  141. package/dist/app/providers/AppProviders.js.map +1 -1
  142. package/dist/app/settings/AccountSettingsCard.d.ts.map +1 -1
  143. package/dist/app/settings/AccountSettingsCard.js +2 -1
  144. package/dist/app/settings/AccountSettingsCard.js.map +1 -1
  145. package/dist/app/settings/index.d.ts +1 -0
  146. package/dist/app/settings/index.d.ts.map +1 -1
  147. package/dist/app/settings/index.js +1 -0
  148. package/dist/app/settings/index.js.map +1 -1
  149. package/dist/app/settings/shell/SettingsShell.d.ts +40 -1
  150. package/dist/app/settings/shell/SettingsShell.d.ts.map +1 -1
  151. package/dist/app/settings/shell/SettingsShell.js +87 -20
  152. package/dist/app/settings/shell/SettingsShell.js.map +1 -1
  153. package/dist/app/settings/shell/bridge.d.ts +5 -3
  154. package/dist/app/settings/shell/bridge.d.ts.map +1 -1
  155. package/dist/app/settings/shell/bridge.js +8 -5
  156. package/dist/app/settings/shell/bridge.js.map +1 -1
  157. package/dist/app/settings/shell/index.d.ts +2 -2
  158. package/dist/app/settings/shell/index.d.ts.map +1 -1
  159. package/dist/app/settings/shell/index.js.map +1 -1
  160. package/dist/app/settings/shell/registry.d.ts +9 -1
  161. package/dist/app/settings/shell/registry.d.ts.map +1 -1
  162. package/dist/app/settings/shell/registry.js +16 -0
  163. package/dist/app/settings/shell/registry.js.map +1 -1
  164. package/dist/app/settings/shell/routing.d.ts +5 -4
  165. package/dist/app/settings/shell/routing.d.ts.map +1 -1
  166. package/dist/app/settings/shell/routing.js +38 -10
  167. package/dist/app/settings/shell/routing.js.map +1 -1
  168. package/dist/app/shared/CommandMenu.d.ts +86 -6
  169. package/dist/app/shared/CommandMenu.d.ts.map +1 -1
  170. package/dist/app/shared/CommandMenu.js +303 -41
  171. package/dist/app/shared/CommandMenu.js.map +1 -1
  172. package/dist/app/shared/ConsentBanner.d.ts +9 -0
  173. package/dist/app/shared/ConsentBanner.d.ts.map +1 -0
  174. package/dist/app/shared/ConsentBanner.js +35 -0
  175. package/dist/app/shared/ConsentBanner.js.map +1 -0
  176. package/dist/app/sharing/ShareButton.d.ts.map +1 -1
  177. package/dist/app/sharing/ShareButton.js +9 -8
  178. package/dist/app/sharing/ShareButton.js.map +1 -1
  179. package/dist/chat-history/ChatHistoryList.d.ts +16 -3
  180. package/dist/chat-history/ChatHistoryList.d.ts.map +1 -1
  181. package/dist/chat-history/ChatHistoryList.js +25 -15
  182. package/dist/chat-history/ChatHistoryList.js.map +1 -1
  183. package/dist/chat-history.css +109 -9
  184. package/dist/ui/sonner.d.ts +2 -2
  185. package/dist/ui/sonner.d.ts.map +1 -1
  186. package/dist/ui/sonner.js +2 -2
  187. package/dist/ui/sonner.js.map +1 -1
  188. package/package.json +10 -3
  189. package/src/app/agent-page/AgentContextTab.spec.ts +50 -2
  190. package/src/app/agent-page/AgentContextTab.tsx +1 -0
  191. package/src/app/agentkit/react/components.interaction.spec.tsx +2 -0
  192. package/src/app/agentkit/react/components.spec.tsx +4 -1
  193. package/src/app/agentkit/react/components.tsx +3 -0
  194. package/src/app/agentkit/react/context.tsx +6 -0
  195. package/src/app/agentkit/react/data-chart-layout.ts +25 -0
  196. package/src/app/agentkit/react/index.ts +1 -0
  197. package/src/app/agentkit/react/root.tsx +4 -0
  198. package/src/app/agentkit/react/styles.css +18 -0
  199. package/src/app/agentkit/react/transcript-layout.ts +2 -0
  200. package/src/app/auth/AuthPage.interaction.spec.tsx +74 -0
  201. package/src/app/auth/AuthPage.tsx +32 -39
  202. package/src/app/chat/AgentKitAssistantChat.tsx +2 -0
  203. package/src/app/chat/MultiTabAssistantChat.spec.tsx +270 -3
  204. package/src/app/chat/MultiTabAssistantChat.tsx +79 -156
  205. package/src/app/chat/agentkit-chat/action-widget.tsx +3 -0
  206. package/src/app/chat/agentkit-chat/rail.ts +11 -0
  207. package/src/app/chat/chat/index.ts +10 -0
  208. package/src/app/chat/chat/surface-types.ts +3 -0
  209. package/src/app/chat/chat/tool-call-display.spec.tsx +29 -0
  210. package/src/app/chat/chat/tool-call-display.tsx +7 -0
  211. package/src/app/chat/chat/tool-render-registry.tsx +3 -0
  212. package/src/app/chat/chat/widgets/DataChartRenderer.spec.tsx +377 -17
  213. package/src/app/chat/chat/widgets/DataChartRenderer.tsx +232 -63
  214. package/src/app/chat/chat/widgets/DataChartWidget.spec.tsx +67 -0
  215. package/src/app/chat/chat/widgets/DataChartWidget.tsx +39 -5
  216. package/src/app/chat/chat/widgets/DataInsightsWidget.tsx +13 -2
  217. package/src/app/chat/chat/widgets/DataWidgetDisplayControls.tsx +6 -4
  218. package/src/app/chat/chat/widgets/builtin-tool-renderers.tsx +5 -1
  219. package/src/app/chat/chat-first/chat-history.spec.tsx +79 -0
  220. package/src/app/chat/chat-first/chat-history.tsx +75 -22
  221. package/src/app/chat/ownership-history/ChatOwnerAvatar.tsx +27 -0
  222. package/src/app/chat/ownership-history/index.tsx +67 -0
  223. package/src/app/chat/ownership-history/ownership-history.spec.tsx +3146 -0
  224. package/src/app/chat/ownership-history/rows.ts +156 -0
  225. package/src/app/chat/ownership-history/useOwnershipChatHistory.ts +709 -0
  226. package/src/app/i18n/catalogs/ar-SA.ts +3 -0
  227. package/src/app/i18n/catalogs/de-DE.ts +3 -0
  228. package/src/app/i18n/catalogs/en-US.ts +3 -0
  229. package/src/app/i18n/catalogs/es-ES.ts +3 -0
  230. package/src/app/i18n/catalogs/fr-FR.ts +3 -0
  231. package/src/app/i18n/catalogs/hi-IN.ts +3 -0
  232. package/src/app/i18n/catalogs/ja-JP.ts +3 -0
  233. package/src/app/i18n/catalogs/ko-KR.ts +3 -0
  234. package/src/app/i18n/catalogs/pt-BR.ts +3 -0
  235. package/src/app/i18n/catalogs/zh-CN.ts +3 -0
  236. package/src/app/i18n/catalogs/zh-TW.ts +3 -0
  237. package/src/app/onboarding/GettingStartedCard.tsx +203 -0
  238. package/src/app/onboarding/index.ts +1 -0
  239. package/src/app/providers/AppProviders.tsx +7 -1
  240. package/src/app/settings/AccountSettingsCard.spec.tsx +31 -0
  241. package/src/app/settings/AccountSettingsCard.tsx +8 -0
  242. package/src/app/settings/index.ts +1 -0
  243. package/src/app/settings/shell/SettingsShell.spec.tsx +472 -1
  244. package/src/app/settings/shell/SettingsShell.tsx +245 -31
  245. package/src/app/settings/shell/bridge.spec.ts +12 -0
  246. package/src/app/settings/shell/bridge.ts +17 -4
  247. package/src/app/settings/shell/index.ts +6 -1
  248. package/src/app/settings/shell/registry.ts +33 -1
  249. package/src/app/settings/shell/routing.spec.ts +92 -1
  250. package/src/app/settings/shell/routing.ts +52 -9
  251. package/src/app/shared/CommandMenu.spec.tsx +1662 -1
  252. package/src/app/shared/CommandMenu.tsx +763 -164
  253. package/src/app/shared/ConsentBanner.spec.tsx +141 -0
  254. package/src/app/shared/ConsentBanner.tsx +141 -0
  255. package/src/app/sharing/ShareButton.tsx +12 -10
  256. package/src/chat-history/ChatHistoryList.spec.tsx +51 -0
  257. package/src/chat-history/ChatHistoryList.tsx +87 -26
  258. package/src/chat-history/ChatHistoryRailSections.spec.tsx +12 -4
  259. package/src/chat-history.css +109 -9
  260. package/src/ui/sonner.spec.ts +10 -0
  261. package/src/ui/sonner.tsx +2 -2
@@ -2,6 +2,7 @@
2
2
 
3
3
  import {
4
4
  captureAttribution,
5
+ getAnalyticsAnonymousId,
5
6
  getAnalyticsSessionId,
6
7
  } from "@agent-native/core/client/analytics";
7
8
  import { frameworkRoutePrefix } from "@agent-native/core/client/api-path";
@@ -12,6 +13,7 @@ import {
12
13
  normalizeOAuthReturnPath,
13
14
  shouldRetryAuthSessionProbe,
14
15
  } from "@agent-native/core/client/auth/auth-page-helpers";
16
+ import { whenConsentDecided } from "@agent-native/core/client/consent";
15
17
  import { openOAuthPopup } from "@agent-native/core/client/oauth-popup";
16
18
  import { navigateForSession } from "@agent-native/core/client/use-session";
17
19
  import { normalizeLocaleCode } from "@agent-native/core/localization/shared";
@@ -65,7 +67,6 @@ type AuthRequestResult = {
65
67
  const EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
66
68
  const TAB_STORAGE_KEY = "an.onboarding.tab";
67
69
  const PENDING_SIGNUP_EMAIL_STORAGE_KEY = "an.onboarding.pendingSignupEmail";
68
- const ANALYTICS_ANONYMOUS_ID_KEY = "agent-native.anonymous_id";
69
70
  const GOOGLE_AUTH_URL_PATH = "/_agent-native/google/auth-url";
70
71
  const BUILDER_DESKTOP_RETURN_ORIGIN = "http://127.0.0.1:8080";
71
72
  const useIsomorphicLayoutEffect =
@@ -261,15 +262,6 @@ async function requestJson(
261
262
  return { response, data, readable };
262
263
  }
263
264
 
264
- function generateAnonymousId(): string {
265
- try {
266
- if (typeof crypto.randomUUID === "function") return crypto.randomUUID();
267
- } catch {
268
- // coercion-ok: the browser fallback remains usable when crypto is unavailable.
269
- }
270
- return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
271
- }
272
-
273
265
  function trackAuth(
274
266
  app: string,
275
267
  name: string,
@@ -298,9 +290,8 @@ function trackAuth(
298
290
  }
299
291
  ).__AGENT_NATIVE_CONFIG__;
300
292
  if (!config?.agentNativeAnalyticsPublicKey) return;
301
- const anonymousId = readStorage(ANALYTICS_ANONYMOUS_ID_KEY);
302
- if (!anonymousId) return;
303
293
  const sessionId = getAnalyticsSessionId();
294
+ const timestamp = new Date().toISOString();
304
295
  const endpoint = resolveLaneEndpoint(
305
296
  config.agentNativeAnalyticsEndpoint ??
306
297
  "https://analytics.agent-native.com/track",
@@ -313,23 +304,33 @@ function trackAuth(
313
304
  }> = [{ name, properties: legacyProperties }];
314
305
  const canonical = canonicalTrackingEvent(name, legacyProperties);
315
306
  if (canonical) events.push(canonical);
316
- for (const event of events) {
317
- const body = JSON.stringify({
318
- publicKey: config.agentNativeAnalyticsPublicKey,
319
- event: event.name,
320
- properties: event.properties,
321
- anonymousId,
322
- sessionId,
323
- timestamp: new Date().toISOString(),
324
- });
325
- if (navigator.sendBeacon?.(endpoint, body)) continue;
326
- void fetch(endpoint, {
327
- method: "POST",
328
- body,
329
- keepalive: true,
330
- headers: { "Content-Type": "text/plain;charset=UTF-8" },
331
- }).catch(() => undefined);
332
- }
307
+ const publicKey = config.agentNativeAnalyticsPublicKey;
308
+ // The visitor id waits for analytics consent, as in the app bundle.
309
+ whenConsentDecided((decided) => {
310
+ const anonymousId = decided ? getAnalyticsAnonymousId() : undefined;
311
+ for (const event of events) {
312
+ const body = JSON.stringify({
313
+ publicKey,
314
+ event: event.name,
315
+ properties: anonymousId
316
+ ? event.properties
317
+ : {
318
+ ...event.properties,
319
+ analytics_consent: decided ? "withheld" : "unknown",
320
+ },
321
+ anonymousId,
322
+ sessionId,
323
+ timestamp,
324
+ });
325
+ if (navigator.sendBeacon?.(endpoint, body)) continue;
326
+ void fetch(endpoint, {
327
+ method: "POST",
328
+ body,
329
+ keepalive: true,
330
+ headers: { "Content-Type": "text/plain;charset=UTF-8" },
331
+ }).catch(() => undefined);
332
+ }
333
+ });
333
334
  } catch {
334
335
  // coercion-ok: analytics is best effort and cannot block authentication.
335
336
  }
@@ -1036,16 +1037,8 @@ export function AuthPage(props: AuthPageProps) {
1036
1037
  }, [apiPath, redirectToSignedInApp, runtimeBasePathResolved, view]);
1037
1038
 
1038
1039
  React.useEffect(() => {
1039
- let anonymousId = readStorage(ANALYTICS_ANONYMOUS_ID_KEY);
1040
- if (!/^[A-Za-z0-9_-]{1,128}$/.test(anonymousId)) {
1041
- anonymousId = generateAnonymousId();
1042
- writeStorage(ANALYTICS_ANONYMOUS_ID_KEY, anonymousId);
1043
- }
1044
- try {
1045
- document.cookie = `an_aid=${encodeURIComponent(anonymousId)}; path=/; max-age=2592000; SameSite=Lax`;
1046
- } catch {
1047
- // coercion-ok: attribution cookies are optional for authentication.
1048
- }
1040
+ // Stores `an_aid` only with analytics consent, and asks for the region.
1041
+ getAnalyticsAnonymousId();
1049
1042
  // Capture is once per page load, so it must wait for a workspace's
1050
1043
  // inferred base path or `landing_path` falls back to the wrong home.
1051
1044
  if (runtimeBasePathResolved) {
@@ -2108,6 +2108,7 @@ export const AgentKitAssistantChat = forwardRef<
2108
2108
  loadRunUsage={loadRunUsage}
2109
2109
  buildFeedbackReport={formatFeedbackReport}
2110
2110
  workedForRowLayout={props.workedForRowLayout}
2111
+ dataChartLayout={props.dataChartLayout}
2111
2112
  formatWorkedForDuration={props.formatWorkedForDuration}
2112
2113
  onThreadForked={(thread) => props.onForkedThread?.(thread.id)}
2113
2114
  onCopyMessage={({ text }) => {
@@ -6474,6 +6475,7 @@ function AgentKitTool({ value, active }: AgentKitRenderProps<AgentToolCall>) {
6474
6475
  activity={metadata.activity === true}
6475
6476
  isActiveTail={active === true}
6476
6477
  workedForRowLayout={surface.props.workedForRowLayout}
6478
+ dataChartLayout={surface.props.dataChartLayout}
6477
6479
  formatWorkedForDuration={surface.props.formatWorkedForDuration}
6478
6480
  />
6479
6481
  </SuppressInlineOpenAppContext.Provider>
@@ -34,6 +34,7 @@ import React, { act } from "react";
34
34
  import { flushSync } from "react-dom";
35
35
  import { createRoot, type Root } from "react-dom/client";
36
36
  import { MemoryRouter } from "react-router";
37
+ import { toast } from "sonner";
37
38
  import {
38
39
  afterEach,
39
40
  beforeEach,
@@ -7874,6 +7875,8 @@ describe("MultiTabAssistantChat page overlay", () => {
7874
7875
  describe("MultiTabAssistantChat history popover", () => {
7875
7876
  let container: HTMLDivElement;
7876
7877
  let root: Root;
7878
+ let sharedHistoryRows: ChatThreadSummary[];
7879
+ let mineHistoryRows: ChatThreadSummary[] | undefined;
7877
7880
 
7878
7881
  beforeEach(() => {
7879
7882
  vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true);
@@ -7882,6 +7885,8 @@ describe("MultiTabAssistantChat history popover", () => {
7882
7885
  return 0;
7883
7886
  });
7884
7887
  resetThreadMocks();
7888
+ sharedHistoryRows = [];
7889
+ mineHistoryRows = undefined;
7885
7890
  const now = Date.now();
7886
7891
  threadMocks.activeThreadId = "thread-1";
7887
7892
  const historyThreads: ChatThreadSummary[] = [
@@ -7917,7 +7922,32 @@ describe("MultiTabAssistantChat history popover", () => {
7917
7922
  threadMocks.threads = historyThreads;
7918
7923
  vi.stubGlobal(
7919
7924
  "fetch",
7920
- vi.fn(async () => Response.json({ value: null })),
7925
+ vi.fn(async (input: RequestInfo | URL) => {
7926
+ const url = new URL(String(input), "http://test.local");
7927
+ const ownership = url.searchParams.get("ownership");
7928
+ if (ownership) {
7929
+ const source =
7930
+ ownership === "mine"
7931
+ ? (mineHistoryRows ?? threadMocks.threads).map((thread) => ({
7932
+ ...thread,
7933
+ ownerEmail: "me@example.com",
7934
+ accessBasis: "owner",
7935
+ visibility: "private",
7936
+ }))
7937
+ : sharedHistoryRows;
7938
+ const query = url.searchParams.get("q") ?? "";
7939
+ const matching = source.filter((row) =>
7940
+ row.title.toLowerCase().includes(query.toLowerCase()),
7941
+ );
7942
+ const offset = Number(url.searchParams.get("offset") ?? 0);
7943
+ const limit = Number(url.searchParams.get("limit") ?? 50);
7944
+ return Response.json({
7945
+ threads: matching.slice(offset, offset + limit),
7946
+ hasMore: matching.length > offset + limit,
7947
+ });
7948
+ }
7949
+ return Response.json({ value: null });
7950
+ }),
7921
7951
  );
7922
7952
  window.localStorage.clear();
7923
7953
  window.localStorage.setItem(
@@ -7947,7 +7977,7 @@ describe("MultiTabAssistantChat history popover", () => {
7947
7977
  'button[aria-label="All chats"]',
7948
7978
  );
7949
7979
  expect(historyButton).not.toBeNull();
7950
- act(() => {
7980
+ await act(async () => {
7951
7981
  historyButton!.click();
7952
7982
  });
7953
7983
  }
@@ -7965,13 +7995,224 @@ describe("MultiTabAssistantChat history popover", () => {
7965
7995
  });
7966
7996
  }
7967
7997
 
7998
+ it.each(["local", "owner"] as const)(
7999
+ "keeps the active proven-%s empty chat before persistence and hides inactive blanks",
8000
+ async (proof) => {
8001
+ mineHistoryRows = [];
8002
+ threadMocks.isNewThread.mockImplementation(() => proof === "local");
8003
+ threadMocks.threads = threadMocks.threads.slice(0, 2).map((thread) => ({
8004
+ ...thread,
8005
+ messageCount: 0,
8006
+ ...(proof === "owner"
8007
+ ? {
8008
+ accessBasis: "owner" as const,
8009
+ ownerEmail: "me@example.com",
8010
+ visibility: "private" as const,
8011
+ }
8012
+ : {}),
8013
+ }));
8014
+ await openHistory();
8015
+
8016
+ const mine = container.querySelector(
8017
+ '.an-chat-history__section[data-section-id="mine"]',
8018
+ )!;
8019
+ expect(
8020
+ Array.from(mine.querySelectorAll(".an-chat-history-row__title")).map(
8021
+ (element) => element.textContent,
8022
+ ),
8023
+ ).toEqual(["Active chat"]);
8024
+ expect(mine.querySelector(".an-chat-history-row--active")).not.toBeNull();
8025
+ expect(
8026
+ mine.querySelector(".an-chat-history-row__timestamp")?.textContent,
8027
+ ).toBe("Active");
8028
+ expect(threadMocks.saveThreadData).not.toHaveBeenCalled();
8029
+ },
8030
+ );
8031
+
8032
+ it.each([false, true])(
8033
+ "never recasts an active shared empty chat as Mine (server row loaded: %s)",
8034
+ async (loaded) => {
8035
+ mineHistoryRows = [];
8036
+ threadMocks.isNewThread.mockReturnValue(true);
8037
+ const shared: ChatThreadSummary = {
8038
+ ...threadMocks.threads[0],
8039
+ messageCount: 0,
8040
+ accessBasis: "direct",
8041
+ ownerEmail: "pat@example.com",
8042
+ visibility: "private",
8043
+ };
8044
+ threadMocks.threads = [shared];
8045
+ sharedHistoryRows = loaded ? [shared] : [];
8046
+ await openHistory();
8047
+
8048
+ expect(
8049
+ container.querySelector(
8050
+ '.an-chat-history__section[data-section-id="mine"] .an-chat-history-row',
8051
+ ),
8052
+ ).toBeNull();
8053
+ const sharedSection = container.querySelector(
8054
+ '.an-chat-history__section[data-section-id="shared"]',
8055
+ );
8056
+ expect(
8057
+ sharedSection?.querySelectorAll(".an-chat-history-row").length ?? 0,
8058
+ ).toBe(loaded ? 1 : 0);
8059
+ const trigger =
8060
+ sharedSection?.querySelector<HTMLButtonElement>(
8061
+ ".an-chat-history-row__menu-trigger",
8062
+ ) ?? null;
8063
+ if (!loaded) {
8064
+ expect(trigger).toBeNull();
8065
+ return;
8066
+ }
8067
+ await openRowMenu(trigger!);
8068
+ expect(
8069
+ Array.from(
8070
+ document.body.querySelectorAll(".an-chat-history-row__menu-item"),
8071
+ (element) => element.textContent,
8072
+ ),
8073
+ ).toEqual(["Copy link"]);
8074
+ },
8075
+ );
8076
+
8077
+ it("loads both ownership lists only while open and debounces search to both", async () => {
8078
+ await act(async () => {
8079
+ root.render(<MultiTabAssistantChat storageKey="history-test" />);
8080
+ });
8081
+ const fetchMock = vi.mocked(fetch);
8082
+ const ownershipRequests = () =>
8083
+ fetchMock.mock.calls
8084
+ .map(([input]) => new URL(String(input), "http://test.local"))
8085
+ .filter((url) => url.searchParams.has("ownership"));
8086
+ expect(ownershipRequests()).toHaveLength(0);
8087
+ await openHistory();
8088
+ expect(
8089
+ ownershipRequests().map((url) => url.searchParams.get("ownership")),
8090
+ ).toEqual(["mine", "shared"]);
8091
+ vi.useFakeTimers();
8092
+ try {
8093
+ const input = container.querySelector<HTMLInputElement>(
8094
+ ".an-chat-history__search-input",
8095
+ )!;
8096
+ act(() => {
8097
+ Object.getOwnPropertyDescriptor(
8098
+ window.HTMLInputElement.prototype,
8099
+ "value",
8100
+ )!.set!.call(input, "Other");
8101
+ input.dispatchEvent(new Event("input", { bubbles: true }));
8102
+ });
8103
+ expect(ownershipRequests()).toHaveLength(2);
8104
+ await act(async () => {
8105
+ await vi.advanceTimersByTimeAsync(250);
8106
+ });
8107
+ expect(
8108
+ ownershipRequests()
8109
+ .slice(-2)
8110
+ .map((url) => [
8111
+ url.searchParams.get("ownership"),
8112
+ url.searchParams.get("q"),
8113
+ ]),
8114
+ ).toEqual([
8115
+ ["mine", "Other"],
8116
+ ["shared", "Other"],
8117
+ ]);
8118
+ expect(threadMocks.searchThreads).not.toHaveBeenCalled();
8119
+ } finally {
8120
+ vi.useRealTimers();
8121
+ }
8122
+ });
8123
+
8124
+ it("previews five shared rows without owner pins or actions and switches through history", async () => {
8125
+ sharedHistoryRows = Array.from({ length: 51 }, (_, index) => ({
8126
+ id: `shared-${index}`,
8127
+ title: `Shared ${index}`,
8128
+ preview: "",
8129
+ messageCount: 1,
8130
+ createdAt: 1,
8131
+ updatedAt: 1,
8132
+ scope: null,
8133
+ accessBasis: index === 0 ? "organization" : "direct",
8134
+ ownerEmail: "pat@example.com",
8135
+ visibility: "org",
8136
+ pinnedAt: 10,
8137
+ }));
8138
+ await openHistory();
8139
+ const sharedSection = container.querySelector(
8140
+ '.an-chat-history__section[data-section-id="shared"]',
8141
+ )!;
8142
+ expect(sharedSection.querySelectorAll(".an-chat-history-row")).toHaveLength(
8143
+ 5,
8144
+ );
8145
+ expect(sharedSection.textContent).toContain("pat@example.com");
8146
+ expect(sharedSection.textContent).toContain(
8147
+ "Shared with your organization",
8148
+ );
8149
+ await openRowMenu(
8150
+ sharedSection.querySelector<HTMLButtonElement>(
8151
+ ".an-chat-history-row__menu-trigger",
8152
+ )!,
8153
+ );
8154
+ expect(
8155
+ Array.from(
8156
+ document.body.querySelectorAll(".an-chat-history-row__menu-item"),
8157
+ (element) => element.textContent,
8158
+ ),
8159
+ ).toEqual(["Copy link"]);
8160
+ await act(async () => {
8161
+ document.dispatchEvent(
8162
+ new KeyboardEvent("keydown", { key: "Escape", bubbles: true }),
8163
+ );
8164
+ });
8165
+ expect(
8166
+ sharedSection.querySelector(".an-chat-history-row--pinned"),
8167
+ ).toBeNull();
8168
+ expect(container.querySelector(".an-chat-history-rail__footer")).toBeNull();
8169
+ await act(async () => {
8170
+ sharedSection
8171
+ .querySelector<HTMLButtonElement>(
8172
+ ".an-chat-history-rail__section-toggle",
8173
+ )!
8174
+ .click();
8175
+ });
8176
+ expect(sharedSection.querySelectorAll(".an-chat-history-row")).toHaveLength(
8177
+ 50,
8178
+ );
8179
+ await act(async () => {
8180
+ sharedSection
8181
+ .querySelector<HTMLButtonElement>(".an-chat-history-rail__load-more")!
8182
+ .click();
8183
+ });
8184
+ expect(sharedSection.querySelectorAll(".an-chat-history-row")).toHaveLength(
8185
+ 51,
8186
+ );
8187
+ await act(async () => {
8188
+ sharedSection
8189
+ .querySelector<HTMLButtonElement>(
8190
+ ".an-chat-history-rail__section-toggle",
8191
+ )!
8192
+ .click();
8193
+ });
8194
+ expect(sharedSection.querySelectorAll(".an-chat-history-row")).toHaveLength(
8195
+ 5,
8196
+ );
8197
+ await act(async () => {
8198
+ sharedSection
8199
+ .querySelector<HTMLButtonElement>(".an-chat-history-row__button")!
8200
+ .click();
8201
+ });
8202
+ expect(threadMocks.switchThread).toHaveBeenCalledWith("shared-0");
8203
+ expect(container.querySelector(".an-chat-history")).toBeNull();
8204
+ expect(
8205
+ chatThreadHookMocks.useChatThreads.mock.calls.at(-1)?.[3]?.ownership,
8206
+ ).toBeUndefined();
8207
+ });
8208
+
7968
8209
  it("groups pinned threads into a dedicated section, sorted ahead of the rest", async () => {
7969
8210
  await openHistory();
7970
8211
 
7971
8212
  const labels = Array.from(
7972
8213
  container.querySelectorAll(".an-chat-history__section-label"),
7973
8214
  ).map((el) => el.textContent);
7974
- expect(labels).toEqual(["Pinned"]);
8215
+ expect(labels).toEqual(["Pinned", "My chats"]);
7975
8216
 
7976
8217
  const titles = Array.from(
7977
8218
  container.querySelectorAll(".an-chat-history-row__title"),
@@ -8055,6 +8296,32 @@ describe("MultiTabAssistantChat history popover", () => {
8055
8296
  expect(threadMocks.pinThread).toHaveBeenCalledWith("thread-3", true);
8056
8297
  });
8057
8298
 
8299
+ it("shows the localized save failure when a history pin fails", async () => {
8300
+ const toastError = vi
8301
+ .spyOn(toast, "error")
8302
+ .mockReturnValue("failure-toast");
8303
+ threadMocks.pinThread.mockResolvedValue(false);
8304
+ try {
8305
+ await openHistory();
8306
+ const row = container.querySelectorAll(".an-chat-history-row")[2];
8307
+ await openRowMenu(
8308
+ row.querySelector<HTMLButtonElement>(
8309
+ ".an-chat-history-row__menu-trigger",
8310
+ )!,
8311
+ );
8312
+ const pinItem = Array.from(
8313
+ document.body.querySelectorAll<HTMLButtonElement>(
8314
+ ".an-chat-history-row__menu-item",
8315
+ ),
8316
+ ).find((element) => element.textContent?.includes("Pin to top"))!;
8317
+ await act(async () => pinItem.click());
8318
+ expect(threadMocks.pinThread).toHaveBeenCalledWith("thread-3", true);
8319
+ expect(toastError).toHaveBeenCalledExactlyOnceWith("Save failed");
8320
+ } finally {
8321
+ toastError.mockRestore();
8322
+ }
8323
+ });
8324
+
8058
8325
  it("unpins an already-pinned thread via the row action menu", async () => {
8059
8326
  await openHistory();
8060
8327