@agent-native/core 0.120.1 → 0.120.3

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 (396) hide show
  1. package/corpus/README.md +3 -3
  2. package/corpus/core/CHANGELOG.md +29 -0
  3. package/corpus/core/docs/content/extensions.mdx +23 -4
  4. package/corpus/core/package.json +1 -1
  5. package/corpus/core/src/agent/production-agent.ts +4 -0
  6. package/corpus/core/src/agent/run-manager.ts +36 -10
  7. package/corpus/core/src/agent/types.ts +2 -0
  8. package/corpus/core/src/cli/context-xray-local.ts +1 -1
  9. package/corpus/core/src/cli/index.ts +63 -18
  10. package/corpus/core/src/cli/skills.ts +5 -0
  11. package/corpus/core/src/cli/telemetry.ts +118 -1
  12. package/corpus/core/src/client/AgentPanel.tsx +10 -0
  13. package/corpus/core/src/client/CommandMenu.tsx +13 -1
  14. package/corpus/core/src/client/agent-page/AgentTabsPage.tsx +39 -4
  15. package/corpus/core/src/client/analytics.ts +13 -0
  16. package/corpus/core/src/client/error-capture.ts +6 -1
  17. package/corpus/core/src/client/extensions/ExtensionsListPage.tsx +28 -8
  18. package/corpus/core/src/client/extensions/agent-native-extension-runtime.ts +1 -1
  19. package/corpus/core/src/client/navigation/index.ts +2 -0
  20. package/corpus/core/src/client/org/OrgSwitcher.tsx +14 -32
  21. package/corpus/core/src/client/settings/ExtensionsSettingsContent.tsx +24 -0
  22. package/corpus/core/src/client/settings/SettingsPanel.tsx +10 -0
  23. package/corpus/core/src/deploy/build.ts +3 -0
  24. package/corpus/core/src/extensions/theme.ts +1 -1
  25. package/corpus/core/src/localization/default-messages.ts +2 -0
  26. package/corpus/core/src/server/analytics.ts +154 -25
  27. package/corpus/core/src/server/core-routes-plugin.ts +10 -1
  28. package/corpus/core/src/server/framework-request-handler.ts +17 -26
  29. package/corpus/core/src/styles/agent-native.css +13 -0
  30. package/corpus/core/src/templates/chat/app/components/layout/Layout.tsx +3 -0
  31. package/corpus/core/src/templates/chat/app/components/layout/Sidebar.tsx +113 -51
  32. package/corpus/core/src/templates/chat/app/global.css +1 -1
  33. package/corpus/core/src/templates/chat/app/routes/extensions._index.tsx +2 -2
  34. package/corpus/core/src/templates/chat/netlify.toml +1 -0
  35. package/corpus/core/src/templates/default/app/global.css +1 -1
  36. package/corpus/core/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +16 -0
  37. package/corpus/core/src/tracking/error-capture.ts +151 -0
  38. package/corpus/core/src/tracking/index.ts +5 -0
  39. package/corpus/core/src/vite/client.ts +6 -0
  40. package/corpus/templates/analytics/.agents/skills/adhoc-analysis/SKILL.md +27 -43
  41. package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +19 -8
  42. package/corpus/templates/analytics/.agents/skills/data-querying/SKILL.md +2 -2
  43. package/corpus/templates/analytics/AGENTS.md +20 -8
  44. package/corpus/templates/analytics/actions/get-explorer-dashboard.ts +3 -0
  45. package/corpus/templates/analytics/actions/list-dashboard-usage-stats.ts +140 -46
  46. package/corpus/templates/analytics/actions/migrate-analytics-artifacts.ts +48 -0
  47. package/corpus/templates/analytics/actions/save-analysis.ts +2 -3
  48. package/corpus/templates/analytics/actions/view-screen.ts +1 -1
  49. package/corpus/templates/analytics/app/components/dashboard/CumulativeNetChart.tsx +1 -1
  50. package/corpus/templates/analytics/app/components/dashboard/DashboardHistoryPanel.tsx +1 -4
  51. package/corpus/templates/analytics/app/components/dashboard/DashboardMetadata.tsx +77 -0
  52. package/corpus/templates/analytics/app/components/dashboard/DashboardPanelSkeleton.tsx +7 -1
  53. package/corpus/templates/analytics/app/components/dashboard/DataTable.tsx +1 -1
  54. package/corpus/templates/analytics/app/components/dashboard/DateRangePicker.tsx +2 -2
  55. package/corpus/templates/analytics/app/components/dashboard/MetricCard.tsx +1 -1
  56. package/corpus/templates/analytics/app/components/dashboard/RecentActivity.tsx +1 -1
  57. package/corpus/templates/analytics/app/components/dashboard/RevenueChart.tsx +1 -1
  58. package/corpus/templates/analytics/app/components/dashboard/RevenueComparisonChart.tsx +1 -1
  59. package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +41 -9
  60. package/corpus/templates/analytics/app/components/dashboard/StatsCard.tsx +1 -1
  61. package/corpus/templates/analytics/app/components/dashboard/TimeSeriesChart.tsx +1 -1
  62. package/corpus/templates/analytics/app/components/layout/CommandPalette.tsx +2 -0
  63. package/corpus/templates/analytics/app/components/layout/Layout.tsx +28 -14
  64. package/corpus/templates/analytics/app/components/layout/NewDashboardDialog.tsx +1 -0
  65. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +255 -626
  66. package/corpus/templates/analytics/app/global.css +31 -1
  67. package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +2 -2
  68. package/corpus/templates/analytics/app/i18n-data.ts +80 -0
  69. package/corpus/templates/analytics/app/lib/dashboard-report-capture.ts +42 -0
  70. package/corpus/templates/analytics/app/pages/Agents.tsx +49 -2
  71. package/corpus/templates/analytics/app/pages/adhoc/_shared/KpiChart.tsx +1 -1
  72. package/corpus/templates/analytics/app/pages/adhoc/explorer/components/ChartTypePicker.tsx +1 -1
  73. package/corpus/templates/analytics/app/pages/adhoc/explorer/components/DateRangePicker.tsx +1 -1
  74. package/corpus/templates/analytics/app/pages/adhoc/explorer/components/EventRow.tsx +1 -1
  75. package/corpus/templates/analytics/app/pages/adhoc/explorer/components/SqlPreview.tsx +2 -2
  76. package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/index.tsx +54 -11
  77. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/DashboardFilterBar.tsx +3 -3
  78. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/EmailReportDialog.tsx +2 -2
  79. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/PanelEditorDialog.tsx +1 -1
  80. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +4 -54
  81. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +178 -69
  82. package/corpus/templates/analytics/app/pages/analyses/AnalysisDetail.tsx +2 -2
  83. package/corpus/templates/analytics/app/root.tsx +28 -0
  84. package/corpus/templates/analytics/app/routes/analyses._index.tsx +3 -2
  85. package/corpus/templates/analytics/app/routes/extensions._index.tsx +2 -2
  86. package/corpus/templates/analytics/changelog/2026-07-23-analytics-now-groups-saved-analyses-and-embedded-extensions-.md +6 -0
  87. package/corpus/templates/analytics/changelog/2026-07-23-dashboard-menus-show-who-created-and-last-updated-each-dashb.md +6 -0
  88. package/corpus/templates/analytics/changelog/2026-07-23-dashboard-usage-now-loads-reliably-for-organizations-with-ma.md +6 -0
  89. package/corpus/templates/analytics/changelog/2026-07-23-long-dashboard-building-requests-now-continue-without-stoppi.md +6 -0
  90. package/corpus/templates/analytics/changelog/2026-07-24-analytics-charts-now-explain-slow-loads-and-offer-a-direct-r.md +6 -0
  91. package/corpus/templates/analytics/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
  92. package/corpus/templates/analytics/changelog/2026-07-24-daily-dashboard-emails-now-load-signed-reports-more-reliably.md +6 -0
  93. package/corpus/templates/analytics/changelog/2026-07-24-manage-agent-is-clearly-marked-as-a-link-to-its-dedicated-pa.md +6 -0
  94. package/corpus/templates/analytics/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
  95. package/corpus/templates/analytics/netlify.toml +4 -0
  96. package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +1 -1
  97. package/corpus/templates/analytics/server/db/schema.ts +9 -0
  98. package/corpus/templates/analytics/server/jobs/dashboard-report.ts +21 -0
  99. package/corpus/templates/analytics/server/lib/agent-chat-plan-mode.ts +0 -1
  100. package/corpus/templates/analytics/server/lib/agent-readable-resource-context.ts +1 -0
  101. package/corpus/templates/analytics/server/lib/canonical-first-party-dashboard-repair.ts +11 -0
  102. package/corpus/templates/analytics/server/lib/dashboard-report-subscriptions.ts +54 -0
  103. package/corpus/templates/analytics/server/lib/dashboard-report.ts +235 -26
  104. package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +4 -1
  105. package/corpus/templates/analytics/server/lib/migrate-analytics-artifacts.ts +1062 -0
  106. package/corpus/templates/analytics/server/plugins/agent-chat.ts +6 -52
  107. package/corpus/templates/analytics/server/plugins/core-routes.ts +1 -1
  108. package/corpus/templates/analytics/server/plugins/db.ts +19 -0
  109. package/corpus/templates/analytics/shared/dashboard-report-timeouts.ts +1 -1
  110. package/corpus/templates/assets/actions/generate-image-batch.ts +36 -1
  111. package/corpus/templates/assets/actions/generate-image.ts +28 -1
  112. package/corpus/templates/assets/app/components/asset/AssetPreviewDialog.tsx +376 -0
  113. package/corpus/templates/assets/app/components/create/RecentDraftsSection.tsx +14 -4
  114. package/corpus/templates/assets/app/components/layout/Layout.tsx +5 -0
  115. package/corpus/templates/assets/app/components/layout/Sidebar.tsx +164 -82
  116. package/corpus/templates/assets/app/global.css +1 -1
  117. package/corpus/templates/assets/app/i18n-data.ts +10 -0
  118. package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +99 -10
  119. package/corpus/templates/assets/app/routes/extensions._index.tsx +2 -2
  120. package/corpus/templates/assets/app/routes/library.tsx +21 -198
  121. package/corpus/templates/assets/changelog/2026-07-21-unified-asset-preview-click-any-asset-to-open-one-preview-wi.md +6 -0
  122. package/corpus/templates/assets/changelog/2026-07-23-assets-image-generation-now-recovers-after-a-completed-tool-.md +6 -0
  123. package/corpus/templates/assets/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
  124. package/corpus/templates/assets/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
  125. package/corpus/templates/assets/netlify.toml +1 -0
  126. package/corpus/templates/brain/app/components/layout/Layout.tsx +3 -0
  127. package/corpus/templates/brain/app/components/layout/Sidebar.tsx +114 -51
  128. package/corpus/templates/brain/app/global.css +1 -1
  129. package/corpus/templates/brain/app/routes/extensions._index.tsx +2 -2
  130. package/corpus/templates/brain/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
  131. package/corpus/templates/brain/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
  132. package/corpus/templates/brain/netlify.toml +1 -0
  133. package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +110 -57
  134. package/corpus/templates/calendar/app/global.css +1 -1
  135. package/corpus/templates/calendar/app/routes/_app.extensions._index.tsx +2 -16
  136. package/corpus/templates/calendar/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
  137. package/corpus/templates/calendar/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
  138. package/corpus/templates/calendar/netlify.toml +1 -0
  139. package/corpus/templates/chat/app/components/layout/Layout.tsx +3 -0
  140. package/corpus/templates/chat/app/components/layout/Sidebar.tsx +113 -51
  141. package/corpus/templates/chat/app/global.css +1 -1
  142. package/corpus/templates/chat/app/routes/extensions._index.tsx +2 -2
  143. package/corpus/templates/chat/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
  144. package/corpus/templates/chat/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
  145. package/corpus/templates/chat/netlify.toml +1 -0
  146. package/corpus/templates/clips/app/components/library/library-layout.tsx +117 -37
  147. package/corpus/templates/clips/app/components/library/organization-switcher.tsx +2 -2
  148. package/corpus/templates/clips/app/components/player/video-player.tsx +16 -1
  149. package/corpus/templates/clips/app/global.css +1 -1
  150. package/corpus/templates/clips/app/hooks/use-view-tracking.ts +55 -12
  151. package/corpus/templates/clips/app/routes/_app.extensions._index.tsx +2 -16
  152. package/corpus/templates/clips/app/routes/embed.$shareId.tsx +6 -5
  153. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +7 -7
  154. package/corpus/templates/clips/app/routes/share.$shareId.tsx +6 -5
  155. package/corpus/templates/clips/changelog/2026-07-23-agent-settings-are-clearly-labeled-manage-agent-in-the-sideb.md +6 -0
  156. package/corpus/templates/clips/changelog/2026-07-23-meeting-recording-meters-and-dismissed-prompts.md +6 -0
  157. package/corpus/templates/clips/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
  158. package/corpus/templates/clips/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
  159. package/corpus/templates/clips/desktop/src/hooks/useMicMeter.ts +1 -1
  160. package/corpus/templates/clips/desktop/src/lib/meeting-notification-dismissal.ts +2 -0
  161. package/corpus/templates/clips/desktop/src/lib/voice-dictation.ts +1 -3
  162. package/corpus/templates/clips/desktop/src/overlays/live-audio-bars.tsx +86 -0
  163. package/corpus/templates/clips/desktop/src/overlays/meeting-notification.tsx +24 -0
  164. package/corpus/templates/clips/desktop/src/overlays/recording-pill.tsx +5 -69
  165. package/corpus/templates/clips/desktop/src/styles.css +35 -3
  166. package/corpus/templates/clips/desktop/src-tauri/src/adhoc_meetings_watcher.rs +13 -44
  167. package/corpus/templates/clips/desktop/src-tauri/src/notifications.rs +119 -17
  168. package/corpus/templates/clips/netlify.toml +1 -0
  169. package/corpus/templates/clips/server/plugins/db.ts +113 -68
  170. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +69 -49
  171. package/corpus/templates/content/app/global.css +1 -1
  172. package/corpus/templates/content/app/routes/_app.extensions._index.tsx +2 -2
  173. package/corpus/templates/content/changelog/2026-07-23-agent-settings-are-clearly-labeled-manage-agent-in-the-sideb.md +6 -0
  174. package/corpus/templates/content/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
  175. package/corpus/templates/content/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
  176. package/corpus/templates/content/netlify.toml +1 -0
  177. package/corpus/templates/design/app/components/layout/Sidebar.tsx +115 -42
  178. package/corpus/templates/design/app/entry.client.tsx +14 -1
  179. package/corpus/templates/design/app/global.css +1 -1
  180. package/corpus/templates/design/app/routes/extensions._index.tsx +2 -2
  181. package/corpus/templates/design/changelog/2026-07-23-agent-settings-are-clearly-labeled-manage-agent-in-the-sideb.md +6 -0
  182. package/corpus/templates/design/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
  183. package/corpus/templates/design/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
  184. package/corpus/templates/design/netlify.toml +1 -0
  185. package/corpus/templates/dispatch/app/dispatch-extensions.tsx +1 -1
  186. package/corpus/templates/dispatch/app/global.css +1 -1
  187. package/corpus/templates/dispatch/changelog/2026-07-23-agent-settings-are-clearly-labeled-manage-agent-in-the-sideb.md +6 -0
  188. package/corpus/templates/dispatch/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
  189. package/corpus/templates/dispatch/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
  190. package/corpus/templates/dispatch/netlify.toml +1 -0
  191. package/corpus/templates/forms/app/components/layout/Layout.tsx +3 -0
  192. package/corpus/templates/forms/app/components/layout/Sidebar.tsx +113 -82
  193. package/corpus/templates/forms/app/global.css +1 -1
  194. package/corpus/templates/forms/app/routes/_app.extensions._index.tsx +2 -2
  195. package/corpus/templates/forms/changelog/2026-07-23-agent-settings-are-clearly-labeled-manage-agent-in-the-sideb.md +6 -0
  196. package/corpus/templates/forms/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
  197. package/corpus/templates/forms/changelog/2026-07-24-forms-sidebar-actions-now-keep-search-language-feedback-and-.md +6 -0
  198. package/corpus/templates/forms/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
  199. package/corpus/templates/forms/netlify.toml +1 -0
  200. package/corpus/templates/macros/app/components/DailyProgress.tsx +5 -5
  201. package/corpus/templates/macros/app/components/ExerciseCard.tsx +2 -2
  202. package/corpus/templates/macros/app/components/MealCard.tsx +2 -2
  203. package/corpus/templates/macros/app/components/WeightCard.tsx +2 -2
  204. package/corpus/templates/macros/app/components/layout/AppLayout.tsx +92 -34
  205. package/corpus/templates/macros/app/components/ui/button.tsx +1 -2
  206. package/corpus/templates/macros/app/components/ui/card.tsx +1 -1
  207. package/corpus/templates/macros/app/global.css +1 -1
  208. package/corpus/templates/macros/app/routes/_index.tsx +1 -1
  209. package/corpus/templates/macros/app/routes/analytics.tsx +4 -7
  210. package/corpus/templates/macros/app/routes/extensions._index.tsx +2 -2
  211. package/corpus/templates/macros/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
  212. package/corpus/templates/macros/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
  213. package/corpus/templates/macros/netlify.toml +1 -0
  214. package/corpus/templates/mail/app/components/layout/AppLayout.tsx +125 -46
  215. package/corpus/templates/mail/app/global.css +1 -1
  216. package/corpus/templates/mail/app/routes/extensions._index.tsx +2 -2
  217. package/corpus/templates/mail/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
  218. package/corpus/templates/mail/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
  219. package/corpus/templates/mail/netlify.toml +1 -0
  220. package/corpus/templates/plan/app/components/layout/Layout.tsx +3 -0
  221. package/corpus/templates/plan/app/components/layout/Sidebar.tsx +128 -65
  222. package/corpus/templates/plan/app/global.css +1 -1
  223. package/corpus/templates/plan/app/routes/extensions._index.tsx +2 -2
  224. package/corpus/templates/plan/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
  225. package/corpus/templates/plan/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
  226. package/corpus/templates/plan/netlify.toml +1 -0
  227. package/corpus/templates/slides/app/components/layout/Sidebar.tsx +116 -36
  228. package/corpus/templates/slides/app/global.css +1 -1
  229. package/corpus/templates/slides/app/routes/extensions._index.tsx +2 -2
  230. package/corpus/templates/slides/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
  231. package/corpus/templates/slides/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
  232. package/corpus/templates/slides/netlify.toml +1 -0
  233. package/corpus/templates/tasks/app/components/layout/Sidebar.tsx +37 -6
  234. package/corpus/templates/tasks/app/global.css +1 -1
  235. package/corpus/templates/tasks/app/routes/extensions._index.tsx +2 -2
  236. package/corpus/templates/tasks/app/routes/settings.tsx +31 -0
  237. package/corpus/templates/tasks/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
  238. package/corpus/templates/tasks/netlify.toml +1 -0
  239. package/corpus/toolkit/CHANGELOG.md +8 -0
  240. package/corpus/toolkit/README.md +8 -1
  241. package/corpus/toolkit/agent-native.eject.json +1 -1
  242. package/corpus/toolkit/package.json +1 -1
  243. package/corpus/toolkit/src/app-shell/index.ts +1 -0
  244. package/corpus/toolkit/src/app-shell/sidebar-footer-actions.tsx +48 -0
  245. package/corpus/toolkit/src/chat-history.css +52 -1
  246. package/corpus/toolkit/src/design-system/default-adapter.tsx +1 -1
  247. package/corpus/toolkit/src/ui/button.tsx +1 -2
  248. package/corpus/toolkit/src/ui/card.tsx +1 -1
  249. package/corpus/toolkit/src/ui/table.tsx +4 -11
  250. package/dist/agent/production-agent.d.ts.map +1 -1
  251. package/dist/agent/production-agent.js +4 -0
  252. package/dist/agent/production-agent.js.map +1 -1
  253. package/dist/agent/run-manager.d.ts +9 -0
  254. package/dist/agent/run-manager.d.ts.map +1 -1
  255. package/dist/agent/run-manager.js +25 -10
  256. package/dist/agent/run-manager.js.map +1 -1
  257. package/dist/agent/types.d.ts +2 -2
  258. package/dist/agent/types.d.ts.map +1 -1
  259. package/dist/agent/types.js.map +1 -1
  260. package/dist/cli/context-xray-local.js +1 -1
  261. package/dist/cli/context-xray-local.js.map +1 -1
  262. package/dist/cli/index.js +56 -17
  263. package/dist/cli/index.js.map +1 -1
  264. package/dist/cli/skills.d.ts.map +1 -1
  265. package/dist/cli/skills.js +5 -0
  266. package/dist/cli/skills.js.map +1 -1
  267. package/dist/cli/telemetry.d.ts +7 -0
  268. package/dist/cli/telemetry.d.ts.map +1 -1
  269. package/dist/cli/telemetry.js +91 -1
  270. package/dist/cli/telemetry.js.map +1 -1
  271. package/dist/client/AgentPanel.d.ts +7 -1
  272. package/dist/client/AgentPanel.d.ts.map +1 -1
  273. package/dist/client/AgentPanel.js +2 -2
  274. package/dist/client/AgentPanel.js.map +1 -1
  275. package/dist/client/CommandMenu.d.ts +2 -0
  276. package/dist/client/CommandMenu.d.ts.map +1 -1
  277. package/dist/client/CommandMenu.js +12 -1
  278. package/dist/client/CommandMenu.js.map +1 -1
  279. package/dist/client/agent-page/AgentTabsPage.d.ts +4 -0
  280. package/dist/client/agent-page/AgentTabsPage.d.ts.map +1 -1
  281. package/dist/client/agent-page/AgentTabsPage.js +8 -4
  282. package/dist/client/agent-page/AgentTabsPage.js.map +1 -1
  283. package/dist/client/analytics.d.ts.map +1 -1
  284. package/dist/client/analytics.js +7 -0
  285. package/dist/client/analytics.js.map +1 -1
  286. package/dist/client/error-capture.d.ts.map +1 -1
  287. package/dist/client/error-capture.js +8 -1
  288. package/dist/client/error-capture.js.map +1 -1
  289. package/dist/client/extensions/ExtensionsListPage.d.ts +5 -1
  290. package/dist/client/extensions/ExtensionsListPage.d.ts.map +1 -1
  291. package/dist/client/extensions/ExtensionsListPage.js +6 -4
  292. package/dist/client/extensions/ExtensionsListPage.js.map +1 -1
  293. package/dist/client/extensions/agent-native-extension-runtime.js +1 -1
  294. package/dist/client/extensions/agent-native-extension-runtime.js.map +1 -1
  295. package/dist/client/navigation/index.d.ts +1 -1
  296. package/dist/client/navigation/index.d.ts.map +1 -1
  297. package/dist/client/navigation/index.js +1 -1
  298. package/dist/client/navigation/index.js.map +1 -1
  299. package/dist/client/org/OrgSwitcher.d.ts.map +1 -1
  300. package/dist/client/org/OrgSwitcher.js +9 -13
  301. package/dist/client/org/OrgSwitcher.js.map +1 -1
  302. package/dist/client/settings/ExtensionsSettingsContent.d.ts +2 -0
  303. package/dist/client/settings/ExtensionsSettingsContent.d.ts.map +1 -0
  304. package/dist/client/settings/ExtensionsSettingsContent.js +10 -0
  305. package/dist/client/settings/ExtensionsSettingsContent.js.map +1 -0
  306. package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
  307. package/dist/client/settings/SettingsPanel.js +10 -1
  308. package/dist/client/settings/SettingsPanel.js.map +1 -1
  309. package/dist/deploy/build.js +1 -0
  310. package/dist/deploy/build.js.map +1 -1
  311. package/dist/extensions/theme.js +1 -1
  312. package/dist/extensions/theme.js.map +1 -1
  313. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  314. package/dist/localization/default-messages.d.ts +2 -0
  315. package/dist/localization/default-messages.d.ts.map +1 -1
  316. package/dist/localization/default-messages.js +2 -0
  317. package/dist/localization/default-messages.js.map +1 -1
  318. package/dist/observability/routes.d.ts +3 -3
  319. package/dist/progress/routes.d.ts +1 -1
  320. package/dist/provider-api/actions/custom-provider-registration.d.ts +7 -7
  321. package/dist/provider-api/actions/provider-api.d.ts +9 -9
  322. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  323. package/dist/resources/handlers.d.ts +1 -1
  324. package/dist/server/analytics.d.ts +9 -4
  325. package/dist/server/analytics.d.ts.map +1 -1
  326. package/dist/server/analytics.js +111 -22
  327. package/dist/server/analytics.js.map +1 -1
  328. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  329. package/dist/server/core-routes-plugin.js +8 -1
  330. package/dist/server/core-routes-plugin.js.map +1 -1
  331. package/dist/server/framework-request-handler.d.ts.map +1 -1
  332. package/dist/server/framework-request-handler.js +17 -27
  333. package/dist/server/framework-request-handler.js.map +1 -1
  334. package/dist/server/realtime-token.d.ts +1 -1
  335. package/dist/styles/agent-native.css +13 -0
  336. package/dist/templates/chat/app/components/layout/Layout.tsx +3 -0
  337. package/dist/templates/chat/app/components/layout/Sidebar.tsx +113 -51
  338. package/dist/templates/chat/app/global.css +1 -1
  339. package/dist/templates/chat/app/routes/extensions._index.tsx +2 -2
  340. package/dist/templates/chat/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
  341. package/dist/templates/chat/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
  342. package/dist/templates/chat/netlify.toml +1 -0
  343. package/dist/templates/default/app/global.css +1 -1
  344. package/dist/templates/workspace-core/.agents/skills/extensions/SKILL.md +16 -0
  345. package/dist/tracking/error-capture.d.ts +14 -0
  346. package/dist/tracking/error-capture.d.ts.map +1 -0
  347. package/dist/tracking/error-capture.js +113 -0
  348. package/dist/tracking/error-capture.js.map +1 -0
  349. package/dist/tracking/index.d.ts +1 -0
  350. package/dist/tracking/index.d.ts.map +1 -1
  351. package/dist/tracking/index.js +1 -0
  352. package/dist/tracking/index.js.map +1 -1
  353. package/dist/vite/client.d.ts.map +1 -1
  354. package/dist/vite/client.js +2 -0
  355. package/dist/vite/client.js.map +1 -1
  356. package/docs/content/extensions.mdx +23 -4
  357. package/package.json +2 -2
  358. package/src/agent/production-agent.ts +4 -0
  359. package/src/agent/run-manager.ts +36 -10
  360. package/src/agent/types.ts +2 -0
  361. package/src/cli/context-xray-local.ts +1 -1
  362. package/src/cli/index.ts +63 -18
  363. package/src/cli/skills.ts +5 -0
  364. package/src/cli/telemetry.ts +118 -1
  365. package/src/client/AgentPanel.tsx +10 -0
  366. package/src/client/CommandMenu.tsx +13 -1
  367. package/src/client/agent-page/AgentTabsPage.tsx +39 -4
  368. package/src/client/analytics.ts +13 -0
  369. package/src/client/error-capture.ts +6 -1
  370. package/src/client/extensions/ExtensionsListPage.tsx +28 -8
  371. package/src/client/extensions/agent-native-extension-runtime.ts +1 -1
  372. package/src/client/navigation/index.ts +2 -0
  373. package/src/client/org/OrgSwitcher.tsx +14 -32
  374. package/src/client/settings/ExtensionsSettingsContent.tsx +24 -0
  375. package/src/client/settings/SettingsPanel.tsx +10 -0
  376. package/src/deploy/build.ts +3 -0
  377. package/src/extensions/theme.ts +1 -1
  378. package/src/localization/default-messages.ts +2 -0
  379. package/src/server/analytics.ts +154 -25
  380. package/src/server/core-routes-plugin.ts +10 -1
  381. package/src/server/framework-request-handler.ts +17 -26
  382. package/src/styles/agent-native.css +13 -0
  383. package/src/templates/chat/app/components/layout/Layout.tsx +3 -0
  384. package/src/templates/chat/app/components/layout/Sidebar.tsx +113 -51
  385. package/src/templates/chat/app/global.css +1 -1
  386. package/src/templates/chat/app/routes/extensions._index.tsx +2 -2
  387. package/src/templates/chat/changelog/2026-07-24-borderless-secondary-surfaces.md +6 -0
  388. package/src/templates/chat/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +6 -0
  389. package/src/templates/chat/netlify.toml +1 -0
  390. package/src/templates/default/app/global.css +1 -1
  391. package/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +16 -0
  392. package/src/tracking/error-capture.ts +151 -0
  393. package/src/tracking/index.ts +5 -0
  394. package/src/vite/client.ts +6 -0
  395. package/corpus/templates/analytics/app/components/layout/NewAnalysisDialog.tsx +0 -165
  396. package/corpus/templates/analytics/app/pages/analyses/AnalysesList.tsx +0 -175
