@agent-native/core 0.119.0 → 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 (274) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +23 -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/ingestion/figma-node-to-html.ts +20 -99
  23. package/corpus/core/src/ingestion/figma-paint-math.ts +371 -0
  24. package/corpus/core/src/ingestion/index.ts +20 -0
  25. package/corpus/core/src/localization/default-messages.ts +1 -0
  26. package/corpus/core/src/server/email.ts +33 -1
  27. package/corpus/core/src/styles/agent-native.css +23 -0
  28. package/corpus/core/src/templates/default/.agents/skills/frontend-design/SKILL.md +28 -0
  29. package/corpus/core/src/templates/default/app/routes/settings.tsx +2 -0
  30. package/corpus/core/src/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +28 -0
  31. package/corpus/core/src/vite/client.ts +76 -8
  32. package/corpus/templates/analytics/.agents/skills/frontend-design/SKILL.md +28 -0
  33. package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +3 -1
  34. package/corpus/templates/analytics/app/components/layout/Layout.tsx +9 -13
  35. package/corpus/templates/analytics/app/components/layout/layout-route-policy.ts +2 -2
  36. package/corpus/templates/analytics/app/global.css +0 -5
  37. package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +0 -13
  38. package/corpus/templates/analytics/app/lib/sql-query.ts +15 -2
  39. package/corpus/templates/analytics/app/pages/Settings.tsx +2 -24
  40. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +6 -0
  41. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +4 -0
  42. package/corpus/templates/analytics/app/pages/settings/settings-search.ts +1 -1
  43. package/corpus/templates/analytics/changelog/2026-07-23-ask-analytics-is-better-centered-with-a-quieter-chat-rail-an.md +6 -0
  44. package/corpus/templates/analytics/changelog/2026-07-23-daily-dashboard-emails-load-read-heavy-panels-reliably.md +6 -0
  45. package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +2 -2
  46. package/corpus/templates/analytics/server/jobs/dashboard-report.ts +39 -5
  47. package/corpus/templates/analytics/server/lib/dashboard-report.ts +186 -62
  48. package/corpus/templates/analytics/server/lib/first-party-analytics.ts +3 -0
  49. package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +18 -2
  50. package/corpus/templates/analytics/shared/dashboard-report-timeouts.ts +3 -0
  51. package/corpus/templates/assets/.agents/skills/frontend-design/SKILL.md +28 -0
  52. package/corpus/templates/assets/app/components/layout/Layout.tsx +3 -4
  53. package/corpus/templates/assets/app/components/layout/Sidebar.tsx +1 -1
  54. package/corpus/templates/assets/app/global.css +0 -5
  55. package/corpus/templates/assets/app/hooks/use-navigation-state.ts +0 -22
  56. package/corpus/templates/assets/app/routes/library.tsx +17 -9
  57. package/corpus/templates/assets/app/routes/settings.tsx +2 -0
  58. package/corpus/templates/assets/changelog/2026-07-23-create-and-chat-surfaces-are-better-centered-with-quieter-ch.md +6 -0
  59. package/corpus/templates/brain/.agents/skills/frontend-design/SKILL.md +28 -0
  60. package/corpus/templates/brain/app/components/layout/Layout.tsx +1 -1
  61. package/corpus/templates/brain/app/components/layout/Sidebar.tsx +1 -1
  62. package/corpus/templates/brain/app/global.css +0 -9
  63. package/corpus/templates/brain/app/hooks/use-navigation-state.ts +0 -12
  64. package/corpus/templates/brain/app/i18n-data.ts +1 -0
  65. package/corpus/templates/brain/app/lib/settings-navigation.ts +1 -1
  66. package/corpus/templates/brain/app/routes/settings.tsx +21 -22
  67. package/corpus/templates/brain/changelog/2026-07-23-ask-brain-is-better-centered-with-quieter-chat-history-and-a.md +6 -0
  68. package/corpus/templates/calendar/.agents/skills/frontend-design/SKILL.md +28 -0
  69. package/corpus/templates/calendar/app/pages/Settings.tsx +2 -0
  70. package/corpus/templates/chat/.agents/skills/frontend-design/SKILL.md +28 -0
  71. package/corpus/templates/chat/app/components/layout/Layout.tsx +1 -1
  72. package/corpus/templates/chat/app/components/layout/Sidebar.tsx +1 -1
  73. package/corpus/templates/chat/app/hooks/use-navigation-state.ts +0 -15
  74. package/corpus/templates/chat/app/routes/settings.tsx +2 -0
  75. package/corpus/templates/chat/changelog/2026-07-23-full-page-chat-is-better-centered-with-quieter-chat-history-.md +6 -0
  76. package/corpus/templates/clips/.agents/skills/frontend-design/SKILL.md +28 -0
  77. package/corpus/templates/clips/app/routes/_app.settings._index.tsx +2 -0
  78. package/corpus/templates/content/.agents/skills/frontend-design/SKILL.md +28 -0
  79. package/corpus/templates/content/app/routes/_app.settings.tsx +2 -0
  80. package/corpus/templates/design/.agents/skills/frontend-design/SKILL.md +28 -0
  81. package/corpus/templates/design/AGENTS.md +18 -5
  82. package/corpus/templates/design/FIGMA_INTEROPERABILITY.md +25 -12
  83. package/corpus/templates/design/actions/hydrate-figma-paste-images.ts +115 -0
  84. package/corpus/templates/design/actions/import-figma-clipboard.ts +83 -18
  85. package/corpus/templates/design/actions/import-figma-frame.ts +34 -21
  86. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +28 -1
  87. package/corpus/templates/design/app/components/design/DesignImportPanel.tsx +106 -11
  88. package/corpus/templates/design/app/components/design/FigmaHydrationDialog.tsx +273 -0
  89. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +7 -7
  90. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +57 -9
  91. package/corpus/templates/design/app/components/design/design-canvas/iframe-events.ts +10 -0
  92. package/corpus/templates/design/app/components/design/multi-screen/board-surface-html.ts +8 -1
  93. package/corpus/templates/design/app/components/design/multi-screen/types.ts +3 -0
  94. package/corpus/templates/design/app/i18n-data.ts +150 -2
  95. package/corpus/templates/design/app/lib/design-file-upload.ts +85 -0
  96. package/corpus/templates/design/app/lib/design-import.ts +19 -1
  97. package/corpus/templates/design/app/lib/figma-clipboard.ts +110 -10
  98. package/corpus/templates/design/app/routes/settings.tsx +2 -0
  99. package/corpus/templates/design/changelog/2026-07-20-paste-figma-frames-directly-onto-the-canvas-no-token-needed.md +6 -0
  100. package/corpus/templates/design/changelog/2026-07-21-fig-upload-frame-url-and-rate-limit-errors.md +5 -0
  101. package/corpus/templates/design/changelog/2026-07-22-connecting-a-figma-token-now-actually-fills-in-a-pasted-desi.md +6 -0
  102. package/corpus/templates/design/changelog/2026-07-22-figma-line-arrow-vectors-and-stroked-icons-now-render-instea.md +6 -0
  103. package/corpus/templates/design/changelog/2026-07-22-fill-a-no-token-figma-paste-s-images-by-dropping-the-origina.md +6 -0
  104. package/corpus/templates/design/changelog/2026-07-22-imported-and-pasted-figma-frames-now-render-faithfully-fixed.md +6 -0
  105. package/corpus/templates/design/server/handlers/import-design-file.ts +50 -0
  106. package/corpus/templates/design/server/lib/fig-file-decoder.ts +90 -49
  107. package/corpus/templates/design/server/lib/fig-file-import.ts +19 -5
  108. package/corpus/templates/design/server/lib/fig-file-to-html.ts +583 -78
  109. package/corpus/templates/design/server/lib/figma-clipboard-local-decode.ts +238 -0
  110. package/corpus/templates/design/server/lib/figma-image-hydration.ts +389 -0
  111. package/corpus/templates/design/server/lib/figma-node-import.ts +136 -36
  112. package/corpus/templates/design/server/register-secrets.ts +14 -2
  113. package/corpus/templates/dispatch/.agents/skills/frontend-design/SKILL.md +28 -0
  114. package/corpus/templates/dispatch/app/routes/settings.tsx +2 -0
  115. package/corpus/templates/dispatch/changelog/2026-07-23-quieter-progressive-disclosure.md +6 -0
  116. package/corpus/templates/forms/.agents/skills/frontend-design/SKILL.md +28 -0
  117. package/corpus/templates/forms/app/global.css +0 -5
  118. package/corpus/templates/forms/app/hooks/use-navigation-state.ts +0 -7
  119. package/corpus/templates/forms/app/root.tsx +1 -7
  120. package/corpus/templates/forms/app/routes/_app.settings.tsx +2 -0
  121. package/corpus/templates/forms/changelog/2026-07-23-ask-forms-is-better-centered-with-quieter-chat-history-and-a.md +6 -0
  122. package/corpus/templates/macros/.agents/skills/frontend-design/SKILL.md +28 -0
  123. package/corpus/templates/macros/app/routes/settings.tsx +2 -0
  124. package/corpus/templates/mail/.agents/skills/frontend-design/SKILL.md +28 -0
  125. package/corpus/templates/mail/app/pages/SettingsPage.tsx +3 -1
  126. package/corpus/templates/plan/.agents/skills/frontend-design/SKILL.md +28 -0
  127. package/corpus/templates/plan/app/components/layout/Layout.tsx +2 -2
  128. package/corpus/templates/plan/app/components/layout/Sidebar.tsx +1 -13
  129. package/corpus/templates/plan/app/hooks/use-navigation-state.ts +0 -4
  130. package/corpus/templates/plan/app/routes/settings.tsx +2 -0
  131. package/corpus/templates/plan/changelog/2026-07-23-ask-plan-is-now-a-clean-full-page-chat-surface-with-better-c.md +6 -0
  132. package/corpus/templates/slides/.agents/skills/frontend-design/SKILL.md +28 -0
  133. package/corpus/templates/slides/app/routes/settings.tsx +2 -0
  134. package/corpus/templates/tasks/.agents/skills/frontend-design/SKILL.md +28 -0
  135. package/corpus/toolkit/CHANGELOG.md +6 -0
  136. package/corpus/toolkit/package.json +1 -1
  137. package/corpus/toolkit/src/chat-history.css +2 -2
  138. package/corpus/toolkit/src/composer/PromptComposer.tsx +0 -31
  139. package/corpus/toolkit/src/composer/RealtimeVoiceMode.tsx +7 -2
  140. package/corpus/toolkit/src/styles.css +183 -36
  141. package/dist/a2a/client.d.ts +2 -0
  142. package/dist/a2a/client.d.ts.map +1 -1
  143. package/dist/a2a/client.js +2 -0
  144. package/dist/a2a/client.js.map +1 -1
  145. package/dist/agent/production-agent.d.ts.map +1 -1
  146. package/dist/agent/production-agent.js +1 -0
  147. package/dist/agent/production-agent.js.map +1 -1
  148. package/dist/client/AssistantChat.d.ts.map +1 -1
  149. package/dist/client/AssistantChat.js +11 -42
  150. package/dist/client/AssistantChat.js.map +1 -1
  151. package/dist/client/ErrorBoundary.d.ts.map +1 -1
  152. package/dist/client/ErrorBoundary.js +3 -2
  153. package/dist/client/ErrorBoundary.js.map +1 -1
  154. package/dist/client/agent-page/AgentTabFrame.js +1 -1
  155. package/dist/client/agent-page/AgentTabFrame.js.map +1 -1
  156. package/dist/client/agent-page/AgentTabsPage.d.ts.map +1 -1
  157. package/dist/client/agent-page/AgentTabsPage.js +13 -1
  158. package/dist/client/agent-page/AgentTabsPage.js.map +1 -1
  159. package/dist/client/components/ui/message-scroller.d.ts.map +1 -1
  160. package/dist/client/components/ui/message-scroller.js +2 -2
  161. package/dist/client/components/ui/message-scroller.js.map +1 -1
  162. package/dist/client/resources/ResourceTree.js +2 -2
  163. package/dist/client/resources/ResourceTree.js.map +1 -1
  164. package/dist/client/resources/ResourcesPanel.d.ts.map +1 -1
  165. package/dist/client/resources/ResourcesPanel.js +11 -11
  166. package/dist/client/resources/ResourcesPanel.js.map +1 -1
  167. package/dist/client/settings/AccountSettingsCard.d.ts +9 -0
  168. package/dist/client/settings/AccountSettingsCard.d.ts.map +1 -0
  169. package/dist/client/settings/AccountSettingsCard.js +78 -0
  170. package/dist/client/settings/AccountSettingsCard.js.map +1 -0
  171. package/dist/client/settings/SecretsSection.d.ts.map +1 -1
  172. package/dist/client/settings/SecretsSection.js +2 -2
  173. package/dist/client/settings/SecretsSection.js.map +1 -1
  174. package/dist/client/settings/SettingsPanel.d.ts +3 -0
  175. package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
  176. package/dist/client/settings/SettingsPanel.js +25 -77
  177. package/dist/client/settings/SettingsPanel.js.map +1 -1
  178. package/dist/client/settings/SettingsTabsPage.d.ts +5 -1
  179. package/dist/client/settings/SettingsTabsPage.d.ts.map +1 -1
  180. package/dist/client/settings/SettingsTabsPage.js +35 -9
  181. package/dist/client/settings/SettingsTabsPage.js.map +1 -1
  182. package/dist/client/settings/agent-settings-search.d.ts.map +1 -1
  183. package/dist/client/settings/agent-settings-search.js +0 -2
  184. package/dist/client/settings/agent-settings-search.js.map +1 -1
  185. package/dist/client/settings/index.d.ts +2 -1
  186. package/dist/client/settings/index.d.ts.map +1 -1
  187. package/dist/client/settings/index.js +2 -1
  188. package/dist/client/settings/index.js.map +1 -1
  189. package/dist/client/vite-dev-recovery-script.d.ts.map +1 -1
  190. package/dist/client/vite-dev-recovery-script.js +18 -1
  191. package/dist/client/vite-dev-recovery-script.js.map +1 -1
  192. package/dist/collab/routes.d.ts +1 -1
  193. package/dist/db/client.d.ts +18 -8
  194. package/dist/db/client.d.ts.map +1 -1
  195. package/dist/db/client.js +34 -11
  196. package/dist/db/client.js.map +1 -1
  197. package/dist/db/index.d.ts +1 -1
  198. package/dist/db/index.d.ts.map +1 -1
  199. package/dist/db/index.js.map +1 -1
  200. package/dist/ingestion/figma-node-to-html.d.ts.map +1 -1
  201. package/dist/ingestion/figma-node-to-html.js +14 -88
  202. package/dist/ingestion/figma-node-to-html.js.map +1 -1
  203. package/dist/ingestion/figma-paint-math.d.ts +157 -0
  204. package/dist/ingestion/figma-paint-math.d.ts.map +1 -0
  205. package/dist/ingestion/figma-paint-math.js +274 -0
  206. package/dist/ingestion/figma-paint-math.js.map +1 -0
  207. package/dist/ingestion/index.d.ts +1 -0
  208. package/dist/ingestion/index.d.ts.map +1 -1
  209. package/dist/ingestion/index.js +1 -0
  210. package/dist/ingestion/index.js.map +1 -1
  211. package/dist/localization/default-messages.d.ts +1 -0
  212. package/dist/localization/default-messages.d.ts.map +1 -1
  213. package/dist/localization/default-messages.js +1 -0
  214. package/dist/localization/default-messages.js.map +1 -1
  215. package/dist/notifications/routes.d.ts +2 -2
  216. package/dist/observability/routes.d.ts +3 -3
  217. package/dist/provider-api/actions/custom-provider-registration.d.ts +9 -9
  218. package/dist/provider-api/actions/provider-api.d.ts +11 -11
  219. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  220. package/dist/secrets/routes.d.ts +6 -6
  221. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  222. package/dist/server/email.d.ts +1 -0
  223. package/dist/server/email.d.ts.map +1 -1
  224. package/dist/server/email.js +29 -1
  225. package/dist/server/email.js.map +1 -1
  226. package/dist/server/transcribe-voice.d.ts +1 -1
  227. package/dist/styles/agent-native.css +23 -0
  228. package/dist/templates/chat/.agents/skills/frontend-design/SKILL.md +28 -0
  229. package/dist/templates/chat/app/components/layout/Layout.tsx +1 -1
  230. package/dist/templates/chat/app/components/layout/Sidebar.tsx +1 -1
  231. package/dist/templates/chat/app/hooks/use-navigation-state.ts +0 -15
  232. package/dist/templates/chat/app/routes/settings.tsx +2 -0
  233. package/dist/templates/chat/changelog/2026-07-23-full-page-chat-is-better-centered-with-quieter-chat-history-.md +6 -0
  234. package/dist/templates/default/.agents/skills/frontend-design/SKILL.md +28 -0
  235. package/dist/templates/default/app/routes/settings.tsx +2 -0
  236. package/dist/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +28 -0
  237. package/dist/vite/client.d.ts.map +1 -1
  238. package/dist/vite/client.js +68 -6
  239. package/dist/vite/client.js.map +1 -1
  240. package/package.json +2 -2
  241. package/src/a2a/client.ts +4 -0
  242. package/src/agent/production-agent.ts +1 -0
  243. package/src/client/AssistantChat.tsx +11 -50
  244. package/src/client/ErrorBoundary.tsx +25 -19
  245. package/src/client/agent-page/AgentTabFrame.tsx +1 -1
  246. package/src/client/agent-page/AgentTabsPage.tsx +23 -0
  247. package/src/client/components/ui/message-scroller.tsx +1 -9
  248. package/src/client/resources/ResourceTree.tsx +3 -3
  249. package/src/client/resources/ResourcesPanel.tsx +64 -60
  250. package/src/client/settings/AccountSettingsCard.tsx +228 -0
  251. package/src/client/settings/SecretsSection.tsx +7 -5
  252. package/src/client/settings/SettingsPanel.tsx +67 -244
  253. package/src/client/settings/SettingsTabsPage.tsx +88 -21
  254. package/src/client/settings/agent-settings-search.ts +0 -2
  255. package/src/client/settings/index.ts +7 -0
  256. package/src/client/vite-dev-recovery-script.ts +18 -1
  257. package/src/db/client.ts +65 -15
  258. package/src/db/index.ts +2 -0
  259. package/src/ingestion/figma-node-to-html.ts +20 -99
  260. package/src/ingestion/figma-paint-math.ts +371 -0
  261. package/src/ingestion/index.ts +20 -0
  262. package/src/localization/default-messages.ts +1 -0
  263. package/src/server/email.ts +33 -1
  264. package/src/styles/agent-native.css +23 -0
  265. package/src/templates/chat/.agents/skills/frontend-design/SKILL.md +28 -0
  266. package/src/templates/chat/app/components/layout/Layout.tsx +1 -1
  267. package/src/templates/chat/app/components/layout/Sidebar.tsx +1 -1
  268. package/src/templates/chat/app/hooks/use-navigation-state.ts +0 -15
  269. package/src/templates/chat/app/routes/settings.tsx +2 -0
  270. package/src/templates/chat/changelog/2026-07-23-full-page-chat-is-better-centered-with-quieter-chat-history-.md +6 -0
  271. package/src/templates/default/.agents/skills/frontend-design/SKILL.md +28 -0
  272. package/src/templates/default/app/routes/settings.tsx +2 -0
  273. package/src/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +28 -0
  274. package/src/vite/client.ts +76 -8
