@agent-native/core 0.157.0 → 0.157.2

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 (114) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/server/jobs/analytics-bigquery-backfill.ts +19 -4
  3. package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +7 -2
  4. package/corpus/templates/assets/app/global.css +4 -0
  5. package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +1 -2
  6. package/corpus/templates/brain/app/routes/sources.tsx +1 -2
  7. package/corpus/templates/calendar/app/hooks/use-bookings.ts +12 -2
  8. package/corpus/templates/calendar/app/pages/BookingLinksPage.tsx +64 -2
  9. package/corpus/templates/calendar/server/handlers/bookings.ts +224 -93
  10. package/corpus/templates/calendar/server/plugins/auth.ts +0 -2
  11. package/corpus/templates/clips/actions/create-folder.ts +7 -1
  12. package/corpus/templates/clips/actions/create-meeting.ts +6 -2
  13. package/corpus/templates/clips/actions/create-recording.ts +5 -3
  14. package/corpus/templates/clips/actions/finalize-recording.ts +97 -65
  15. package/corpus/templates/clips/actions/import-loom-recording.ts +5 -3
  16. package/corpus/templates/clips/actions/request-recording-access.ts +305 -0
  17. package/corpus/templates/clips/actions/stitch-recordings.ts +5 -3
  18. package/corpus/templates/clips/app/components/meetings/share-meeting-dialog.tsx +38 -63
  19. package/corpus/templates/clips/app/components/player/share-dialog.tsx +75 -75
  20. package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +3 -21
  21. package/corpus/templates/clips/app/components/sharing/share-ui.tsx +13 -30
  22. package/corpus/templates/clips/app/i18n/en-US.ts +10 -0
  23. package/corpus/templates/clips/app/routes/_app.meetings.$meetingId.tsx +5 -5
  24. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +9 -16
  25. package/corpus/templates/clips/app/routes/share.$shareId.tsx +135 -18
  26. package/corpus/templates/clips/changelog/2026-08-14-long-resumable-uploads-now-abort-cleanly-and-show-errors.md +6 -0
  27. package/corpus/templates/clips/changelog/2026-08-14-standardized-share-popovers.md +6 -0
  28. package/corpus/templates/clips/chrome-extension/public/manifest.json +11 -3
  29. package/corpus/templates/clips/chrome-extension/src/github-preview-content.ts +37 -8
  30. package/corpus/templates/clips/chrome-extension/src/github-preview.ts +3 -3
  31. package/corpus/templates/clips/chrome-extension/src/offscreen.ts +8 -25
  32. package/corpus/templates/clips/desktop/src/app.tsx +24 -0
  33. package/corpus/templates/clips/desktop/src/lib/media-capture-constraints.ts +42 -13
  34. package/corpus/templates/clips/desktop/src/lib/recorder.ts +11 -18
  35. package/corpus/templates/clips/desktop/src/overlays/onboarding.tsx +1 -0
  36. package/corpus/templates/clips/desktop/src/shared/config.ts +1 -0
  37. package/corpus/templates/clips/desktop/src/styles.css +24 -9
  38. package/corpus/templates/clips/desktop/src-tauri/Cargo.toml +3 -0
  39. package/corpus/templates/clips/desktop/src-tauri/src/config.rs +8 -0
  40. package/corpus/templates/clips/desktop/src-tauri/src/native_screen/custom_capture.rs +154 -176
  41. package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +115 -31
  42. package/corpus/templates/clips/desktop/src-tauri/src/rewind_clip.rs +7 -0
  43. package/corpus/templates/clips/server/db/index.ts +1 -0
  44. package/corpus/templates/clips/server/db/schema.ts +1 -0
  45. package/corpus/templates/clips/server/lib/calendar-event-meetings.ts +6 -1
  46. package/corpus/templates/clips/server/lib/emails.ts +23 -0
  47. package/corpus/templates/clips/server/lib/public-agent-context.ts +55 -0
  48. package/corpus/templates/clips/server/lib/recordings.ts +2 -1
  49. package/corpus/templates/clips/server/lib/resumable-upload-cleanup.ts +49 -0
  50. package/corpus/templates/clips/server/lib/s3-upload-provider.ts +34 -7
  51. package/corpus/templates/clips/server/lib/upload-interruption.ts +15 -1
  52. package/corpus/templates/clips/server/lib/upload-lease.ts +94 -9
  53. package/corpus/templates/clips/server/routes/api/agent-context.json.get.ts +82 -7
  54. package/corpus/templates/clips/server/routes/api/public-recording.get.ts +1 -0
  55. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +71 -12
  56. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/interrupt.post.ts +4 -3
  57. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/reset-chunks.post.ts +133 -4
  58. package/corpus/templates/clips/server/routes/api/uploads/reap.post.ts +8 -1
  59. package/corpus/templates/clips/shared/recording-link.ts +4 -0
  60. package/corpus/templates/clips/shared/share-loader-response.ts +2 -1
  61. package/corpus/templates/content/app/components/editor/DocumentToolbar.tsx +7 -16
  62. package/corpus/templates/factory/AGENTS.md +5 -1
  63. package/corpus/templates/factory/README.md +6 -2
  64. package/corpus/templates/factory/actions/view-screen.ts +15 -0
  65. package/corpus/templates/factory/app/components/factory/FactoryAgentsView.tsx +58 -189
  66. package/corpus/templates/factory/app/components/layout/Header.tsx +1 -0
  67. package/corpus/templates/factory/app/components/layout/Layout.tsx +1 -1
  68. package/corpus/templates/factory/app/components/layout/Sidebar.tsx +7 -0
  69. package/corpus/templates/factory/app/hooks/use-navigation-state.ts +3 -0
  70. package/corpus/templates/factory/app/i18n/en-US.ts +3 -1
  71. package/corpus/templates/factory/app/routes/agents.tsx +9 -0
  72. package/corpus/templates/mail/server/plugins/auth.ts +0 -2
  73. package/corpus/templates/plan/app/components/plan/LocalCodebasePicker.tsx +50 -14
  74. package/corpus/templates/plan/app/pages/PlansPage.tsx +9 -19
  75. package/corpus/templates/slides/app/components/editor/HistoryPanel.tsx +7 -1
  76. package/corpus/templates/slides/app/context/DeckContext.tsx +71 -22
  77. package/corpus/templates/slides/app/lib/editor-shortcuts.ts +46 -0
  78. package/corpus/templates/slides/app/pages/DeckEditor.tsx +13 -0
  79. package/dist/client/AgentPanel.d.ts +13 -1
  80. package/dist/client/AgentPanel.js +12 -10
  81. package/dist/client/AssistantChat.js +25 -5
  82. package/dist/client/HighlightedCodeBlock.js +12 -2
  83. package/dist/client/agent-chat/index.d.ts +1 -0
  84. package/dist/client/agent-chat/index.js +1 -0
  85. package/dist/client/chat/markdown-renderer.js +16 -3
  86. package/dist/client/chat-first/apps-rail.js +1 -1
  87. package/dist/client/components/ui/message-scroller.js +1 -1
  88. package/dist/client/settings/AccountSettingsCard.js +48 -19
  89. package/dist/client/sharing/ShareButton.d.ts +1 -2
  90. package/dist/client/sharing/ShareButton.js +5 -41
  91. package/dist/client/sharing/ShareDialog.js +5 -4
  92. package/dist/client/use-chat-threads.js +10 -14
  93. package/dist/collab/awareness.d.ts +2 -2
  94. package/dist/collab/routes.d.ts +1 -1
  95. package/dist/collab/struct-routes.d.ts +1 -1
  96. package/dist/localization/default-messages.d.ts +1 -0
  97. package/dist/localization/default-messages.js +1 -0
  98. package/dist/notifications/routes.d.ts +3 -3
  99. package/dist/provider-api/actions/custom-provider-registration.d.ts +11 -11
  100. package/dist/provider-api/actions/provider-api.d.ts +15 -15
  101. package/dist/server/auth-marketing.d.ts +1 -0
  102. package/dist/server/auth-marketing.js +0 -2
  103. package/dist/server/auth.d.ts +1 -0
  104. package/dist/server/auth.js +18 -0
  105. package/dist/server/onboarding-html.d.ts +1 -0
  106. package/dist/server/onboarding-html.js +2 -88
  107. package/dist/server/realtime-token.d.ts +1 -1
  108. package/dist/shared/markdown-block-split.d.ts +16 -0
  109. package/dist/shared/markdown-block-split.js +81 -5
  110. package/dist/styles/agent-native.css +13 -0
  111. package/dist/voice/voice-cleanup-prompt.js +1 -0
  112. package/docs/content/cloneable-saas.mdx +24 -26
  113. package/package.json +3 -3
  114. package/corpus/templates/clips/shared/mic-audio-cleanup.ts +0 -350