@@ -709,7 +709,7 @@ export default createAgentChatPlugin({
709
709
  // render every organization metric before the model request starts.
710
710
  const sourceGuidance =
711
711
  analyticsSourceGuidanceOpening() +
712
- "DASHBOARD CREATION RULE — You may create dashboards, analyses, SQL panels, or other resources only when the user explicitly asks you to (e.g. 'build me a dashboard for...', 'create a new analysis', 'add a chart for...'). Never create any resource proactively during research, trend analysis, or answering questions. If you think a dashboard would be useful, suggest it and wait for explicit confirmation before creating anything. Never add new items to the sidebar or modify existing dashboards without an explicit user directive. " +
712
+ "DASHBOARD CREATION RULE — You may create dashboard artifacts, SQL panels, or other resources only when the user explicitly asks you to (e.g. 'build me a dashboard for...', 'save this analysis', 'add a chart for...'). Treat a requested saved analysis or deep-dive report as a dashboard request. Never create any resource proactively during research, trend analysis, or answering questions. If you think a dashboard would be useful, suggest it and wait for explicit confirmation before creating anything. Never add new items to the sidebar or modify existing dashboards without an explicit user directive. " +
713
713
  "DASHBOARD MUTATION RULE — For dashboard edits, default to `mutate-dashboard` with the typed `dashboard.*` script API so the main payload is a string and avoids native-array serialization traps. It can move panels by id, edit titles/SQL/config, insert, duplicate, remove, and patch dashboard fields in one atomic save. The script API is constrained: no variables/imports/loops/functions, only JSON-compatible arguments on documented dashboard methods. Do not count shifting `/panels/<index>` positions for ordinary dashboard edits unless the user specifically asks for low-level JSON-pointer operations. " +
714
714
  'DASHBOARD EXTENSION BOX RULE — Analytics dashboards support extension boxes. For ordinary requests such as "put X in this dashboard," insert a `chartType: "extension"` panel with `config.extensionId`; this author-selected embed is shared with the dashboard, appears in scheduled reports, and receives dashboard/panel/current-filter context. Use `config.extensionSlotId` only when the user explicitly asks for a personal/per-viewer slot. Slot ids use `analytics.dashboard.<dashboard-id>.panel.<panel-id>` and require `add-extension-slot-target` plus `install-extension`; installs are per-user, so viewers can see different content and report identities may see an empty slot. Use `get-sql-dashboard` panel summaries to inspect an existing box. ' +
715
715
  'EXTENSION DATA-REPAIR RULE — When fixing data in an existing extension-backed dashboard or migrated surface such as Risk Meeting, inspect the current dashboard and extension first, then call `update-extension` with exactly `id`, `operation="edit"`, and a `payloadJson` string containing focused patches/edits that change only the data-loading seam. Never send empty placeholder fields. Preserve the existing layout, CSS, copy, and interactions; never reconstruct the full HTML body for a data-only fix. A request that combines a visual rewrite such as compacting, removing sections, renaming, or changing padding with a data repair is a broad rewrite; after inspecting the current extension, use `operation="replace"` with the complete replacement in `payloadJson`. If a focused edit fails, change the target instead of retrying identical arguments. ' +
@@ -721,20 +721,19 @@ export default createAgentChatPlugin({
721
721
  "When the user names a provider such as first-party Analytics, BigQuery, HubSpot, Gong, Jira, Pylon, Slack, Sentry, GA4, or another connected source, that source is authoritative for the turn. Use its first-class query action when available; if it is not on the initial tool surface, use tool-search for that provider instead of loading unrelated catalogs. For an ordinary structured lookup, make one bounded query and stop on success. " +
722
722
  "Load provider API, corpus, staging, or code tools only when the user explicitly requests cross-source work, exhaustive unstructured-record coverage, or an absence claim that the first-class action cannot support. For those genuinely broad workflows, fetch every relevant page or an explicitly bounded cohort, preserve coverage counts, and state any uncovered records. " +
723
723
  "For named deal, account, renewal, churn-risk, or customer deep dives that need HubSpot and Gong context, `account-deep-dive` can provide a bounded evidence bundle. Do not answer a requested transcript deep dive from call metadata alone. " +
724
- "When the user refers to the current analysis, this analysis, this project, or asks to spin off, adapt, modify, or reuse a saved analysis, call `view-screen` first and use the returned analysis details; if an analysis id or @mention is provided, call `get-analysis` before responding. " +
724
+ "When the user refers to the current dashboard artifact, this analysis, this project, or asks to spin off, adapt, modify, or reuse a saved analysis, call `view-screen` first and use the returned dashboard details; for an explicitly named legacy analysis id, call `get-analysis` before responding and preserve its legacy deep link only for compatibility. " +
725
725
  "If a query action fails because its arguments are invalid, correct the arguments once. Never repeat the identical failed call. For credential, permission, quota, network, or repeated schema failures, stop using that source for the turn and surface the actual error instead of trying unrelated providers. " +
726
726
  "For ordinary ad-hoc structured data questions, answer the explicit question after the first relevant successful query or bounded evidence batch. The words all, total, or exact do not require cross-source validation when a single structured query fully covers the requested source and filters. " +
727
- "If the user challenges coverage, asks why more records were not included, or asks for the updated answer, rerun the relevant source query or revise from the corrected cohort and provide the updated deliverable directly. Do not claim an analysis was revised unless the revised answer is included in the response or saved with `save-analysis`. " +
727
+ "If the user challenges coverage, asks why more records were not included, or asks for the updated answer, rerun the relevant source query or revise from the corrected cohort and provide the updated deliverable directly. Do not claim a dashboard artifact was revised unless the revised answer is included in the response or saved with `update-dashboard`. " +
728
728
  "Unstructured source records are valid analytics evidence: Pylon tickets, Jira issues, Gong calls/transcripts, Slack messages, and similar text records may be coded for themes, mention counts, sentiment, objections, and qualitative patterns as long as the answer states the inspected sample size and does not imply unsupported statistical certainty. " +
729
729
  "For schema questions, prefer data-dictionary entries and configured warehouse schemas over assumptions; use `search-bigquery-schema` for BigQuery metadata before inventing datasets, tables, or columns. " +
730
730
  "Before finalizing any analytics answer, make the evidence trail explicit enough to audit: answer the user's question, name the source(s), time window, sample size or row count, filters, join/match method, caveats/gaps, and recommended next action when useful. Never substitute fabricated numbers for a failed query or unavailable provider. It is fine to ask a clarifying question, provide a plan, or say exactly which source is unavailable as long as you do not present metrics or source-record conclusions without evidence.\n" +
731
731
  "</data-source-guidance>";
732
732
  const artifactGuidance =
733
733
  "<analytics-artifact-guidance>\n" +
734
- "Native Analytics dashboards and saved analyses are constrained artifacts: dashboards are JSON configs rendered by the built-in dashboard components, and analyses are Markdown reports with generated chart images plus structured resultData. " +
735
- "If the user's requested dashboard, analysis surface, visualization, interaction model, custom layout, or bespoke workflow cannot be faithfully represented within those native components/config fields, do not hand-wave, force an approximate JSON dashboard, or route to source-code changes. In production mode, automatically create a sandboxed extension with `create-extension` instead, using Alpine.js HTML and the available app/data helpers. " +
736
- "After creating the extension, briefly tell the user that the request needed bespoke UI/code beyond the native Analytics dashboard or analysis format, so you built it as an extension. " +
737
- "Do not also create a same-named dashboard or saved analysis unless the user explicitly asked for multiple artifacts; saved analyses appear in the sidebar and should not be used for throwaway notes or scratch summaries.\n" +
734
+ "Analytics has one user-facing artifact type: dashboards. Dashboards are JSON configs rendered by the built-in dashboard components, and an extension is a dashboard block—not a separate Analytics result. " +
735
+ 'If the user\'s requested dashboard, saved analysis/report, visualization, interaction model, custom layout, or bespoke workflow cannot be faithfully represented within the native dashboard components/config fields, do not hand-wave, force an approximate JSON dashboard, or route to source-code changes. In production mode, automatically create a sandboxed extension with `create-extension`, then immediately call `update-dashboard` to embed it as one or more `chartType: "extension"` panels with `config.extensionId`. ' +
736
+ "After creating the dashboard, briefly tell the user that a dashboard block uses an embedded extension because the request needed bespoke UI/code. Never leave a newly created extension standalone, never create a separate saved analysis for the same request, and never direct the user to an Extensions page from Analytics. Legacy analyses may be read or updated only for compatibility with an existing deep link.\n" +
738
737
  "</analytics-artifact-guidance>";
739
738
 
740
739
  return `${sourceGuidance}\n\n${artifactGuidance}\n\n${analyticsDataDictionaryRoutingContext()}`;
@@ -779,50 +778,5 @@ export default createAgentChatPlugin({
779
778
  }
780
779
  },
781
780
  },
782
- analyses: {
783
- label: "Analyses",
784
- icon: "document",
785
- search: async (query: string, event?: any) => {
786
- if (!event) return [];
787
- try {
788
- const { getOrgContext } = await import("@agent-native/core/org");
789
- const { listAnalyses } = await import("../lib/dashboards-store.js");
790
- const ctx = await getOrgContext(event);
791
- const rows = await listAnalyses({
792
- email: ctx.email,
793
- orgId: ctx.orgId ?? null,
794
- });
795
- const q = (query || "").toLowerCase().trim();
796
- const filtered = q
797
- ? rows.filter(
798
- (analysis) =>
799
- (analysis.name || "").toLowerCase().includes(q) ||
800
- (analysis.description || "").toLowerCase().includes(q) ||
801
- analysis.id.toLowerCase().includes(q),
802
- )
803
- : rows;
804
-
805
- return filtered
806
- .sort(
807
- (a, b) =>
808
- new Date(b.updatedAt).getTime() -
809
- new Date(a.updatedAt).getTime(),
810
- )
811
- .slice(0, 20)
812
- .map((analysis) => ({
813
- id: `analysis:${analysis.id}`,
814
- label: analysis.name || "Untitled analysis",
815
- description: `/analyses/${analysis.id}`,
816
- icon: "document",
817
- refType: "analysis",
818
- refId: analysis.id,
819
- refPath: `/analyses/${analysis.id}`,
820
- }));
821
- } catch (err) {
822
- console.error("[analytics] Analysis mention provider failed:", err);
823
- return [];
824
- }
825
- },
826
- },
827
781
  },
828
782
  });
