@agent-native/core 0.195.0 → 0.195.1-nightly-20260927210321

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 (31) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/design/actions/generate-home-suggestions.ts +55 -7
  3. package/corpus/templates/design/app/components/design/design-canvas/LocalNetworkAccessPrompt.tsx +82 -18
  4. package/corpus/templates/design/app/i18n/en-US.ts +6 -1
  5. package/corpus/templates/design/changelog/2026-09-27-local-access-setup-shows-chrome-permission-screenshots-and-c.md +6 -0
  6. package/corpus/templates/design/changelog/2026-09-27-personalized-home-suggestions-work-when-responses-include-ex.md +6 -0
  7. package/corpus/templates/mail/app/components/email/EmailList.tsx +10 -0
  8. package/corpus/templates/mail/app/components/email/EmailListItem.tsx +78 -1
  9. package/corpus/templates/mail/app/components/layout/AppLayout.tsx +9 -9
  10. package/corpus/templates/mail/app/components/layout/SearchBar.tsx +192 -175
  11. package/corpus/templates/mail/app/components/onboarding/AiInboxSetup.tsx +4 -0
  12. package/corpus/templates/mail/app/global.css +14 -0
  13. package/corpus/templates/mail/changelog/2026-09-27-inbox-priority-shows-each-email-s-score-with-quick-feedback-.md +6 -0
  14. package/corpus/templates/mail/changelog/2026-09-27-mail-setup-waits-for-first-run-onboarding-and-empty-importan.md +6 -0
  15. package/corpus/templates/mail/changelog/2026-09-27-mobile-inbox-toolbar-scrolls-together.md +6 -0
  16. package/corpus/templates/slides/actions/generate-home-suggestions.ts +55 -11
  17. package/corpus/templates/slides/changelog/2026-09-27-personalized-presentation-suggestions-work-when-responses-in.md +6 -0
  18. package/dist/client/onboarding/first-run-startup-gate.js +3 -2
  19. package/dist/collab/routes.d.ts +1 -1
  20. package/dist/observability/routes.d.ts +5 -5
  21. package/dist/org/auth-policy.js +0 -2
  22. package/dist/progress/routes.d.ts +1 -1
  23. package/dist/provider-api/actions/provider-api.d.ts +4 -4
  24. package/dist/secrets/routes.d.ts +6 -6
  25. package/dist/server/auth-plugin.js +2 -1
  26. package/dist/server/auth.js +22 -43
  27. package/dist/triggers/actions/manage-automation.d.ts +2 -2
  28. package/dist/vite/agent-native-config-loader.js +4 -2
  29. package/package.json +3 -3
  30. package/dist/server/session-email-cache.d.ts +0 -14
  31. package/dist/server/session-email-cache.js +0 -53
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: 10366
34
+ - template files: 10372
@@ -48,22 +48,70 @@ function roleContext(value: string | null | undefined): string {
48
48
  return `The user's selected onboarding role is ${JSON.stringify(role)}. Tailor suggestions to that role's typical work and goals.`;
49
49
  }
50
50
 