@@ -1,9 +1,10 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { ActionButton, Avatar, TextField, } from "@agent-native/toolkit/design-system";
3
- import { IconCamera, IconCheck, IconLock } from "@tabler/icons-react";
2
+ import { ActionButton, Avatar, IconButton, TextField, } from "@agent-native/toolkit/design-system";
3
+ import { IconCamera, IconCheck, IconLock, IconPencil, } from "@tabler/icons-react";
4
4
  import { useEffect, useRef, useState } from "react";
5
5
  import { PASSWORD_MIN_LENGTH } from "../../shared/password-policy.js";
6
6
  import { Popover, PopoverContent, PopoverTrigger, } from "../components/ui/popover.js";
7
+ import { Tooltip, TooltipContent, TooltipTrigger, } from "../components/ui/tooltip.js";
7
8
  import { useT } from "../i18n.js";
8
9
  import { useActionMutation, useActionQuery } from "../use-action.js";
9
10
  import { uploadAvatar, useAvatarUrl } from "../use-avatar.js";
@@ -107,21 +108,23 @@ export function AccountSettingsForm({ compact = false, }) {
107
108
  const [uploading, setUploading] = useState(false);
108
109
  const [photoStatus, setPhotoStatus] = useState("idle");
109
110
  const [name, setName] = useState("");
110
- const nameEditedRef = useRef(false);
111
- const displayName = profileQuery.data?.name ||
112
- session?.name ||
113
- email ||
114
- t("settings.profileSignedOut");
111
+ const [savedName, setSavedName] = useState("");
112
+ const [isEditingName, setIsEditingName] = useState(false);
113
+ const nameFieldEditedRef = useRef(false);
114
+ const profileName = profileQuery.data?.name || session?.name || "";
115
+ const displayName = name || profileName || email || t("settings.profileSignedOut");
115
116
  useEffect(() => {
116
- nameEditedRef.current = false;
117
+ nameFieldEditedRef.current = false;
118
+ setIsEditingName(false);
119
+ setName("");
120
+ setSavedName("");
117
121
  }, [email]);
118
122
  useEffect(() => {
119
- if (nameEditedRef.current)
123
+ if (nameFieldEditedRef.current)
120
124
  return;
121
- const nextName = profileQuery.data?.name || session?.name;
122
- if (nextName)
123
- setName(nextName);
124
- }, [profileQuery.data?.name, session?.name]);
125
+ setName(profileName);
126
+ setSavedName(profileName);
127
+ }, [email, profileName]);
125
128
  const handleAvatarChange = async (event) => {
126
129
  const file = event.target.files?.[0];
127
130
  event.target.value = "";
@@ -142,20 +145,46 @@ export function AccountSettingsForm({ compact = false, }) {
142
145
  };
143
146
  const handleProfileSave = () => {
144
147
  const nextName = name.trim();
145
- if (!nextName || !email)
148
+ if (!nextName || !email || nextName === savedName.trim())
146
149
  return;
147
- updateProfile.mutate({ name: nextName });
150
+ updateProfile.mutate({ name: nextName }, {
151
+ onSuccess: (profile) => {
152
+ setName(profile.name);
153
+ setSavedName(profile.name);
154
+ nameFieldEditedRef.current = false;
155
+ setIsEditingName(false);
156
+ },
157
+ });
148
158
  };
159
+ const handleProfileEdit = () => {
160
+ updateProfile.reset();
161
+ nameFieldEditedRef.current = false;
162
+ setName(savedName);
163
+ setIsEditingName(true);
164
+ };
165
+ const handleProfileCancel = () => {
166
+ updateProfile.reset();
167
+ nameFieldEditedRef.current = false;
168
+ setName(savedName);
169
+ setIsEditingName(false);
170
+ };
171
+ const canSaveName = !!email &&
172
+ !updateProfile.isPending &&
173
+ !!name.trim() &&
174
+ name.trim() !== savedName.trim();
149
175
  const profileStatus = photoStatus === "saved" ? (_jsx("span", { className: "text-primary", children: t("settings.profilePhotoUpdated") })) : photoStatus === "error" ? (_jsx("span", { className: "text-destructive", children: t("settings.profilePhotoError") })) : email ? (email) : undefined;
150
176
  return (_jsxs(SettingsGroup, { id: "account", title: t("settings.profileTitle"), description: t("settings.profileDescription"), className: cn(compact && "[&>div:last-child>div>div]:py-3"), children: [_jsx(SettingsRow, { id: "profile", label: t("settings.profileTitle"), description: profileStatus, control: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Avatar, { name: displayName, src: avatarUrl, fallback: profileInitials(displayName), size: "default", className: "size-10 shrink-0 rounded-full border border-border bg-accent font-semibold text-muted-foreground" }), _jsx("input", { ref: fileInputRef, type: "file", accept: "image/*", className: "hidden", onChange: handleAvatarChange }), _jsx(ActionButton, { type: "button", intent: "neutral", emphasis: "outline", size: "compact", disabled: !email || uploading, leadingIcon: _jsx(IconCamera, { className: "size-3.5" }), onPress: () => fileInputRef.current?.click(), children: uploading
151
177
  ? t("settings.profileUploading")
152
- : t("settings.profileChangePhoto") })] }) }), _jsx(SettingsRow, { id: "profile-name", label: t("settings.profileNameLabel"), description: updateProfile.isSuccess ? (_jsx("span", { className: "text-primary", children: t("settings.profileSaved") })) : updateProfile.error ? (_jsx("span", { className: "text-destructive", children: t("settings.profileSaveError") })) : (t("settings.profileNameDescription")), control: _jsxs("div", { className: "flex w-full items-center gap-2 sm:w-80", children: [_jsx(TextField, { id: "agent-native-profile-name", value: name, onChange: (value) => {
153
- nameEditedRef.current = true;
178
+ : t("settings.profileChangePhoto") })] }) }), _jsx(SettingsRow, { id: "profile-name", label: t("settings.profileNameLabel"), description: updateProfile.isSuccess ? (_jsx("span", { className: "text-primary", children: t("settings.profileSaved") })) : updateProfile.error ? (_jsx("span", { className: "text-destructive", children: t("settings.profileSaveError") })) : (t("settings.profileNameDescription")), control: isEditingName ? (_jsxs("div", { className: "flex w-full items-center gap-2 sm:w-80", children: [_jsx(TextField, { id: "agent-native-profile-name", value: name, onChange: (value) => {
179
+ nameFieldEditedRef.current = true;
154
180
  updateProfile.reset();
155
181
  setName(value);
156
- }, placeholder: t("settings.profileNamePlaceholder"), disabled: !email || updateProfile.isPending, "aria-label": t("settings.profileNameLabel"), className: "min-w-0 flex-1" }), _jsx(ActionButton, { type: "button", intent: "primary", emphasis: "solid", size: "compact", pending: updateProfile.isPending, disabled: !email || updateProfile.isPending || !name.trim(), onPress: handleProfileSave, children: updateProfile.isPending
182
+ }, placeholder: t("settings.profileNamePlaceholder"), disabled: !email || updateProfile.isPending, autoFocus: true, "aria-label": t("settings.profileNameLabel"), className: "min-w-0 flex-1", onKeyDown: (event) => {
183
+ if (event.key === "Escape")
184
+ handleProfileCancel();
185
+ } }), _jsx(ActionButton, { type: "button", intent: "primary", emphasis: "solid", size: "compact", pending: updateProfile.isPending, disabled: !canSaveName, onPress: handleProfileSave, children: updateProfile.isPending
157
186
  ? t("settings.profileSaving")
158
- : t("settings.profileSave") })] }) }), _jsx(SettingsRow, { id: "timezone", label: t("settings.timezoneLabel", { defaultValue: "Timezone" }), description: t("settings.timezoneHint", {
187
+ : t("settings.profileSave") })] })) : (_jsxs("div", { className: "flex w-full items-center justify-end gap-2 sm:w-80", children: [_jsx("span", { className: "min-w-0 flex-1 truncate text-sm text-foreground", children: displayName }), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(IconButton, { type: "button", intent: "neutral", emphasis: "ghost", size: "compact", icon: _jsx(IconPencil, { size: 14 }), label: t("settings.profileNameEdit"), title: t("settings.profileNameEdit"), disabled: !email, onPress: handleProfileEdit }) }), _jsx(TooltipContent, { children: t("settings.profileNameEdit") })] })] })) }), _jsx(SettingsRow, { id: "timezone", label: t("settings.timezoneLabel", { defaultValue: "Timezone" }), description: t("settings.timezoneHint", {
159
188
  defaultValue: "Used for timestamps and scheduled automations.",
160
189
  }), control: _jsx(SchedulingTimezoneField, { compact: true }) }), _jsx(PasswordSettings, {})] }));
