@agent-native/core 0.190.0 → 0.190.1-nightly-20260926035143

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 (27) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/assets/agent-native.json +1 -1
  3. package/corpus/templates/calendar/app/hooks/use-bookings.ts +6 -2
  4. package/corpus/templates/calendar/app/hooks/use-public-data.ts +3 -2
  5. package/corpus/templates/calendar/app/pages/BookingPage.tsx +4 -3
  6. package/corpus/templates/calendar/changelog/2026-09-25-personal-booking-pages-use-the-host-s-saved-timezone-for-ava.md +6 -0
  7. package/corpus/templates/calendar/server/handlers/availability.ts +35 -4
  8. package/corpus/templates/calendar/server/handlers/booking-usernames.ts +6 -0
  9. package/corpus/templates/calendar/server/handlers/bookings.ts +40 -18
  10. package/corpus/templates/clips/agent-native.json +1 -1
  11. package/corpus/templates/content/agent-native.json +1 -1
  12. package/corpus/templates/design/agent-native.json +1 -1
  13. package/corpus/templates/forms/agent-native.json +1 -1
  14. package/corpus/templates/slides/agent-native.json +1 -1
  15. package/corpus/templates/tasks/agent-native.json +1 -1
  16. package/dist/client/chat/message-components.d.ts +4 -4
  17. package/dist/client/chat/message-components.js +11 -43
  18. package/dist/client/observability/OutputPreview.js +5 -3
  19. package/dist/client/onboarding/use-onboarding.js +37 -9
  20. package/dist/collab/struct-routes.d.ts +1 -1
  21. package/dist/notifications/routes.d.ts +1 -1
  22. package/dist/org/handlers.d.ts +1 -1
  23. package/dist/resources/handlers.d.ts +1 -1
  24. package/dist/server/auth.js +60 -38
  25. package/dist/server/transcribe-voice.d.ts +1 -1
  26. package/dist/sharing/actions/list-resource-shares.d.ts +1 -1
  27. package/package.json +3 -3
package/corpus/README.md CHANGED
@@ -31,4 +31,4 @@ rg -n "defineAction|useActionQuery" node_modules/@agent-native/core/corpus
31
31
 
32
32
  ## Generated Counts
33
33
 
34
- - template files: 10001
34
+ - template files: 10002
@@ -3,7 +3,7 @@
3
3
  "onboarding": {
4
4
  "firstRun": {
5
5
  "development": "off",
6
- "production": "connect-and-integrations"
6
+ "production": "connect"
7
7
  }
8
8
  },
