@agent-native/core 0.119.1 → 0.119.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 (230) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +15 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/a2a/client.ts +4 -0
  5. package/corpus/core/src/agent/production-agent.ts +1 -0
  6. package/corpus/core/src/client/AssistantChat.tsx +11 -50
  7. package/corpus/core/src/client/ErrorBoundary.tsx +25 -19
  8. package/corpus/core/src/client/agent-page/AgentTabFrame.tsx +1 -1
  9. package/corpus/core/src/client/agent-page/AgentTabsPage.tsx +23 -0
  10. package/corpus/core/src/client/components/ui/message-scroller.tsx +1 -9
  11. package/corpus/core/src/client/resources/ResourceTree.tsx +3 -3
  12. package/corpus/core/src/client/resources/ResourcesPanel.tsx +64 -60
  13. package/corpus/core/src/client/settings/AccountSettingsCard.tsx +228 -0
  14. package/corpus/core/src/client/settings/SecretsSection.tsx +7 -5
  15. package/corpus/core/src/client/settings/SettingsPanel.tsx +67 -244
  16. package/corpus/core/src/client/settings/SettingsTabsPage.tsx +88 -21
  17. package/corpus/core/src/client/settings/agent-settings-search.ts +0 -2
  18. package/corpus/core/src/client/settings/index.ts +7 -0
  19. package/corpus/core/src/client/vite-dev-recovery-script.ts +18 -1
  20. package/corpus/core/src/db/client.ts +65 -15
  21. package/corpus/core/src/db/index.ts +2 -0
  22. package/corpus/core/src/localization/default-messages.ts +1 -0
  23. package/corpus/core/src/server/email.ts +33 -1
  24. package/corpus/core/src/styles/agent-native.css +23 -0
  25. package/corpus/core/src/templates/default/.agents/skills/frontend-design/SKILL.md +28 -0
  26. package/corpus/core/src/templates/default/app/routes/settings.tsx +2 -0
  27. package/corpus/core/src/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +28 -0
  28. package/corpus/core/src/vite/client.ts +76 -8
  29. package/corpus/templates/analytics/.agents/skills/frontend-design/SKILL.md +28 -0
  30. package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +3 -1
  31. package/corpus/templates/analytics/app/components/layout/Layout.tsx +9 -13
  32. package/corpus/templates/analytics/app/components/layout/layout-route-policy.ts +2 -2
  33. package/corpus/templates/analytics/app/global.css +0 -5
  34. package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +0 -13
  35. package/corpus/templates/analytics/app/lib/sql-query.ts +15 -2
  36. package/corpus/templates/analytics/app/pages/Settings.tsx +2 -24
  37. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +6 -0
  38. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +4 -0
  39. package/corpus/templates/analytics/app/pages/settings/settings-search.ts +1 -1
  40. package/corpus/templates/analytics/changelog/2026-07-23-ask-analytics-is-better-centered-with-a-quieter-chat-rail-an.md +6 -0
  41. package/corpus/templates/analytics/changelog/2026-07-23-daily-dashboard-emails-load-read-heavy-panels-reliably.md +6 -0
  42. package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +2 -2
  43. package/corpus/templates/analytics/server/jobs/dashboard-report.ts +39 -5
  44. package/corpus/templates/analytics/server/lib/dashboard-report.ts +186 -62
  45. package/corpus/templates/analytics/server/lib/first-party-analytics.ts +3 -0
  46. package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +18 -2
  47. package/corpus/templates/analytics/shared/dashboard-report-timeouts.ts +3 -0
  48. package/corpus/templates/assets/.agents/skills/frontend-design/SKILL.md +28 -0
  49. package/corpus/templates/assets/app/components/layout/Layout.tsx +3 -4
  50. package/corpus/templates/assets/app/components/layout/Sidebar.tsx +1 -1
  51. package/corpus/templates/assets/app/global.css +0 -5
  52. package/corpus/templates/assets/app/hooks/use-navigation-state.ts +0 -22
  53. package/corpus/templates/assets/app/routes/settings.tsx +2 -0
  54. package/corpus/templates/assets/changelog/2026-07-23-create-and-chat-surfaces-are-better-centered-with-quieter-ch.md +6 -0
  55. package/corpus/templates/brain/.agents/skills/frontend-design/SKILL.md +28 -0
  56. package/corpus/templates/brain/app/components/layout/Layout.tsx +1 -1
  57. package/corpus/templates/brain/app/components/layout/Sidebar.tsx +1 -1
  58. package/corpus/templates/brain/app/global.css +0 -9
  59. package/corpus/templates/brain/app/hooks/use-navigation-state.ts +0 -12
  60. package/corpus/templates/brain/app/i18n-data.ts +1 -0
  61. package/corpus/templates/brain/app/lib/settings-navigation.ts +1 -1
  62. package/corpus/templates/brain/app/routes/settings.tsx +21 -22
  63. package/corpus/templates/brain/changelog/2026-07-23-ask-brain-is-better-centered-with-quieter-chat-history-and-a.md +6 -0
  64. package/corpus/templates/calendar/.agents/skills/frontend-design/SKILL.md +28 -0
  65. package/corpus/templates/calendar/app/pages/Settings.tsx +2 -0
  66. package/corpus/templates/chat/.agents/skills/frontend-design/SKILL.md +28 -0
  67. package/corpus/templates/chat/app/components/layout/Layout.tsx +1 -1
  68. package/corpus/templates/chat/app/components/layout/Sidebar.tsx +1 -1
  69. package/corpus/templates/chat/app/hooks/use-navigation-state.ts +0 -15
  70. package/corpus/templates/chat/app/routes/settings.tsx +2 -0
  71. package/corpus/templates/chat/changelog/2026-07-23-full-page-chat-is-better-centered-with-quieter-chat-history-.md +6 -0
  72. package/corpus/templates/clips/.agents/skills/frontend-design/SKILL.md +28 -0
  73. package/corpus/templates/clips/app/routes/_app.settings._index.tsx +2 -0
  74. package/corpus/templates/content/.agents/skills/frontend-design/SKILL.md +28 -0
  75. package/corpus/templates/content/app/routes/_app.settings.tsx +2 -0
  76. package/corpus/templates/design/.agents/skills/frontend-design/SKILL.md +28 -0
  77. package/corpus/templates/design/app/routes/settings.tsx +2 -0
  78. package/corpus/templates/dispatch/.agents/skills/frontend-design/SKILL.md +28 -0
  79. package/corpus/templates/dispatch/app/routes/settings.tsx +2 -0
  80. package/corpus/templates/dispatch/changelog/2026-07-23-quieter-progressive-disclosure.md +6 -0
  81. package/corpus/templates/forms/.agents/skills/frontend-design/SKILL.md +28 -0
  82. package/corpus/templates/forms/app/global.css +0 -5
  83. package/corpus/templates/forms/app/hooks/use-navigation-state.ts +0 -7
  84. package/corpus/templates/forms/app/root.tsx +1 -7
  85. package/corpus/templates/forms/app/routes/_app.settings.tsx +2 -0
  86. package/corpus/templates/forms/changelog/2026-07-23-ask-forms-is-better-centered-with-quieter-chat-history-and-a.md +6 -0
  87. package/corpus/templates/macros/.agents/skills/frontend-design/SKILL.md +28 -0
  88. package/corpus/templates/macros/app/routes/settings.tsx +2 -0
  89. package/corpus/templates/mail/.agents/skills/frontend-design/SKILL.md +28 -0
  90. package/corpus/templates/mail/app/pages/SettingsPage.tsx +3 -1
  91. package/corpus/templates/plan/.agents/skills/frontend-design/SKILL.md +28 -0
  92. package/corpus/templates/plan/app/components/layout/Layout.tsx +2 -2
  93. package/corpus/templates/plan/app/components/layout/Sidebar.tsx +1 -13
  94. package/corpus/templates/plan/app/hooks/use-navigation-state.ts +0 -4
  95. package/corpus/templates/plan/app/routes/settings.tsx +2 -0
  96. package/corpus/templates/plan/changelog/2026-07-23-ask-plan-is-now-a-clean-full-page-chat-surface-with-better-c.md +6 -0
  97. package/corpus/templates/slides/.agents/skills/frontend-design/SKILL.md +28 -0
  98. package/corpus/templates/slides/app/routes/settings.tsx +2 -0
  99. package/corpus/templates/tasks/.agents/skills/frontend-design/SKILL.md +28 -0
  100. package/corpus/toolkit/CHANGELOG.md +6 -0
  101. package/corpus/toolkit/package.json +1 -1
  102. package/corpus/toolkit/src/chat-history.css +2 -2
  103. package/corpus/toolkit/src/composer/PromptComposer.tsx +0 -31
  104. package/corpus/toolkit/src/composer/RealtimeVoiceMode.tsx +7 -2
  105. package/corpus/toolkit/src/styles.css +183 -36
  106. package/dist/a2a/client.d.ts +2 -0
  107. package/dist/a2a/client.d.ts.map +1 -1
  108. package/dist/a2a/client.js +2 -0
  109. package/dist/a2a/client.js.map +1 -1
  110. package/dist/agent/production-agent.d.ts.map +1 -1
  111. package/dist/agent/production-agent.js +1 -0
  112. package/dist/agent/production-agent.js.map +1 -1
  113. package/dist/client/AssistantChat.d.ts.map +1 -1
  114. package/dist/client/AssistantChat.js +11 -42
  115. package/dist/client/AssistantChat.js.map +1 -1
  116. package/dist/client/ErrorBoundary.d.ts.map +1 -1
  117. package/dist/client/ErrorBoundary.js +3 -2
  118. package/dist/client/ErrorBoundary.js.map +1 -1
  119. package/dist/client/agent-page/AgentTabFrame.js +1 -1
  120. package/dist/client/agent-page/AgentTabFrame.js.map +1 -1
  121. package/dist/client/agent-page/AgentTabsPage.d.ts.map +1 -1
  122. package/dist/client/agent-page/AgentTabsPage.js +13 -1
  123. package/dist/client/agent-page/AgentTabsPage.js.map +1 -1
  124. package/dist/client/components/ui/message-scroller.d.ts.map +1 -1
  125. package/dist/client/components/ui/message-scroller.js +2 -2
  126. package/dist/client/components/ui/message-scroller.js.map +1 -1
  127. package/dist/client/resources/ResourceTree.js +2 -2
  128. package/dist/client/resources/ResourceTree.js.map +1 -1
  129. package/dist/client/resources/ResourcesPanel.d.ts.map +1 -1
  130. package/dist/client/resources/ResourcesPanel.js +11 -11
  131. package/dist/client/resources/ResourcesPanel.js.map +1 -1
  132. package/dist/client/settings/AccountSettingsCard.d.ts +9 -0
  133. package/dist/client/settings/AccountSettingsCard.d.ts.map +1 -0
  134. package/dist/client/settings/AccountSettingsCard.js +78 -0
  135. package/dist/client/settings/AccountSettingsCard.js.map +1 -0
  136. package/dist/client/settings/SecretsSection.d.ts.map +1 -1
  137. package/dist/client/settings/SecretsSection.js +2 -2
  138. package/dist/client/settings/SecretsSection.js.map +1 -1
  139. package/dist/client/settings/SettingsPanel.d.ts +3 -0
  140. package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
  141. package/dist/client/settings/SettingsPanel.js +25 -77
  142. package/dist/client/settings/SettingsPanel.js.map +1 -1
  143. package/dist/client/settings/SettingsTabsPage.d.ts +5 -1
  144. package/dist/client/settings/SettingsTabsPage.d.ts.map +1 -1
  145. package/dist/client/settings/SettingsTabsPage.js +35 -9
  146. package/dist/client/settings/SettingsTabsPage.js.map +1 -1
  147. package/dist/client/settings/agent-settings-search.d.ts.map +1 -1
  148. package/dist/client/settings/agent-settings-search.js +0 -2
  149. package/dist/client/settings/agent-settings-search.js.map +1 -1
  150. package/dist/client/settings/index.d.ts +2 -1
  151. package/dist/client/settings/index.d.ts.map +1 -1
  152. package/dist/client/settings/index.js +2 -1
  153. package/dist/client/settings/index.js.map +1 -1
  154. package/dist/client/vite-dev-recovery-script.d.ts.map +1 -1
  155. package/dist/client/vite-dev-recovery-script.js +18 -1
  156. package/dist/client/vite-dev-recovery-script.js.map +1 -1
  157. package/dist/collab/awareness.d.ts +2 -2
  158. package/dist/collab/awareness.d.ts.map +1 -1
  159. package/dist/collab/routes.d.ts +1 -1
  160. package/dist/collab/struct-routes.d.ts +1 -1
  161. package/dist/db/client.d.ts +18 -8
  162. package/dist/db/client.d.ts.map +1 -1
  163. package/dist/db/client.js +34 -11
  164. package/dist/db/client.js.map +1 -1
  165. package/dist/db/index.d.ts +1 -1
  166. package/dist/db/index.d.ts.map +1 -1
  167. package/dist/db/index.js.map +1 -1
  168. package/dist/localization/default-messages.d.ts +1 -0
  169. package/dist/localization/default-messages.d.ts.map +1 -1
  170. package/dist/localization/default-messages.js +1 -0
  171. package/dist/localization/default-messages.js.map +1 -1
  172. package/dist/notifications/routes.d.ts +2 -2
  173. package/dist/observability/routes.d.ts +3 -3
  174. package/dist/progress/routes.d.ts +1 -1
  175. package/dist/provider-api/actions/custom-provider-registration.d.ts +7 -7
  176. package/dist/provider-api/actions/provider-api.d.ts +7 -7
  177. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  178. package/dist/resources/handlers.d.ts +1 -1
  179. package/dist/secrets/routes.d.ts +9 -9
  180. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  181. package/dist/server/email.d.ts +1 -0
  182. package/dist/server/email.d.ts.map +1 -1
  183. package/dist/server/email.js +29 -1
  184. package/dist/server/email.js.map +1 -1
  185. package/dist/server/realtime-token.d.ts +1 -1
  186. package/dist/styles/agent-native.css +23 -0
  187. package/dist/templates/chat/.agents/skills/frontend-design/SKILL.md +28 -0
  188. package/dist/templates/chat/app/components/layout/Layout.tsx +1 -1
  189. package/dist/templates/chat/app/components/layout/Sidebar.tsx +1 -1
  190. package/dist/templates/chat/app/hooks/use-navigation-state.ts +0 -15
  191. package/dist/templates/chat/app/routes/settings.tsx +2 -0
  192. package/dist/templates/chat/changelog/2026-07-23-full-page-chat-is-better-centered-with-quieter-chat-history-.md +6 -0
  193. package/dist/templates/default/.agents/skills/frontend-design/SKILL.md +28 -0
  194. package/dist/templates/default/app/routes/settings.tsx +2 -0
  195. package/dist/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +28 -0
  196. package/dist/vite/client.d.ts.map +1 -1
  197. package/dist/vite/client.js +68 -6
  198. package/dist/vite/client.js.map +1 -1
  199. package/package.json +2 -2
  200. package/src/a2a/client.ts +4 -0
  201. package/src/agent/production-agent.ts +1 -0
  202. package/src/client/AssistantChat.tsx +11 -50
  203. package/src/client/ErrorBoundary.tsx +25 -19
  204. package/src/client/agent-page/AgentTabFrame.tsx +1 -1
  205. package/src/client/agent-page/AgentTabsPage.tsx +23 -0
  206. package/src/client/components/ui/message-scroller.tsx +1 -9
  207. package/src/client/resources/ResourceTree.tsx +3 -3
  208. package/src/client/resources/ResourcesPanel.tsx +64 -60
  209. package/src/client/settings/AccountSettingsCard.tsx +228 -0
  210. package/src/client/settings/SecretsSection.tsx +7 -5
  211. package/src/client/settings/SettingsPanel.tsx +67 -244
  212. package/src/client/settings/SettingsTabsPage.tsx +88 -21
  213. package/src/client/settings/agent-settings-search.ts +0 -2
  214. package/src/client/settings/index.ts +7 -0
  215. package/src/client/vite-dev-recovery-script.ts +18 -1
  216. package/src/db/client.ts +65 -15
  217. package/src/db/index.ts +2 -0
  218. package/src/localization/default-messages.ts +1 -0
  219. package/src/server/email.ts +33 -1
  220. package/src/styles/agent-native.css +23 -0
  221. package/src/templates/chat/.agents/skills/frontend-design/SKILL.md +28 -0
  222. package/src/templates/chat/app/components/layout/Layout.tsx +1 -1
  223. package/src/templates/chat/app/components/layout/Sidebar.tsx +1 -1
  224. package/src/templates/chat/app/hooks/use-navigation-state.ts +0 -15
  225. package/src/templates/chat/app/routes/settings.tsx +2 -0
  226. package/src/templates/chat/changelog/2026-07-23-full-page-chat-is-better-centered-with-quieter-chat-history-.md +6 -0
  227. package/src/templates/default/.agents/skills/frontend-design/SKILL.md +28 -0
  228. package/src/templates/default/app/routes/settings.tsx +2 -0
  229. package/src/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +28 -0
  230. package/src/vite/client.ts +76 -8