@@ -28,6 +28,7 @@ export interface SendEmailArgs {
28
28
  inReplyTo?: string;
29
29
  references?: string;
30
30
  attachments?: EmailAttachment[];
31
+ timeoutMs?: number;
31
32
  }
32
33
  export declare function isEmailConfigured(): Promise<boolean>;
33
34
  export declare function getEmailProvider(): Promise<EmailProvider>;
@@ -1 +1 @@
1
- {"version":3,"file":"email.d.ts","sourceRoot":"","sources":["../../src/server/email.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAOH,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,UAAU,GAAG,KAAK,CAAC;AAE1D,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,YAAY,GAAG,QAAQ,CAAC;CACvC;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,eAAe,EAAE,CAAC;CACjC;AAiED,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,OAAO,CAAC,CAE1D;AAED,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,aAAa,CAAC,CAE/D;AAkBD,wBAAsB,SAAS,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAgHlE"}
1
+ {"version":3,"file":"email.d.ts","sourceRoot":"","sources":["../../src/server/email.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAOH,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,UAAU,GAAG,KAAK,CAAC;AAE1D,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,YAAY,GAAG,QAAQ,CAAC;CACvC;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,eAAe,EAAE,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAiED,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,OAAO,CAAC,CAE1D;AAED,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,aAAa,CAAC,CAE/D;AA0ID,wBAAsB,SAAS,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBlE"}
@@ -72,8 +72,9 @@ function getFromAddress(config, override) {
72
72
  }
73
73
  return "Agent Native <onboarding@resend.dev>";
74
74
  }
75
- export async function sendEmail(args) {
75
+ async function sendEmailWithSignal(args, signal) {
76
76
  const config = await resolveEmailTransport();
77
+ signal?.throwIfAborted();
77
78
  const provider = config.provider;
78
79
  const from = getFromAddress(config, args.from);
79
80
  const attachments = resolveAttachments(args);
@@ -113,6 +114,7 @@ export async function sendEmail(args) {
113
114
  "Content-Type": "application/json",
114
115
  },
115
116
  body: JSON.stringify(payload),
117
+ signal,
116
118
  });
117
119
  if (!res.ok) {
118
120
  const body = await res.text().catch(() => "");
@@ -164,6 +166,7 @@ export async function sendEmail(args) {
164
166
  "Content-Type": "application/json",
165
167
  },
166
168
  body: JSON.stringify(sgPayload),
169
+ signal,
167
170
  });