9
9
  "doctor": {
@@ -28,15 +28,17 @@ export function useAvailableSlots(
28
28
  duration: number,
29
29
  slug?: string,
30
30
  draft?: BookingAvailabilityPreview,
31
+ username?: string,
31
32
  ) {
32
33
  const draftParam = draft ? JSON.stringify(draft) : undefined;
33
34
 
34
35
  return useQuery<{ start: string; end: string }[]>({
35
- queryKey: ["available-slots", date, duration, slug, draftParam],
36
+ queryKey: ["available-slots", date, duration, slug, draftParam, username],
36
37
  queryFn: async () => {
37
38
  const params = new URLSearchParams({ date, duration: String(duration) });
38
39
  if (slug) params.set("slug", slug);
39
40
  if (draftParam) params.set("draft", draftParam);
41
+ if (username) params.set("username", username);
40
42
  const res = await fetch(
41
43
  appApiPath(`/api/bookings/available-slots?${params}`),
42
44
  );
@@ -55,10 +57,11 @@ export function useAvailableDays(
55
57
  to: string,
56
58
  duration: number,
57
59
  slug?: string,
60
+ username?: string,
58
61
  enabled = true,
59
62
  ) {
60
63
  return useQuery<string[]>({
61
- queryKey: ["available-days", from, to, duration, slug],
64
+ queryKey: ["available-days", from, to, duration, slug, username],
62
65
  queryFn: async () => {
63
66
  const params = new URLSearchParams({
64
67
  from,
@@ -66,6 +69,7 @@ export function useAvailableDays(
66
69
  duration: String(duration),
67
70
  });
68
71
  if (slug) params.set("slug", slug);
72
+ if (username) params.set("username", username);
69
73
  const res = await fetch(
70
74
  appApiPath(`/api/bookings/available-slots?${params}`),
71
75
  );
@@ -16,12 +16,13 @@ export function usePublicSettings() {
16
16
  }
17
17
 
18
18
  /** Fetches availability from the public (unauthenticated) endpoint */
19
- export function usePublicAvailability(slug?: string) {
19
+ export function usePublicAvailability(slug?: string, username?: string) {
20
20
  return useQuery<AvailabilityConfig>({
21
- queryKey: ["public-availability", slug],
21
+ queryKey: ["public-availability", slug, username],
22
22
  queryFn: async () => {
23
23
  const params = new URLSearchParams();
24
24
  if (slug) params.set("slug", slug);
25
+ if (username) params.set("username", username);
25
26
  const path = params.size
26
27
  ? `/api/public/availability?${params}`
27
28
  : "/api/public/availability";
@@ -79,7 +79,7 @@ function BookingPageShell({
79
79
  <LanguagePicker variant="ghost-icon" />
80
80
  <ThemeToggle />
81
81
  </div>
82
- <div className="fixed bottom-[21px] left-4 z-50 max-sm:static max-sm:mx-auto max-sm:mt-8 [&_.an-powered-logo]:!h-3.5 [&_.an-powered-logo]:brightness-0 dark:[&_.an-powered-logo]:invert">
82
+ <div className="fixed bottom-[21px] left-4 z-50 max-sm:static max-sm:mx-auto max-sm:pt-8 [&_.an-powered-logo]:!h-3.5 [&_.an-powered-logo]:brightness-0 dark:[&_.an-powered-logo]:invert">
83
83
  <PoweredByBadge variant="plain" embedded />
84
84
  </div>
85
85
  <div className="relative z-10 min-h-screen overflow-x-hidden p-4">
@@ -96,7 +96,7 @@ export default function BookingPage() {
96
96
  const navigate = useNavigate();
97
97
  const { data: settings, isLoading: settingsLoading } = usePublicSettings();
98
98
  const { data: availability, isLoading: availabilityLoading } =
99
- usePublicAvailability(slug);
99
+ usePublicAvailability(slug, username);
100
100
  const {
101
101
  data: bookingLink,
102
102
  isLoading: bookingLinkLoading,
@@ -233,7 +233,7 @@ export default function BookingPage() {
233
233
  data: slots = [],
234
234
  isLoading: slotsLoading,
235
235
  error: slotsError,
236
- } = useAvailableSlots(dateStr, duration, slug);
236
+ } = useAvailableSlots(dateStr, duration, slug, undefined, username);
237
237
  const monthStart = format(startOfMonth(viewMonth), "yyyy-MM-dd");
238
238
  const monthEnd = format(endOfMonth(viewMonth), "yyyy-MM-dd");
239
239
  const {
@@ -245,6 +245,7 @@ export default function BookingPage() {
245
245
  monthEnd,
246
246
  duration,
247
247
  slug,
248
+ username,
248
249
  step === "date" &&
249
250
  !!availability &&
250
251
  (!hasDurationChoice || selectedDuration !== null),
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-09-25
4
+ ---
5
+
6
+ Personal booking pages use the host's saved timezone for availability.
@@ -8,12 +8,14 @@ import { eq } from "drizzle-orm";
8
8
  import {
9
9
  defineEventHandler,
10
10
  getQuery,
11
+ createError,
11
12
  setResponseStatus,
12
13
  type H3Event,
13
14
  } from "h3";
14
15
 
15
16
  import type { AvailabilityConfig } from "../../shared/api.js";
16
17
  import { getDb, schema } from "../db/index.js";
18
+ import { getBookingUsernameOwner } from "./booking-usernames.js";
17
19
 
18
20
  function createDefaultAvailability(timezone: string): AvailabilityConfig {
19
21
  return {
@@ -66,6 +68,7 @@ export const getPublicAvailability = defineEventHandler(
66
68
  async (event: H3Event) => {
67
69
  const query = getQuery(event);
68
70
  const slug = typeof query.slug === "string" ? query.slug : "";
71
+ const username = typeof query.username === "string" ? query.username : "";
69
72
  if (slug) {
70
73
  const link = await getDb()
71
74
  .select({ ownerEmail: schema.bookingLinks.ownerEmail })
@@ -86,12 +89,40 @@ export const getPublicAvailability = defineEventHandler(
86
89
  ownerSettings?.timezone || "America/New_York",
87
90
  );
88
91
  }
92
+
93
+ if (username) {
94
+ const ownerEmail = await getBookingUsernameOwner(username);
95
+ if (!ownerEmail) {
96
+ throw createError({
97
+ statusCode: 404,
98
+ statusMessage: "Booking page not found",
99
+ });
100
+ }
101
+
102
+ const ownerConfig = (await getUserSetting(
103
+ ownerEmail,
104
+ "calendar-availability",
105
+ )) as unknown as AvailabilityConfig | null;
106
+ if (ownerConfig?.bookingPageSlug === slug) return ownerConfig;
107
+ if (!ownerConfig && slug === "book") {
108
+ const ownerSettings = (await getUserSetting(
109
+ ownerEmail,
110
+ "calendar-settings",
111
+ )) as { timezone?: string } | null;
112
+ return createDefaultAvailability(
113
+ ownerSettings?.timezone || "America/New_York",
114
+ );
115
+ }
116
+
117
+ throw createError({
118
+ statusCode: 404,
119
+ statusMessage: "Booking page not found",
120
+ });
121
+ }
89
122
  }
90
123
 
91
- // Fall back to defaults — never read the unscoped `calendar-availability`
92
- // setting. That key was historically dual-written by every user's update
93
- // (see the matching fix in updateAvailability), which meant a brand-new
94
- // user's public booking link advertised whoever last saved their hours.
124
+ // Username-scoped pages fail closed above; legacy links without a username
125
+ // still use defaults and never read the unscoped availability setting.
95
126
  return createDefaultAvailability("America/New_York");
96
127
  },
97
128
  );
@@ -64,6 +64,12 @@ async function findUsernameOwner(username: string): Promise<string | null> {
64
64
  return row?.ownerEmail ?? null;
65
65
  }
66
66
 
67
+ export function getBookingUsernameOwner(
68
+ username: string,
69
+ ): Promise<string | null> {
70
+ return findUsernameOwner(username);
71
+ }
72
+
67
73
  async function chooseAvailableUsername(
68
74
  base: string,
69
75
  ownerEmail: string,
@@ -61,6 +61,7 @@ import { getOwnerBookingTimeZone } from "../lib/booking-timezone.js";
61
61
  import { eventBlocksAvailability } from "../lib/calendar-availability.js";
62
62
  import * as googleCalendar from "../lib/google-calendar.js";
63
63
  import { createZoomMeeting } from "../lib/zoom.js";
64
+ import { getBookingUsernameOwner } from "./booking-usernames.js";
64
65
 
65
66
  async function requireRequestContext<T>(
66
67
  event: H3Event,
@@ -598,10 +599,12 @@ function unavailableAvailabilityResponse(event: H3Event) {
598
599
 
599
600
  async function resolveAvailabilityContext({
600
601
  slug,
602
+ username,
601
603
  draft,
602
604
  db = getDb(),
603
605
  }: {
604
606
  slug: string;
607
+ username?: string;
605
608
  draft?: BookingAvailabilityDraft;
606
609
  db?: ConflictDb;
607
610
  }): Promise<AvailabilityContext> {
@@ -634,26 +637,42 @@ async function resolveAvailabilityContext({
634
637
  });
635
638
  }
636
639
  const config = configRaw as unknown as AvailabilityConfig | null;
637
- const ownerEmail = bookingLink?.ownerEmail;
640
+ const usernameOwnerEmail =
641
+ !bookingLink && username && !draft
642
+ ? await getBookingUsernameOwner(username)
643
+ : null;
644
+ const candidateOwnerEmail = bookingLink?.ownerEmail || usernameOwnerEmail;
645
+ const [candidateOwnerConfigRaw, candidateOwnerSettingsRaw] =
646
+ candidateOwnerEmail
647
+ ? await Promise.all([
648
+ getUserSetting(candidateOwnerEmail, "calendar-availability"),
649
+ getUserSetting(candidateOwnerEmail, "calendar-settings"),
650
+ ])
651
+ : [null, null];
652
+ const candidateOwnerConfig =
653
+ candidateOwnerConfigRaw as AvailabilityConfig | null;
654
+ const usernameSlugMatches =
655
+ !usernameOwnerEmail ||
656
+ candidateOwnerConfig?.bookingPageSlug === slug ||
657
+ (!candidateOwnerConfig && slug === "book");
658
+ const ownerEmail =
659
+ bookingLink?.ownerEmail ||
660
+ (usernameSlugMatches ? usernameOwnerEmail || undefined : undefined);
638
661
  const overrides = bookingLink
639
662
  ? resolveBookingLinkAvailabilityOverrides({ bookingLink, draft })
640
663
  : undefined;
641
664
  const hostEmails = overrides?.hostEmails ?? (ownerEmail ? [ownerEmail] : []);
642
- const [ownerConfigRaw, ownerSettingsRaw, conflictSlugs] = await Promise.all([
643
- ownerEmail
644
- ? getUserSetting(ownerEmail, "calendar-availability")
645
- : Promise.resolve(null),
646
- ownerEmail
647
- ? getUserSetting(ownerEmail, "calendar-settings")
648
- : Promise.resolve(null),
649
- ownerEmail
650
- ? getBookingLinkSlugsForOwners(hostEmails, db)
651
- : slug
652
- ? Promise.resolve([slug])
653
- : Promise.resolve([]),
654
- ]);
655
- const ownerConfig = ownerConfigRaw as unknown as AvailabilityConfig | null;
656
- const ownerSettings = ownerSettingsRaw as { timezone?: string } | null;
665
+ const ownerConfig = ownerEmail ? candidateOwnerConfig : null;
666
+ const ownerSettings = ownerEmail
667
+ ? (candidateOwnerSettingsRaw as { timezone?: string } | null)
668
+ : null;
669
+ const conflictSlugs = ownerEmail
670
+ ? await getBookingLinkSlugsForOwners(hostEmails, db).then((slugs) =>
671
+ Array.from(new Set([slug, ...slugs])),
672
+ )
673
+ : slug
674
+ ? [slug]
675
+ : [];
657
676
  const eligibleHosts = await getEligibleHostAvailability(
658
677
  ownerEmail,
659
678
  hostEmails,
@@ -666,7 +685,9 @@ async function resolveAvailabilityContext({
666
685
  ? createDefaultAvailability(
667
686
  ownerSettings?.timezone || "America/New_York",
668
687
  )
669
- : config),
688
+ : username
689
+ ? null
690
+ : config),
670
691
  ownerEmail,
671
692
  hostEmails,
672
693
  eligibleHosts,
@@ -1700,6 +1721,7 @@ async function getAvailableSlotsForQuery(
1700
1721
  const to = parseDateOnly(query.to);
1701
1722
  const hasRangeQuery = query.from !== undefined || query.to !== undefined;
1702
1723
  const slug = typeof query.slug === "string" ? query.slug : "";
1724
+ const username = typeof query.username === "string" ? query.username : "";
1703
1725
 
1704
1726
  if (hasRangeQuery) {
1705
1727
  if (!from || !to) {
@@ -1724,7 +1746,7 @@ async function getAvailableSlotsForQuery(
1724
1746
  return { error: "date query parameter is required" };
1725
1747
  }
1726
1748
 
1727
- const context = await resolveAvailabilityContext({ slug, draft });
1749
+ const context = await resolveAvailabilityContext({ slug, username, draft });
1728
1750
  if (!context.effectiveConfig) {
1729
1751
  return hasRangeQuery ? { dates: [] } : { slots: [] };
1730
1752
  }
@@ -3,7 +3,7 @@
3
3
  "onboarding": {
4
4
  "firstRun": {
5
5
  "development": "off",
6
- "production": "connect-and-integrations"
6
+ "production": "connect"
7
7
  }
8
8
  },
9
9
  "doctor": {
@@ -3,7 +3,7 @@
3
3
  "onboarding": {
4
4
  "firstRun": {
5
5
  "development": "off",
6
- "production": "connect-and-integrations"
6
+ "production": "connect"
7
7
  }
8
8
  },
9
9
  "doctor": {
@@ -3,7 +3,7 @@
3
3
  "onboarding": {
4
4
  "firstRun": {
5
5
  "development": "off",
6
- "production": "connect-and-integrations"
6
+ "production": "connect"
7
7
  }
8
8
  },
9
9
  "doctor": {
@@ -3,7 +3,7 @@
3
3
  "onboarding": {
4
4
  "firstRun": {
5
5
  "development": "off",
6
- "production": "connect-and-integrations"
6
+ "production": "connect"
7
7
  }
8
8
  },
9
9
  "doctor": {
@@ -3,7 +3,7 @@
3
3
  "onboarding": {
4
4
  "firstRun": {
5
5
  "development": "off",
6
- "production": "connect-and-integrations"
6
+ "production": "connect"
7
7
  }
8
8
  },
9
9
  "doctor": {
@@ -3,7 +3,7 @@
3
3
  "onboarding": {
4
4
  "firstRun": {
5
5
  "development": "off",
6
- "production": "connect-and-integrations"
6
+ "production": "connect"
7
7
  }
8
8
  },
9
9
  "doctor": {
@@ -23,10 +23,11 @@ export interface AssistantMessageActionBarProps {
23
23
  messageSeq: number;
24
24
  onFork?: () => void | boolean | Promise<void | boolean>;
25
25
  onRestore?: () => void;
26
+ trailingActions?: React.ReactNode;
26
27
  className?: string;
27
28
  }
28
29
  /** Compact, hover-revealed actions for a completed assistant response. */
29
- export declare function AssistantMessageActionBar({ timestamp, threadId, runId, messageSeq, onFork, onRestore, className, }: AssistantMessageActionBarProps): React.JSX.Element;
30
+ export declare function AssistantMessageActionBar({ timestamp, threadId, runId, messageSeq, onFork, onRestore, trailingActions, className, }: AssistantMessageActionBarProps): React.JSX.Element;
30
31
  export declare function SelectionAttachedPill(): React.JSX.Element | null;
31
32
  export declare const CheckpointContext: React.Context<{
32
33
  apiUrl: string;
@@ -130,9 +131,8 @@ export declare function ChatImageAttachmentPreview({ src, alt, }: {
130
131
  src: string;
131
132
  alt: string;
132
133
  }): React.JSX.Element;
133
- export declare function MessageActionsMenu({ showRevert, onRevert, threadId, }?: {
134
- showRevert?: boolean;
135
- onRevert?: () => void;
134
+ export declare function MessageActionsMenu({ onFork, threadId, }?: {
135
+ onFork?: () => void | boolean | Promise<void | boolean>;
136
136
  threadId?: string;
137
137
  }): React.JSX.Element;
138
138
  export declare function AssistantChatHistoryBeginningRevertButton(): React.JSX.Element | null;
@@ -14,7 +14,7 @@ import { getActiveRun } from "../active-run-state.js";
14
14
  import { agentNativePath } from "../api-path.js";
15
15
  import { writeClipboardText } from "../clipboard.js";
16
16
  import { Dialog, DialogClose, DialogContent, DialogTitle, } from "../components/ui/dialog.js";
17
- import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from "../components/ui/dropdown-menu.js";
17
+ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "../components/ui/dropdown-menu.js";
18
18
  import { Popover, PopoverContent, PopoverTrigger, } from "../components/ui/popover.js";
19
19
  import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "../components/ui/tooltip.js";
20
20
  import { isCreditsLimitErrorCode, localizeKnownChatErrorText, } from "../error-format.js";
@@ -101,7 +101,7 @@ function MessageActionButton({ label, onClick, children, className, }) {
101
101
  return (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", "aria-label": label, onClick: onClick, className: cn("flex size-6 items-center justify-center rounded-md text-muted-foreground/75 transition-colors duration-150 hover:bg-accent hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring", className), children: children }) }), _jsx(TooltipContent, { side: "top", className: "text-xs", children: label })] }));
102
102
  }
103
103
  /** Compact, hover-revealed actions for a completed assistant response. */
104
- export function AssistantMessageActionBar({ timestamp, threadId, runId, messageSeq, onFork, onRestore, className, }) {
104
+ export function AssistantMessageActionBar({ timestamp, threadId, runId, messageSeq, onFork, onRestore, trailingActions, className, }) {
105
105
  const t = useT();
106
106
  const messageRuntime = useMessageRuntime();
107
107
  const [copied, setCopied] = useState(false);
@@ -119,9 +119,9 @@ export function AssistantMessageActionBar({ timestamp, threadId, runId, messageS
119
119
  setTimeout(() => setCopied(false), 1000);
120
120
  });
121
121
  }, [messageRuntime]);
122
- return (_jsx(TooltipProvider, { delayDuration: 400, children: _jsxs("div", { className: cn("pointer-events-none inline-flex items-center gap-0.5", messageFooterFadeClassName, "group-hover:pointer-events-auto group-focus-within:pointer-events-auto", className), children: [_jsx(MessageActionButton, { label: copied
123
- ? t("agentChat.common.copied")
124
- : t("agentChat.message.copyMessage"), onClick: handleCopy, children: copied ? (_jsx(IconCheck, { className: "size-4" })) : (_jsx(IconCopy, { className: "size-4" })) }), _jsx(ThumbsFeedback, { threadId: threadId, runId: runId, messageSeq: messageSeq }), onFork && (_jsx(MessageActionButton, { label: t("agentChat.message.forkChat"), onClick: () => void onFork(), children: _jsx(IconGitFork, { className: "size-4" }) })), onRestore && (_jsx(MessageActionButton, { label: t("agentChat.message.revertHere"), onClick: onRestore, children: _jsx(IconArrowBackUp, { className: "size-4" }) })), timestamp && _jsx(MessageTimestamp, { timestamp: timestamp })] }) }));
122
+ return (_jsx(TooltipProvider, { delayDuration: 400, children: _jsxs("div", { className: cn("pointer-events-none flex w-full items-center justify-between gap-2", messageFooterFadeClassName, "group-hover:pointer-events-auto group-focus-within:pointer-events-auto", className), children: [_jsxs("div", { className: "inline-flex min-w-0 items-center gap-0.5", "data-message-action-group": "feedback", children: [_jsx(MessageActionButton, { label: copied
123
+ ? t("agentChat.common.copied")
124
+ : t("agentChat.message.copyMessage"), onClick: handleCopy, children: copied ? (_jsx(IconCheck, { className: "size-4" })) : (_jsx(IconCopy, { className: "size-4" })) }), _jsx(ThumbsFeedback, { threadId: threadId, runId: runId, messageSeq: messageSeq })] }), _jsxs("div", { className: "inline-flex shrink-0 items-center gap-0.5", "data-message-action-group": "conversation", children: [onRestore && (_jsx(MessageActionButton, { label: t("agentChat.message.revertHere"), onClick: onRestore, children: _jsx(IconArrowBackUp, { className: "size-4" }) })), trailingActions, timestamp && _jsx(MessageTimestamp, { timestamp: timestamp }), _jsx(MessageActionsMenu, { onFork: onFork, threadId: threadId })] })] }) }));
125
125
  }
126
126
  // ─── SelectionAttachedPill ────────────────────────────────────────────────────
127
127
  export function SelectionAttachedPill() {
@@ -439,33 +439,12 @@ function UserMessageEditComposer() {
439
439
  return (_jsxs(ComposerPrimitive.Root, { className: "flex flex-col gap-2 rounded-lg border border-border bg-background px-3 py-2 shadow-sm", children: [_jsx(ComposerPrimitive.Input, { className: "w-full resize-none bg-transparent text-sm leading-relaxed text-foreground placeholder:text-muted-foreground focus:outline-none", rows: 1, submitMode: "enter" }), _jsxs("div", { className: "flex justify-end gap-2", children: [_jsx(ComposerPrimitive.Cancel, { asChild: true, children: _jsx("button", { type: "button", className: "cursor-pointer rounded-md px-2 py-1 text-xs font-medium text-muted-foreground hover:bg-accent hover:text-foreground", children: t("agentChat.common.cancel") }) }), _jsx(ComposerPrimitive.Send, { asChild: true, children: _jsx("button", { type: "submit", className: "cursor-pointer rounded-md bg-foreground px-2 py-1 text-xs font-medium text-background hover:opacity-90 disabled:opacity-50", children: t("agentChat.common.save") }) })] })] }));
440
440
  }
441
441
  // ─── MessageActionsMenu ────────────────────────────────────────────────────────
442
- export function MessageActionsMenu({ showRevert, onRevert, threadId = "", } = {}) {
442
+ export function MessageActionsMenu({ onFork, threadId = "", } = {}) {
443
443
  const t = useT();
444
- const locale = useOptionalLocale()?.locale ?? DEFAULT_LOCALE;
445
444
  const [open, setOpen] = useState(false);
446
445
  const [copied, setCopied] = useState(null);
447
446
  const messageRuntime = useMessageRuntime();
448
447
  const actionsCtx = React.useContext(MessageActionsContext);
449
- const timestamp = formatMessageTimestamp(messageRuntime.getState().createdAt, locale, t("agentChat.history.yesterday"));
450
- const handleCopyMessage = useCallback(() => {
451
- const m = messageRuntime.getState();
452
- const text = m.content
453
- .filter((p) => p.type === "text")
454
- .map((p) => p.text)
455
- .join("\n");
456
- // Rich flavor keeps formatting in targets that read text/html (e.g. Slack);
457
- // null when the markdown renderer isn't ready yet, so we copy plain markdown.
458
- const html = renderMarkdownToClipboardHtml(text);
459
- void writeClipboardText(text, html ? { html } : undefined).then((ok) => {
460
- if (!ok)
461
- return;
462
- setCopied("message");
463
- setTimeout(() => {
464
- setCopied(null);
465
- setOpen(false);
466
- }, 1000);
467
- });
468
- }, [messageRuntime]);
469
448
  const handleCopyRequestId = useCallback(() => {
470
449
  const m = messageRuntime.getState();
471
450
  const runId = resolveAssistantRequestId(m, getActiveRun(), threadId);
@@ -487,18 +466,9 @@ export function MessageActionsMenu({ showRevert, onRevert, threadId = "", } = {}
487
466
  }, [messageRuntime, threadId]);
488
467
  const handleForkChat = useCallback(() => {
489
468
  setOpen(false);
490
- void actionsCtx?.onForkChat?.();
491
- }, [actionsCtx]);
492
- const handleRevert = useCallback(() => {
493
- setOpen(false);
494
- onRevert?.();
495
- }, [onRevert]);
496
- return (_jsxs(DropdownMenu, { open: open, onOpenChange: setOpen, children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx("button", { "aria-label": t("agentChat.message.actions"), className: cn("flex h-6 w-6 items-center justify-center rounded-md text-muted-foreground/70 transition-colors duration-150 hover:bg-accent hover:text-foreground", open && "bg-accent text-foreground"), children: _jsx(IconDots, { className: "h-3.5 w-3.5" }) }) }), _jsxs(DropdownMenuContent, { align: "start", sideOffset: 6, className: "w-48 rounded-lg border-border p-1.5 shadow-xl", children: [actionsCtx?.onForkChat && (_jsxs(DropdownMenuItem, { onSelect: handleForkChat, children: [_jsx(IconGitFork, { className: "h-3.5 w-3.5" }), t("agentChat.message.forkChat")] })), _jsxs(DropdownMenuItem, { onSelect: (e) => {
497
- e.preventDefault();
498
- handleCopyMessage();
499
- }, children: [copied === "message" ? (_jsx(IconCheck, { className: "h-3.5 w-3.5" })) : (_jsx(IconCopy, { className: "h-3.5 w-3.5" })), copied === "message"
500
- ? t("agentChat.common.copied")
501
- : t("agentChat.message.copyMessage")] }), _jsxs(DropdownMenuItem, { onSelect: (e) => {
469
+ void (onFork ?? actionsCtx?.onForkChat)?.();
470
+ }, [actionsCtx, onFork]);
471
+ return (_jsxs(DropdownMenu, { open: open, onOpenChange: setOpen, children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx("button", { "aria-label": t("agentChat.message.actions"), className: cn("flex h-6 w-6 items-center justify-center rounded-md text-muted-foreground/70 transition-colors duration-150 hover:bg-accent hover:text-foreground", open && "bg-accent text-foreground"), children: _jsx(IconDots, { className: "h-3.5 w-3.5" }) }) }), _jsxs(DropdownMenuContent, { align: "end", sideOffset: 6, className: "w-48 rounded-lg border-border p-1.5 shadow-xl", children: [(onFork || actionsCtx?.onForkChat) && (_jsxs(DropdownMenuItem, { onSelect: handleForkChat, children: [_jsx(IconGitFork, { className: "h-3.5 w-3.5" }), t("agentChat.message.forkChat")] })), _jsxs(DropdownMenuItem, { onSelect: (e) => {
502
472
  e.preventDefault();
503
473
  handleCopyRequestId();
504
474
  }, children: [copied === "id" ? (_jsx(IconCheck, { className: "h-3.5 w-3.5" })) : (_jsx(IconId, { className: "h-3.5 w-3.5" })), copied === "id"
@@ -507,7 +477,7 @@ export function MessageActionsMenu({ showRevert, onRevert, threadId = "", } = {}
507
477
  ? t("agentChat.message.requestIdUnavailable")
508
478
  : copied === "id-failed"
509
479
  ? t("agentChat.recovery.copyFailed")
510
- : t("agentChat.message.copyRequestId")] }), showRevert && (_jsxs(DropdownMenuItem, { onSelect: handleRevert, children: [_jsx(IconArrowBackUp, { className: "h-3.5 w-3.5" }), t("agentChat.message.revertHere")] })), timestamp && (_jsxs(_Fragment, { children: [_jsx(DropdownMenuSeparator, {}), _jsx(DropdownMenuLabel, { className: "px-2 py-1 text-[11px] font-normal text-muted-foreground", children: t("agentChat.message.sentAt", { time: timestamp.short }) })] }))] })] }));
480
+ : t("agentChat.message.copyRequestId")] })] })] }));
511
481
  }
512
482
  function AssistantChatHistoryRevertButton({ onRestore, onRestored, label, persistent = false, }) {
513
483
  const t = useT();
@@ -1413,9 +1383,7 @@ export function AssistantMessage() {
1413
1383
  ? messageActions?.onRetryRunError
1414
1384
  : undefined })), missingWarningText == null &&
1415
1385
  missingFinalResponseNoticeText != null &&
1416
- !showInlineRunError && (_jsx(MissingFinalResponseNotice, { messageId: msg.id, text: missingFinalResponseNoticeText, animate: animateMissingFinalResponse, onRevealComplete: handleMissingFinalResponseReveal })), isComplete && hasCodeAgentTools && msgContent && (_jsx(FilesChangedSummary, { parts: msgContent }))] }), isComplete && (_jsxs("div", { className: "mt-1 flex items-center justify-between", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-1", children: [showHistoryRevert && (_jsx(AssistantChatHistoryRevertButton, { onRestore: handleHistoryRestore, onRestored: () => setHistoryReverted(true) })), _jsx(AssistantMessageActionBar, { timestamp: timestamp, threadId: cpCtx?.threadId ?? "", runId: messageRunId ?? "", messageSeq: msg.index, onFork: messageActions?.onForkChat, onRestore: showRestore && restoreState === "idle"
1417
- ? handleRestore
1418
- : undefined }), isLast && (_jsx(TooltipProvider, { delayDuration: 400, children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(ActionBarPrimitive.Reload, { asChild: true, children: _jsx("button", { type: "button", "aria-label": t("agentChat.message.regenerate"), className: `flex h-6 w-6 cursor-pointer items-center justify-center rounded-md text-muted-foreground/70 hover:bg-accent hover:text-foreground ${messageFooterFadeClassName} disabled:cursor-not-allowed disabled:opacity-40`, children: _jsx(IconRefresh, { className: "h-3.5 w-3.5" }) }) }) }), _jsx(TooltipContent, { side: "top", className: "text-xs", children: t("agentChat.message.regenerate") })] }) })), _jsx(MessageBranchPicker, {})] }), showRestore && restoreState === "confirming" ? (_jsxs("div", { className: "flex items-center gap-1 text-xs", children: [_jsx("button", { onClick: handleRestore, className: "rounded-md bg-destructive px-1.5 py-0.5 text-destructive-foreground hover:bg-destructive/90", children: t("agentChat.message.restoreQuestion") }), _jsx("button", { onClick: cancelRestore, className: "rounded-md px-1.5 py-0.5 text-muted-foreground hover:bg-accent", children: t("agentChat.common.cancel") })] })) : showRestore && restoreState === "restoring" ? (_jsxs("span", { className: "flex items-center gap-1 text-xs text-muted-foreground", children: [_jsx(IconLoader2, { className: "h-3 w-3 animate-spin" }), t("agentChat.message.restoring")] })) : restoreState === "error" ? (_jsxs("span", { className: "flex items-center gap-1 text-xs text-destructive", children: [_jsx(IconAlertTriangle, { className: "h-3 w-3 shrink-0" }), _jsx("span", { className: "truncate", children: restoreError }), _jsx("button", { onClick: cancelRestore, className: "cursor-pointer rounded-md px-1.5 py-0.5 text-muted-foreground hover:bg-accent", children: t("agentChat.common.dismiss") })] })) : null] }))] }));
1386
+ !showInlineRunError && (_jsx(MissingFinalResponseNotice, { messageId: msg.id, text: missingFinalResponseNoticeText, animate: animateMissingFinalResponse, onRevealComplete: handleMissingFinalResponseReveal })), isComplete && hasCodeAgentTools && msgContent && (_jsx(FilesChangedSummary, { parts: msgContent }))] }), isComplete && (_jsx(AssistantMessageActionBar, { className: "mt-1", timestamp: timestamp, threadId: cpCtx?.threadId ?? "", runId: messageRunId ?? "", messageSeq: msg.index, onFork: messageActions?.onForkChat, onRestore: showRestore && restoreState === "idle" ? handleRestore : undefined, trailingActions: _jsxs(_Fragment, { children: [showHistoryRevert && (_jsx(AssistantChatHistoryRevertButton, { onRestore: handleHistoryRestore, onRestored: () => setHistoryReverted(true) })), isLast && (_jsx(TooltipProvider, { delayDuration: 400, children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(ActionBarPrimitive.Reload, { asChild: true, children: _jsx("button", { type: "button", "aria-label": t("agentChat.message.regenerate"), className: `flex h-6 w-6 cursor-pointer items-center justify-center rounded-md text-muted-foreground/70 hover:bg-accent hover:text-foreground ${messageFooterFadeClassName} disabled:cursor-not-allowed disabled:opacity-40`, children: _jsx(IconRefresh, { className: "h-3.5 w-3.5" }) }) }) }), _jsx(TooltipContent, { side: "top", className: "text-xs", children: t("agentChat.message.regenerate") })] }) })), _jsx(MessageBranchPicker, {}), showRestore && restoreState === "confirming" ? (_jsxs("div", { className: "flex items-center gap-1 text-xs", children: [_jsx("button", { onClick: handleRestore, className: "rounded-md bg-destructive px-1.5 py-0.5 text-destructive-foreground hover:bg-destructive/90", children: t("agentChat.message.restoreQuestion") }), _jsx("button", { onClick: cancelRestore, className: "rounded-md px-1.5 py-0.5 text-muted-foreground hover:bg-accent", children: t("agentChat.common.cancel") })] })) : showRestore && restoreState === "restoring" ? (_jsxs("span", { className: "flex items-center gap-1 text-xs text-muted-foreground", children: [_jsx(IconLoader2, { className: "h-3 w-3 animate-spin" }), t("agentChat.message.restoring")] })) : restoreState === "error" ? (_jsxs("span", { className: "flex items-center gap-1 text-xs text-destructive", children: [_jsx(IconAlertTriangle, { className: "h-3 w-3 shrink-0" }), _jsx("span", { className: "truncate", children: restoreError }), _jsx("button", { onClick: cancelRestore, className: "cursor-pointer rounded-md px-1.5 py-0.5 text-muted-foreground hover:bg-accent", children: t("agentChat.common.dismiss") })] })) : null] }) }))] }));
1419
1387
  }
1420
1388
  // ─── RunningActivityStatus / ThinkingIndicator ────────────────────────────────
1421
1389
  export function RunningActivityStatus({ label }) {
@@ -217,10 +217,12 @@ function ReviewPreviewFrame({ url, artifactAppId, artifactId, previewLabel, comp
217
217
  }) })), !loaded && (_jsx("div", { className: unavailable
218
218
  ? "absolute inset-0 flex items-center justify-center bg-muted px-3 text-center text-xs text-muted-foreground"
219
219
  : "absolute inset-0 animate-pulse bg-muted", role: unavailable ? "status" : undefined, children: unavailable ? t("observability.reviewPreviewUnavailable") : null })), mounted && (!artifactAppId || safeSrcDoc) && (_jsx("iframe", { "aria-hidden": "true", className: compact
220
- ? "pointer-events-none absolute left-0 top-0 h-[600%] w-[600%] origin-top-left scale-[0.166667] border-0"
220
+ ? "pointer-events-none absolute left-0 top-0 h-[1200%] w-[1200%] origin-top-left scale-[0.083333] border-0"
221
221
  : showSlideStrip
222
- ? "absolute inset-x-0 bottom-0 top-10 border-0"
223
- : "absolute inset-0 size-full border-0", loading: "lazy", onLoad: () => setLoaded(true), referrerPolicy: "no-referrer", sandbox: artifactAppId ? "" : "allow-scripts", ...(artifactAppId ? { srcDoc: safeSrcDoc } : { src: url }), tabIndex: -1, title: previewLabel }))] })) }));
222
+ ? "pointer-events-none absolute left-0 top-10 h-[calc(400%_-_10rem)] w-[400%] origin-top-left scale-[0.25] border-0"
223
+ : artifactAppId
224
+ ? "pointer-events-none absolute left-0 top-0 h-[400%] w-[400%] origin-top-left scale-[0.25] border-0"
225
+ : "absolute inset-0 size-full border-0", loading: "lazy", onLoad: () => setLoaded(true), referrerPolicy: "no-referrer", sandbox: artifactAppId ? "" : "allow-scripts", ...(artifactAppId ? { srcDoc: safeSrcDoc } : { src: url }), tabIndex: -1, title: previewLabel }))] })) }));
224
226
  }
