@agent-native/core 0.184.0 → 0.184.1-nightly-20260922203115

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 (56) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/changelog/2026-09-22-apps-start-with-an-app-shaped-skeleton-while-session-data-lo.md +6 -0
  3. package/corpus/templates/calendar/changelog/2026-09-22-apps-start-with-an-app-shaped-skeleton-while-session-data-lo.md +6 -0
  4. package/corpus/templates/clips/changelog/2026-09-22-apps-start-with-an-app-shaped-skeleton-while-session-data-lo.md +6 -0
  5. package/corpus/templates/content/AGENTS.md +1 -1
  6. package/corpus/templates/content/actions/_document-search-ranking.ts +191 -0
  7. package/corpus/templates/content/actions/search-documents.ts +184 -49
  8. package/corpus/templates/content/changelog/2026-09-21-search-ranks-title-matches-first.md +6 -0
  9. package/corpus/templates/content/docs/product/capabilities/content.knowledge.search.md +11 -8
  10. package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +10 -3
  11. package/corpus/templates/design/.generated/bridge/nav.generated.ts +1 -0
  12. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +67 -6
  13. package/corpus/templates/design/app/components/design/bridge/nav.bridge.ts +16 -0
  14. package/corpus/templates/design/app/components/design/multi-screen/board-surface-html.ts +26 -0
  15. package/corpus/templates/design/changelog/2026-09-22-apps-start-with-an-app-shaped-skeleton-while-session-data-lo.md +6 -0
  16. package/corpus/templates/design/changelog/2026-09-22-blocks-can-be-selected-with-one-click-inside-screens.md +6 -0
  17. package/corpus/templates/mail/app/components/layout/AppLayout.tsx +136 -123
  18. package/corpus/templates/mail/changelog/2026-09-22-apps-start-with-an-app-shaped-skeleton-while-session-data-lo.md +6 -0
  19. package/corpus/templates/slides/changelog/2026-09-22-apps-start-with-an-app-shaped-skeleton-while-session-data-lo.md +6 -0
  20. package/dist/client/AppShellSkeleton.d.ts +5 -0
  21. package/dist/client/AppShellSkeleton.js +69 -0
  22. package/dist/client/ClientOnly.d.ts +1 -1
  23. package/dist/client/ClientOnly.js +1 -1
  24. package/dist/client/LanguagePicker.d.ts +6 -0
  25. package/dist/client/LanguagePicker.js +128 -0
  26. package/dist/client/analytics.d.ts +3 -2
  27. package/dist/client/analytics.js +1 -1
  28. package/dist/client/app-providers.d.ts +3 -3
  29. package/dist/client/app-providers.js +53 -23
  30. package/dist/client/client-status-requests.d.ts +5 -0
  31. package/dist/client/client-status-requests.js +31 -18
  32. package/dist/client/i18n.d.ts +1 -6
  33. package/dist/client/i18n.js +3 -117
  34. package/dist/client/onboarding/first-run-startup-gate.js +14 -3
  35. package/dist/client/require-session.d.ts +1 -1
  36. package/dist/client/require-session.js +3 -3
  37. package/dist/client/session-replay-context.d.ts +16 -0
  38. package/dist/client/session-replay-context.js +93 -0
  39. package/dist/client/session-replay.d.ts +2 -26
  40. package/dist/client/session-replay.js +1 -64
  41. package/dist/collab/awareness.d.ts +2 -2
  42. package/dist/collab/routes.d.ts +1 -1
  43. package/dist/collab/struct-routes.d.ts +1 -1
  44. package/dist/deploy/build.js +19 -29
  45. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  46. package/dist/observability/routes.d.ts +1 -1
  47. package/dist/progress/routes.d.ts +1 -1
  48. package/dist/resources/handlers.d.ts +1 -1
  49. package/dist/secrets/routes.d.ts +6 -6
  50. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  51. package/dist/server/core-routes-plugin.d.ts +1 -1
  52. package/dist/shared/ssr-session-bootstrap.d.ts +3 -0
  53. package/dist/shared/ssr-session-bootstrap.js +44 -0
  54. package/dist/sharing/actions/list-resource-shares.d.ts +1 -1
  55. package/dist/triggers/actions/manage-automation.d.ts +2 -2
  56. package/package.json +3 -3