168
171
  if (!res.ok) {
169
172
  const body = await res.text().catch(() => "");
@@ -182,6 +185,31 @@ export async function sendEmail(args) {
182
185
  `---\nTo: ${args.to}\nFrom: ${from}\nSubject: ${args.subject}\n\n` +
183
186
  `${args.text || stripHtml(args.html)}\n---\n`);
184
187
  }
188
+ export async function sendEmail(args) {
189
+ const requestedTimeoutMs = Number(args.timeoutMs);
190
+ if (!Number.isFinite(requestedTimeoutMs) || requestedTimeoutMs <= 0) {
191
+ return sendEmailWithSignal(args);
192
+ }
193
+ const timeoutMs = Math.floor(requestedTimeoutMs);
194
+ const controller = new AbortController();
195
+ const timeoutError = new Error(`Email send timed out after ${timeoutMs}ms`);
196
+ let timeout;
197
+ try {
198
+ await Promise.race([
199
+ sendEmailWithSignal(args, controller.signal),
200
+ new Promise((_, reject) => {
201
+ timeout = setTimeout(() => {
202
+ controller.abort(timeoutError);
203
+ reject(timeoutError);
204
+ }, timeoutMs);
205
+ }),
206
+ ]);
207
+ }
208
+ finally {
209
+ if (timeout)
210
+ clearTimeout(timeout);
211
+ }
212
+ }
185
213
  function parseSendGridFrom(from) {
186
214
  const m = from.match(/^\s*(.*?)\s*<(.+)>\s*$/);
187
215
  if (m && m[2])
@@ -1 +1 @@
1
- {"version":3,"file":"email.js","sourceRoot":"","sources":["../../src/server/email.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Email transport for system emails (password resets, invitations, notifications).\n *\n * Providers are selected by scoped secrets:\n * RESEND_API_KEY — https://resend.com\n * SENDGRID_API_KEY — https://sendgrid.com\n * EMAIL_FROM — \"Name <addr@domain>\" (optional; defaults to Resend's sandbox)\n *\n * With neither provider configured, `sendEmail` logs the message to the console\n * so the reset-password flow still works end-to-end for local development.\n */\n\nimport { readFileSync } from \"node:fs\";\n\nimport { resolveSecret } from \"./credential-provider.js\";\nimport { AGENT_NATIVE_EMAIL_LOGO_CONTENT_ID } from \"./email-template.js\";\n\nexport type EmailProvider = \"resend\" | \"sendgrid\" | \"dev\";\n\nexport interface EmailAttachment {\n filename: string;\n content: string | Buffer;\n contentType?: string;\n contentId?: string;\n disposition?: \"attachment\" | \"inline\";\n}\n\nexport interface SendEmailArgs {\n to: string;\n subject: string;\n html: string;\n text?: string;\n from?: string;\n cc?: string | string[];\n replyTo?: string;\n inReplyTo?: string;\n references?: string;\n attachments?: EmailAttachment[];\n}\n\nlet cachedAgentNativeLogo: Buffer | undefined;\n\nfunction getAgentNativeLogoAttachment(): EmailAttachment {\n cachedAgentNativeLogo ??= readFileSync(\n new URL(\"../../src/assets/branding/favicon.png\", import.meta.url),\n );\n return {\n filename: \"agent-native-logo.png\",\n content: cachedAgentNativeLogo,\n contentType: \"image/png\",\n contentId: AGENT_NATIVE_EMAIL_LOGO_CONTENT_ID,\n disposition: \"inline\",\n };\n}\n\nfunction resolveAttachments(\n args: SendEmailArgs,\n): EmailAttachment[] | undefined {\n if (!args.html.includes(`cid:${AGENT_NATIVE_EMAIL_LOGO_CONTENT_ID}`)) {\n return args.attachments;\n }\n if (\n args.attachments?.some(\n (attachment) =>\n attachment.contentId === AGENT_NATIVE_EMAIL_LOGO_CONTENT_ID,\n )\n ) {\n return args.attachments;\n }\n return [...(args.attachments ?? []), getAgentNativeLogoAttachment()];\n}\n\ninterface EmailTransportConfig {\n provider: EmailProvider;\n resendApiKey?: string;\n sendgridApiKey?: string;\n from?: string;\n}\n\nasync function resolveEmailTransport(): Promise<EmailTransportConfig> {\n const [resendApiKey, sendgridApiKey, from] = await Promise.all([\n resolveSecret(\"RESEND_API_KEY\"),\n resolveSecret(\"SENDGRID_API_KEY\"),\n resolveSecret(\"EMAIL_FROM\"),\n ]);\n const resolvedFrom = from ?? undefined;\n if (resendApiKey) {\n return {\n provider: \"resend\",\n resendApiKey,\n from: resolvedFrom,\n };\n }\n if (sendgridApiKey) {\n return {\n provider: \"sendgrid\",\n sendgridApiKey,\n from: resolvedFrom,\n };\n }\n return { provider: \"dev\", from: resolvedFrom };\n}\n\nexport async function isEmailConfigured(): Promise<boolean> {\n return (await resolveEmailTransport()).provider !== \"dev\";\n}\n\nexport async function getEmailProvider(): Promise<EmailProvider> {\n return (await resolveEmailTransport()).provider;\n}\n\nfunction getFromAddress(\n config: EmailTransportConfig,\n override?: string,\n): string {\n const explicit = override || config.from;\n if (explicit) return explicit;\n // Resend lets unverified accounts send from its sandbox domain; SendGrid\n // does not, so falling back there would cause silent 403s at runtime.\n if (config.provider === \"sendgrid\") {\n throw new Error(\n \"EMAIL_FROM is required when using SendGrid — save it as a verified sender address.\",\n );\n }\n return \"Agent Native <onboarding@resend.dev>\";\n}\n\nexport async function sendEmail(args: SendEmailArgs): Promise<void> {\n const config = await resolveEmailTransport();\n const provider = config.provider;\n const from = getFromAddress(config, args.from);\n const attachments = resolveAttachments(args);\n\n if (provider === \"resend\") {\n const payload: Record<string, unknown> = {\n from,\n to: args.to,\n subject: args.subject,\n html: args.html,\n text: args.text,\n };\n if (args.cc) payload.cc = Array.isArray(args.cc) ? args.cc : [args.cc];\n if (args.replyTo) payload.reply_to = args.replyTo;\n if (attachments?.length) {\n payload.attachments = attachments.map((a) => ({\n filename: a.filename,\n content:\n typeof a.content === \"string\"\n ? a.content\n : a.content.toString(\"base64\"),\n content_type: a.contentType,\n content_id: a.contentId,\n }));\n }\n const headers: Record<string, string> = {};\n if (args.inReplyTo) headers[\"In-Reply-To\"] = args.inReplyTo;\n if (args.references) headers[\"References\"] = args.references;\n if (Object.keys(headers).length) payload.headers = headers;\n\n const res = await fetch(\"https://api.resend.com/emails\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${config.resendApiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(payload),\n });\n if (!res.ok) {\n const body = await res.text().catch(() => \"\");\n throw new Error(`Resend error ${res.status}: ${body}`);\n }\n return;\n }\n\n if (provider === \"sendgrid\") {\n const personalization: Record<string, unknown> = {\n to: [{ email: args.to }],\n };\n if (args.cc) {\n const ccList = Array.isArray(args.cc) ? args.cc : [args.cc];\n personalization.cc = ccList.map((email) => ({ email }));\n }\n\n const sgPayload: Record<string, unknown> = {\n personalizations: [personalization],\n from: parseSendGridFrom(from),\n subject: args.subject,\n content: [\n ...(args.text ? [{ type: \"text/plain\", value: args.text }] : []),\n { type: \"text/html\", value: args.html },\n ],\n };\n if (args.replyTo) sgPayload.reply_to = parseSendGridFrom(args.replyTo);\n const sgHeaders: Record<string, string> = {};\n if (args.inReplyTo) sgHeaders[\"In-Reply-To\"] = args.inReplyTo;\n if (args.references) sgHeaders[\"References\"] = args.references;\n if (Object.keys(sgHeaders).length) sgPayload.headers = sgHeaders;\n if (attachments?.length) {\n sgPayload.attachments = attachments.map((a) => ({\n filename: a.filename,\n content:\n typeof a.content === \"string\"\n ? Buffer.from(a.content).toString(\"base64\")\n : a.content.toString(\"base64\"),\n type: a.contentType,\n disposition: a.disposition ?? (a.contentId ? \"inline\" : undefined),\n content_id: a.contentId,\n }));\n }\n\n const res = await fetch(\"https://api.sendgrid.com/v3/mail/send\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${config.sendgridApiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(sgPayload),\n });\n if (!res.ok) {\n const body = await res.text().catch(() => \"\");\n throw new Error(`SendGrid error ${res.status}: ${body}`);\n }\n return;\n }\n\n // Dev fallback — no provider configured. Logging the full body exposes\n // reset tokens, so only do it outside production. In production, refuse\n // to send rather than silently leaking secrets to logs.\n if (process.env.NODE_ENV === \"production\") {\n throw new Error(\n \"No email provider configured. Save RESEND_API_KEY or SENDGRID_API_KEY in settings.\",\n );\n }\n console.log(\n `\\n[agent-native:email] No email provider configured. ` +\n `Save RESEND_API_KEY or SENDGRID_API_KEY in settings to send real emails.\\n` +\n `---\\nTo: ${args.to}\\nFrom: ${from}\\nSubject: ${args.subject}\\n\\n` +\n `${args.text || stripHtml(args.html)}\\n---\\n`,\n );\n}\n\nfunction parseSendGridFrom(from: string): { email: string; name?: string } {\n const m = from.match(/^\\s*(.*?)\\s*<(.+)>\\s*$/);\n if (m && m[2]) return { name: m[1] || undefined, email: m[2] };\n return { email: from.trim() };\n}\n\nfunction stripHtml(html: string): string {\n return html\n .replace(/<br\\s*\\/?>/gi, \"\\n\")\n .replace(/<\\/p>/gi, \"\\n\\n\")\n .replace(/<[^>]+>/g, \"\")\n .replace(/&nbsp;/g, \" \")\n .trim();\n}\n"]}
1
+ {"version":3,"file":"email.js","sourceRoot":"","sources":["../../src/server/email.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Email transport for system emails (password resets, invitations, notifications).\n *\n * Providers are selected by scoped secrets:\n * RESEND_API_KEY — https://resend.com\n * SENDGRID_API_KEY — https://sendgrid.com\n * EMAIL_FROM — \"Name <addr@domain>\" (optional; defaults to Resend's sandbox)\n *\n * With neither provider configured, `sendEmail` logs the message to the console\n * so the reset-password flow still works end-to-end for local development.\n */\n\nimport { readFileSync } from \"node:fs\";\n\nimport { resolveSecret } from \"./credential-provider.js\";\nimport { AGENT_NATIVE_EMAIL_LOGO_CONTENT_ID } from \"./email-template.js\";\n\nexport type EmailProvider = \"resend\" | \"sendgrid\" | \"dev\";\n\nexport interface EmailAttachment {\n filename: string;\n content: string | Buffer;\n contentType?: string;\n contentId?: string;\n disposition?: \"attachment\" | \"inline\";\n}\n\nexport interface SendEmailArgs {\n to: string;\n subject: string;\n html: string;\n text?: string;\n from?: string;\n cc?: string | string[];\n replyTo?: string;\n inReplyTo?: string;\n references?: string;\n attachments?: EmailAttachment[];\n timeoutMs?: number;\n}\n\nlet cachedAgentNativeLogo: Buffer | undefined;\n\nfunction getAgentNativeLogoAttachment(): EmailAttachment {\n cachedAgentNativeLogo ??= readFileSync(\n new URL(\"../../src/assets/branding/favicon.png\", import.meta.url),\n );\n return {\n filename: \"agent-native-logo.png\",\n content: cachedAgentNativeLogo,\n contentType: \"image/png\",\n contentId: AGENT_NATIVE_EMAIL_LOGO_CONTENT_ID,\n disposition: \"inline\",\n };\n}\n\nfunction resolveAttachments(\n args: SendEmailArgs,\n): EmailAttachment[] | undefined {\n if (!args.html.includes(`cid:${AGENT_NATIVE_EMAIL_LOGO_CONTENT_ID}`)) {\n return args.attachments;\n }\n if (\n args.attachments?.some(\n (attachment) =>\n attachment.contentId === AGENT_NATIVE_EMAIL_LOGO_CONTENT_ID,\n )\n ) {\n return args.attachments;\n }\n return [...(args.attachments ?? []), getAgentNativeLogoAttachment()];\n}\n\ninterface EmailTransportConfig {\n provider: EmailProvider;\n resendApiKey?: string;\n sendgridApiKey?: string;\n from?: string;\n}\n\nasync function resolveEmailTransport(): Promise<EmailTransportConfig> {\n const [resendApiKey, sendgridApiKey, from] = await Promise.all([\n resolveSecret(\"RESEND_API_KEY\"),\n resolveSecret(\"SENDGRID_API_KEY\"),\n resolveSecret(\"EMAIL_FROM\"),\n ]);\n const resolvedFrom = from ?? undefined;\n if (resendApiKey) {\n return {\n provider: \"resend\",\n resendApiKey,\n from: resolvedFrom,\n };\n }\n if (sendgridApiKey) {\n return {\n provider: \"sendgrid\",\n sendgridApiKey,\n from: resolvedFrom,\n };\n }\n return { provider: \"dev\", from: resolvedFrom };\n}\n\nexport async function isEmailConfigured(): Promise<boolean> {\n return (await resolveEmailTransport()).provider !== \"dev\";\n}\n\nexport async function getEmailProvider(): Promise<EmailProvider> {\n return (await resolveEmailTransport()).provider;\n}\n\nfunction getFromAddress(\n config: EmailTransportConfig,\n override?: string,\n): string {\n const explicit = override || config.from;\n if (explicit) return explicit;\n // Resend lets unverified accounts send from its sandbox domain; SendGrid\n // does not, so falling back there would cause silent 403s at runtime.\n if (config.provider === \"sendgrid\") {\n throw new Error(\n \"EMAIL_FROM is required when using SendGrid — save it as a verified sender address.\",\n );\n }\n return \"Agent Native <onboarding@resend.dev>\";\n}\n\nasync function sendEmailWithSignal(\n args: SendEmailArgs,\n signal?: AbortSignal,\n): Promise<void> {\n const config = await resolveEmailTransport();\n signal?.throwIfAborted();\n const provider = config.provider;\n const from = getFromAddress(config, args.from);\n const attachments = resolveAttachments(args);\n\n if (provider === \"resend\") {\n const payload: Record<string, unknown> = {\n from,\n to: args.to,\n subject: args.subject,\n html: args.html,\n text: args.text,\n };\n if (args.cc) payload.cc = Array.isArray(args.cc) ? args.cc : [args.cc];\n if (args.replyTo) payload.reply_to = args.replyTo;\n if (attachments?.length) {\n payload.attachments = attachments.map((a) => ({\n filename: a.filename,\n content:\n typeof a.content === \"string\"\n ? a.content\n : a.content.toString(\"base64\"),\n content_type: a.contentType,\n content_id: a.contentId,\n }));\n }\n const headers: Record<string, string> = {};\n if (args.inReplyTo) headers[\"In-Reply-To\"] = args.inReplyTo;\n if (args.references) headers[\"References\"] = args.references;\n if (Object.keys(headers).length) payload.headers = headers;\n\n const res = await fetch(\"https://api.resend.com/emails\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${config.resendApiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(payload),\n signal,\n });\n if (!res.ok) {\n const body = await res.text().catch(() => \"\");\n throw new Error(`Resend error ${res.status}: ${body}`);\n }\n return;\n }\n\n if (provider === \"sendgrid\") {\n const personalization: Record<string, unknown> = {\n to: [{ email: args.to }],\n };\n if (args.cc) {\n const ccList = Array.isArray(args.cc) ? args.cc : [args.cc];\n personalization.cc = ccList.map((email) => ({ email }));\n }\n\n const sgPayload: Record<string, unknown> = {\n personalizations: [personalization],\n from: parseSendGridFrom(from),\n subject: args.subject,\n content: [\n ...(args.text ? [{ type: \"text/plain\", value: args.text }] : []),\n { type: \"text/html\", value: args.html },\n ],\n };\n if (args.replyTo) sgPayload.reply_to = parseSendGridFrom(args.replyTo);\n const sgHeaders: Record<string, string> = {};\n if (args.inReplyTo) sgHeaders[\"In-Reply-To\"] = args.inReplyTo;\n if (args.references) sgHeaders[\"References\"] = args.references;\n if (Object.keys(sgHeaders).length) sgPayload.headers = sgHeaders;\n if (attachments?.length) {\n sgPayload.attachments = attachments.map((a) => ({\n filename: a.filename,\n content:\n typeof a.content === \"string\"\n ? Buffer.from(a.content).toString(\"base64\")\n : a.content.toString(\"base64\"),\n type: a.contentType,\n disposition: a.disposition ?? (a.contentId ? \"inline\" : undefined),\n content_id: a.contentId,\n }));\n }\n\n const res = await fetch(\"https://api.sendgrid.com/v3/mail/send\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${config.sendgridApiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(sgPayload),\n signal,\n });\n if (!res.ok) {\n const body = await res.text().catch(() => \"\");\n throw new Error(`SendGrid error ${res.status}: ${body}`);\n }\n return;\n }\n\n // Dev fallback — no provider configured. Logging the full body exposes\n // reset tokens, so only do it outside production. In production, refuse\n // to send rather than silently leaking secrets to logs.\n if (process.env.NODE_ENV === \"production\") {\n throw new Error(\n \"No email provider configured. Save RESEND_API_KEY or SENDGRID_API_KEY in settings.\",\n );\n }\n console.log(\n `\\n[agent-native:email] No email provider configured. ` +\n `Save RESEND_API_KEY or SENDGRID_API_KEY in settings to send real emails.\\n` +\n `---\\nTo: ${args.to}\\nFrom: ${from}\\nSubject: ${args.subject}\\n\\n` +\n `${args.text || stripHtml(args.html)}\\n---\\n`,\n );\n}\n\nexport async function sendEmail(args: SendEmailArgs): Promise<void> {\n const requestedTimeoutMs = Number(args.timeoutMs);\n if (!Number.isFinite(requestedTimeoutMs) || requestedTimeoutMs <= 0) {\n return sendEmailWithSignal(args);\n }\n\n const timeoutMs = Math.floor(requestedTimeoutMs);\n const controller = new AbortController();\n const timeoutError = new Error(`Email send timed out after ${timeoutMs}ms`);\n let timeout: ReturnType<typeof setTimeout> | undefined;\n try {\n await Promise.race([\n sendEmailWithSignal(args, controller.signal),\n new Promise<never>((_, reject) => {\n timeout = setTimeout(() => {\n controller.abort(timeoutError);\n reject(timeoutError);\n }, timeoutMs);\n }),\n ]);\n } finally {\n if (timeout) clearTimeout(timeout);\n }\n}\n\nfunction parseSendGridFrom(from: string): { email: string; name?: string } {\n const m = from.match(/^\\s*(.*?)\\s*<(.+)>\\s*$/);\n if (m && m[2]) return { name: m[1] || undefined, email: m[2] };\n return { email: from.trim() };\n}\n\nfunction stripHtml(html: string): string {\n return html\n .replace(/<br\\s*\\/?>/gi, \"\\n\")\n .replace(/<\\/p>/gi, \"\\n\\n\")\n .replace(/<[^>]+>/g, \"\")\n .replace(/&nbsp;/g, \" \")\n .trim();\n}\n"]}
@@ -20,7 +20,7 @@ export declare function createTranscribeVoiceHandler(): import("h3").EventHandle
20
20
  error: string;
21
21
  text?: undefined;
22
22
  } | {
23
- text: string;
24
23
  error?: undefined;
24
+ text: string;
25
25
  }>>;