51
+ function findArrayEnd(text: string, start: number): number | undefined {
52
+ let depth = 0;
53
+ let inString = false;
54
+ let escaped = false;
55
+
56
+ for (let index = start; index < text.length; index++) {
57
+ const char = text[index];
58
+ if (inString) {
59
+ if (escaped) escaped = false;
60
+ else if (char === "\\") escaped = true;
61
+ else if (char === '"') inString = false;
62
+ continue;
63
+ }
64
+ if (char === '"') inString = true;
65
+ else if (char === "[") depth++;
66
+ else if (char === "]" && --depth === 0) return index;
67
+ }
68
+ }
69
+
51
70
  function parseSuggestions(text: string) {
52
71
  const unwrapped = text
53
72
  .trim()
54
73
  .replace(/^```(?:json)?\s*/i, "")
55
74
  .replace(/\s*```$/i, "");
56
- let parsed: unknown;
75
+ let parsedJson: unknown;
76
+ let hasTopLevelJson = false;
57
77
  try {
58
- parsed = JSON.parse(unwrapped);
59
- } catch {
60
- throw new Error("Home suggestions returned invalid JSON.");
78
+ parsedJson = JSON.parse(unwrapped);
79
+ hasTopLevelJson = true;
80
+ } catch (error) {
81
+ if (!(error instanceof SyntaxError)) throw error;
82
+ }
83
+ if (hasTopLevelJson) {
84
+ const result = suggestionsSchema.safeParse(parsedJson);
85
+ if (!result.success) {
86
+ throw new Error("Home suggestions returned an invalid shape.");
87
+ }
88
+ return result.data;
89
+ }
90
+
91
+ let parsedCandidateJson = false;
92
+ for (
93
+ let start = unwrapped.indexOf("[");
94
+ start >= 0;
95
+ start = unwrapped.indexOf("[", start + 1)
96
+ ) {
97
+ const end = findArrayEnd(unwrapped, start);
98
+ if (end === undefined) continue;
99
+
100
+ let parsed: unknown;
101
+ try {
102
+ parsed = JSON.parse(unwrapped.slice(start, end + 1));
103
+ } catch (error) {
104
+ if (!(error instanceof SyntaxError)) throw error;
105
+ continue;
106
+ }
107
+ parsedCandidateJson = true;
108
+ const result = suggestionsSchema.safeParse(parsed);
109
+ if (result.success) return result.data;
61
110
  }
62
- const result = suggestionsSchema.safeParse(parsed);
63
- if (!result.success) {
111
+ if (parsedCandidateJson) {
64
112
  throw new Error("Home suggestions returned an invalid shape.");
65
113
  }
66
- return result.data;
114
+ throw new Error("Home suggestions returned invalid JSON.");
67
115
  }
68
116
 
69
117
  export default defineAction({
@@ -4,7 +4,18 @@ import {
4
4
  IconPlugConnectedX,
5
5
  IconX,
6
6
  } from "@tabler/icons-react";
7
+ import { useState } from "react";
7
8
 
9
+ import {
10
+ AlertDialog,
11
+ AlertDialogAction,
12
+ AlertDialogCancel,
13
+ AlertDialogContent,
14
+ AlertDialogDescription,
15
+ AlertDialogFooter,
16
+ AlertDialogHeader,
17
+ AlertDialogTitle,
18
+ } from "@/components/ui/alert-dialog";
8
19
  import { Button } from "@/components/ui/button";
9
20
  import {
10
21
  Dialog,
@@ -30,6 +41,7 @@ export function LocalNetworkAccessPrompt({
30
41
  proactive?: boolean;
31
42
  }) {
32
43
  const t = useT();
44
+ const [confirmClose, setConfirmClose] = useState(false);
33
45
  // "unreachable" is the one confident case (permission is confirmed
34
46
  // granted, so it's confirmed NOT the cause) — every other kind is
35
47
  // deliberately hedged copy, never a diagnosed permission claim. See
@@ -69,35 +81,87 @@ export function LocalNetworkAccessPrompt({
69
81
  "designCanvas.localBridge.permissionPromptSettingsInstructions",
70
82
  {
71
83
  defaultValue:
72
- "Click the site information icon to the left of the address bar, open Site settings, then set Local network access to Allow.",
84
+ "Click the site controls icon to the left of the address bar, open Site settings, then allow access to apps on your device.",
73
85
  },
74
86
  );
75
87
  const permissionHelp = showPermissionHelp ? (
76
88
  <details className="text-xs text-muted-foreground">
77
89
  <summary className="cursor-pointer">{noPromptLabel}</summary>
78
90
  <p className="mt-2 leading-relaxed">{permissionSettingsInstructions}</p>
91
+ <img
92
+ src="/local-network-access-settings.jpg"
93
+ alt={permissionSettingsInstructions}
94
+ className="mt-3 block w-full rounded-md border border-border"
95
+ width={200}
96
+ height={140}
97
+ loading="lazy"
98
+ />
79
99
  </details>
80
100
  ) : null;
81
101
 
82
102
  if (proactive) {
83
103
  return (
84
- <Dialog
85
- open
86
- onOpenChange={(open) => {
87
- if (!open) onDismiss();
88
- }}
89
- >
90
- <DialogContent className="sm:max-w-md">
91
- <DialogHeader>
92
- <DialogTitle className="flex items-center gap-2">
93
- <IconPlugConnected className="size-4" />
94
- {title}
95
- </DialogTitle>
96
- <DialogDescription>{description}</DialogDescription>
97
- </DialogHeader>
98
- {permissionHelp}
99
- </DialogContent>
100
- </Dialog>
104
+ <>
105
+ <Dialog
106
+ open
107
+ onOpenChange={(open) => {
108
+ if (!open) setConfirmClose(true);
109
+ }}
110
+ >
111
+ <DialogContent
112
+ className="sm:max-w-md"
113
+ overlayClassName="bg-[color-mix(in_srgb,var(--design-editor-accent-color)_22%,transparent)] backdrop-blur-[2px]"
114
+ >
115
+ <DialogHeader>
116
+ <DialogTitle className="flex items-center gap-2">
117
+ <IconPlugConnected className="size-4" />
118
+ {title}
119
+ </DialogTitle>
120
+ <DialogDescription>{description}</DialogDescription>
121
+ </DialogHeader>
122
+ <img
123
+ src="/local-network-access-permission.jpg"
124
+ alt={description}
125
+ className="block w-full rounded-md border border-border"
126
+ width={240}
127
+ height={150}
128
+ />
129
+ {permissionHelp}
130
+ </DialogContent>
131
+ </Dialog>
132
+ <AlertDialog open={confirmClose} onOpenChange={setConfirmClose}>
133
+ <AlertDialogContent>
134
+ <AlertDialogHeader>
135
+ <AlertDialogTitle>
136
+ {t("designCanvas.localBridge.permissionCloseTitle", {
137
+ defaultValue: "Close setup?",
138
+ })}
139
+ </AlertDialogTitle>
140
+ <AlertDialogDescription>
141
+ {t("designCanvas.localBridge.permissionCloseDescription", {
142
+ defaultValue:
143
+ "Live editing won't work until you allow access in Chrome.",
144
+ })}
145
+ </AlertDialogDescription>
146
+ </AlertDialogHeader>
147
+ <AlertDialogFooter>
148
+ <AlertDialogCancel>
149
+ {t("designCanvas.localBridge.permissionCloseStay", {
150
+ defaultValue: "Keep setup open",
151
+ })}
152
+ </AlertDialogCancel>
153
+ <AlertDialogAction
154
+ className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
155
+ onClick={onDismiss}
156
+ >
157
+ {t("designCanvas.localBridge.permissionCloseAnyway", {
158
+ defaultValue: "Close anyway",
159
+ })}
160
+ </AlertDialogAction>
161
+ </AlertDialogFooter>
162
+ </AlertDialogContent>
163
+ </AlertDialog>
164
+ </>
101
165
  );
102
166
  }
103
167
 
@@ -1478,7 +1478,12 @@ export default {
1478
1478
  "Choose Allow in Chrome's prompt to enable live editing.",
1479
1479
  permissionPromptNoPrompt: "No Chrome prompt?",
1480
1480
  permissionPromptSettingsInstructions:
1481
- "Click the site information icon to the left of the address bar, open Site settings, then set Local network access to Allow.",
1481
+ "Click the site controls icon to the left of the address bar, open Site settings, then allow access to apps on your device.",
1482
+ permissionCloseTitle: "Close setup?",
1483
+ permissionCloseDescription:
1484
+ "Live editing won't work until you allow access in Chrome.",
1485
+ permissionCloseStay: "Keep setup open",
1486
+ permissionCloseAnyway: "Close anyway",
1482
1487
  permissionPromptRetry: "Retry connection",
1483
1488
  confirmationRetryExhausted:
1484
1489
  "Live editor bridge did not confirm after several attempts.",
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-09-27
4
+ ---
5
+
6
+ Local access setup shows Chrome permission screenshots and confirms before closing
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-09-27
4
+ ---
5
+
6
+ Personalized home suggestions work when responses include extra text
@@ -2672,6 +2672,16 @@ export function EmailList({
2672
2672
  >
2673
2673
  <EmailListItem
2674
2674
  email={thread.latestMessage}
2675
+ importanceScore={
2676
+ view === "inbox" && currentSortMode === "priority"
2677
+ ? cachedPriorityScores.get(
2678
+ aiPriorityEmailKey(
2679
+ thread.latestMessage.accountEmail,
2680
+ thread.latestMessage.id,
2681
+ ),
2682
+ )
2683
+ : undefined
2684
+ }
2675
2685
  labelNames={labelNames}
2676
2686
  thread={thread}
2677
2687
  isSelected={thread.latestMessage.id === threadId}
@@ -14,10 +14,18 @@ import {
14
14
  IconSquareCheck,
15
15
  IconSend,
16
16
  IconX,
17
+ IconThumbDown,
18
+ IconThumbUp,
17
19
  } from "@tabler/icons-react";
18
- import { memo, useRef, useState, useCallback } from "react";
20
+ import { memo, useRef, useState, useCallback, type CSSProperties } from "react";
21
+ import { Link } from "react-router";
19
22
 
20
23
  import { ImportanceFeedbackMenu } from "@/components/email/ImportanceFeedbackMenu";
24
+ import {
25
+ Popover,
26
+ PopoverContent,
27
+ PopoverTrigger,
28
+ } from "@/components/ui/popover";
21
29
  import {
22
30
  Tooltip,
23
31
  TooltipContent,
@@ -31,6 +39,7 @@ import { cn, formatEmailDate } from "@/lib/utils";
31
39
 
32
40
  interface EmailListItemProps {
33
41
  email: EmailMessage;
42
+ importanceScore?: number;
34
43
  labelNames?: ReadonlyMap<string, string>;
35
44
  thread?: ThreadSummary;
36
45
  isSelected: boolean;
@@ -109,6 +118,7 @@ function getAccountColor(
109
118
 
110
119
  export const EmailListItem = memo(function EmailListItem({
111
120
  email,
121
+ importanceScore,
112
122
  labelNames,
113
123
  thread,
114
124
  isSelected,
@@ -145,6 +155,8 @@ export const EmailListItem = memo(function EmailListItem({
145
155
 
146
156
  const [dragX, setDragX] = useState(0);
147
157
  const [isDragging, setIsDragging] = useState(false);
158
+ const [priorityScorePopoverOpen, setPriorityScorePopoverOpen] =
159
+ useState(false);
148
160
  const gestureRef = useRef<{
149
161
  startX: number;
150
162
  startY: number;
@@ -614,6 +626,71 @@ export const EmailListItem = memo(function EmailListItem({
614
626
  <span className="row-time text-xs text-muted-foreground tabular-nums sm:text-[12px]">
615
627
  {formatEmailDate(email.date)}
616
628
  </span>
629
+ {importanceScore !== undefined && (
630
+ <Popover
631
+ open={priorityScorePopoverOpen}
632
+ onOpenChange={setPriorityScorePopoverOpen}
633
+ >
634
+ <PopoverTrigger asChild>
635
+ <button
636
+ type="button"
637
+ onClick={(event) => event.stopPropagation()}
638
+ onKeyDown={(event) => event.stopPropagation()}
639
+ aria-label={`${t("mail.sort.priority")} ${importanceScore.toFixed(2)}`}
640
+ className="email-importance-score mx-1 inline-flex h-6 w-10 shrink-0 items-center justify-end rounded px-1 text-[11px] font-medium tabular-nums hover:bg-accent"
641
+ style={
642
+ {
643
+ "--mail-importance-weight": `${Math.round(importanceScore * 100)}%`,
644
+ } as CSSProperties
645
+ }
646
+ >
647
+ {importanceScore.toFixed(2)}
648
+ </button>
649
+ </PopoverTrigger>
650
+ <PopoverContent
651
+ align="end"
652
+ className="w-56 p-2"
653
+ onClick={(event) => event.stopPropagation()}
654
+ onKeyDown={(event) => event.stopPropagation()}
655
+ onPointerDown={(event) => event.stopPropagation()}
656
+ >
657
+ <div className="mb-1 text-xs font-medium">
658
+ {t("mail.sort.priority")} · {importanceScore.toFixed(2)}
659
+ </div>
660
+ <p className="mb-2 text-xs text-muted-foreground">
661
+ {t("mail.sort.priorityScoreHelp")}
662
+ </p>
663
+ <button
664
+ type="button"
665
+ onClick={() => {
666
+ setPriorityScorePopoverOpen(false);
667
+ onImportanceFeedback?.("important");
668
+ }}
669
+ className="flex w-full items-center gap-2 whitespace-nowrap rounded px-2 py-1.5 text-xs hover:bg-accent"
670
+ >
671
+ <IconThumbUp aria-hidden="true" className="size-3.5" />
672
+ {t("mail.aiFilter.importantMode")}
673
+ </button>
674
+ <button
675
+ type="button"
676
+ onClick={() => {
677
+ setPriorityScorePopoverOpen(false);
678
+ onImportanceFeedback?.("not-important");
679
+ }}
680
+ className="flex w-full items-center gap-2 whitespace-nowrap rounded px-2 py-1.5 text-xs hover:bg-accent"
681
+ >
682
+ <IconThumbDown aria-hidden="true" className="size-3.5" />
683
+ {t("mail.aiFilter.notImportantMode")}
684
+ </button>
685
+ <Link
686
+ to="/settings?section=ai-filter"
687
+ className="mt-1 block border-t border-border/40 px-2 pt-2 text-xs text-muted-foreground hover:text-foreground"
688
+ >
689
+ {t("mail.sort.priorityEditRules")}
690
+ </Link>
691
+ </PopoverContent>
692
+ </Popover>
693
+ )}
617
694
  {/* Hover actions overlay the preview while the time stays fixed. */}
618
695
  <div className="hover-actions gap-0.5">
619
696
  {onToggleRead && (
@@ -1251,14 +1251,14 @@ function AppLayoutInner({ children }: AppLayoutProps) {
1251
1251
  <AccountFilterContext.Provider value={accountFilterValue}>
1252
1252
  <div className="relative flex flex-1 flex-col overflow-hidden bg-background">
1253
1253
  {/* Top nav bar */}
1254
- <header className="relative z-20 flex h-12 shrink-0 items-center gap-1 border-b border-border/50 bg-card px-2 inbox-zero-header">
1254
+ <header className="relative z-20 flex h-12 shrink-0 items-center gap-1 overflow-x-auto overflow-y-hidden overscroll-x-contain border-b border-border/50 bg-card px-2 inbox-zero-header hide-scrollbar">
1255
1255
  <Dialog open={sidebarOpen} onOpenChange={setSidebarOpen}>
1256
1256
  {/* Hamburger menu */}
1257
1257
  <Tooltip>
1258
1258
  <TooltipTrigger asChild>
1259
1259
  <DialogTrigger asChild>
1260
1260
  <button
1261
- className="flex h-9 w-9 sm:h-7 sm:w-7 items-center justify-center rounded text-muted-foreground hover:text-foreground hover:bg-accent/50 transition-colors shrink-0"
1261
+ className="sticky start-0 z-10 flex h-9 w-9 shrink-0 items-center justify-center rounded bg-card text-muted-foreground hover:text-foreground hover:bg-accent/50 transition-colors sm:h-7 sm:w-7"
1262
1262
  aria-label={t("mail.toolbar.toggleMenu")}
1263
1263
  >
1264
1264
  <IconMenu2 className="h-4 w-4" />
@@ -1522,18 +1522,18 @@ function AppLayoutInner({ children }: AppLayoutProps) {
1522
1522
  {/* Primary tabs stay mounted during search so navigation does not jump. */}
1523
1523
  <>
1524
1524
  {tabsLoading ? (
1525
- <nav className="hidden sm:flex flex-1 min-w-0 items-center gap-2 overflow-x-auto hide-scrollbar">
1525
+ <nav className="flex w-max shrink-0 items-center gap-2 sm:w-auto sm:flex-1 sm:min-w-0 sm:overflow-x-auto sm:hide-scrollbar">
1526
1526
  {[1, 2, 3].map((i) => (
1527
1527
  <span
1528
1528
  key={i}
1529
- className="h-4 rounded bg-muted animate-pulse"
1529
+ className="h-4 shrink-0 rounded bg-muted animate-pulse"
1530
1530
  style={{ width: `${48 + i * 12}px` }}
1531
1531
  />
1532
1532
  ))}
1533
1533
  </nav>
1534
1534
  ) : (
1535
1535
  <nav
1536
- className="hidden sm:flex flex-1 min-w-0 flex-nowrap items-center gap-1 overflow-x-auto hide-scrollbar"
1536
+ className="flex w-max shrink-0 flex-nowrap items-center gap-1 sm:w-auto sm:flex-1 sm:min-w-0 sm:overflow-x-auto sm:hide-scrollbar"
1537
1537
  data-mail-tab-list
1538
1538
  >
1539
1539
  {topBarTabs.map((tab, tabIndex) => {
@@ -1637,7 +1637,7 @@ function AppLayoutInner({ children }: AppLayoutProps) {
1637
1637
  <PopoverTrigger asChild>
1638
1638
  <button
1639
1639
  className={cn(
1640
- "flex h-6 w-6 items-center justify-center rounded transition-colors",
1640
+ "flex h-9 w-9 items-center justify-center rounded transition-colors sm:h-6 sm:w-6",
1641
1641
  tabSettingsOpen
1642
1642
  ? "text-foreground bg-accent/50"
1643
1643
  : "text-muted-foreground hover:text-foreground hover:bg-accent/30",
@@ -1731,7 +1731,7 @@ function AppLayoutInner({ children }: AppLayoutProps) {
1731
1731
  <TooltipTrigger asChild>
1732
1732
  <button
1733
1733
  onClick={() => setSearchFocused(true)}
1734
- className="flex h-9 w-9 sm:h-7 sm:w-7 items-center justify-center rounded text-muted-foreground hover:text-foreground hover:bg-accent/50 transition-colors"
1734
+ className="flex h-9 w-9 shrink-0 items-center justify-center rounded text-muted-foreground hover:text-foreground hover:bg-accent/50 transition-colors sm:h-7 sm:w-7"
1735
1735
  aria-label={t("mail.search.label")}
1736
1736
  >
1737
1737
  <IconSearch className="h-4 w-4" />
@@ -1796,7 +1796,7 @@ function AppLayoutInner({ children }: AppLayoutProps) {
1796
1796
  onClick={handleCompose}
1797
1797
  variant="outline"
1798
1798
  size="sm"
1799
- className="h-9 sm:h-7 px-3 text-[13px]"
1799
+ className="h-9 shrink-0 px-3 text-[13px] sm:h-7"
1800
1800
  aria-label={t("mail.toolbar.composeEmail")}
1801
1801
  >
1802
1802
  <span>{t("mail.toolbar.compose")}</span>
@@ -1822,7 +1822,7 @@ function AppLayoutInner({ children }: AppLayoutProps) {
1822
1822
  <button
1823
1823
  type="button"
1824
1824
  aria-label={t("mail.toolbar.accounts")}
1825
- className="flex items-center hover:opacity-90 transition-opacity ms-1"
1825
+ className="flex shrink-0 items-center hover:opacity-90 transition-opacity ms-1"
1826
1826
  >
1827
1827
  <div
1828
1828
  className="flex items-center"