161
190
  }
@@ -7,8 +7,7 @@ export interface ShareButtonProps {
7
7
  /** @deprecated No longer affects rendering — trigger always says
8
8
  * "Share". Kept for callsite compatibility. */
9
9
  variant?: "compact" | "label";
10
- /** Optional trigger style. Defaults to a text-only "Share" label.
11
- * "label-icon" opts into an icon plus label; "icon" is icon-only. */
10
+ /** @deprecated Share triggers are standardized as text-only buttons. */
12
11
  trigger?: "label" | "icon" | "label-icon";
13
12
  /** @deprecated No longer affects rendering — kept for callsite compatibility. */
14
13
  hideTriggerIcon?: boolean;
@@ -1,6 +1,7 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { ShareCopyRow, ShareTrigger } from "@agent-native/toolkit/sharing";
2
3
  import * as Select from "@radix-ui/react-select";
3
- import { IconLock, IconWorld, IconTrash, IconCheck, IconChevronDown, IconLoader2, IconLink, IconSearch, IconSearchOff, IconShare3, IconUsersGroup, } from "@tabler/icons-react";
4
+ import { IconLock, IconWorld, IconTrash, IconCheck, IconChevronDown, IconLoader2, IconSearch, IconSearchOff, IconUsersGroup, } from "@tabler/icons-react";
4
5
  import { useEffect, useId, useRef, useState, } from "react";
5
6
  import { writeClipboardText } from "../clipboard.js";
6
7
  import { Popover, PopoverAnchor, PopoverContent, PopoverTrigger, } from "../components/ui/popover.js";
@@ -10,7 +11,6 @@ import { useShareButtonController, } from "./useShareButtonController.js";
10
11
  // Match shadcn's <Button size="sm" variant="outline"> sizing so the trigger
11
12
  // sits flush next to other controls while staying transparent at rest.
12
13
  const BUTTON_BASE = "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0";
13
- const BUTTON_OUTLINE_SM = cn(BUTTON_BASE, "h-9 px-3 border border-[hsl(var(--sidebar-border,var(--input)))] bg-transparent text-foreground hover:bg-[hsl(var(--sidebar-accent,var(--accent)))] hover:text-[hsl(var(--sidebar-accent-foreground,var(--accent-foreground)))]");
14
14
  const BUTTON_PRIMARY_SM = cn(BUTTON_BASE, "h-9 px-4 bg-primary text-primary-foreground hover:bg-primary/90");
15
15
  const BUTTON_GHOST_ICON = cn(BUTTON_BASE, "h-7 w-7 p-0 text-muted-foreground hover:bg-accent hover:text-accent-foreground");
16
16
  const SHARE_POPOVER_SURFACE = "border border-border bg-popover text-popover-foreground";
@@ -124,20 +124,8 @@ export function ShareButton(props) {
124
124
  allowedRoles: props.allowedRoles,
125
125
  hideInSearchControl: props.hideInSearchControl,
126
126
  });
