@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.
- package/corpus/README.md +1 -1
- package/corpus/templates/assets/agent-native.json +1 -1
- package/corpus/templates/calendar/app/hooks/use-bookings.ts +6 -2
- package/corpus/templates/calendar/app/hooks/use-public-data.ts +3 -2
- package/corpus/templates/calendar/app/pages/BookingPage.tsx +4 -3
- package/corpus/templates/calendar/changelog/2026-09-25-personal-booking-pages-use-the-host-s-saved-timezone-for-ava.md +6 -0
- package/corpus/templates/calendar/server/handlers/availability.ts +35 -4
- package/corpus/templates/calendar/server/handlers/booking-usernames.ts +6 -0
- package/corpus/templates/calendar/server/handlers/bookings.ts +40 -18
- package/corpus/templates/clips/agent-native.json +1 -1
- package/corpus/templates/content/agent-native.json +1 -1
- package/corpus/templates/design/agent-native.json +1 -1
- package/corpus/templates/forms/agent-native.json +1 -1
- package/corpus/templates/slides/agent-native.json +1 -1
- package/corpus/templates/tasks/agent-native.json +1 -1
- package/dist/client/chat/message-components.d.ts +4 -4
- package/dist/client/chat/message-components.js +11 -43
- package/dist/client/observability/OutputPreview.js +5 -3
- package/dist/client/onboarding/use-onboarding.js +37 -9
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/org/handlers.d.ts +1 -1
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/server/auth.js +60 -38
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/sharing/actions/list-resource-shares.d.ts +1 -1
- package/package.json +3 -3
package/corpus/README.md
CHANGED
|
@@ -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:
|
|
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),
|
|
@@ -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
|
-
//
|
|
92
|
-
//
|
|
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
|
|
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
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
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
|
-
:
|
|
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
|
}
|
|
@@ -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({
|
|
134
|
-
|
|
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,
|
|
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
|
|
123
|
-
|
|
124
|
-
|
|
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({
|
|
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
|
-
|
|
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")] })
|
|
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 && (
|
|
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-[
|
|
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
|
-
? "
|
|
223
|
-
:
|
|
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
|
|
68
|
-
|
|
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
|
-
|
|
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;
|
package/dist/org/handlers.d.ts
CHANGED
|
@@ -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;
|
package/dist/server/auth.js
CHANGED
|
@@ -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
|
|
1413
|
-
* Auth's
|
|
1414
|
-
*
|
|
1415
|
-
* Better Auth's
|
|
1416
|
-
*
|
|
1417
|
-
*
|
|
1418
|
-
*
|
|
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
|
-
|
|
1435
|
-
|
|
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
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5720
|
-
return { ok: true };
|
|
5742
|
+
return performLogout(event, () => null);
|
|
5721
5743
|
}));
|
|
5722
5744
|
const byoaLoginHtml = options.loginHtml ?? getCustomAuthRequiredHtml();
|
|
5723
5745
|
_authGuardConfig = {
|
|
@@ -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.
|
|
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",
|