@@ -3,6 +3,7 @@ import {
3
3
  IconHistory,
4
4
  IconSearch,
5
5
  IconSettings,
6
+ IconUserCircle,
6
7
  IconUsers,
7
8
  IconX,
8
9
  } from "@tabler/icons-react";
@@ -15,6 +16,7 @@ import {
15
16
  type ComponentType,
16
17
  type ReactNode,
17
18
  } from "react";
19
+ import { Link } from "react-router";
18
20
 
19
21
  import { cn } from "../utils.js";
20
22
 
@@ -50,6 +52,8 @@ export interface SettingsTabItem {
50
52
  label: string;
51
53
  icon?: SettingsTabIcon;
52
54
  content: ReactNode;
55
+ /** Optional route for settings that live on a canonical page elsewhere. */
56
+ href?: string;
53
57
  /** Whether a parent surface may expose a personal/organization scope for this tab. */
54
58
  scopeAware?: boolean;
55
59
  /**
@@ -66,10 +70,12 @@ export interface SettingsTabItem {
66
70
 
67
71
  export interface SettingsTabsPageProps {
68
72
  general: ReactNode;
73
+ account?: ReactNode;
69
74
  team?: ReactNode;
70
75
  whatsNew?: ReactNode;
71
76
  extraTabs?: SettingsTabItem[];
72
77
  generalLabel?: string;
78
+ accountLabel?: string;
73
79
  teamLabel?: string;
74
80
  whatsNewLabel?: string;
75
81
  ariaLabel?: string;
@@ -182,10 +188,12 @@ function isEditableElement(element: Element | null): boolean {
182
188
 
183
189
  export function SettingsTabsPage({
184
190
  general,
191
+ account,
185
192
  team,
186
193
  whatsNew,
187
194
  extraTabs = [],
188
195
  generalLabel = "General",
196
+ accountLabel = "Account",
189
197
  teamLabel = "Team",
190
198
  whatsNewLabel = "What's new",
191
199
  ariaLabel = "Settings sections",
@@ -217,6 +225,15 @@ export function SettingsTabsPage({
217
225
  searchEntries: generalSearchEntries,
218
226
  },
219
227
  ];
228
+ if (account) {
229
+ next.push({
230
+ id: "account",
231
+ label: accountLabel,
232
+ icon: IconUserCircle,
233
+ content: account,
234
+ keywords: "profile photo avatar identity signed in email name",
235
+ });
236
+ }
220
237
  next.push(...extraTabs);
221
238
  if (team && !hasOrganizationTab) {
222
239
  next.push({
@@ -238,6 +255,8 @@ export function SettingsTabsPage({
238
255
  }
239
256
  return next;
240
257
  }, [
258
+ account,
259
+ accountLabel,
241
260
  extraTabs,
242
261
  general,
243
262
  generalLabel,
@@ -271,7 +290,9 @@ export function SettingsTabsPage({
271
290
  const activeTab = isControlled ? value : internalTab;
272
291
  const [query, setQuery] = useState("");
273
292
  const designSystem = useDesignSystem();
274
- const hasCustomTabs = Boolean(designSystem?.components?.Tabs);
293
+ const hasLinkedTabs = tabs.some((tab) => Boolean(tab.href));
294
+ const hasCustomTabs =
295
+ Boolean(designSystem?.components?.Tabs) && !hasLinkedTabs;
275
296
 
276
297
  const changeTab = useCallback(
277
298
  (tabId: string) => {
@@ -493,15 +514,16 @@ export function SettingsTabsPage({
493
514
  ) : (
494
515
  results.map((entry) => {
495
516
  const Icon = entry.icon;
496
- return (
497
- <button
498
- key={entry.id}
499
- type="button"
500
- role="option"
501
- aria-selected={false}
502
- onClick={() => selectEntry(entry)}
503
- className="flex items-start gap-2 rounded-md px-2.5 py-2 text-start text-sm text-foreground transition-colors hover:bg-accent/60"
504
- >
517
+ const tab = tabs.find(
518
+ (candidate) => candidate.id === entry.tabId,
519
+ );
520
+ const resultHref = tab?.href
521
+ ? entry.hash
522
+ ? `${tab.href.split("#", 1)[0]}#${entry.hash.replace(/^#/, "")}`
523
+ : tab.href
524
+ : null;
525
+ const result = (
526
+ <>
505
527
  {Icon ? (
506
528
  <Icon className="mt-0.5 size-4 shrink-0 text-muted-foreground" />
507
529
  ) : null}
@@ -513,6 +535,28 @@ export function SettingsTabsPage({
513
535
  {entry.description ?? entry.tabLabel}
514
536
  </span>
515
537
  </span>
538
+ </>
539
+ );
540
+ return resultHref ? (
541
+ <Link
542
+ key={entry.id}
543
+ to={resultHref}
544
+ role="option"
545
+ aria-selected={false}
546
+ className="flex items-start gap-2 rounded-md px-2.5 py-2 text-start text-sm text-foreground transition-colors hover:bg-accent/60"
547
+ >
548
+ {result}
549
+ </Link>
550
+ ) : (
551
+ <button
552
+ key={entry.id}
553
+ type="button"
554
+ role="option"
555
+ aria-selected={false}
556
+ onClick={() => selectEntry(entry)}
557
+ className="flex items-start gap-2 rounded-md px-2.5 py-2 text-start text-sm text-foreground transition-colors hover:bg-accent/60"
558
+ >
559
+ {result}
516
560
  </button>
517
561
  );
518
562
  })
@@ -558,6 +602,39 @@ export function SettingsTabsPage({
558
602
  {group.tabs.map((tab) => {
559
603
  const Icon = tab.icon;
560
604
  const selected = tab.id === selectedTab?.id;
605
+ const tabContent = (
606
+ <>
607
+ {Icon ? (
608
+ <Icon
609
+ className={cn(
610
+ "size-4 shrink-0",
611
+ selected
612
+ ? "text-foreground"
613
+ : "text-muted-foreground",
614
+ )}
615
+ />
616
+ ) : null}
617
+ <span className="truncate">{tab.label}</span>
618
+ </>
619
+ );
620
+ if (tab.href) {
621
+ return (
622
+ <Link
623
+ key={tab.id}
624
+ role="tab"
625
+ aria-selected={selected}
626
+ to={tab.href}
627
+ className={cn(
628
+ "flex min-h-9 shrink-0 items-center gap-2 rounded-md px-3 py-2 text-start text-sm font-medium transition-colors sm:w-full",
629
+ selected
630
+ ? "bg-accent text-foreground"
631
+ : "text-muted-foreground hover:bg-accent/50 hover:text-foreground",
632
+ )}
633
+ >
634
+ {tabContent}
635
+ </Link>
636
+ );
637
+ }
561
638
  return (
562
639
  <button
563
640
  key={tab.id}
@@ -577,17 +654,7 @@ export function SettingsTabsPage({
577
654
  : "text-muted-foreground hover:bg-accent/50 hover:text-foreground",
578
655
  )}
579
656
  >
580
- {Icon ? (
581
- <Icon
582
- className={cn(
583
- "size-4 shrink-0",
584
- selected
585
- ? "text-foreground"
586
- : "text-muted-foreground",
587
- )}
588
- />
589
- ) : null}
590
- <span className="truncate">{tab.label}</span>
657
+ {tabContent}
591
658
  </button>
592
659
  );
593
660
  })}
@@ -42,7 +42,6 @@ export const SETTINGS_SECTION_IDS = new Set<SettingsSectionId>([
42
42
  ]);
43
43
 
44
44
  export const ALL_SETTINGS_SECTIONS: readonly SettingsSectionId[] = [
45
- "account",
46
45
  "llm",
47
46
  "app-models",
48
47
  "limits",
@@ -81,7 +80,6 @@ export const CONNECTION_SETTINGS_SECTIONS: readonly SettingsSectionId[] = [
81
80
  ];
82
81
 
83
82
  export const WORKSPACE_SETTINGS_SECTIONS: readonly SettingsSectionId[] = [
84
- "account",
85
83
  "demo-mode",
86
84
  "hosting",
87
85
  "database",
@@ -1,4 +1,5 @@
1
1
  export {
2
+ AgentSettingsContent,
2
3
  SettingsPanel,
3
4
  useAgentSettingsTabs,
4
5
  type SettingsPanelProps,
@@ -13,6 +14,12 @@ export {
13
14
  type SettingsTabItem,
14
15
  type SettingsTabsPageProps,
15
16
  } from "./SettingsTabsPage.js";
17
+ export {
18
+ AccountSettingsCard,
19
+ AccountSettingsForm,
20
+ type AccountSettingsCardProps,
21
+ type AccountSettingsFormProps,
22
+ } from "./AccountSettingsCard.js";
16
23
  export {
17
24
  openBuilderConnectPopup,
18
25
  useBuilderConnectFlow,
@@ -22,8 +22,13 @@ export function getViteDevRecoveryScript(): string {
22
22
  if (params.has(${embedTokenParam}) || embedded === "1" || embedded === "true") return;
23
23
  } catch (e) {}
24
24
 
25
+ var INSTALL_KEY = "__agentNativeViteDevRecoveryInstalled";
26
+ if (window[INSTALL_KEY]) return;
27
+ window[INSTALL_KEY] = true;
28
+
25
29
  var RELOAD_KEY = "__an_optimize_reload";
26
30
  var MAX_RELOADS = 3;
31
+ var MIN_RELOAD_INTERVAL_MS = 2000;
27
32
  var RESET_AFTER_MS = 8000;
28
33
 
29
34
  var reloadTimer = null;
@@ -100,9 +105,21 @@ export function getViteDevRecoveryScript(): string {
100
105
  if (history.length >= 1) {
101
106
  showOverlay("Updating dev server\\u2026", "Reloading the page");
102
107
  }
103
- reloadTimer = setTimeout(function() { window.location.reload(); }, 300);
108
+ var lastReloadAt = history.length ? history[history.length - 1] : 0;
109
+ var delay = Math.max(
110
+ 300,
111
+ MIN_RELOAD_INTERVAL_MS - Math.max(0, Date.now() - lastReloadAt),
112
+ );
113
+ reloadTimer = setTimeout(function() { window.location.reload(); }, delay);
104
114
  }
105
115
 
116
+ window.addEventListener("vite:beforeFullReload", function() {
117
+ if (reloadTimer) {
118
+ clearTimeout(reloadTimer);
119
+ reloadTimer = null;
120
+ }
121
+ });
122
+
106
123
  function looksLikeViteFailureMessage(message) {
107
124
  if (!message) return false;
108
125
  return message.indexOf("Failed to fetch dynamically imported module") !== -1
@@ -24,13 +24,28 @@ const loggedNeonPools = new WeakSet<object>();
24
24
 
25
25
  export type Dialect = "sqlite" | "postgres" | "d1";
26
26
 
27
+ export interface DbExecQuery {
28
+ sql: string;
29
+ args?: unknown[];
30
+ /**
31
+ * Client-side wall-clock budget for this statement. Use only for idempotent
32
+ * reads unless the caller can safely tolerate a late write completing.
33
+ */
34
+ timeoutMs?: number;
35
+ /** Maximum connection-level attempts for this statement, including the first. */
36
+ maxAttempts?: number;
37
+ }
38
+
39
+ export type DbExecStatement = string | DbExecQuery;
40
+
27
41
  export interface DbExec {
28
- execute(
29
- sql: string | { sql: string; args?: unknown[] },
30
- ): Promise<{ rows: any[]; rowsAffected: number }>;
42
+ execute(sql: DbExecStatement): Promise<{
43
+ rows: any[];
44
+ rowsAffected: number;
45
+ }>;
31
46
  transaction?<T>(fn: (tx: DbExec) => Promise<T>): Promise<T>;
32
47
  atomicBatch?(
33
- statements: readonly (string | { sql: string; args?: unknown[] })[],
48
+ statements: readonly DbExecStatement[],
34
49
  ): Promise<Array<{ rows: any[]; rowsAffected: number }>>;
35
50
  /**
36
51
  * Release the underlying connection/pool held by this exec.
@@ -613,7 +628,7 @@ export function sqliteToPostgresParams(sql: string): string {
613
628
  return out;
614
629
  }
615
630
 
616
- function sqlAndArgs(sql: string | { sql: string; args?: unknown[] }): {
631
+ function sqlAndArgs(sql: DbExecStatement): {
617
632
  rawSql: string;
618
633
  args: unknown[];
619
634
  } {
@@ -622,6 +637,27 @@ function sqlAndArgs(sql: string | { sql: string; args?: unknown[] }): {
622
637
  : { rawSql: sql.sql, args: sql.args || [] };
623
638
  }
624
639
 
640
+ export function dbExecQueryBudget(statement: DbExecStatement): {
641
+ timeoutMs: number;
642
+ maxAttempts: number;
643
+ } {
644
+ if (typeof statement === "string") {
645
+ return { timeoutMs: dbOpTimeoutMs(), maxAttempts: 3 };
646
+ }
647
+ const timeoutMs = Number(statement.timeoutMs);
648
+ const maxAttempts = Number(statement.maxAttempts);
649
+ return {
650
+ timeoutMs:
651
+ Number.isFinite(timeoutMs) && timeoutMs > 0
652
+ ? Math.floor(timeoutMs)
653
+ : dbOpTimeoutMs(),
654
+ maxAttempts:
655
+ Number.isFinite(maxAttempts) && maxAttempts > 0
656
+ ? Math.floor(maxAttempts)
657
+ : 3,
658
+ };
659
+ }
660
+
625
661
  function explicitTransaction(
626
662
  execute: DbExec["execute"],
627
663
  begin = "BEGIN",
@@ -1173,8 +1209,10 @@ async function createDbExecInternal(
1173
1209
  async function queryNeonClient(
1174
1210
  client: any,
1175
1211
  sql: Parameters<DbExec["execute"]>[0],
1212
+ timeoutOverrideMs?: number,
1176
1213
  ) {
1177
1214
  const { rawSql, args } = sqlAndArgs(sql);
1215
+ const { timeoutMs } = dbExecQueryBudget(sql);
1178
1216
  const pgSql = sqliteToPostgresParams(rawSql);
1179
1217
  const result = await withDbTimeout(
1180
1218
  "query",
@@ -1183,7 +1221,7 @@ async function createDbExecInternal(
1183
1221
  rows: unknown[];
1184
1222
  rowCount?: number;
1185
1223
  }>,
1186
- dbOpTimeoutMs(),
1224
+ timeoutOverrideMs ?? timeoutMs,
1187
1225
  );
1188
1226
  return {
1189
1227
  rows: result.rows,
@@ -1192,6 +1230,7 @@ async function createDbExecInternal(
1192
1230
  }
1193
1231
  return {
1194
1232
  async execute(sql) {
1233
+ const { timeoutMs, maxAttempts } = dbExecQueryBudget(sql);
1195
1234
  if (bgHttp) {
1196
1235
  // HTTP-per-query path (poolQueryViaFetch=true): no pool.connect(), no
1197
1236
  // persistent socket to stall. queryNeonClient calls pool.query(),
@@ -1199,12 +1238,15 @@ async function createDbExecInternal(
1199
1238
  return retryOnConnectionError<{
1200
1239
  rows: unknown[];
1201
1240
  rowsAffected: number;
1202
- }>(() => queryNeonClient(pool, sql));
1241
+ }>(() => queryNeonClient(pool, sql), maxAttempts);
1203
1242
  }
1204
1243
  const result = await retryOnConnectionError<{
1205
1244
  rows: unknown[];
1206
1245
  rowsAffected: number;
1207
1246
  }>(async () => {
1247
+ const attemptStartedAt = Date.now();
1248
+ const remainingAttemptMs = () =>
1249
+ Math.max(1, timeoutMs - (Date.now() - attemptStartedAt));
1208
1250
  // Bound the pooled-connection ACQUIRE, not just the query below.
1209
1251
  // Neon's pooler can stall on `connect()` when cold or exhausted,
1210
1252
  // and that happens BEFORE `client.query`, so the query-level
@@ -1222,7 +1264,7 @@ async function createDbExecInternal(
1222
1264
  if (acquireTimedOut) c.release();
1223
1265
  return c;
1224
1266
  }),
1225
- dbOpTimeoutMs(),
1267
+ remainingAttemptMs(),
1226
1268
  () => {
1227
1269
  acquireTimedOut = true;
1228
1270
  },
@@ -1235,14 +1277,18 @@ async function createDbExecInternal(
1235
1277
  };
1236
1278
 
1237
1279
  try {
1238
- const result = await queryNeonClient(client, sql);
1280
+ const result = await queryNeonClient(
1281
+ client,
1282
+ sql,
1283
+ remainingAttemptMs(),
1284
+ );
1239
1285
  releaseClient();
1240
1286
  return result;
1241
1287
  } catch (err) {
1242
1288
  releaseClient(isConnectionError(err) ? true : undefined);
1243
1289
  throw err;
1244
1290
  }
1245
- });
1291
+ }, maxAttempts);
1246
1292
  return {
1247
1293
  rows: result.rows,
1248
1294
  rowsAffected: result.rowsAffected,
@@ -1310,6 +1356,7 @@ async function createDbExecInternal(
1310
1356
  try {
1311
1357
  const rawSql = typeof sql === "string" ? sql : sql.sql;
1312
1358
  const args = typeof sql === "string" ? [] : sql.args || [];
1359
+ const { timeoutMs } = dbExecQueryBudget(sql);
1313
1360
  const pgSql = sqliteToPostgresParams(rawSql);
1314
1361
  const result = await withDbTimeout<
1315
1362
  ArrayLike<unknown> & { count?: number }
@@ -1319,7 +1366,7 @@ async function createDbExecInternal(
1319
1366
  conn.unsafe(pgSql, args as any[]) as Promise<
1320
1367
  ArrayLike<unknown> & { count?: number }
1321
1368
  >,
1322
- dbOpTimeoutMs(),
1369
+ timeoutMs,
1323
1370
  () => {
1324
1371
  timedOut = true;
1325
1372
  disposePostgresPoolEventually(conn, "timed-out worker query");
@@ -1351,6 +1398,7 @@ async function createDbExecInternal(
1351
1398
  const tx: DbExec = {
1352
1399
  async execute(sql) {
1353
1400
  const { rawSql, args } = sqlAndArgs(sql);
1401
+ const { timeoutMs } = dbExecQueryBudget(sql);
1354
1402
  const pgSql = sqliteToPostgresParams(rawSql);
1355
1403
  const result = await withDbTimeout<
1356
1404
  ArrayLike<unknown> & { count?: number }
@@ -1360,7 +1408,7 @@ async function createDbExecInternal(
1360
1408
  txSql.unsafe(pgSql, args as any[]) as Promise<
1361
1409
  ArrayLike<unknown> & { count?: number }
1362
1410
  >,
1363
- dbOpTimeoutMs(),
1411
+ timeoutMs,
1364
1412
  );
1365
1413
  return {
1366
1414
  rows: Array.from(result),
@@ -1402,6 +1450,7 @@ async function createDbExecInternal(
1402
1450
  return {
1403
1451
  async execute(sql) {
1404
1452
  const { rawSql, args } = sqlAndArgs(sql);
1453
+ const { timeoutMs, maxAttempts } = dbExecQueryBudget(sql);
1405
1454
  const pgSql = sqliteToPostgresParams(rawSql);
1406
1455
  const result = await retryOnConnectionError<
1407
1456
  ArrayLike<unknown> & { count?: number }
@@ -1411,10 +1460,10 @@ async function createDbExecInternal(
1411
1460
  return withDbTimeout(
1412
1461
  "query",
1413
1462
  () => query,
1414
- dbOpTimeoutMs(),
1463
+ timeoutMs,
1415
1464
  () => recyclePool(queryPool),
1416
1465
  );
1417
- });
1466
+ }, maxAttempts);
1418
1467
  return {
1419
1468
  rows: Array.from(result),
1420
1469
  rowsAffected: result.count ?? 0,
@@ -1425,6 +1474,7 @@ async function createDbExecInternal(
1425
1474
  const tx: DbExec = {
1426
1475
  async execute(sql) {
1427
1476
  const { rawSql, args } = sqlAndArgs(sql);
1477
+ const { timeoutMs } = dbExecQueryBudget(sql);
1428
1478
  const pgSql = sqliteToPostgresParams(rawSql);
1429
1479
  const result = await withDbTimeout<
1430
1480
  ArrayLike<unknown> & { count?: number }
@@ -1434,7 +1484,7 @@ async function createDbExecInternal(
1434
1484
  txSql.unsafe(pgSql, args as any[]) as Promise<
1435
1485
  ArrayLike<unknown> & { count?: number }
1436
1486
  >,
1437
- dbOpTimeoutMs(),
1487
+ timeoutMs,
1438
1488
  );
1439
1489
  return {
1440
1490
  rows: Array.from(result),
@@ -46,6 +46,8 @@ export {
46
46
  closeDbExec,
47
47
  type DbExec,
48
48
  type DbExecConfig,
49
+ type DbExecQuery,
50
+ type DbExecStatement,
49
51
  type Dialect,
50
52
  } from "./client.js";
51
53
  export { table, text, integer, now } from "./schema.js";
@@ -18,6 +18,7 @@ const messages = {
18
18
  },
19
19
  settings: {
20
20
  title: "Settings",
21
+ pageTitle: "Settings",
21
22
  description: "Language and workspace preferences for this app.",
22
23
  languageTitle: "Language",
23
24
  languageDescription:
@@ -36,6 +36,7 @@ export interface SendEmailArgs {
36
36
  inReplyTo?: string;
37
37
  references?: string;
38
38
  attachments?: EmailAttachment[];
39
+ timeoutMs?: number;
39
40
  }
40
41
 
41
42
  let cachedAgentNativeLogo: Buffer | undefined;
@@ -125,8 +126,12 @@ function getFromAddress(
125
126
  return "Agent Native <onboarding@resend.dev>";
126
127
  }
127
128
 
128
- export async function sendEmail(args: SendEmailArgs): Promise<void> {
129
+ async function sendEmailWithSignal(
130
+ args: SendEmailArgs,
131
+ signal?: AbortSignal,
132
+ ): Promise<void> {
129
133
  const config = await resolveEmailTransport();
134
+ signal?.throwIfAborted();
130
135
  const provider = config.provider;
131
136
  const from = getFromAddress(config, args.from);
132
137
  const attachments = resolveAttachments(args);
@@ -164,6 +169,7 @@ export async function sendEmail(args: SendEmailArgs): Promise<void> {
164
169
  "Content-Type": "application/json",
165
170
  },
166
171
  body: JSON.stringify(payload),
172
+ signal,
167
173
  });
168
174
  if (!res.ok) {
169
175
  const body = await res.text().catch(() => "");
@@ -215,6 +221,7 @@ export async function sendEmail(args: SendEmailArgs): Promise<void> {
215
221
  "Content-Type": "application/json",
216
222
  },
217
223
  body: JSON.stringify(sgPayload),
224
+ signal,
218
225
  });
219
226
  if (!res.ok) {
220
227
  const body = await res.text().catch(() => "");
@@ -239,6 +246,31 @@ export async function sendEmail(args: SendEmailArgs): Promise<void> {
239
246
  );
240
247
  }
241
248
 
249
+ export async function sendEmail(args: SendEmailArgs): Promise<void> {
250
+ const requestedTimeoutMs = Number(args.timeoutMs);
251
+ if (!Number.isFinite(requestedTimeoutMs) || requestedTimeoutMs <= 0) {
252
+ return sendEmailWithSignal(args);
253
+ }
254
+
255
+ const timeoutMs = Math.floor(requestedTimeoutMs);
256
+ const controller = new AbortController();
257
+ const timeoutError = new Error(`Email send timed out after ${timeoutMs}ms`);
258
+ let timeout: ReturnType<typeof setTimeout> | undefined;
259
+ try {
260
+ await Promise.race([
261
+ sendEmailWithSignal(args, controller.signal),
262
+ new Promise<never>((_, reject) => {
263
+ timeout = setTimeout(() => {
264
+ controller.abort(timeoutError);
265
+ reject(timeoutError);
266
+ }, timeoutMs);
267
+ }),
268
+ ]);
269
+ } finally {
270
+ if (timeout) clearTimeout(timeout);
271
+ }
272
+ }
273
+
242
274
  function parseSendGridFrom(from: string): { email: string; name?: string } {
243
275
  const m = from.match(/^\s*(.*?)\s*<(.+)>\s*$/);
244
276
  if (m && m[2]) return { name: m[1] || undefined, email: m[2] };
@@ -139,6 +139,28 @@
139
139
  background: inherit;
140
140
  }
141
141
 
142
+ .message-scroller-viewport--top-fade {
143
+ --message-scroller-top-fade-size: 48px;
144
+ -webkit-mask-image: linear-gradient(
145
+ to bottom,
146
+ transparent 0,
147
+ black var(--message-scroller-top-fade-size),
148
+ black 100%
149
+ );
150
+ mask-image: linear-gradient(
151
+ to bottom,
152
+ transparent 0,
153
+ black var(--message-scroller-top-fade-size),
154
+ black 100%
155
+ );
156
+ -webkit-mask-position: 0 0;
157
+ mask-position: 0 0;
158
+ -webkit-mask-repeat: no-repeat;
159
+ mask-repeat: no-repeat;
160
+ -webkit-mask-size: 100% 100%;
161
+ mask-size: 100% 100%;
162
+ }
163
+
142
164
  .agent-sidebar-resize-handle {
143
165
  background: transparent;
144
166
  }
@@ -888,6 +910,7 @@
888
910
 
889
911
  [data-agent-empty-state="centered"] {
890
912
  justify-content: center;
913
+ padding-bottom: clamp(3rem, 8vh, 6rem);
891
914
  }
892
915
 
893
916
  [data-agent-empty-state="centered"] > .agent-chat-scroll {
@@ -35,6 +35,34 @@ Then implement working code that is cohesive, accessible, responsive, and polish
35
35
 
36
36
  Default to Apple/Linear-level restraint: make the primary workflow obvious, then remove everything that does not help that workflow right now. A polished UI often has fewer visible controls, fewer borders, fewer labels, and fewer explanatory surfaces than the first reasonable implementation.
37
37
 
38
+ ### Progressive Disclosure Is A Design Requirement
39
+
40
+ Treat an all-at-once interface as a defect to fix during design, not as a
41
+ styling preference. Before coding, inventory every piece of content and action
42
+ on the surface, then assign each one to the smallest useful visibility level:
43
+
44
+ 1. **Immediate** — the page title, current state, one primary action, and the
45
+ compact context needed to choose what to do next.
46
+ 2. **Expanded** — the details needed for the selected item or active workflow.
47
+ 3. **On demand** — advanced settings, diagnostics, credentials, metadata,
48
+ destructive actions, documentation, and rarely used tools.
49
+
50
+ Use single-select accordions or simple disclosure rows for sibling panels when
51
+ the user is choosing one item at a time. Inside an expanded panel, keep another
52
+ layer for independent concerns instead of dumping every form, explanation, and
53
+ secondary action into the first reveal. Prefer one flat panel with alignment,
54
+ dividers, and whitespace over nested cards; provider or product icons should
55
+ not receive decorative borders or containers unless the container communicates
56
+ state or interaction. A collapsed row should still show the item name, status,
57
+ and a concise summary so the user can scan the whole surface without opening
58
+ everything.
59
+
60
+ Before shipping, ask: “What can disappear until the user asks for it?” Then
61
+ verify collapsed, expanded, loading, empty, error, and narrow-width states.
62
+ If the first viewport contains multiple forms, repeated explanatory copy,
63
+ documentation links, and controls for unrelated tasks, the surface has not
64
+ passed this requirement yet.
65
+
38
66
  - **Start by subtracting**: Before adding a visible control, banner, toolbar row, card, or explanatory block, ask what can be removed, merged, renamed, or moved into an existing affordance.
39
67
  - **One primary action**: Each surface should have one dominant next action. Secondary actions belong in menus, popovers, command palettes, disclosure rows, or contextual hover/focus states unless they are used constantly.
40
68
  - **Progressively disclose rare work**: Advanced options, diagnostics, metadata, settings, import/export, destructive actions, and inspection tools should stay tucked away until requested. Prefer small icon triggers with tooltips, popovers, drawers, or detail panels over permanent chrome.
@@ -1,5 +1,6 @@
1
1
  import { LanguagePicker, useT } from "@agent-native/core/client/i18n";
2
2
  import {
3
+ AccountSettingsCard,
3
4
  SettingsTabsPage,
4
5
  useAgentSettingsTabs,
5
6
  } from "@agent-native/core/client/settings";
@@ -32,6 +33,7 @@ export default function SettingsPage() {
32
33
  </div>
33
34
 
34
35
  <SettingsTabsPage
36
+ account={<AccountSettingsCard />}
35
37
  extraTabs={agentSettingsTabs}
36
38
  general={
37
39
  <section className="mx-auto w-full max-w-2xl rounded-lg border border-border/50 bg-card px-4 py-4">