127
- const triggerVisibility = controller.triggerVisibility;
128
- const triggerMeta = triggerVisibility
129
- ? visibilityMeta(triggerVisibility, t, props.visibilityCopy)
130
- : null;
131
- const TriggerIcon = triggerMeta?.Icon ?? IconShare3;
132
- const iconOnly = props.trigger === "icon";
133
- const showTriggerIcon = iconOnly || props.trigger === "label-icon";
134
- const triggerLabel = iconOnly && triggerMeta
135
- ? t("agentChat.share.triggerWithVisibility", {
136
- defaultValue: "Share ({{visibility}})",
137
- visibility: triggerMeta.label,
138
- })
139
- : t("agentChat.share.share", { defaultValue: "Share" });
140
- return (_jsxs(Popover, { open: controller.open, onOpenChange: controller.handleOpenChange, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs("button", { type: "button", className: cn(iconOnly ? BUTTON_GHOST_ICON : BUTTON_OUTLINE_SM, props.triggerClassName), "aria-label": triggerLabel, title: triggerLabel, children: [showTriggerIcon && _jsx(TriggerIcon, { size: 16, strokeWidth: 1.75 }), !iconOnly && (_jsx("span", { children: t("agentChat.share.share", { defaultValue: "Share" }) }))] }) }), _jsx(PopoverContent, { align: "end", sideOffset: 6, "data-agent-native-share-overlay": "", className: cn("z-[2000] w-[min(460px,92vw)] rounded-lg p-4 shadow-lg", SHARE_POPOVER_SURFACE, props.popoverClassName), onOpenAutoFocus: (e) => e.preventDefault(), onInteractOutside: handleSharePopoverInteractOutside, children: _jsx(SharePanel, { ...props, controller: controller }) })] }));
127
+ const triggerLabel = t("agentChat.share.share", { defaultValue: "Share" });
128
+ return (_jsxs(Popover, { open: controller.open, onOpenChange: controller.handleOpenChange, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx(ShareTrigger, { label: triggerLabel, className: props.triggerClassName, "aria-label": triggerLabel, title: triggerLabel }) }), _jsx(PopoverContent, { align: "end", sideOffset: 6, "data-agent-native-share-overlay": "", className: cn("z-[2000] w-[min(460px,92vw)] rounded-lg p-4 shadow-lg", SHARE_POPOVER_SURFACE, props.popoverClassName), onOpenAutoFocus: (e) => e.preventDefault(), onInteractOutside: handleSharePopoverInteractOutside, children: _jsx(SharePanel, { ...props, controller: controller }) })] }));
141
129
  }