225
227
  function isRecord(value) {
226
228
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -4,6 +4,7 @@ import { agentNativePath } from "../api-path.js";
4
4
  import { scheduleAfterPaint } from "../use-after-paint.js";
5
5
  import { dispatchFirstRunOnboardingStatus, fetchFirstRunOnboardingStatus, } from "./first-run-status.js";
6
6
  const seenOnboardingEvents = new Set();
7
+ const ONBOARDING_SUMMARY_TIMEOUT_MS = 15_000;
7
8
  export function trackOnboardingEvent(name, properties) {
8
9
  if (typeof window === "undefined")
9
10
  return;
@@ -45,10 +46,12 @@ export function useOnboarding(options = {}) {
45
46
  const [completeFirstRunError, setCompleteFirstRunError] = useState(null);
46
47
  const stepsRef = useRef([]);
47
48
  const mountedRef = useRef(true);
49
+ const fetchGenerationRef = useRef(0);
48
50
  useEffect(() => {
49
51
  setFirstRun(preview || initialFirstRun);
50
52
  }, [initialFirstRun, preview]);
51
53
  const fetchAll = useCallback(async () => {
54
+ const fetchGeneration = ++fetchGenerationRef.current;
52
55
  try {
53
56
  // One composed read replaces the three per-mount calls (steps,
54
57
  // dismissed, profile); first-run status keeps its own endpoint because
@@ -64,16 +67,36 @@ export function useOnboarding(options = {}) {
64
67
  : initialFirstRun
65
68
  ? Promise.resolve(true)
66
69
  : fetchFirstRunOnboardingStatus();
67
- const [summaryRes, firstRunRes] = await Promise.all([
68
- fetch(summaryUrl),
70
+ const summaryController = typeof AbortController === "undefined" ? null : new AbortController();
71
+ let summaryTimeoutId;
72
+ const summaryTimeout = new Promise((_resolve, reject) => {
73
+ summaryTimeoutId = setTimeout(() => {
74
+ summaryController?.abort();
75
+ reject(new Error("onboarding summary timed out"));
76
+ }, ONBOARDING_SUMMARY_TIMEOUT_MS);
77
+ });
78
+ // Keep the deadline armed through json(); a response body can stall after headers.
79
+ const summaryRequest = (async () => {
80
+ const response = await fetch(summaryUrl, {
81
+ ...(summaryController ? { signal: summaryController.signal } : {}),
82
+ });
83
+ if (!response.ok) {
84
+ throw new Error(`summary: ${response.status}`);
85
+ }
86
+ return (await response.json());
87
+ })();
88
+ const [summary, firstRunRes] = await Promise.all([
89
+ Promise.race([summaryRequest, summaryTimeout]),
69
90
  firstRunPromise,
70
- ]);
71
- if (!mountedRef.current)
91
+ ]).finally(() => {
92
+ if (summaryTimeoutId !== undefined)
93
+ clearTimeout(summaryTimeoutId);
94
+ summaryController?.abort();
95
+ });
96
+ if (!mountedRef.current ||
97
+ fetchGeneration !== fetchGenerationRef.current) {
72
98
  return;
73
- if (!summaryRes.ok) {
74
- throw new Error(`summary: ${summaryRes.status}`);
75
99
  }
76
- const summary = (await summaryRes.json());
77
100
  const previousSteps = stepsRef.current;
78
101
  if (previousSteps.length > 0) {
79
102
  for (const [stepIndex, step] of summary.steps.entries()) {
@@ -100,13 +123,17 @@ export function useOnboarding(options = {}) {
100
123
  setError(null);
101
124
  }
102
125
  catch (e) {
103
- if (!mountedRef.current)
126
+ if (!mountedRef.current ||
127
+ fetchGeneration !== fetchGenerationRef.current) {
104
128
  return;
129
+ }
105
130
  setError(e instanceof Error ? e.message : "Failed to load onboarding");
106
131
  }
107
132
  finally {
108
- if (mountedRef.current)
133
+ if (mountedRef.current &&
134
+ fetchGeneration === fetchGenerationRef.current) {
109
135
  setLoading(false);
136
+ }
110
137
  }
111
138
  }, [preview]);
112
139
  useEffect(() => {
@@ -141,6 +168,7 @@ export function useOnboarding(options = {}) {
141
168
  window.addEventListener("focus", onFocus);
142
169
  return () => {
143
170
  mountedRef.current = false;
171
+ fetchGenerationRef.current += 1;
144
172
  cancelInitialFetch();
145
173
  document.removeEventListener("visibilitychange", onVisibility);
146
174
  window.removeEventListener("focus", onFocus);
@@ -13,8 +13,8 @@
13
13
  * Body: { json: any, fieldName?: string, type?: "map"|"array", requestSource?: string }
14
14
  */
15
15
  export declare const postCollabJson: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
16
- error: string;
17
16
  ok?: undefined;
17
+ error: string;
18
18
  } | {
19
19
  error?: undefined;
20
20
  ok: boolean;
@@ -28,6 +28,6 @@ export declare function createNotificationsHandler(): import("h3").EventHandlerW
28
28
  } | {
29
29
  count?: undefined;
30
30
  updated?: undefined;
31
- ok: boolean;
32
31
  error?: undefined;
32
+ ok: boolean;
33
33
  }>>;
@@ -75,8 +75,8 @@ export declare const setWorkspaceAppDefaultVisibilityHandler: import("h3").Event
75
75
  export declare const createOrgHandler: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
76
76
  success: boolean;
77
77
  name?: undefined;
78
- role?: undefined;
79
78
  id?: undefined;
79
+ role?: undefined;
80
80
  } | {
81
81
  success?: undefined;
82
82
  id: string;
@@ -48,8 +48,8 @@ export declare function handleUpdateResource(event: any): Promise<import("./stor
48
48
  }>;
49
49
  /** DELETE /_agent-native/resources/:id — delete a resource */
50
50
  export declare function handleDeleteResource(event: any): Promise<{
51
- ok?: undefined;
52
51
  error: string;
52
+ ok?: undefined;
53
53
  } | {
54
54
  error?: undefined;
55
55
  ok: boolean;
@@ -224,6 +224,16 @@ function getCookieValues(event, name) {
224
224
  export function getFrameworkSessionCookieValues(event) {
225
225
  return getFrameworkSessionCookieEntries(event).map((entry) => entry.value);
226
226
  }
227
+ function betterAuthSessionCookieNames() {
228
+ return ["session_token", "session_data", "dont_remember"].flatMap((suffix) => {
229
+ const name = `${BETTER_AUTH_COOKIE_PREFIX}.${suffix}`;
230
+ return [name, `__Secure-${name}`];
231
+ });
232
+ }
233
+ function getBetterAuthSessionTokenValues(event) {
234
+ const cookie = `${BETTER_AUTH_COOKIE_PREFIX}.session_token`;
235
+ return [cookie, `__Secure-${cookie}`].flatMap((name) => getCookieValues(event, name));
236
+ }
227
237
  function getFrameworkSessionCookieEntries(event) {
228
238
  const entries = [];
229
239
  const seenValues = new Set();
@@ -336,6 +346,12 @@ export function clearFrameworkSessionCookies(event) {
336
346
  deleteCookieFromEveryScope(event, name);
337
347
  }
338
348
  }
349
+ function clearBetterAuthSessionCookies(event) {
350
+ for (const name of betterAuthSessionCookieNames()) {
351
+ const attributes = name.startsWith("__Secure-") ? { secure: true } : {};
352
+ deleteCookieFromEveryScope(event, name, attributes);
353
+ }
354
+ }
339
355
  async function getLegacyCookieSession(event) {
340
356
  for (const { name, value } of getFrameworkSessionCookieEntries(event)) {
341
357
  let resolvedToken;
@@ -1409,31 +1425,31 @@ export async function removeSession(token) {
1409
1425
  *
1410
1426
  * Login mints a session by mirroring one token into the framework's
1411
1427
  * `an_session` cookie, the legacy `sessions` table (`addSession`), AND
1412
- * Better Auth's own `"session"` table — but never gives the browser Better
1413
- * Auth's own session cookie. `auth.api.signOut()` identifies what to revoke
1414
- * from THAT cookie, which was never issued, so it silently finds nothing and
1415
- * Better Auth's `"session"` row survives sign-out. `getSession`'s legacy-
1416
- * cookie fallback then falls through to a direct Better-Auth-table lookup by
1417
- * token (kept for magic-link resilience — see `getLegacyCookieSession`) and
1418
- * resurrects the "logged out" user. Deleting the `"session"` row directly by
1419
- * the same token candidates closes that gap regardless of whether
1420
- * `auth.api.signOut()` finds anything.
1428
+ * Better Auth's own `"session"` table. The framework sign-in path does not
1429
+ * issue Better Auth's session cookie, so `auth.api.signOut()` may not find the
1430
+ * token. Deleting the `"session"` row directly by tokens from either cookie
1431
+ * family closes that gap. Better Auth's own cookies are also cleared across
1432
+ * host/domain and partition scopes because its signOut only clears the current
1433
+ * scope. Failed revocation preserves session cookies so the same token can be
1434
+ * retried instead of making the browser appear signed out while it stays live.
1421
1435
  */
1422
1436
  async function performLogout(event, getAuth) {
1423
1437
  const bearerToken = getBearerSessionToken(event);
1438
+ const betterAuthTokens = getBetterAuthSessionTokenValues(event);
1424
1439
  const rawTokens = [
1425
1440
  ...getFrameworkSessionCookieValues(event),
1441
+ ...betterAuthTokens,
1426
1442
  ...(bearerToken ? [bearerToken] : []),
1427
1443
  ];
1428
1444
  const candidates = rawTokens.flatMap(sessionTokenLookupCandidates);
1445
+ let revocationFailed = false;
1429
1446
  let auth = null;
1430
1447
  try {
1431
1448
  auth = await getAuth();
1432
1449
  }
1433
1450
  catch (error) {
1434
- // The fallback route's `getAuth` retries resolving Better Auth here and
1435
- // may still find it unavailable — expected on that route, not tracked.
1436
- console.warn("[auth] could not resolve Better Auth instance during logout:", error);
1451
+ revocationFailed = true;
1452
+ captureAuthError(error, { route: "logout" });
1437
1453
  }
1438
1454
  for (const token of candidates) {
1439
1455
  await removeSession(token);
@@ -1454,32 +1470,41 @@ async function performLogout(event, getAuth) {
1454
1470
  // have survived logout — not routine noise. `route: "logout"` is
1455
1471
  // captured at `warning` level (see `captureAuthError`), so a spike is
1456
1472
  // visible without paging anyone on a one-off.
1473
+ revocationFailed = true;
1457
1474
  captureAuthError(error, { route: "logout" });
1458
1475
  }
1459
1476
  }
1460
1477
  invalidateSessionEmailCache();
1461
- clearFrameworkSessionCookies(event);
1462
- clearIdentityGoogleAuthCookie(event);
1463
- clearFirstRunOnboardingCookie(event);
1464
- optOutOfAuthDisabledSession(event);
1465
- if (auth) {
1466
- try {
1467
- const result = await auth.api.signOut({
1468
- headers: event.headers,
1469
- returnHeaders: true,
1470
- });
1471
- forwardBetterAuthSetCookies(event, result);
1472
- }
1473
- catch (error) {
1474
- // Better Auth's own signOut looks for its own session cookie, which
1475
- // this framework never issues to the browser (see the doc comment
1476
- // above) — expected to fail on essentially every call today, so this
1477
- // is logged for local debugging rather than tracked as an anomaly.
1478
- console.warn("[auth] Better Auth signOut failed during logout:", error);
1478
+ if (!revocationFailed) {
1479
+ clearFrameworkSessionCookies(event);
1480
+ clearIdentityGoogleAuthCookie(event);
1481
+ clearFirstRunOnboardingCookie(event);
1482
+ optOutOfAuthDisabledSession(event);
1483
+ if (auth) {
1484
+ try {
1485
+ const result = await auth.api.signOut({
1486
+ headers: event.headers,
1487
+ returnHeaders: true,
1488
+ });
1489
+ forwardBetterAuthSetCookies(event, result);
1490
+ }
1491
+ catch (error) {
1492
+ // Better Auth's own signOut looks for its own session cookie, which
1493
+ // this framework never issues to the browser (see the doc comment
1494
+ // above) — expected to fail on essentially every call today, so this
1495
+ // is logged for local debugging rather than tracked as an anomaly.
1496
+ console.warn("[auth] Better Auth signOut failed during logout:", error);
1497
+ }
1479
1498
  }
1499
+ clearBetterAuthSessionCookies(event);
1500
+ if (isElectronRequest(event))
1501
+ await clearDesktopSso();
1502
+ }
1503
+ if (revocationFailed) {
1504
+ setResponseStatus(event, 503);
1505
+ return { error: "Unable to revoke session" };
1480
1506
  }
1481
- if (isElectronRequest(event))
1482
- await clearDesktopSso();
1507
+ return { ok: true };
1483
1508
  }
1484
1509
  /**
1485
1510
  * Look up the email associated with a legacy session token.
@@ -5328,8 +5353,7 @@ async function mountBetterAuthRoutes(app, options) {
5328
5353
  }));
5329
5354
  // Backward-compat: POST /_agent-native/auth/logout
5330
5355
  app.use("/_agent-native/auth/logout", defineEventHandler(async (event) => {
5331
- await performLogout(event, () => auth);
5332
- return { ok: true };
5356
+ return performLogout(event, () => auth);
5333
5357
  }));
5334
5358
  // POST /_agent-native/auth/logout-all — revoke every session row for
5335
5359
  // the authenticated user across both auth tables. Companion to the
@@ -5571,8 +5595,7 @@ function mountAuthFallbackRoutes(app) {
5571
5595
  }
5572
5596
  }));
5573
5597
  app.use("/_agent-native/auth/logout", defineEventHandler(async (event) => {
5574
- await performLogout(event, () => getBetterAuth());
5575
- return { ok: true };
5598
+ return performLogout(event, () => getBetterAuth());
5576
5599
  }));
5577
5600
  app.use("/_agent-native/auth/session", defineEventHandler(async (event) => {
5578
5601
  if (!isReadMethod(event)) {
@@ -5716,8 +5739,7 @@ export async function autoMountAuth(app, options = {}) {
5716
5739
  }));
5717
5740
  app.use("/_agent-native/auth/login", defineEventHandler(() => ({ ok: true })));
5718
5741
  app.use("/_agent-native/auth/logout", defineEventHandler(async (event) => {
5719
- await performLogout(event, () => null);
5720
- return { ok: true };
5742
+ return performLogout(event, () => null);
5721
5743
  }));
5722
5744
  const byoaLoginHtml = options.loginHtml ?? getCustomAuthRequiredHtml();
5723
5745
  _authGuardConfig = {
@@ -20,6 +20,6 @@ export declare function createTranscribeVoiceHandler(): import("h3").EventHandle
20
20
  error: string;
21
21
  text?: undefined;
22
22
  } | {
23
- text: string;
24
23
  error?: undefined;
24
+ text: string;
25
25
  }>>;
@@ -2,7 +2,6 @@ declare const _default: import("../../action.js").ActionDefinition<{
2
2
  resourceType: string;
3
3
  resourceId: string;
4
4
  }, {
5
- role?: undefined;
6
5
  ownerEmail: any;
7
6
  visibility: any;
8
7
  shares: any[];
@@ -12,6 +11,7 @@ declare const _default: import("../../action.js").ActionDefinition<{
12
11
  supportsGroupShares?: boolean;
13
12
  };
14
13
  orgId?: undefined;
14
+ role?: undefined;
15
15
  agentReadable?: undefined;
16
16
  } | {
17
17
  ownerEmail: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.190.0",
3
+ "version": "0.190.1-nightly-20260926035143",
4
4
  "description": "The agentic application framework for building autonomous agents with intuitive UIs",
5
5
  "homepage": "https://github.com/BuilderIO/agent-native#readme",
6
6
  "bugs": {
@@ -510,9 +510,9 @@
510
510
  "yaml": "^2.9.0",
511
511
  "yjs": "^13.6.32",
512
512
  "zod": "^4.3.6",
513
+ "@agent-native/agentkit": "^0.2.12",
513
514
  "@agent-native/recap-cli": "0.5.42",
514
- "@agent-native/toolkit": "^0.21.2",
515
- "@agent-native/agentkit": "^0.2.12"
515
+ "@agent-native/toolkit": "^0.21.2"
516
516
  },
517
517
  "devDependencies": {
518
518
  "@ai-sdk/anthropic": "^3.0.71",