26
26
  //# sourceMappingURL=transcribe-voice.d.ts.map
@@ -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.
@@ -60,7 +60,7 @@ export function Layout({ children }: LayoutProps) {
60
60
  useAgentChatHomeHandoffLinks({
61
61
  storageKey: "chat",
62
62
  isChatPath: (pathname) => pathname === "/" || pathname.startsWith("/chat/"),
63
- requireActiveHandoff: false,
63
+ requireActiveHandoff: true,
64
64
  });
65
65
 
66
66
  useEffect(() => {
@@ -228,7 +228,7 @@ function ChatThreadsSection() {
228
228
  }
229
229
 
230
230
  return (
231
- <div className="mt-2 border-s border-sidebar-border/70 ps-3">
231
+ <div className="mt-2 ms-4">
232
232
  <ChatHistoryRail
233
233
  items={chatItems}
234
234
  activeId={displayedActiveThreadId}
@@ -1,7 +1,5 @@
1
- import { markAgentChatHomeHandoff } from "@agent-native/core/client/agent-chat";
2
1
  import { appBasePath, appPath } from "@agent-native/core/client/api-path";
3
2
  import { useAgentRouteState } from "@agent-native/core/client/navigation";
4
- import { useLocation } from "react-router";
5
3
 
6
4
  import { TAB_ID } from "@/lib/tab-id";
7
5
 
@@ -12,7 +10,6 @@ export interface NavigationState {
12
10
  }
13
11
 
14
12
  export function useNavigationState() {
15
- const location = useLocation();
16
13
  useAgentRouteState<NavigationState>({
17
14
  browserTabId: TAB_ID,
18
15
  requestSource: TAB_ID,
@@ -26,21 +23,9 @@ export function useNavigationState() {
26
23
  },
27
24
  getCommandPath: (command) =>
28
25
  routerPath(command.path || pathForCommand(command)),
29
- onNavigate: (_command, path) => {
30
- if (
31
- isChatPath(location.pathname) &&
32
- !isChatPath(pathnameFromPath(path))
33
- ) {
34
- markAgentChatHomeHandoff("chat");
35
- }
36
- },
37
26
  });
38
27
  }
39
28
 
40
- function pathnameFromPath(path: string): string {
41
- return path.split(/[?#]/, 1)[0] || "/";
42
- }
43
-
44
29
  function threadIdFromPath(pathname: string): string | null {
45
30
  const match = pathname.match(/^\/chat\/([^/]+)/);
46
31
  if (!match) return null;
@@ -2,6 +2,7 @@ import { ChangelogSettingsCard } from "@agent-native/core/client/changelog";
2
2
  import { LanguagePicker, useT } from "@agent-native/core/client/i18n";
3
3
  import { TeamPage } from "@agent-native/core/client/org";
4
4
  import {
5
+ AccountSettingsCard,
5
6
  SettingsTabsPage,
6
7
  useAgentSettingsTabs,
7
8
  type SettingsSearchEntry,
@@ -44,6 +45,7 @@ export default function SettingsRoute() {
44
45
 
45
46
  return (
46
47
  <SettingsTabsPage
48
+ account={<AccountSettingsCard />}
47
49
  teamLabel={t("navigation.team")}
48
50
  extraTabs={agentSettingsTabs}
49
51
  generalSearchEntries={generalSearchEntries}
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-07-23
4
+ ---
5
+
6
+ Full-page chat is better centered, with quieter chat history and a left-aligned New chat action.
@@ -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">
@@ -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 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/vite/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/vite/client.ts"],"names":[],"mappings":"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"}
@@ -194,6 +194,9 @@ function nitroVitePlugin(...args) {
194
194
  * rate-limits an unrelated client-reload nudge); keep the two independent.
195
195
  */
196
196
  const NITRO_FULL_RELOAD_DEBOUNCE_MS = 300;
197
+ const OPTIMIZE_DEP_FULL_RELOAD_COOLDOWN_MS = 2_000;
198
+ const OPTIMIZE_DEP_FULL_RELOAD_WINDOW_MS = 30_000;
199
+ const OPTIMIZE_DEP_MAX_FULL_RELOADS = 3;
197
200
  /**
198
201
  * Wraps a single Nitro-provided Vite plugin so that, if it defines a
199
202
  * `hotUpdate` hook, any `environment.hot.send({ type: "full-reload" })` call
@@ -518,6 +521,11 @@ function hasCoreDep(pkg, cwd) {
518
521
  }
519
522
  }
520
523
  function hasOptimizeDep(pkg, cwd) {
524
+ // The nested dependency entry below is rooted at @agent-native/core, so
525
+ // monorepo consumers need to retain it even though the source package does
526
+ // not list itself as a dependency.
527
+ if (pkg === "@agent-native/core" && findCorePackageRoot(cwd))
528
+ return true;
521
529
  return hasDep(pkg, cwd) || hasCoreDep(pkg, cwd);
522
530
  }
523
531
  /**
@@ -591,6 +599,44 @@ function getClientDedupe(cwd) {
591
599
  * of dist/ at startup and never picks up new exports).
592
600
  */
593
601
  function findCorePackageRoot(cwd) {
602
+ const localSourceRoot = findLocalCoreSourceRoot(cwd);
603
+ if (localSourceRoot)
604
+ return localSourceRoot;
605
+ // Published Core packages are installed as transitive dependency roots in
606
+ // pnpm. The consuming app cannot resolve Core's client-only dependencies
607
+ // from its own node_modules unless we first locate that installed package
608
+ // and read its manifest. This is also what lets optimizeDeps use Vite's
609
+ // nested-dependency syntax for standalone CLI-generated apps.
610
+ try {
611
+ const appRequire = createRequire(path.join(cwd, "package.json"));
612
+ const resolved = appRequire.resolve("@agent-native/core");
613
+ let dir = path.dirname(resolved);
614
+ for (let i = 0; i < 20; i++) {
615
+ const packageJsonPath = path.join(dir, "package.json");
616
+ if (fs.existsSync(packageJsonPath)) {
617
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
618
+ if (packageJson.name === "@agent-native/core") {
619
+ return fs.realpathSync(dir);
620
+ }
621
+ }
622
+ const parent = path.dirname(dir);
623
+ if (parent === dir)
624
+ break;
625
+ dir = parent;
626
+ }
627
+ }
628
+ catch {
629
+ // The app may not have installed Core yet; fall through to null.
630
+ }
631
+ return null;
632
+ }
633
+ /**
634
+ * Locate a local framework checkout whose source should be aliased for HMR.
635
+ * This intentionally does not use Node package resolution: published Core
636
+ * tarballs include `src/` for source maps/docs, but must still be consumed
637
+ * through their built `dist/` exports.
638
+ */
639
+ function findLocalCoreSourceRoot(cwd) {
594
640
  try {
595
641
  const pkg = JSON.parse(fs.readFileSync(path.join(cwd, "package.json"), "utf-8"));
596
642
  const spec = pkg.dependencies?.["@agent-native/core"] ??
@@ -623,7 +669,7 @@ function findCorePackageRoot(cwd) {
623
669
  return null;
624
670
  }
625
671
  function findCoreSrcDir(cwd) {
626
- const root = findCorePackageRoot(cwd);
672
+ const root = findLocalCoreSourceRoot(cwd);
627
673
  return root ? path.join(root, "src") : null;
628
674
  }
629
675
  /**
@@ -731,6 +777,17 @@ function getDefaultOptimizeDeps(cwd) {
731
777
  { specifier: "@libsql/client" },
732
778
  { specifier: "@amplitude/analytics-browser" },
733
779
  { specifier: "@assistant-ui/react" },
780
+ { specifier: "@assistant-ui/react-markdown" },
781
+ { specifier: "@assistant-ui/store" },
782
+ { specifier: "@assistant-ui/tap" },
783
+ {
784
+ specifier: "@agent-native/core > @assistant-ui/react > assistant-stream",
785
+ packageName: "@agent-native/core",
786
+ },
787
+ {
788
+ specifier: "@agent-native/core > @assistant-ui/react > assistant-stream/utils",
789
+ packageName: "@agent-native/core",
790
+ },
734
791
  { specifier: "@codemirror/lang-sql" },
735
792
  { specifier: "@codemirror/theme-one-dark" },
736
793
  { specifier: "@excalidraw/excalidraw" },
@@ -901,9 +958,7 @@ function getDefaultOptimizeDeps(cwd) {
901
958
  // then rebundles and reloads the editor. Its documented nested-dependency
902
959
  // syntax resolves the right-hand package from core's package directory,
903
960
  // while app-owned dependencies should remain direct entries.
904
- if (inMonorepo &&
905
- !hasDep(dependencyName, cwd) &&
906
- hasCoreDep(dependencyName, cwd)) {
961
+ if (!hasDep(dependencyName, cwd) && hasCoreDep(dependencyName, cwd)) {
907
962
  return `@agent-native/core > ${specifier}`;
908
963
  }
909
964
  return specifier;
@@ -1066,7 +1121,8 @@ function fullReloadOnOptimizeDep504() {
1066
1121
  name: "agent-native-full-reload-optimize-dep-504",
1067
1122
  apply: "serve",
1068
1123
  configureServer(server) {
1069
- let lastReloadAt = 0;
1124
+ let lastReloadAt = null;
1125
+ let reloadHistory = [];
1070
1126
  server.middlewares.use((req, res, next) => {
1071
1127
  const originalEnd = res.end;
1072
1128
  res.end = (...endArgs) => {
@@ -1074,8 +1130,12 @@ function fullReloadOnOptimizeDep504() {
1074
1130
  if (res.statusCode === 504 &&
1075
1131
  statusMessage === "Outdated Optimize Dep") {
1076
1132
  const now = Date.now();
1077
- if (now - lastReloadAt > 500) {
1133
+ reloadHistory = reloadHistory.filter((timestamp) => now - timestamp < OPTIMIZE_DEP_FULL_RELOAD_WINDOW_MS);
1134
+ if ((lastReloadAt === null ||
1135
+ now - lastReloadAt >= OPTIMIZE_DEP_FULL_RELOAD_COOLDOWN_MS) &&
1136
+ reloadHistory.length < OPTIMIZE_DEP_MAX_FULL_RELOADS) {
1078
1137
  lastReloadAt = now;
1138
+ reloadHistory.push(now);
1079
1139
  server.ws.send({ type: "full-reload" });
1080
1140
  server.config.logger.info(`[agent-native] Vite optimized deps changed while loading ${req.url ?? "a module"}; reloading the page.`, { timestamp: true });
1081
1141
  }
@@ -1608,6 +1668,7 @@ function ssrStubPlugin(packages) {
1608
1668
  "ThreadPrimitive",
1609
1669
  "WebLinksAddon",
1610
1670
  "captureException",
1671
+ "codeToHtml",
1611
1672
  "common",
1612
1673
  "createLowlight",
1613
1674
  "createNodeFromContent",
@@ -1627,6 +1688,7 @@ function ssrStubPlugin(packages) {
1627
1688
  "encodeStateAsUpdate",
1628
1689
  "mergeUpdates",
1629
1690
  "useAui",
1691
+ "useAuiState",
1630
1692
  "useComposer",
1631
1693
  "useComposerRuntime",
1632
1694
  "useCurrentEditor",