142
130
  function SharePanel(props) {
143
131
  const t = useT();
@@ -341,31 +329,7 @@ function optionId(baseId, index) {
341
329
  }
342
330
  function CopyLinkField({ value, label, description, }) {
343
331
  const t = useT();
344
- const [copied, setCopied] = useState(false);
345
- const resetRef = useRef(undefined);
346
- useEffect(() => {
347
- return () => {
348
- if (resetRef.current)
349
- clearTimeout(resetRef.current);
350
- };
351
- }, []);
352
- const handleCopy = async () => {
353
- if (await writeClipboardText(value)) {
354
- setCopied(true);
355
- if (resetRef.current)
356
- clearTimeout(resetRef.current);
357
- resetRef.current = setTimeout(() => setCopied(false), 1400);
358
- }
359
- else {
360
- setCopied(false);
361
- }
362
- };
363
- return (_jsxs("div", { className: "mb-4 flex items-center justify-between gap-4", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "text-sm font-semibold", children: label ??
364
- t("agentChat.share.shareLink", { defaultValue: "Share link" }) }), description ? (_jsx("div", { className: "mt-1 text-xs text-muted-foreground", children: description })) : null] }), _jsxs("button", { type: "button", onClick: handleCopy, "aria-label": copied
365
- ? t("agentChat.share.copied", { defaultValue: "Copied" })
366
- : t("agentChat.share.copy", { defaultValue: "Copy" }), className: "inline-flex h-9 shrink-0 items-center gap-2 whitespace-nowrap rounded-md border border-input bg-card px-3 text-sm font-medium text-foreground hover:bg-accent", children: [copied ? _jsx(IconCheck, { size: 15 }) : _jsx(IconLink, { size: 15 }), copied
367
- ? t("agentChat.share.copied", { defaultValue: "Copied" })
368
- : t("agentChat.share.copy", { defaultValue: "Copy" })] })] }));
332
+ return (_jsx(ShareCopyRow, { value: value, label: label ?? t("agentChat.share.shareLink", { defaultValue: "Share link" }), description: description, copyLabel: t("agentChat.share.copy", { defaultValue: "Copy" }), copiedLabel: t("agentChat.share.copied", { defaultValue: "Copied" }), onCopy: writeClipboardText, className: "mb-4" }));
369
333
  }
370
334
  // ---------------------------------------------------------------------------
371
335
  // Radix Select wrappers styled like shadcn Select (no native <select> anywhere)
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { ActionButton, Avatar as DesignSystemAvatar, Dialog as DesignSystemDialog, IconButton, Picker, Status, TextArea, TextField, } from "@agent-native/toolkit/design-system";
2
+ import { ActionButton, Avatar as DesignSystemAvatar, Dialog as DesignSystemDialog, IconButton, Picker, Status, TextArea, } from "@agent-native/toolkit/design-system";
3
+ import { ShareCopyRow } from "@agent-native/toolkit/sharing";
3
4
  import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from "@agent-native/toolkit/ui";
4
5
  import { IconCheck, IconCode, IconCopy, IconChevronDown, IconLink, IconLock, IconMail, IconTrash, IconUsersGroup, IconWorld, } from "@tabler/icons-react";
5
6
  import { useState } from "react";
@@ -53,7 +54,7 @@ function TabTrigger({ active, onClick, icon, label, }) {
53
54
  }
54
55
  function LinkTab({ controller, extras, }) {
55
56
  const Icon = VIS_ICONS[controller.visibility.value];
56
- return (_jsxs("div", { className: "space-y-3", children: [_jsx(CopyField, { field: "share-link", label: controller.labels.shareLink, value: controller.shareUrl, controller: controller }), _jsxs("div", { children: [_jsx("div", { className: "mb-2 text-xs font-medium text-muted-foreground", children: controller.labels.generalAccess }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("span", { "aria-hidden": true, className: "inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-muted text-muted-foreground", children: _jsx(Icon, { size: 16, strokeWidth: 1.75 }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx(VisibilitySelect, { controller: controller }), _jsx("div", { className: "sr-only text-xs text-muted-foreground", children: controller.visibility.description })] })] })] }), extras] }));
57
+ return (_jsxs("div", { className: "space-y-3", children: [_jsx(ShareCopyRow, { label: controller.labels.shareLink, value: controller.shareUrl, copyLabel: controller.labels.copy, copiedLabel: "Copied", onCopy: (value) => controller.copy("share-link", value), className: "mb-4" }), _jsxs("div", { children: [_jsx("div", { className: "mb-2 text-xs font-medium text-muted-foreground", children: controller.labels.generalAccess }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("span", { "aria-hidden": true, className: "inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-muted text-muted-foreground", children: _jsx(Icon, { size: 16, strokeWidth: 1.75 }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx(VisibilitySelect, { controller: controller }), _jsx("div", { className: "sr-only text-xs text-muted-foreground", children: controller.visibility.description })] })] })] }), extras] }));
57
58
  }