@@ -0,0 +1,128 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Picker, useDesignSystemComponent, } from "@agent-native/toolkit/design-system";
3
+ import * as PopoverPrimitive from "@radix-ui/react-popover";
4
+ import { IconCheck, IconChevronDown, IconLanguage } from "@tabler/icons-react";
5
+ import { useState } from "react";
6
+ import { DEFAULT_LOCALE, localeDisplayName, normalizeLocalizationPreference, resolveLocaleFromCandidates, } from "../localization/shared.js";
7
+ import { useLocale } from "./i18n.js";
8
+ import { cn } from "./utils.js";
9
+ const LANGUAGE_PICKER_COPY = {
10
+ "en-US": {
11
+ label: "Language",
12
+ system: "System",
13
+ systemDescription: "Use your browser language",
14
+ },
15
+ "zh-CN": {
16
+ label: "语言",
17
+ system: "系统",
18
+ systemDescription: "使用浏览器语言",
19
+ },
20
+ "zh-TW": {
21
+ label: "語言",
22
+ system: "系統",
23
+ systemDescription: "使用瀏覽器語言",
24
+ },
25
+ "es-ES": {
26
+ label: "Idioma",
27
+ system: "Sistema",
28
+ systemDescription: "Usar el idioma del navegador",
29
+ },
30
+ "fr-FR": {
31
+ label: "Langue",
32
+ system: "Système",
33
+ systemDescription: "Utiliser la langue du navigateur",
34
+ },
35
+ "de-DE": {
36
+ label: "Sprache",
37
+ system: "System",
38
+ systemDescription: "Browsersprache verwenden",
39
+ },
40
+ "ja-JP": {
41
+ label: "言語",
42
+ system: "システム",
43
+ systemDescription: "ブラウザーの言語を使用",
44
+ },
45
+ "ko-KR": {
46
+ label: "언어",
47
+ system: "시스템",
48
+ systemDescription: "브라우저 언어 사용",
49
+ },
50
+ "pt-BR": {
51
+ label: "Idioma",
52
+ system: "Sistema",
53
+ systemDescription: "Usar o idioma do navegador",
54
+ },
55
+ "hi-IN": {
56
+ label: "भाषा",
57
+ system: "सिस्टम",
58
+ systemDescription: "ब्राउज़र की भाषा का उपयोग करें",
59
+ },
60
+ "ar-SA": {
61
+ label: "اللغة",
62
+ system: "النظام",
63
+ systemDescription: "استخدام لغة المتصفح",
64
+ },
65
+ };
66
+ function browserLanguageCandidates() {
67
+ if (typeof navigator === "undefined")
68
+ return [];
69
+ return navigator.languages?.length
70
+ ? [...navigator.languages]
71
+ : navigator.language
72
+ ? [navigator.language]
73
+ : [];
74
+ }
75
+ export function LanguagePicker({ className, includeSystem = true, label, variant = "select", }) {
76
+ const { locale, preference, setPreference, supportedLocales, localeMetadata, } = useLocale();
77
+ const [open, setOpen] = useState(false);
78
+ const copy = LANGUAGE_PICKER_COPY[locale] ?? LANGUAGE_PICKER_COPY[DEFAULT_LOCALE];
79
+ const systemCopy = LANGUAGE_PICKER_COPY[resolveLocaleFromCandidates(browserLanguageCandidates())] ?? LANGUAGE_PICKER_COPY[DEFAULT_LOCALE];
80
+ const resolvedLabel = label ?? copy.label;
81
+ const options = [
82
+ ...(includeSystem
83
+ ? [
84
+ {
85
+ value: "system",
86
+ label: systemCopy.system,
87
+ description: systemCopy.systemDescription,
88
+ },
89
+ ]
90
+ : []),
91
+ ...supportedLocales.map((code) => ({
92
+ value: code,
93
+ label: localeDisplayName(code, localeMetadata),
94
+ description: code,
95
+ })),
96
+ ];
97
+ const selected = options.find((option) => option.value === preference);
98
+ const selectedLabel = selected?.label ?? preference;
99
+ const triggerLabel = `${resolvedLabel}: ${selectedLabel}`;
100
+ const customPicker = useDesignSystemComponent("Picker");
101
+ function handleOptionClick(value) {
102
+ setOpen(false);
103
+ void setPreference(normalizeLocalizationPreference(value).locale);
104
+ }
105
+ if (variant === "select" && customPicker) {
106
+ return (_jsx(Picker, { mode: "select", options: options, value: preference, onChange: (value) => {
107
+ if (value == null)
108
+ return;
109
+ void setPreference(normalizeLocalizationPreference(value).locale);
110
+ }, placeholder: selectedLabel, "aria-label": triggerLabel, className: className }));
111
+ }
112
+ return (_jsx("div", { className: className, children: _jsxs(PopoverPrimitive.Root, { open: open, onOpenChange: setOpen, children: [_jsx(PopoverPrimitive.Trigger, { asChild: true, children: _jsxs("button", { type: "button", "aria-label": triggerLabel, title: triggerLabel, "data-language-picker-trigger": true, className: cn("shrink-0 rounded-md outline-none transition-colors focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", variant === "ghost-icon"
113
+ ? "flex h-9 w-9 items-center justify-center text-muted-foreground hover:bg-accent hover:text-foreground data-[state=open]:bg-accent data-[state=open]:text-foreground"
114
+ : "border border-border bg-background text-foreground hover:border-foreground/30 hover:bg-accent/40 hover:text-foreground data-[state=open]:border-foreground/30 data-[state=open]:bg-accent/40", variant === "icon"
115
+ ? "flex h-8 w-8 items-center justify-center"
116
+ : variant === "select"
117
+ ? "flex h-9 w-full items-center justify-between gap-2 px-3 text-start text-sm"
118
+ : null), children: [_jsxs("span", { className: "flex min-w-0 items-center gap-2", children: [_jsx(IconLanguage, { className: "h-4 w-4 shrink-0 text-muted-foreground" }), variant === "select" ? (_jsx("span", { className: "truncate", children: selectedLabel })) : (_jsx("span", { className: "sr-only", children: triggerLabel }))] }), variant === "select" ? (_jsx(IconChevronDown, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground", "aria-hidden": "true" })) : null] }) }), _jsx(PopoverPrimitive.Portal, { children: _jsx(PopoverPrimitive.Content, { align: variant === "select" ? "start" : "end", sideOffset: 6, role: "menu", className: cn(
119
+ // compositing-ok: popover content unmounts on close.
120
+ "z-[9999] max-h-[min(20rem,var(--radix-popover-content-available-height))] overflow-y-auto rounded-lg border border-border bg-popover p-1 text-popover-foreground shadow-lg outline-none will-change-[transform,opacity] data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:duration-100 data-[state=open]:duration-150 data-[state=closed]:ease-in data-[state=open]:ease-out data-[side=bottom]:slide-in-from-top-1 data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1 data-[side=top]:slide-in-from-bottom-1", variant === "icon" || variant === "ghost-icon"
121
+ ? "min-w-56"
122
+ : "w-[min(20rem,calc(100vw-2rem))] min-w-[var(--radix-popover-trigger-width)]"), children: options.map((option) => {
123
+ const optionSelected = option.value === preference;
124
+ return (_jsxs("button", { type: "button", role: "menuitemradio", "aria-checked": optionSelected, title: option.description, onClick: () => handleOptionClick(option.value), className: cn("flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-start text-sm outline-none transition-colors hover:bg-accent/60 hover:text-foreground focus-visible:bg-accent/60 focus-visible:text-foreground", optionSelected
125
+ ? "bg-accent/40 text-foreground"
126
+ : "text-muted-foreground"), children: [_jsx(IconCheck, { className: cn("h-3.5 w-3.5 shrink-0", optionSelected ? "opacity-100" : "opacity-0"), "aria-hidden": "true" }), _jsx("span", { className: "truncate", children: option.label })] }, option.value));
127
+ }) }) })] }) }));
128
+ }
@@ -4,8 +4,9 @@ export { clearAnalyticsSessionId, setAnalyticsSessionId, } from "./analytics-ses
4
4
  import type { SessionReplayOptions, SessionReplayStartResult } from "./session-replay.js";
5
5
  export { scrubUrl } from "./url-scrub.js";
6
6
  export { addErrorBreadcrumb, captureException, captureMessage, isErrorCaptureInstalled, type CaptureExceptionContext, type CapturedExceptionEvent, type ExceptionBreadcrumb, type ExceptionLevel, } from "./error-capture.js";
7
- export type { SessionReplayConsoleOptions, SessionReplayNetworkOptions, SessionReplayOptions, SessionReplayStartResult, SessionReplayContext, SessionReplayLinkOptions, SessionReplayUrlMatcher, } from "./session-replay.js";
8
- export { getSessionReplayContext, getSessionReplayUrl, } from "./session-replay.js";
7
+ export type { SessionReplayConsoleOptions, SessionReplayNetworkOptions, SessionReplayOptions, SessionReplayStartResult, SessionReplayUrlMatcher, } from "./session-replay.js";
8
+ export { getSessionReplayContext, getSessionReplayUrl, } from "./session-replay-context.js";
9
+ export type { SessionReplayContext, SessionReplayLinkOptions, } from "./session-replay-context.js";
9
10
  declare global {
10
11
  interface Window {
11
12
  gtag?: (...args: any[]) => void;
@@ -17,7 +17,7 @@ import { isDynamicImportFailureMessage } from "./route-chunk-recovery.js";
17
17
  import { scrubUrl } from "./url-scrub.js";
18
18
  export { scrubUrl } from "./url-scrub.js";
19
19
  export { addErrorBreadcrumb, captureException, captureMessage, isErrorCaptureInstalled, } from "./error-capture.js";
20
- export { getSessionReplayContext, getSessionReplayUrl, } from "./session-replay.js";
20
+ export { getSessionReplayContext, getSessionReplayUrl, } from "./session-replay-context.js";
21
21
  let _getDefaultProps = null;
22
22
  let _configuredAnalyticsClientPlatform = null;
23
23
  let _agentNativeAnalyticsPublicKey = null;
@@ -17,7 +17,7 @@
17
17
  * <AppProviders
18
18
  * queryClient={queryClient}
19
19
  * isPublicPath={isPublicBookingPath(location.pathname)}
20
- * clientOnlyFallback={<DefaultSpinner />}
20
+ * clientOnlyFallback={<AppShellSkeleton />}
21
21
  * >
22
22
  * ...
23
23
  * </AppProviders>
@@ -27,7 +27,7 @@
27
27
  * default), `<ClientOnly>` hydrates the shared SSR shell and
28
28
  * `<RequireSession>` redirects signed-out visitors to the framework sign-in
29
29
  * page before private app chrome mounts. When `clientOnlyFallback` is
30
- * omitted, `<DefaultSpinner />` is used.
30
+ * omitted, `<AppShellSkeleton />` is used.
31
31
  *
32
32
  * Customisation props:
33
33
  * themeAttribute — passed to next-themes ThemeProvider `attribute`.
@@ -116,7 +116,7 @@ export interface AppProvidersProps {
116
116
  isPublicPath?: boolean;
117
117
  /**
118
118
  * Fallback rendered by `<ClientOnly>` while JS hydrates on private paths.
119
- * Defaults to `<DefaultSpinner />`.
119
+ * Defaults to `<AppShellSkeleton />`.
120
120
  */
121
121
  clientOnlyFallback?: React.ReactNode;
122
122
  /**
@@ -18,7 +18,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
18
18
  * <AppProviders
19
19
  * queryClient={queryClient}
20
20
  * isPublicPath={isPublicBookingPath(location.pathname)}
21
- * clientOnlyFallback={<DefaultSpinner />}
21
+ * clientOnlyFallback={<AppShellSkeleton />}
22
22
  * >
23
23
  * ...
24
24
  * </AppProviders>
@@ -28,7 +28,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
28
28
  * default), `<ClientOnly>` hydrates the shared SSR shell and
29
29
  * `<RequireSession>` redirects signed-out visitors to the framework sign-in
30
30
  * page before private app chrome mounts. When `clientOnlyFallback` is
31
- * omitted, `<DefaultSpinner />` is used.
31
+ * omitted, `<AppShellSkeleton />` is used.
32
32
  *
33
33
  * Customisation props:
34
34
  * themeAttribute — passed to next-themes ThemeProvider `attribute`.
@@ -61,9 +61,10 @@ import { useEffect, useRef } from "react";
61
61
  import { useInRouterContext } from "react-router";
62
62
  import { isHumanReadableDocumentTitle, normalizeDocumentTitle, } from "../shared/document-title.js";
63
63
  import { getSsrBetaRedirectScriptBody } from "../shared/ssr-beta-redirect.js";
64
+ import { getSsrSessionBootstrapScriptBody } from "../shared/ssr-session-bootstrap.js";
64
65
  import { agentNativePath, frameworkRoutePrefix } from "./api-path.js";
66
+ import { AppShellSkeleton } from "./AppShellSkeleton.js";
65
67
  import { ClientOnly } from "./ClientOnly.js";
66
- import { DefaultSpinner } from "./DefaultSpinner.js";
67
68
  import { EnvironmentBadge } from "./EnvironmentBadge.js";
68
69
  import { AgentNativeI18nProvider, } from "./i18n.js";
69
70
  import { FirstRunOnboardingStartupGate } from "./onboarding/first-run-startup-gate.js";
@@ -74,19 +75,27 @@ import { RuntimeConfigNotice } from "./RuntimeConfigNotice.js";
74
75
  import { EMBEDDED_THEME_CHANGE_EVENT, applyEmbeddedThemeUpdate, parseEmbeddedThemeUpdate, } from "./theme.js";
75
76
  import { scheduleAfterPaint } from "./use-after-paint.js";
76
77
  import { useSession } from "./use-session.js";
77
- import { createAgentNativeServerActionWebMcpRegistration } from "./webmcp.js";
78
78
  const DEFAULT_TOASTER = (_jsx(Toaster, { richColors: true, position: "bottom-left", offset: { bottom: 44, left: 32 }, mobileOffset: { bottom: 44, left: 16 } }));
79
79
  function EarlyBetaRedirectScript() {
80
80
  return (_jsx("script", { "data-agent-native-beta-redirect": "1", dangerouslySetInnerHTML: {
81
81
  __html: getSsrBetaRedirectScriptBody(agentNativePath("/_agent-native/auth/session"), frameworkRoutePrefix()),
82
82
  } }));
83
83
  }
84
+ function EarlySessionBootstrapScript() {
85
+ return (_jsx("script", { "data-agent-native-session-bootstrap": "1", dangerouslySetInnerHTML: {
86
+ __html: getSsrSessionBootstrapScriptBody(agentNativePath("/_agent-native/auth/session")),
87
+ } }));
88
+ }
84
89
  function RoutedAppEnhancements() {
85
90
  const isInRouter = useInRouterContext();
86
91
  if (!isInRouter)
87
92
  return null;
88
93
  return (_jsxs(_Fragment, { children: [_jsx(AgentNativeRouteWarmup, {}), _jsx(RouteTransitionIndicator, {})] }));
89
94
  }
95
+ let webMcpModulePromise = null;
96
+ function loadWebMcpModule() {
97
+ return (webMcpModulePromise ??= import("./webmcp.js"));
98
+ }
90
99
  function AgentNativeWebMcpRegistration({ excludeActionNames, }) {
91
100
  const excludeActionNamesKey = JSON.stringify(excludeActionNames ?? []);
92
101
  useEffect(() => {
@@ -95,16 +104,27 @@ function AgentNativeWebMcpRegistration({ excludeActionNames, }) {
95
104
  // paint-aligned window — only the cookie-session-gated variant defers.
96
105
  // Ownership is local to this effect: two coexisting surfaces each stop
97
106
  // only the registration they created.
98
- const registration = createAgentNativeServerActionWebMcpRegistration({
99
- excludeActionNames,
100
- });
101
- void registration.start().catch(() => {
102
- // WebMCP is progressive enhancement. Session expiry or a transient
103
- // manifest failure must not prevent the authenticated app from
104
- // loading.
107
+ let disposed = false;
108
+ let registration = null;
109
+ void loadWebMcpModule()
110
+ .then(({ createAgentNativeServerActionWebMcpRegistration }) => {
111
+ if (disposed)
112
+ return;
113
+ registration = createAgentNativeServerActionWebMcpRegistration({
114
+ excludeActionNames,
115
+ });
116
+ void registration.start().catch(() => {
117
+ // WebMCP is progressive enhancement. Session expiry or a transient
118
+ // manifest failure must not prevent the authenticated app from
119
+ // loading.
120
+ });
121
+ })
122
+ .catch(() => {
123
+ // A failed optional WebMCP chunk must not delay the app shell.
105
124
  });
106
125
  return () => {
107
- registration.stop();
126
+ disposed = true;
127
+ registration?.stop();
108
128
  };
109
129
  }, [excludeActionNamesKey]);
110
130
  return null;
@@ -139,19 +159,29 @@ function SessionGatedAgentNativeWebMcpRegistration({ excludeActionNames, }) {
139
159
  registrationRef.current?.stop();
140
160
  registrationRef.current = null;
141
161
  registrationExcludeActionNamesKeyRef.current = null;
162
+ let disposed = false;
142
163
  const cancel = scheduleAfterPaint(() => {
143
- const registration = createAgentNativeServerActionWebMcpRegistration({
144
- excludeActionNames,
145
- });
146
- void registration.start().catch(() => {
147
- // WebMCP is progressive enhancement. Session expiry or a transient
148
- // manifest failure must not prevent the authenticated app from
149
- // loading.
164
+ void loadWebMcpModule()
165
+ .then(({ createAgentNativeServerActionWebMcpRegistration }) => {
166
+ if (disposed)
167
+ return;
168
+ const registration = createAgentNativeServerActionWebMcpRegistration({
169
+ excludeActionNames,
170
+ });
171
+ void registration.start().catch(() => {
172
+ // WebMCP is progressive enhancement. Session expiry or a transient
173
+ // manifest failure must not prevent the authenticated app from
174
+ // loading.
175
+ });
176
+ registrationRef.current = registration;
177
+ registrationExcludeActionNamesKeyRef.current = excludeActionNamesKey;
178
+ })
179
+ .catch(() => {
180
+ // A failed optional WebMCP chunk must not delay the app shell.
150
181
  });
151
- registrationRef.current = registration;
152
- registrationExcludeActionNamesKeyRef.current = excludeActionNamesKey;
153
182
  });
154
183
  return () => {
184
+ disposed = true;
155
185
  cancel();
156
186
  };
157
187
  // Unmount stops exactly the registration this surface created, whether
@@ -256,11 +286,11 @@ function publicPathI18n(i18n) {
256
286
  return { ...(i18n ?? {}), persistPreference: false };
257
287
  }
258
288
  export function AppProviders({ queryClient, isPublicPath = false, clientOnlyFallback, sessionBypass = false, disableWebMcp = false, webMcpExcludeActionNames, showEnvironmentBadge = false, defaultTheme, themeAttribute, tooltipDelayDuration, toaster, disableThemeTransitions, i18n, documentTitleFallback, children, }) {
259
- const fallback = clientOnlyFallback ?? _jsx(DefaultSpinner, {});
289
+ const fallback = clientOnlyFallback ?? _jsx(AppShellSkeleton, {});
260
290
  if (isPublicPath) {
261
291
  return (_jsx(ProvidersInner, { queryClient: queryClient, defaultTheme: defaultTheme, themeAttribute: themeAttribute, tooltipDelayDuration: tooltipDelayDuration, toaster: toaster, disableThemeTransitions: disableThemeTransitions, disableWebMcp: disableWebMcp, webMcpExcludeActionNames: webMcpExcludeActionNames, sessionBypass: sessionBypass, i18n: publicPathI18n(i18n), documentTitleFallback: documentTitleFallback, showProductionEnvironmentBadge: false, showEnvironmentBadge: showEnvironmentBadge, children: children }));
262
292
  }
263
293
  // Keep the bootstrap outside ClientOnly so the HTML parser can run it before
264
294
  // the authenticated client bundle starts.
265
- return (_jsxs(_Fragment, { children: [!sessionBypass && _jsx(EarlyBetaRedirectScript, {}), _jsx(ClientOnly, { fallback: fallback, children: _jsx(ProvidersInner, { queryClient: queryClient, defaultTheme: defaultTheme, themeAttribute: themeAttribute, tooltipDelayDuration: tooltipDelayDuration, toaster: toaster, disableThemeTransitions: disableThemeTransitions, disableWebMcp: disableWebMcp, webMcpExcludeActionNames: webMcpExcludeActionNames, sessionBypass: sessionBypass, i18n: i18n, documentTitleFallback: documentTitleFallback, showProductionEnvironmentBadge: !sessionBypass, showEnvironmentBadge: showEnvironmentBadge, children: _jsx(RequireSession, { bypass: sessionBypass, fallback: fallback, children: sessionBypass ? (children) : (_jsx(FirstRunOnboardingStartupGate, { children: children })) }) }) })] }));
295
+ return (_jsxs(_Fragment, { children: [!sessionBypass && (_jsxs(_Fragment, { children: [_jsx(EarlySessionBootstrapScript, {}), _jsx(EarlyBetaRedirectScript, {})] })), _jsx(ClientOnly, { fallback: fallback, children: _jsx(ProvidersInner, { queryClient: queryClient, defaultTheme: defaultTheme, themeAttribute: themeAttribute, tooltipDelayDuration: tooltipDelayDuration, toaster: toaster, disableThemeTransitions: disableThemeTransitions, disableWebMcp: disableWebMcp, webMcpExcludeActionNames: webMcpExcludeActionNames, sessionBypass: sessionBypass, i18n: i18n, documentTitleFallback: documentTitleFallback, showProductionEnvironmentBadge: !sessionBypass, showEnvironmentBadge: showEnvironmentBadge, children: _jsx(RequireSession, { bypass: sessionBypass, fallback: fallback, children: sessionBypass ? (children) : (_jsx(FirstRunOnboardingStartupGate, { children: children })) }) }) })] }));
266
296
  }
@@ -5,6 +5,11 @@ export type ClientStatusResult<T> = {
5
5
  state: "unavailable";
6
6
  status?: number;
7
7
  };
8
+ declare global {
9
+ interface Window {
10
+ __agentNativeSessionBootstrap?: Promise<ClientStatusResult<unknown>>;
11
+ }
12
+ }
8
13
  export declare function invalidateClientStatusRequest(path: string): void;
9
14
  export declare function invalidateClientStatusRequests(): void;
10
15
  export declare function fetchAgentEngineStatus<T = unknown>(): Promise<ClientStatusResult<T>>;
@@ -2,6 +2,7 @@ import { agentNativePath } from "./api-path.js";
2
2
  import { agentNativeApiDisabledReason } from "./api-surface.js";
3
3
  const RESULT_TTL_MS = 500;
4
4
  const REQUEST_TIMEOUT_MS = 15_000;
5
+ const SESSION_STATUS_PATH = "/_agent-native/auth/session";
5
6
  const cache = new Map();
6
7
  const requests = new Map();
7
8
  const requestControllers = new Map();
@@ -56,23 +57,29 @@ async function fetchClientStatus(path) {
56
57
  resolve({ state: "unavailable" });
57
58
  }, REQUEST_TIMEOUT_MS);
58
59
  });
59
- const transport = fetch(url, {
60
- cache: "no-store",
61
- credentials: "same-origin",
62
- ...(controller ? { signal: controller.signal } : {}),
63
- })
64
- .then(async (response) => {
65
- if (!response.ok) {
66
- return { state: "unavailable", status: response.status };
67
- }
68
- try {
69
- return { state: "available", value: await response.json() };
70
- }
71
- catch {
72
- return { state: "unavailable", status: response.status };
73
- }
74
- })
75
- .catch(() => ({ state: "unavailable" }));
60
+ const bootstrappedSession = path === SESSION_STATUS_PATH && typeof window !== "undefined"
61
+ ? window.__agentNativeSessionBootstrap
62
+ : undefined;
63
+ if (bootstrappedSession)
64
+ delete window.__agentNativeSessionBootstrap;
65
+ const transport = bootstrappedSession ??
66
+ fetch(url, {
67
+ cache: "no-store",
68
+ credentials: "same-origin",
69
+ ...(controller ? { signal: controller.signal } : {}),
70
+ })
71
+ .then(async (response) => {
72
+ if (!response.ok) {
73
+ return { state: "unavailable", status: response.status };
74
+ }
75
+ try {
76
+ return { state: "available", value: await response.json() };
77
+ }
78
+ catch {
79
+ return { state: "unavailable", status: response.status };
80
+ }
81
+ })
82
+ .catch(() => ({ state: "unavailable" }));
76
83
  const request = Promise.race([transport, timeout])
77
84
  .then((result) => {
78
85
  if (generation === requestGeneration &&
@@ -101,6 +108,9 @@ async function fetchClientStatus(path) {
101
108
  }
102
109
  export function invalidateClientStatusRequest(path) {
103
110
  const url = agentNativePath(path);
111
+ if (path === SESSION_STATUS_PATH && typeof window !== "undefined") {
112
+ delete window.__agentNativeSessionBootstrap;
113
+ }
104
114
  requestGenerations.set(url, (requestGenerations.get(url) ?? 0) + 1);
105
115
  cache.delete(url);
106
116
  requestControllers.get(url)?.abort();
@@ -109,6 +119,9 @@ export function invalidateClientStatusRequest(path) {
109
119
  }
110
120
  export function invalidateClientStatusRequests() {
111
121
  generation += 1;
122
+ if (typeof window !== "undefined") {
123
+ delete window.__agentNativeSessionBootstrap;
124
+ }
112
125
  cache.clear();
113
126
  for (const controller of requestControllers.values()) {
114
127
  controller.abort();
@@ -126,5 +139,5 @@ export function fetchBuilderStatus() {
126
139
  return fetchClientStatus("/_agent-native/builder/status");
127
140
  }
128
141
  export function fetchAuthSessionStatus() {
129
- return fetchClientStatus("/_agent-native/auth/session");
142
+ return fetchClientStatus(SESSION_STATUS_PATH);
130
143
  }
@@ -2,6 +2,7 @@ import React from "react";
2
2
  import { type LocaleCode, type LocaleMetadata, type LocaleMetadataMap, type LocalePreference, type LocalizationPreference } from "../localization/shared.js";
3
3
  export { DEFAULT_LOCALE, LOCALE_HYDRATION_GLOBAL, LOCALE_METADATA, LOCALE_STORAGE_KEY, SUPPORTED_LOCALES, isLocaleCode, isValidLocaleCode, localeDirection, localeMetadataFor, type BuiltinLocaleCode, normalizeLocaleCode, normalizeLocalePreference, normalizeLocalizationPreference, resolveLocaleFromCandidates, resolveLocaleFromPreference, type LocaleCode, type LocaleMetadata, type LocalePreference, type LocalizationPreference, } from "../localization/shared.js";
4
4
  export { getLocaleInitScript } from "../localization/server.js";
5
+ export { LanguagePicker } from "./LanguagePicker.js";
5
6
  export type LocaleMessages = Record<string, unknown>;
6
7
  export type AgentNativeI18nLocaleLoader = () => Promise<LocaleMessages | {
7
8
  default: LocaleMessages;
@@ -76,9 +77,3 @@ export declare function useFormatters(): {
76
77
  formatRelativeTime(value: number, unit: Intl.RelativeTimeFormatUnit, options?: Intl.RelativeTimeFormatOptions): string;
77
78
  formatList(value: string[], options?: Intl.ListFormatOptions): string;
78
79
  };
79
- export declare function LanguagePicker({ className, includeSystem, label, variant, }: {
80
- className?: string;
81
- includeSystem?: boolean;
82
- label?: string;
83
- variant?: "select" | "icon" | "ghost-icon";
84
- }): React.JSX.Element;
@@ -1,20 +1,17 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Picker, useDesignSystemComponent, } from "@agent-native/toolkit/design-system";
3
- import * as PopoverPrimitive from "@radix-ui/react-popover";
4
- import { IconCheck, IconChevronDown, IconLanguage } from "@tabler/icons-react";
1
+ import { jsx as _jsx } from "react/jsx-runtime";
5
2
  import i18next from "i18next";
6
3
  import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, } from "react";
7
4
  import { I18nextProvider, initReactI18next, useTranslation, } from "react-i18next";
8
5
  import { coreMessagesForLocale, englishAgentChatMessages, loadCoreMessagesForLocale, normalizeCoreMessageOverrides, } from "../localization/core-messages.js";
9
6
  import defaultEnglishMessages from "../localization/default-messages.js";
10
- import { DEFAULT_LOCALE, LOCALE_HYDRATION_GLOBAL, LOCALE_STORAGE_KEY, SUPPORTED_LOCALES, isValidLocaleCode, localeDisplayName, localeDirection, localeMetadataFor, normalizeLocaleCode, normalizeLocalizationPreference, resolveLocaleFromCandidates, resolveLocaleFromPreference, } from "../localization/shared.js";
7
+ import { DEFAULT_LOCALE, LOCALE_HYDRATION_GLOBAL, LOCALE_STORAGE_KEY, SUPPORTED_LOCALES, isValidLocaleCode, localeDirection, localeMetadataFor, normalizeLocaleCode, normalizeLocalizationPreference, resolveLocaleFromCandidates, resolveLocaleFromPreference, } from "../localization/shared.js";
11
8
  import { injectedAgentNativeConfig } from "./app-config.js";
12
9
  import { setClientAppState } from "./application-state.js";
13
10
  import { callAction } from "./use-action.js";
14
11
  import { useSession } from "./use-session.js";
15
- import { cn } from "./utils.js";
16
12
  export { DEFAULT_LOCALE, LOCALE_HYDRATION_GLOBAL, LOCALE_METADATA, LOCALE_STORAGE_KEY, SUPPORTED_LOCALES, isLocaleCode, isValidLocaleCode, localeDirection, localeMetadataFor, normalizeLocaleCode, normalizeLocalePreference, normalizeLocalizationPreference, resolveLocaleFromCandidates, resolveLocaleFromPreference, } from "../localization/shared.js";
17
13
  export { getLocaleInitScript } from "../localization/server.js";
14
+ export { LanguagePicker } from "./LanguagePicker.js";
18
15
  function isAgentNativeI18nModuleNamespace(value) {
19
16
  return Object.prototype.toString.call(value) === "[object Module]";
20
17
  }
@@ -48,63 +45,6 @@ function catalogLocaleMetadata(locales) {
48
45
  const LocaleContext = globalThis.__AGENT_NATIVE_LOCALE_CONTEXT__ ??
49
46
  (globalThis.__AGENT_NATIVE_LOCALE_CONTEXT__ =
50
47
  createContext(null));
51
- const LANGUAGE_PICKER_COPY = {
52
- "en-US": {
53
- label: "Language",
54
- system: "System",
55
- systemDescription: "Use your browser language",
56
- },
57
- "zh-CN": {
58
- label: "语言",
59
- system: "系统",
60
- systemDescription: "使用浏览器语言",
61
- },
62
- "zh-TW": {
63
- label: "語言",
64
- system: "系統",
65
- systemDescription: "使用瀏覽器語言",
66
- },
67
- "es-ES": {
68
- label: "Idioma",
69
- system: "Sistema",
70
- systemDescription: "Usar el idioma del navegador",
71
- },
72
- "fr-FR": {
73
- label: "Langue",
74
- system: "Système",
75
- systemDescription: "Utiliser la langue du navigateur",
76
- },
77
- "de-DE": {
78
- label: "Sprache",
79
- system: "System",
80
- systemDescription: "Browsersprache verwenden",
81
- },
82
- "ja-JP": {
83
- label: "言語",
84
- system: "システム",
85
- systemDescription: "ブラウザーの言語を使用",
86
- },
87
- "ko-KR": {
88
- label: "언어",
89
- system: "시스템",
90
- systemDescription: "브라우저 언어 사용",
91
- },
92
- "pt-BR": {
93
- label: "Idioma",
94
- system: "Sistema",
95
- systemDescription: "Usar o idioma do navegador",
96
- },
97
- "hi-IN": {
98
- label: "भाषा",
99
- system: "सिस्टम",
100
- systemDescription: "ब्राउज़र की भाषा का उपयोग करें",
101
- },
102
- "ar-SA": {
103
- label: "اللغة",
104
- system: "النظام",
105
- systemDescription: "استخدام لغة المتصفح",
106
- },
107
- };
108
48
  function browserLanguageCandidates() {
109
49
  if (typeof navigator === "undefined")
110
50
  return [];
@@ -665,57 +605,3 @@ export function useFormatters() {
665
605
  },
666
606
  }), [locale]);
667
607
  }
668
- export function LanguagePicker({ className, includeSystem = true, label, variant = "select", }) {
669
- const { locale, preference, setPreference, supportedLocales, localeMetadata, } = useLocale();
670
- const [open, setOpen] = useState(false);
671
- const copy = LANGUAGE_PICKER_COPY[locale] ?? LANGUAGE_PICKER_COPY[DEFAULT_LOCALE];
672
- const systemCopy = LANGUAGE_PICKER_COPY[resolveLocaleFromCandidates(browserLanguageCandidates())] ?? LANGUAGE_PICKER_COPY[DEFAULT_LOCALE];
673
- const resolvedLabel = label ?? copy.label;
674
- const options = [
675
- ...(includeSystem
676
- ? [
677
- {
678
- value: "system",
679
- label: systemCopy.system,
680
- description: systemCopy.systemDescription,
681
- },
682
- ]
683
- : []),
684
- ...supportedLocales.map((code) => ({
685
- value: code,
686
- label: localeDisplayName(code, localeMetadata),
687
- description: code,
688
- })),
689
- ];
690
- const selected = options.find((option) => option.value === preference);
691
- const selectedLabel = selected?.label ?? preference;
692
- const triggerLabel = `${resolvedLabel}: ${selectedLabel}`;
693
- const customPicker = useDesignSystemComponent("Picker");
694
- function handleOptionClick(value) {
695
- setOpen(false);
696
- void setPreference(normalizeLocalizationPreference(value).locale);
697
- }
698
- if (variant === "select" && customPicker) {
699
- return (_jsx(Picker, { mode: "select", options: options, value: preference, onChange: (value) => {
700
- if (value == null)
701
- return;
702
- void setPreference(normalizeLocalizationPreference(value).locale);
703
- }, placeholder: selectedLabel, "aria-label": triggerLabel, className: className }));
704
- }
705
- return (_jsx("div", { className: className, children: _jsxs(PopoverPrimitive.Root, { open: open, onOpenChange: setOpen, children: [_jsx(PopoverPrimitive.Trigger, { asChild: true, children: _jsxs("button", { type: "button", "aria-label": triggerLabel, title: triggerLabel, "data-language-picker-trigger": true, className: cn("shrink-0 rounded-md outline-none transition-colors focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", variant === "ghost-icon"
706
- ? "flex h-9 w-9 items-center justify-center text-muted-foreground hover:bg-accent hover:text-foreground data-[state=open]:bg-accent data-[state=open]:text-foreground"
707
- : "border border-border bg-background text-foreground hover:border-foreground/30 hover:bg-accent/40 hover:text-foreground data-[state=open]:border-foreground/30 data-[state=open]:bg-accent/40", variant === "icon"
708
- ? "flex h-8 w-8 items-center justify-center"
709
- : variant === "select"
710
- ? "flex h-9 w-full items-center justify-between gap-2 px-3 text-start text-sm"
711
- : null), children: [_jsxs("span", { className: "flex min-w-0 items-center gap-2", children: [_jsx(IconLanguage, { className: "h-4 w-4 shrink-0 text-muted-foreground" }), variant === "select" ? (_jsx("span", { className: "truncate", children: selectedLabel })) : (_jsx("span", { className: "sr-only", children: triggerLabel }))] }), variant === "select" ? (_jsx(IconChevronDown, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground", "aria-hidden": "true" })) : null] }) }), _jsx(PopoverPrimitive.Portal, { children: _jsx(PopoverPrimitive.Content, { align: variant === "select" ? "start" : "end", sideOffset: 6, role: "menu", className: cn(
712
- // compositing-ok: popover content unmounts on close.
713
- "z-[9999] max-h-[min(20rem,var(--radix-popover-content-available-height))] overflow-y-auto rounded-lg border border-border bg-popover p-1 text-popover-foreground shadow-lg outline-none will-change-[transform,opacity] data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:duration-100 data-[state=open]:duration-150 data-[state=closed]:ease-in data-[state=open]:ease-out data-[side=bottom]:slide-in-from-top-1 data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1 data-[side=top]:slide-in-from-bottom-1", variant === "icon" || variant === "ghost-icon"
714
- ? "min-w-56"
715
- : "w-[min(20rem,calc(100vw-2rem))] min-w-[var(--radix-popover-trigger-width)]"), children: options.map((option) => {
716
- const optionSelected = option.value === preference;
717
- return (_jsxs("button", { type: "button", role: "menuitemradio", "aria-checked": optionSelected, title: option.description, onClick: () => handleOptionClick(option.value), className: cn("flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-start text-sm outline-none transition-colors hover:bg-accent/60 hover:text-foreground focus-visible:bg-accent/60 focus-visible:text-foreground", optionSelected
718
- ? "bg-accent/40 text-foreground"
719
- : "text-muted-foreground"), children: [_jsx(IconCheck, { className: cn("h-3.5 w-3.5 shrink-0", optionSelected ? "opacity-100" : "opacity-0"), "aria-hidden": "true" }), _jsx("span", { className: "truncate", children: option.label })] }, option.value));
720
- }) }) })] }) }));
721
- }
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Suspense, createContext, lazy, useContext, useEffect, useState, } from "react";
3
- import { DefaultSpinner } from "../DefaultSpinner.js";
3
+ import { FIRST_RUN_ONBOARDING_COOKIE } from "../../shared/first-run-onboarding.js";
4
+ import { AppShellSkeleton } from "../AppShellSkeleton.js";
4
5
  import { isFirstRunOnboardingEnabled } from "./first-run-enabled.js";
5
6
  import { fetchFirstRunOnboardingStatus } from "./first-run-status.js";
6
7
  import { trackOnboardingEvent } from "./use-onboarding.js";
@@ -9,12 +10,22 @@ const FirstRunOnboarding = lazy(() => import("./FirstRunOnboarding.js").then((mo
9
10
  default: module.FirstRunOnboarding,
10
11
  })));
11
12
  const FirstRunOnboardingGateContext = createContext(false);
13
+ function hasFirstRunOnboardingCookie() {
14
+ if (typeof document === "undefined")
15
+ return true;
16
+ const prefix = `${FIRST_RUN_ONBOARDING_COOKIE}=`;
17
+ return document.cookie.split(";").some((cookie) => {
18
+ const entry = cookie.trim();
19
+ return entry.startsWith(prefix) && entry.slice(prefix.length) === "1";
20
+ });
21
+ }
12
22
  export function useFirstRunOnboardingGateOwnsSurface() {
13
23
  return useContext(FirstRunOnboardingGateContext);
14
24
  }
15
25
  export function FirstRunOnboardingStartupGate({ children, }) {
16
26
  const previewMode = useOnboardingPreviewMode();
17
- const shouldResolve = isFirstRunOnboardingEnabled() && !previewMode;
27
+ const [hadFirstRunCookie] = useState(hasFirstRunOnboardingCookie);
28
+ const shouldResolve = isFirstRunOnboardingEnabled() && !previewMode && hadFirstRunCookie;
18
29
  const [decision, setDecision] = useState(shouldResolve ? "pending" : "ineligible");
19
30
  useEffect(() => {
20
31
  if (!shouldResolve) {
@@ -55,5 +66,5 @@ export function FirstRunOnboardingStartupGate({ children, }) {
55
66
  return (_jsxs(FirstRunOnboardingGateContext.Provider, { value: ownsSurface, children: [app, decision === "pending" && _jsx(FirstRunOnboardingStartupLoading, {}), ownsSurface && (_jsx(Suspense, { fallback: _jsx(FirstRunOnboardingStartupLoading, {}), children: _jsx(FirstRunOnboarding, { initialFirstRun: true }) }))] }));
56
67
  }
57
68
  function FirstRunOnboardingStartupLoading() {
58
- return (_jsx("div", { "data-first-run-startup-loading": "true", "aria-busy": "true", className: "fixed inset-0 z-[110] bg-background", children: _jsx(DefaultSpinner, {}) }));
69
+ return (_jsx("div", { "data-first-run-startup-loading": "true", "aria-busy": "true", className: "fixed inset-0 z-[110] bg-background", children: _jsx(AppShellSkeleton, {}) }));
59
70
  }
@@ -32,7 +32,7 @@ export interface RequireSessionProps {
32
32
  children: React.ReactNode;
33
33
  /**
34
34
  * Rendered while the session is being resolved and while a redirect is in
35
- * flight. Defaults to the framework `<DefaultSpinner />`.
35
+ * flight. Defaults to the framework `<AppShellSkeleton />`.
36
36
  */
37
37
  fallback?: React.ReactNode;
38
38
  /**