@@ -10,7 +10,7 @@ export default createCoreRoutesPlugin({
10
10
  resolveOpenPath: ({ view, params }) => {
11
11
  if (params.dashboardId) return `/dashboards/${params.dashboardId}`;
12
12
  if (params.analysisId) return `/analyses/${params.analysisId}`;
13
- if (view === "analyses") return "/analyses";
13
+ if (view === "analyses") return "/dashboards";
14
14
  // `adhoc`/unknown with no id: there is no bare `/dashboards` record route —
15
15
  // send to the app root rather than 404 (the polled `navigate` command
16
16
  // still applies any record focus once the SPA is loaded).
@@ -1265,6 +1265,25 @@ const runAnalyticsMigrations = runMigrations(
1265
1265
  name: "error-events-user-key-filter-idx",
1266
1266
  sql: `CREATE INDEX IF NOT EXISTS error_events_user_key_filter_idx ON error_events (user_key, owner_email, org_id, issue_id)`,
1267
1267
  },
1268
+ {
1269
+ version: 121,
1270
+ name: "analytics-events-org-path-event-idx",
1271
+ sql: `CREATE INDEX IF NOT EXISTS analytics_events_org_path_event_idx ON analytics_events (org_id, path, event_name)`,
1272
+ },
1273
+ {
1274
+ version: 122,
1275
+ name: "dashboard-revisions-org-dashboard-idx",
1276
+ sql: `CREATE INDEX IF NOT EXISTS dashboard_revisions_org_dashboard_idx ON dashboard_revisions (org_id, dashboard_id)`,
1277
+ },
1278
+ {
1279
+ version: 123,
1280
+ name: "dashboard-report-capture-diagnostics",
1281
+ sql: `
1282
+ ALTER TABLE dashboard_report_subscriptions ADD COLUMN IF NOT EXISTS last_capture_at TEXT;
1283
+ ALTER TABLE dashboard_report_subscriptions ADD COLUMN IF NOT EXISTS last_capture_mode TEXT;
1284
+ ALTER TABLE dashboard_report_subscriptions ADD COLUMN IF NOT EXISTS last_capture_error TEXT;
1285
+ `,
1286
+ },
1268
1287
  ],
1269
1288
  { table: "analytics_migrations" },
1270
1289
  );
@@ -1,3 +1,3 @@
1
1
  export const FIRST_PARTY_ANALYTICS_QUERY_TIMEOUT_MS = 30_000;
2
2
  export const DASHBOARD_REPORT_ACTION_TIMEOUT_MS = 38_000;
3
- export const DASHBOARD_REPORT_READY_TIMEOUT_MS = 45_000;
3
+ export const DASHBOARD_REPORT_READY_TIMEOUT_MS = 60_000;
@@ -33,13 +33,47 @@ const presetReferenceFillSchema = z.object({
33
33
  assetIds: z.array(z.string().min(1)).min(1).max(4),
34
34
  });
35
35
 
36
+ const imageBatchAgentInputSchema = z.object({
37
+ libraryId: z
38
+ .string()
39
+ .min(1)
40
+ .describe("Brand kit/library ID to use for every generated image."),
41
+ collectionId: z.string().optional(),
42
+ presetId: z.string().optional(),
43
+ presetReferenceFills: z.array(presetReferenceFillSchema).max(6).optional(),
44
+ sessionId: z.string().optional(),
45
+ slots: z
46
+ .array(
47
+ z.object({
48
+ slotId: z.string(),
49
+ prompt: z.string().min(1),
50
+ embeddedText: z.string().optional(),
51
+ textPlacement: z.string().optional(),
52
+ aspectRatio: z.enum(ASPECT_RATIOS).optional(),
53
+ imageSize: z.enum(IMAGE_SIZES).optional(),
54
+ categories: z.array(z.enum(IMAGE_CATEGORIES)).optional(),
55
+ referenceAssetIds: z.array(z.string()).optional(),
56
+ sourceAssetId: z.string().optional(),
57
+ subjectAssetId: z.string().optional(),
58
+ intent: z.enum(GENERATION_INTENTS).optional(),
59
+ styleStrength: z.enum(STYLE_STRENGTHS).optional(),
60
+ }),
61
+ )
62
+ .min(1)
63
+ .max(12),
64
+ model: z.enum(IMAGE_MODELS).optional(),
65
+ tier: z.enum(IMAGE_QUALITY_TIERS).optional(),
66
+ includeLogo: z.boolean().optional(),
67
+ groundingMode: z.enum(["auto", "off", "google-search"]).optional(),
68
+ });
69
+
36
70
  export default defineAction({
37
71
  description:
38
72
  "Generate several brand-consistent images in parallel from one brand kit/library. Use @brand-kit mentions as libraryId and @preset mentions as presetId when present. If no preset is tagged, call list-generation-presets first and use a matching preset's presetId; the user may not know presets exist. Generate presetless only when no preset matches the request. This is synchronous for images: one call waits for every slot and returns final image artifacts. Use this for slide decks, landing pages, and multi-slot design work. Do not call get-generation-run or refresh-generation-run after a normal image batch result.",
39
73
  schema: z.object({
40
74
  libraryId: z
41
75
  .string()
42
- .optional()
76
+ .min(1)
43
77
  .describe(
44
78
  "Brand kit/library ID. Pass the refId from a brand-kit @mention, or choose a kit from view-screen/list-libraries.",
45
79
  ),
@@ -135,6 +169,7 @@ export default defineAction({
135
169
  "Disable Creative Context for this batch only without changing the saved preference.",
136
170
  ),
137
171
  }),
172
+ agentInputSchema: imageBatchAgentInputSchema,
138
173
  parallelSafe: true,
139
174
  timeoutMs: IMAGE_GENERATION_TOOL_TIMEOUT_MS,
140
175
  run: async ({ slots, ...inputBase }, context?: ActionRunContext) => {
@@ -83,13 +83,39 @@ const presetReferenceFillSchema = z.object({
83
83
  assetIds: z.array(z.string().min(1)).min(1).max(4),
84
84
  });
85
85
 
86
+ const imageGenerationAgentInputSchema = z.object({
87
+ libraryId: z
88
+ .string()
89
+ .min(1)
90
+ .describe("Brand kit/library ID to use for this image."),
91
+ collectionId: z.string().optional(),
92
+ presetId: z.string().optional(),
93
+ sessionId: z.string().optional(),
94
+ prompt: z.string().min(1),
95
+ embeddedText: z.string().optional(),
96
+ textPlacement: z.string().optional(),
97
+ aspectRatio: z.enum(ASPECT_RATIOS).optional(),
98
+ imageSize: z.enum(IMAGE_SIZES).optional(),
99
+ model: z.enum(IMAGE_MODELS).optional(),
100
+ tier: z.enum(IMAGE_QUALITY_TIERS).optional(),
101
+ intent: z.enum(GENERATION_INTENTS).optional(),
102
+ styleStrength: z.enum(STYLE_STRENGTHS).optional(),
103
+ categories: z.array(z.enum(IMAGE_CATEGORIES)).optional(),
104
+ referenceAssetIds: z.array(z.string()).optional(),
105
+ presetReferenceFills: z.array(presetReferenceFillSchema).max(6).optional(),
106
+ includeLogo: z.boolean().optional(),
107
+ sourceAssetId: z.string().optional(),
108
+ subjectAssetId: z.string().optional(),
109
+ groundingMode: z.enum(["auto", "off", "google-search"]).optional(),
110
+ });
111
+
86
112
  export default defineAction({
87
113
  description:
88
114
  "Generate one brand-consistent image from a brand kit/library. This is synchronous for images and returns the final asset with preview/download/embed URLs. Use @brand-kit mentions as libraryId and @preset mentions as presetId when present. If no preset is tagged, call list-generation-presets first and use a matching preset's presetId; the user may not know presets exist. Generate presetless only when no preset matches the request. Use generate-image-batch for multiple independent slots; do not poll image runs after this action returns.",
89
115
  schema: z.object({
90
116
  libraryId: z
91
117
  .string()
92
- .optional()
118
+ .min(1)
93
119
  .describe(
94
120
  "Brand kit/library ID. Pass the refId from a brand-kit @mention, or choose a kit from view-screen/list-libraries.",
95
121
  ),
@@ -205,6 +231,7 @@ export default defineAction({
205
231
  "When false, attach the output to the session without making it the active asset. Batch generation selects the active asset deterministically after all slots finish.",
206
232
  ),
207
233
  }),
234
+ agentInputSchema: imageGenerationAgentInputSchema,
208
235
  parallelSafe: true,
209
236
  timeoutMs: IMAGE_GENERATION_TOOL_TIMEOUT_MS,
210
237
  run: async (input, context?: ActionRunContext) => {
@@ -0,0 +1,376 @@
1
+ import { useActionMutation } from "@agent-native/core/client/hooks";
2
+ import { useT } from "@agent-native/core/client/i18n";
3
+ import {
4
+ IconChevronLeft,
5
+ IconChevronRight,
6
+ IconDownload,
7
+ IconInfoCircle,
8
+ IconX,
9
+ } from "@tabler/icons-react";
10
+ import { useEffect, useState, type ReactNode } from "react";
11
+ import { toast } from "sonner";
12
+
13
+ import { Badge } from "@/components/ui/badge";
14
+ import { Button } from "@/components/ui/button";
15
+ import {
16
+ Dialog,
17
+ DialogClose,
18
+ DialogContent,
19
+ DialogDescription,
20
+ DialogTitle,
21
+ } from "@/components/ui/dialog";
22
+ import { assetPreviewSources } from "@/lib/asset-preview-sources";
23
+ import { assetMediaUrl } from "@/lib/asset-urls";
24
+
25
+ export type PreviewAsset = {
26
+ id: string;
27
+ libraryId?: string | null;
28
+ role?: string | null;
29
+ status?: string | null;
30
+ title?: string | null;
31
+ description?: string | null;
32
+ altText?: string | null;
33
+ prompt?: string | null;
34
+ mediaType?: string | null;
35
+ mimeType?: string | null;
36
+ width?: number | null;
37
+ height?: number | null;
38
+ url?: string | null;
39
+ previewUrl?: string | null;
40
+ thumbnailUrl?: string | null;
41
+ downloadUrl?: string | null;
42
+ folderId?: string | null;
43
+ category?: string | null;
44
+ model?: string | null;
45
+ aspectRatio?: string | null;
46
+ durationSeconds?: number | null;
47
+ metadata?: Record<string, unknown> | null;
48
+ lineage?: { label?: string | null } | null;
49
+ };
50
+
51
+ /**
52
+ * The single side-panel asset preview used everywhere an asset is opened:
53
+ * large media on the left, details on the right, a top toolbar (download,
54
+ * details toggle, close), and previous/next navigation across `assets`.
55
+ */
56
+ export function AssetPreviewDialog({
57
+ asset,
58
+ assets,
59
+ onAssetChange,
60
+ renderImage,
61
+ }: {
62
+ asset: PreviewAsset | null;
63
+ assets: PreviewAsset[];
64
+ onAssetChange: (asset: PreviewAsset | null) => void;
65
+ /** Optional media renderer (e.g. an embed/COEP-aware image loader). */
66
+ renderImage?: (asset: PreviewAsset) => ReactNode;
67
+ }) {
68
+ const t = useT();
69
+ const exportAsset = useActionMutation("export-asset");
70
+ const [showDetails, setShowDetails] = useState(true);
71
+ return (
72
+ <Dialog
73
+ open={Boolean(asset)}
74
+ onOpenChange={(open) => {
75
+ if (!open) onAssetChange(null);
76
+ }}
77
+ >
78
+ {asset &&
79
+ (() => {
80
+ const previewIndex = assets.findIndex(
81
+ (candidate) => candidate.id === asset.id,
82
+ );
83
+ const hasPrev = previewIndex > 0;
84
+ const hasNext = previewIndex >= 0 && previewIndex < assets.length - 1;
85
+ const showPreviousAsset = () => {
86
+ if (hasPrev) onAssetChange(assets[previewIndex - 1]);
87
+ };
88
+ const showNextAsset = () => {
89
+ if (hasNext) onAssetChange(assets[previewIndex + 1]);
90
+ };
91
+ const isVideo =
92
+ asset.mediaType === "video" ||
93
+ Boolean(asset.mimeType?.startsWith("video/"));
94
+ const videoSrc = assetPreviewSources(asset)[0];
95
+ const downloadAsset = () => {
96
+ // Synthetic starter-preset assets aren't database rows, so
97
+ // export-asset can't resolve them; download the source directly.
98
+ if (isStarterPreviewAsset(asset)) {
99
+ const directUrl = assetPreviewSources(asset)[0];
100
+ if (directUrl) window.location.href = directUrl;
101
+ else toast.error(t("assetDetail.downloadFailed"));
102
+ return;
103
+ }
104
+ exportAsset.mutate(
105
+ { assetId: asset.id },
106
+ {
107
+ onSuccess: (result: any) => {
108
+ const url =
109
+ assetMediaUrl(result?.downloadUrl) ?? result?.downloadUrl;
110
+ if (url) window.location.href = url;
111
+ else toast.error(t("assetDetail.downloadFailed"));
112
+ },
113
+ onError: () => toast.error(t("assetDetail.downloadFailed")),
114
+ },
115
+ );
116
+ };
117
+ return (
118
+ <DialogContent
119
+ hideClose
120
+ onKeyDown={(event) => {
121
+ if (event.key === "ArrowLeft") showPreviousAsset();
122
+ if (event.key === "ArrowRight") showNextAsset();
123
+ }}
124
+ className="assets-preview-dialog flex max-h-[92vh] w-[94vw] max-w-6xl flex-col gap-0 overflow-hidden p-0"
125
+ >
126
+ <DialogTitle className="sr-only">
127
+ {assetPreviewTitle(asset)}
128
+ </DialogTitle>
129
+ <DialogDescription className="sr-only">
130
+ {t("library.fullSizePreview", {
131
+ title: assetPreviewTitle(asset),
132
+ })}
133
+ </DialogDescription>
134
+
135
+ <div className="flex items-center justify-end gap-1 border-b border-border px-3 py-2">
136
+ <Button
137
+ variant="ghost"
138
+ size="sm"
139
+ className="gap-2"
140
+ disabled={exportAsset.isPending}
141
+ onClick={downloadAsset}
142
+ >
143
+ <IconDownload className="h-4 w-4" />
144
+ {t("assetDetail.download")}
145
+ </Button>
146
+ <Button
147
+ variant="ghost"
148
+ size="sm"
149
+ className="gap-2"
150
+ aria-pressed={showDetails}
151
+ onClick={() => setShowDetails((value) => !value)}
152
+ >
153
+ <IconInfoCircle className="h-4 w-4" />
154
+ {t("library.viewDetails")}
155
+ </Button>
156
+ <DialogClose
157
+ aria-label={t("library.closePreview")}
158
+ className="inline-flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground transition hover:bg-muted hover:text-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-ring"
159
+ >
160
+ <IconX className="h-5 w-5" />
161
+ </DialogClose>
162
+ </div>
163
+
164
+ <div className="flex min-h-0 flex-1 flex-col md:flex-row">
165
+ <div className="relative flex min-h-0 flex-1 items-center justify-center bg-muted/30 p-4">
166
+ {isVideo ? (
167
+ <video
168
+ src={videoSrc}
169
+ poster={asset.thumbnailUrl ?? undefined}
170
+ controls
171
+ autoPlay
172
+ playsInline
173
+ className="max-h-[72vh] max-w-full rounded-lg bg-black object-contain"
174
+ />
175
+ ) : renderImage ? (
176
+ renderImage(asset)
177
+ ) : (
178
+ <AssetPreviewImage asset={asset} />
179
+ )}
180
+ </div>
181
+ {showDetails && (
182
+ <aside className="w-full shrink-0 overflow-y-auto border-t border-border p-5 md:w-80 md:border-l md:border-t-0">
183
+ <AssetPreviewDetails asset={asset} isVideo={isVideo} />
184
+ </aside>
185
+ )}
186
+ </div>
187
+
188
+ {(hasPrev || hasNext) && (
189
+ <div className="flex justify-center gap-2 border-t border-border py-3">
190
+ <button
191
+ type="button"
192
+ aria-label={t("library.previousImage")}
193
+ onClick={showPreviousAsset}
194
+ disabled={!hasPrev}
195
+ className="inline-flex h-9 w-9 items-center justify-center rounded-full border border-border bg-background text-foreground transition hover:bg-muted focus:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-40"
196
+ >
197
+ <IconChevronLeft className="h-5 w-5" />
198
+ </button>
199
+ <button
200
+ type="button"
201
+ aria-label={t("library.nextImage")}
202
+ onClick={showNextAsset}
203
+ disabled={!hasNext}
204
+ className="inline-flex h-9 w-9 items-center justify-center rounded-full border border-border bg-background text-foreground transition hover:bg-muted focus:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-40"
205
+ >
206
+ <IconChevronRight className="h-5 w-5" />
207
+ </button>
208
+ </div>
209
+ )}
210
+ </DialogContent>
211
+ );
212
+ })()}
213
+ </Dialog>
214
+ );
215
+ }
216
+
217
+ function AssetPreviewImage({ asset }: { asset: PreviewAsset }) {
218
+ const t = useT();
219
+ const sources = assetPreviewSources(asset);
220
+ const [sourceIndex, setSourceIndex] = useState(0);
221
+ const [unavailable, setUnavailable] = useState(false);
222
+ const sourcesKey = sources.join("\n");
223
+
224
+ useEffect(() => {
225
+ setSourceIndex(0);
226
+ setUnavailable(false);
227
+ }, [sourcesKey]);
228
+
229
+ const src = sources[sourceIndex];
230
+ if (!src || unavailable) {
231
+ return (
232
+ <div className="flex aspect-square w-full max-w-sm items-center justify-center rounded-lg border border-dashed border-border bg-background px-6 text-sm font-medium text-muted-foreground">
233
+ {t("assetDetail.previewUnavailable")}
234
+ </div>
235
+ );
236
+ }
237
+ return (
238
+ <img
239
+ src={src}
240
+ alt={asset.altText ?? asset.title ?? ""}
241
+ className="max-h-[72vh] max-w-full rounded-lg object-contain"
242
+ onError={() => {
243
+ const nextIndex = sourceIndex + 1;
244
+ if (nextIndex < sources.length) setSourceIndex(nextIndex);
245
+ else setUnavailable(true);
246
+ }}
247
+ />
248
+ );
249
+ }
250
+
251
+ function AssetPreviewDetails({
252
+ asset,
253
+ isVideo,
254
+ }: {
255
+ asset: PreviewAsset;
256
+ isVideo: boolean;
257
+ }) {
258
+ const t = useT();
259
+ const category = assetPreviewCategoryLabel(asset, t);
260
+ return (
261
+ <div className="space-y-5">
262
+ <div>
263
+ <h2 className="text-base font-semibold leading-tight tracking-tight">
264
+ {assetPreviewTitle(asset)}
265
+ </h2>
266
+ <div className="mt-2 flex flex-wrap gap-1.5">
267
+ {asset.status ? (
268
+ <Badge variant="secondary">{asset.status}</Badge>
269
+ ) : null}
270
+ {asset.role ? <Badge variant="outline">{asset.role}</Badge> : null}
271
+ <Badge variant="outline">{isVideo ? "video" : "image"}</Badge>
272
+ {category ? <Badge variant="outline">{category}</Badge> : null}
273
+ </div>
274
+ </div>
275
+ <div className="space-y-4 text-sm">
276
+ {isVideo ? (
277
+ <PreviewField
278
+ label={t("assetDetail.video")}
279
+ value={`${asset.durationSeconds || "?"}s · ${asset.aspectRatio || "n/a"} · ${asset.model || "n/a"}`}
280
+ />
281
+ ) : (
282
+ <PreviewField
283
+ label={t("assetDetail.dimensions")}
284
+ value={formatPreviewDimensions(asset.width, asset.height)}
285
+ />
286
+ )}
287
+ <PreviewField label="MIME" value={asset.mimeType || "n/a"} />
288
+ <PreviewField
289
+ label={t("assetDetail.folder")}
290
+ value={asset.folderId || t("assetDetail.unfiled")}
291
+ />
292
+ <PreviewField
293
+ label={t("assetDetail.description")}
294
+ value={
295
+ asset.description || asset.altText || t("assetDetail.noDescription")
296
+ }
297
+ multiline
298
+ />
299
+ <PreviewField
300
+ label={t("assetDetail.prompt")}
301
+ value={asset.prompt || t("assetDetail.noPrompt")}
302
+ multiline
303
+ />
304
+ </div>
305
+ </div>
306
+ );
307
+ }
308
+
309
+ function PreviewField({
310
+ label,
311
+ value,
312
+ multiline,
313
+ }: {
314
+ label: string;
315
+ value: ReactNode;
316
+ multiline?: boolean;
317
+ }) {
318
+ return (
319
+ <div>
320
+ <div className="text-xs font-medium uppercase tracking-wide text-muted-foreground">
321
+ {label}
322
+ </div>
323
+ <div className={multiline ? "mt-1 whitespace-pre-wrap" : "mt-1 truncate"}>
324
+ {value}
325
+ </div>
326
+ </div>
327
+ );
328
+ }
329
+
330
+ function assetPreviewTitle(asset: PreviewAsset): string {
331
+ return (
332
+ asset.lineage?.label || asset.title || asset.prompt || "Untitled asset"
333
+ );
334
+ }
335
+
336
+ function assetPreviewCategoryLabel(
337
+ asset: PreviewAsset,
338
+ t: (key: string, options?: Record<string, unknown>) => string,
339
+ ): string | null {
340
+ const metadata = (asset.metadata ?? {}) as Record<string, unknown>;
341
+ if (metadata.intent === "subject" || asset.role === "subject_reference") {
342
+ return t("assetDetail.contentOnly");
343
+ }
344
+ const category = (metadata.category as string | undefined) ?? asset.category;
345
+ if (typeof category !== "string") return null;
346
+ if (category === "style-only") return t("assetDetail.styleReference");
347
+ if (category === "skeleton") return t("assetDetail.skeletonPlate");
348
+ return category.replace(/-/g, " ");
349
+ }
350
+
351
+ function formatPreviewDimensions(
352
+ width?: number | null,
353
+ height?: number | null,
354
+ ) {
355
+ const dimensions = `${width || "?"} x ${height || "?"}`;
356
+ if (!width || !height) return dimensions;
357
+ const divisor = previewGcd(width, height);
358
+ return (
359
+ <span className="flex items-center gap-2">
360
+ {dimensions}
361
+ <span className="h-4 w-px bg-border" />
362
+ {`${width / divisor}:${height / divisor}`}
363
+ </span>
364
+ );
365
+ }
366
+
367
+ function previewGcd(a: number, b: number): number {
368
+ return b === 0 ? a : previewGcd(b, a % b);
369
+ }
370
+
371
+ function isStarterPreviewAsset(asset: PreviewAsset): boolean {
372
+ return (
373
+ asset.id.startsWith("starter-") ||
374
+ Boolean(asset.libraryId?.startsWith("starter:"))
375
+ );
376
+ }
@@ -5,8 +5,10 @@ import {
5
5
  IconArrowUpRight,
6
6
  IconPhoto,
7
7
  } from "@tabler/icons-react";
8
+ import { useState } from "react";
8
9
  import { Link } from "react-router";
9
10
 
11
+ import { AssetPreviewDialog } from "@/components/asset/AssetPreviewDialog";
10
12
  import { Button } from "@/components/ui/button";
11
13
  import { Skeleton } from "@/components/ui/skeleton";
12
14
 
@@ -32,6 +34,7 @@ export function RecentDraftsSection() {
32
34
  },
33
35
  );
34
36
  const drafts = ((data as any)?.assets ?? []) as DraftAsset[];
37
+ const [previewAsset, setPreviewAsset] = useState<DraftAsset | null>(null);
35
38
 
36
39
  if (isError) {
37
40
  return (
@@ -74,18 +77,25 @@ export function RecentDraftsSection() {
74
77
  <Skeleton key={index} className="aspect-square rounded-lg" />
75
78
  ))
76
79
  : drafts.map((draft) => (
77
- <Link
80
+ <button
78
81
  key={draft.id}
79
- to={`/asset/${encodeURIComponent(draft.id)}`}
82
+ type="button"
83
+ onClick={() => setPreviewAsset(draft)}
80
84
  title={draft.title || draft.prompt || t("library.draftAsset")}
81
- className="group block overflow-hidden rounded-lg border border-border bg-card shadow-sm transition hover:border-primary/60 hover:shadow-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
85
+ className="group block overflow-hidden rounded-lg border border-border bg-card text-left shadow-sm transition hover:border-primary/60 hover:shadow-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
82
86
  >
83
87
  <div className="aspect-square bg-muted">
84
88
  <DraftThumbnail draft={draft} />
85
89
  </div>
86
- </Link>
90
+ </button>
87
91
  ))}
88
92
  </div>
93
+
94
+ <AssetPreviewDialog
95
+ asset={previewAsset}
96
+ assets={drafts}
97
+ onAssetChange={(next) => setPreviewAsset(next as DraftAsset | null)}
98
+ />
89
99
  </section>
90
100
  );
91
101
  }