58
59
  function InviteTab({ controller, showVisibility, }) {
59
60
  const Icon = VIS_ICONS[controller.visibility.value];
@@ -71,11 +72,11 @@ function PersonRow({ person, canManage, removeLabel, onRemove, }) {
71
72
  return (_jsxs("li", { className: "flex items-center gap-3 px-1 py-1.5 text-sm", children: [_jsx(DesignSystemAvatar, { name: person.label, fallback: person.principalType === "org" ? (_jsx(IconUsersGroup, { size: 14, strokeWidth: 1.75 })) : (person.avatarText), size: "compact", className: "inline-flex h-7 w-7 shrink-0 text-[11px] font-semibold" }), _jsx("span", { className: "flex-1 min-w-0 truncate", children: person.label }), _jsx(Status, { tone: "neutral", size: "compact", className: "border-0 bg-transparent px-0 text-xs text-muted-foreground", children: person.roleLabel }), canManage && person.share ? (_jsx(IconButton, { intent: "danger", emphasis: "ghost", size: "compact", icon: _jsx(IconTrash, { size: 14 }), label: removeLabel, "aria-label": removeLabel, onPress: () => onRemove(person.share), className: cn(BUTTON_GHOST_ICON, "[&_svg]:!size-auto") })) : null] }));
72
73
  }
73
74
  function DefaultEmbedBody({ controller, }) {
74
- return (_jsxs("div", { className: "space-y-3", children: [_jsx(CopyField, { field: "embed-url", label: controller.labels.embedUrl, value: controller.embedUrl, controller: controller }), _jsx(CopyField, { field: "embed-code", label: controller.labels.embedCode, value: controller.embedCode, controller: controller, multiline: true })] }));
75
+ return (_jsxs("div", { className: "space-y-3", children: [_jsx(ShareCopyRow, { label: controller.labels.embedUrl, value: controller.embedUrl, copyLabel: controller.labels.copy, copiedLabel: "Copied", onCopy: (value) => controller.copy("embed-url", value) }), _jsx(CopyField, { field: "embed-code", label: controller.labels.embedCode, value: controller.embedCode, controller: controller, multiline: true })] }));
75
76
  }
76
77
  function CopyField({ field, label, value, controller, multiline, }) {
77
78
  const copied = controller.copiedField === field;
78
- return (_jsxs("div", { children: [_jsx("div", { className: "mb-1 text-xs font-medium text-muted-foreground", children: label }), _jsxs("div", { className: "flex items-stretch gap-2", children: [multiline ? (_jsx(TextArea, { readOnly: true, value: value, onChange: () => undefined, "aria-label": label, className: "flex-1 h-20 text-xs font-mono" })) : (_jsx(TextField, { readOnly: true, value: value, onChange: () => undefined, "aria-label": label, className: "flex-1 min-w-0 text-xs font-mono" })), _jsx(IconButton, { emphasis: "outline", size: "compact", icon: copied ? _jsx(IconCheck, { size: 14 }) : _jsx(IconCopy, { size: 14 }), label: controller.labels.copy, onPress: () => void controller.copy(field, value), "aria-label": controller.labels.copy, className: cn(BUTTON_OUTLINE_SM, "!w-9 !px-0 [&_svg]:!size-auto") })] })] }));
79
+ return (_jsxs("div", { children: [_jsx("div", { className: "mb-1 text-xs font-medium text-muted-foreground", children: label }), _jsxs("div", { className: "flex items-stretch gap-2", children: [multiline ? (_jsx(TextArea, { readOnly: true, value: value, onChange: () => undefined, "aria-label": label, className: "flex-1 h-20 text-xs font-mono" })) : null, _jsx(IconButton, { emphasis: "outline", size: "compact", icon: copied ? _jsx(IconCheck, { size: 14 }) : _jsx(IconCopy, { size: 14 }), label: controller.labels.copy, onPress: () => void controller.copy(field, value), "aria-label": controller.labels.copy, className: cn(BUTTON_OUTLINE_SM, "!w-9 !px-0 [&_svg]:!size-auto") })] })] }));
79
80
  }
80
81
  function RoleSelect({ controller }) {
81
82
  return (_jsx(Picker, { mode: "select", options: controller.invite.roleOptions, value: controller.invite.role, onChange: (value) => {
@@ -466,17 +466,11 @@ export function useChatThreads(apiUrl = agentNativePath("/_agent-native/agent-ch
466
466
  // - savedId in newlyCreatedRef (we just created it this session) → keep
467
467
  // it; the server hasn't seen it yet because there's no POST anymore,
468
468
  // the row gets written when the user sends a message.
469
- // - savedId is set but neither on the server nor newly created here →
470
- // it's an empty tab the user left open. A never-messaged tab is never
471
- // POSTed (that was the 127-ghost-threads problem), and the only record
472
- // that it's a deliberately-open tab — newlyCreatedRef — is wiped by the
473
- // reload. So on refresh we can't tell it apart from a stale ghost.
474
- // Keep it exactly as the user left it: re-register it as an optimistic
475
- // empty tab rather than resurrecting an unrelated old conversation. The
476
- // composer is fully functional with this id (the server writes the row
477
- // on first message, same as any new tab), so there's no 404 to avoid.
478
- // This is what makes "the state you left is the state you see on
479
- // refresh" hold — stale (>12h) tabs are still cleared downstream.
469
+ // - savedId is set but not on the current page → look it up directly. A
470
+ // found thread stays active. An auto-creating surface replaces a confirmed
471
+ // 404 with a fresh local tab rather than reusing an inaccessible server id.
472
+ // A list-only surface keeps the saved id as an optimistic empty tab so a
473
+ // never-posted tab remains selectable after reload.
480
474
  // - No savedId → synthesize a fresh local id (no POST; server creates the
481
475
  // row on first message). The server may contain chats from another
482
476
  // branch, preview, or project that shares the same user/database, so
@@ -505,7 +499,7 @@ export function useChatThreads(apiUrl = agentNativePath("/_agent-native/agent-ch
505
499
  ? loadedThreads.find((t) => t.id === restoredId)
506
500
  : undefined;
507
501
  // One page, so absence from it is not absence from the server — this is what
508
- // separates an older real thread from the ghost tab reclassified below.
502
+ // separates an older real thread from an unavailable saved id below.
509
503
  const restoredThread = lookupRestored && !restoredOnPage
510
504
  ? await fetchThreadById(apiUrl, restoredId)
511
505
  : restoredOnPage;
@@ -515,11 +509,13 @@ export function useChatThreads(apiUrl = agentNativePath("/_agent-native/agent-ch
515
509
  setIsLoading(false);
516
510
  return;
517
511
  }
512
+ const restoredIsUnavailable = restoredThread === null && lookupRestored && !restoredOnPage;
518
513
  const restoredBelongsElsewhere = Boolean(restoredThread &&
519
514
  !threadCanStayVisibleInScope(restoredThread.scope ?? null, scopeRef.current));
520
- if (restoredBelongsElsewhere)
515
+ const restoredNeedsReplacement = restoredBelongsElsewhere || (restoredIsUnavailable && autoCreate);
516
+ if (restoredNeedsReplacement)
521
517
  setActiveThreadId(null);
522
- const savedId = restoredBelongsElsewhere ? null : restoredId;
518
+ const savedId = restoredNeedsReplacement ? null : restoredId;
523
519
  const loadedHasSavedId = Boolean(savedId &&
524
520
  (restoredThread || loadedThreads.some((t) => t.id === savedId)));
525
521
  const savedIdCameFromRoute = Boolean(savedId) &&
@@ -62,11 +62,11 @@ export declare const postAwareness: import("h3").EventHandlerWithFetch<import("h
62
62
  error: string;
63
63
  states?: undefined;
64
64
  } | {
65
+ error?: undefined;
65
66
  states: {
66
67
  clientId: number;
67
68
  state: string;
68
69
  }[];
69
- error?: undefined;
70
70
  }>>;
71
71
  /**
72
72
  * GET /_agent-native/collab/:docId/users
@@ -77,9 +77,9 @@ export declare const getActiveUsers: import("h3").EventHandlerWithFetch<import("
77
77
  error: string;
78
78
  users?: undefined;
79
79
  } | {
80
+ error?: undefined;
80
81
  users: {
81
82
  clientId: number;
82
83
  lastSeen: number;
83
84
  }[];
84
- error?: undefined;
85
85
  }>>;
@@ -26,8 +26,8 @@ export declare const getCollabState: import("h3").EventHandlerWithFetch<import("
26
26
  * Body: { update: string (base64), requestSource?: string }
27
27
  */
28
28
  export declare const postCollabUpdate: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
29
- ok?: undefined;
30
29
  error: string;
30
+ ok?: undefined;
31
31
  } | {
32
32
  error?: undefined;
33
33
  ok: boolean;
@@ -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;
@@ -45,6 +45,7 @@ declare const messages: {
45
45
  profileUploading: string;
46
46
  profileChangePhoto: string;
47
47
  profileNameLabel: string;
48
+ profileNameEdit: string;
48
49
  profileNamePlaceholder: string;
49
50
  profileNameDescription: string;
50
51
  profileSave: string;
@@ -51,6 +51,7 @@ const messages = {
51
51
  profileUploading: "Uploading...",
52
52
  profileChangePhoto: "Change photo",
53
53
  profileNameLabel: "Name",
54
+ profileNameEdit: "Edit name",
54
55
  profileNamePlaceholder: "Your name",
55
56
  profileNameDescription: "This name is used when referring to you across Agent-Native apps.",
56
57
  profileSave: "Save changes",
@@ -11,14 +11,14 @@
11
11
  * DELETE /_agent-native/notifications/:id — delete
12
12
  */
13
13
  export declare function createNotificationsHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<"" | import("./types.js").Notification[] | {
14
+ error?: undefined;
14
15
  count: number;
15
16
  updated?: undefined;
16
- error?: undefined;
17
17
  ok?: undefined;
18
18
  } | {
19
+ error?: undefined;
19
20
  count?: undefined;
20
21
  updated: number;
21
- error?: undefined;
22
22
  ok?: undefined;
23
23
  } | {
24
24
  count?: undefined;
@@ -26,8 +26,8 @@ export declare function createNotificationsHandler(): import("h3").EventHandlerW
26
26
  error: string;
27
27
  ok?: undefined;
28
28
  } | {
29
+ error?: undefined;
29
30
  count?: undefined;
30
31
  updated?: undefined;
31
- error?: undefined;
32
32
  ok: boolean;
33
33
  }>>;
@@ -75,11 +75,9 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
75
75
  user: "user";
76
76
  }>>;
77
77
  }, z.core.$strip>>, {
78
+ deleted?: undefined;
78
79
  id?: undefined;
79
80
  provider?: undefined;
80
- deleted?: undefined;
81
- found?: undefined;
82
- message?: undefined;
83
81
  providers: {
84
82
  id: string;
85
83
  label: string;
@@ -90,44 +88,46 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
90
88
  updatedAt: number;
91
89
  }[];
92
90
  count: number;
91
+ found?: undefined;
93
92
  registered?: undefined;
94
93
  label?: undefined;
94
+ message?: undefined;
95
95
  } | {
96
- id?: undefined;
97
96
  deleted?: undefined;
98
- message?: undefined;
97
+ id?: undefined;
99
98
  providers?: undefined;
100
99
  count?: undefined;
101
100
  found: boolean;
102
101
  provider: import("../custom-registry.js").CustomProviderConfig;
103
102
  registered?: undefined;
104
103
  label?: undefined;
104
+ message?: undefined;
105
105
  } | {
106
- provider?: undefined;
107
106
  deleted?: undefined;
108
- message?: undefined;
107
+ provider?: undefined;
109
108
  providers?: undefined;
110
109
  count?: undefined;
111
110
  found: boolean;
112
111
  id: string;
113
112
  registered?: undefined;
114
113
  label?: undefined;
114
+ message?: undefined;
115
115
  } | {
116
116
  provider?: undefined;
117
- found?: undefined;
118
- message?: undefined;
119
117
  providers?: undefined;
120
118
  count?: undefined;
119
+ found?: undefined;
121
120
  deleted: boolean;
122
121
  id: string;
123
122
  registered?: undefined;
124
123
  label?: undefined;
124
+ message?: undefined;
125
125
  } | {
126
- provider?: undefined;
127
126
  deleted?: undefined;
128
- found?: undefined;
127
+ provider?: undefined;
129
128
  providers?: undefined;
130
129
  count?: undefined;
130
+ found?: undefined;
131
131
  registered: boolean;
132
132
  id: string;
133
133
  label: string;
@@ -312,7 +312,8 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
312
312
  scope?: "org" | "user" | undefined;
313
313
  }, {
314
314
  id?: undefined;
315
- found?: undefined;
315
+ message?: undefined;
316
+ deleted?: undefined;
316
317
  providers: {
317
318
  id: string;
318
319
  label: string;
@@ -324,46 +325,45 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
324
325
  }[];
325
326
  count: number;
326
327
  provider?: undefined;
327
- deleted?: undefined;
328
+ found?: undefined;
328
329
  registered?: undefined;
329
330
  label?: undefined;
330
- message?: undefined;
331
331
  } | {
332
332
  id?: undefined;
333
- providers?: undefined;
333
+ message?: undefined;
334
334
  count?: undefined;
335
+ deleted?: undefined;
336
+ providers?: undefined;
335
337
  found: boolean;
336
338
  provider: import("../custom-registry.js").CustomProviderConfig;
337
- deleted?: undefined;
338
339
  registered?: undefined;
339
340
  label?: undefined;
340
- message?: undefined;
341
341
  } | {
342
- providers?: undefined;
342
+ message?: undefined;
343
343
  count?: undefined;
344
+ deleted?: undefined;
345
+ providers?: undefined;
344
346
  provider?: undefined;
345
347
  found: boolean;
346
348
  id: string;
347
- deleted?: undefined;
348
349
  registered?: undefined;
349
350
  label?: undefined;
350
- message?: undefined;
351
351
  } | {
352
- found?: undefined;
353
- providers?: undefined;
352
+ message?: undefined;
354
353
  count?: undefined;
354
+ providers?: undefined;
355
355
  provider?: undefined;
356
+ found?: undefined;
356
357
  deleted: boolean;
357
358
  id: string;
358
359
  registered?: undefined;
359
360
  label?: undefined;
360
- message?: undefined;
361
361
  } | {
362
- found?: undefined;
363
- providers?: undefined;
364
362
  count?: undefined;
365
- provider?: undefined;
366
363
  deleted?: undefined;
364
+ providers?: undefined;
365
+ provider?: undefined;
366
+ found?: undefined;
367
367
  registered: boolean;
368
368
  id: string;
369
369
  label: string;
@@ -3,6 +3,7 @@ export interface AuthMarketingContent {
3
3
  tagline: string;
4
4
  description?: string;
5
5
  features?: string[];
6
+ /** @deprecated Local execution is no longer offered from auth pages. */
6
7
  runLocalCommand?: string;
7
8
  signupLocalModeNote?: {
8
9
  text: string;
@@ -53,7 +53,6 @@ export const BUILT_IN_AUTH_MARKETING = {
53
53
  "Review PR recaps with diagrams, file maps, and annotated code",
54
54
  "Share links for async comments and product review",
55
55
  ],
56
- runLocalCommand: PLAN_LOCAL_FILES_COMMAND,
57
56
  signupLocalModeNote: {
58
57
  text: "Prefer no account or self-hosting? Switch /visual-plan to local files only:",
59
58
  command: PLAN_LOCAL_FILES_COMMAND,
@@ -103,7 +102,6 @@ export const BUILT_IN_AUTH_MARKETING = {
103
102
  "Multi-account Gmail in a single unified inbox",
104
103
  "Autonomous triage, archiving, and follow-ups",
105
104
  ],
106
- runLocalCommand: "npx @agent-native/core@latest create my-mail-app --template mail",
107
105
  },
108
106
  slides: {
109
107
  appName: "Agent-Native Slides",
@@ -110,6 +110,7 @@ export interface AuthOptions {
110
110
  tagline: string;
111
111
  description?: string;
112
112
  features?: string[];
113
+ /** @deprecated Local execution is no longer offered from auth pages. */
113
114
  runLocalCommand?: string;
114
115
  };
115
116
  /**
@@ -3309,6 +3309,24 @@ async function mountBetterAuthRoutes(app, options) {
3309
3309
  }));
3310
3310
  // Passwordless login via Better Auth's rate-limited magic-link plugin.
3311
3311
  app.use("/_agent-native/auth/magic-link", defineEventHandler(async (event) => {
3312
+ if (getMethod(event) === "GET") {
3313
+ const query = getQuery(event);
3314
+ if (typeof query.token === "string") {
3315
+ const verificationUrl = new URL(`${getAppBasePath()}/_agent-native/auth/ba/magic-link/verify`, getOrigin(event));
3316
+ for (const key of [
3317
+ "token",
3318
+ "callbackURL",
3319
+ "newUserCallbackURL",
3320
+ "errorCallbackURL",
3321
+ ]) {
3322
+ const value = query[key];
3323
+ if (typeof value === "string") {
3324
+ verificationUrl.searchParams.set(key, value);
3325
+ }
3326
+ }
3327
+ return redirectWithStagedCookies(event, verificationUrl.toString());
3328
+ }
3329
+ }
3312
3330
  if (getMethod(event) !== "POST") {
3313
3331
  setResponseStatus(event, 405);
3314
3332
  return { error: "Method not allowed" };
@@ -37,6 +37,7 @@ export interface OnboardingHtmlOptions {
37
37
  tagline: string;
38
38
  description?: string;
39
39
  features?: string[];
40
+ /** @deprecated Local execution is no longer offered from auth pages. */
40
41
  runLocalCommand?: string;
41
42
  };
42
43
  /**