@agent-native/core 0.141.5 → 0.141.7

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 (235) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +3 -14
  3. package/corpus/templates/analytics/.agents/skills/data-querying/SKILL.md +20 -8
  4. package/corpus/templates/analytics/.agents/skills/prometheus/SKILL.md +5 -5
  5. package/corpus/templates/analytics/AGENTS.md +2 -2
  6. package/corpus/templates/analytics/actions/compose-dashboard.ts +1 -1
  7. package/corpus/templates/analytics/actions/get-first-party-analytics-health.ts +15 -1
  8. package/corpus/templates/analytics/actions/migrate-first-party-analytics-to-bigquery.ts +135 -0
  9. package/corpus/templates/analytics/actions/navigate.ts +1 -1
  10. package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +2 -2
  11. package/corpus/templates/analytics/actions/update-dashboard.ts +0 -1
  12. package/corpus/templates/analytics/actions/view-screen.ts +0 -20
  13. package/corpus/templates/analytics/app/components/layout/Header.tsx +0 -1
  14. package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +5 -5
  15. package/corpus/templates/analytics/app/i18n-data.ts +330 -90
  16. package/corpus/templates/analytics/app/pages/Agents.tsx +0 -3
  17. package/corpus/templates/analytics/app/pages/DataSources.tsx +66 -30
  18. package/corpus/templates/analytics/app/pages/Settings.tsx +22 -43
  19. package/corpus/templates/analytics/app/pages/settings/settings-search.ts +23 -20
  20. package/corpus/templates/analytics/app/routes/extensions._index.tsx +1 -1
  21. package/corpus/templates/analytics/app/routes/settings.$.tsx +1 -0
  22. package/corpus/templates/analytics/app/routes/team.tsx +1 -1
  23. package/corpus/templates/analytics/changelog/2026-08-05-historical-analytics-rollups-now-repair-automatically-after-.md +6 -0
  24. package/corpus/templates/analytics/changelog/2026-08-05-settings-no-longer-includes-an-unnecessary-about-section.md +6 -0
  25. package/corpus/templates/analytics/docs/schemas/first-party-analytics.md +1 -1
  26. package/corpus/templates/analytics/netlify.toml +4 -0
  27. package/corpus/templates/analytics/scripts/emit-netlify-dashboard-report-cron.ts +109 -0
  28. package/corpus/templates/analytics/scripts/first-party-analytics-bigquery.sql +104 -0
  29. package/corpus/templates/analytics/server/jobs/analytics-rollup-backfill.ts +342 -0
  30. package/corpus/templates/analytics/server/lib/agent-chat-plan-mode.ts +0 -2
  31. package/corpus/templates/analytics/server/lib/analytics-rollup-lock.ts +8 -0
  32. package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +11 -29
  33. package/corpus/templates/analytics/server/lib/dashboards-store.ts +55 -0
  34. package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +1058 -0
  35. package/corpus/templates/analytics/server/lib/first-party-analytics-health.ts +151 -46
  36. package/corpus/templates/analytics/server/lib/first-party-analytics-rollups.ts +15 -9
  37. package/corpus/templates/analytics/server/lib/first-party-analytics.ts +59 -17
  38. package/corpus/templates/analytics/server/lib/real-data-actions.ts +1 -3
  39. package/corpus/templates/analytics/server/plugins/agent-chat.ts +2 -4
  40. package/corpus/templates/analytics/server/plugins/analytics-rollup-backfill-jobs.ts +61 -0
  41. package/corpus/templates/analytics/server/plugins/db.ts +58 -187
  42. package/corpus/templates/analytics/server/routes/api/analytics-rollups/backfill.post.ts +53 -0
  43. package/corpus/templates/assets/.agents/skills/asset-generation/SKILL.md +8 -6
  44. package/corpus/templates/assets/.agents/skills/library-management/SKILL.md +16 -1
  45. package/corpus/templates/assets/AGENTS.md +1 -0
  46. package/corpus/templates/assets/actions/_helpers.ts +12 -0
  47. package/corpus/templates/assets/actions/generate-image.ts +9 -3
  48. package/corpus/templates/assets/actions/import-style-from-url.ts +115 -0
  49. package/corpus/templates/assets/app/hooks/use-navigation-state.ts +3 -1
  50. package/corpus/templates/assets/app/routes/extensions._index.tsx +1 -1
  51. package/corpus/templates/assets/app/routes/settings.$.tsx +1 -0
  52. package/corpus/templates/assets/app/routes/settings.tsx +5 -0
  53. package/corpus/templates/assets/app/routes/team.tsx +1 -1
  54. package/corpus/templates/assets/changelog/2026-08-05-brand-kits-can-import-a-website-s-hydrated-visual-language-a.md +6 -0
  55. package/corpus/templates/assets/changelog/2026-08-05-generated-images-now-hand-off-embeddable-previews-to-other-w.md +6 -0
  56. package/corpus/templates/assets/server/lib/generation.ts +58 -0
  57. package/corpus/templates/assets/shared/api.ts +12 -0
  58. package/corpus/templates/brain/app/routes/extensions._index.tsx +1 -1
  59. package/corpus/templates/brain/app/routes/team.tsx +1 -1
  60. package/corpus/templates/brain/app/routes.ts +1 -1
  61. package/corpus/templates/calendar/app/components/layout/AppLayout.tsx +3 -1
  62. package/corpus/templates/calendar/app/routes/_app.extensions._index.tsx +1 -1
  63. package/corpus/templates/calendar/app/routes/_app.settings.$.tsx +1 -0
  64. package/corpus/templates/calendar/app/routes/_app.team.tsx +1 -1
  65. package/corpus/templates/chat/app/components/layout/Layout.tsx +1 -1
  66. package/corpus/templates/chat/app/hooks/use-navigation-state.ts +1 -1
  67. package/corpus/templates/chat/app/root.tsx +1 -1
  68. package/corpus/templates/chat/app/routes/agent.tsx +7 -7
  69. package/corpus/templates/chat/app/routes/extensions._index.tsx +1 -1
  70. package/corpus/templates/chat/app/routes/settings.$.tsx +1 -0
  71. package/corpus/templates/chat/app/routes/team.tsx +1 -1
  72. package/corpus/templates/clips/app/components/library/library-layout.tsx +1 -1
  73. package/corpus/templates/clips/app/components/player/transcript-panel.tsx +1 -1
  74. package/corpus/templates/clips/app/components/recorder/storage-setup-card.tsx +1 -1
  75. package/corpus/templates/clips/app/routes/_app.extensions._index.tsx +1 -1
  76. package/corpus/templates/clips/app/routes/_app.settings.$.tsx +1 -0
  77. package/corpus/templates/clips/changelog/2026-08-05-clips-overlays-now-use-clean-surfaces-without-clipped-shadow.md +6 -0
  78. package/corpus/templates/clips/desktop/src/overlays/meeting-notification.tsx +2 -2
  79. package/corpus/templates/clips/desktop/src/styles.css +3 -54
  80. package/corpus/templates/clips/desktop/src-tauri/src/notifications.rs +3 -3
  81. package/corpus/templates/content/app/routes/_app.extensions._index.tsx +1 -1
  82. package/corpus/templates/content/app/routes/_app.settings.$.tsx +1 -0
  83. package/corpus/templates/content/app/routes/_app.team.tsx +1 -1
  84. package/corpus/templates/content/e2e/global-setup.ts +1 -1
  85. package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +6 -1
  86. package/corpus/templates/design/actions/analyze-brand-assets.ts +7 -7
  87. package/corpus/templates/design/actions/get-design-system.ts +3 -1
  88. package/corpus/templates/design/actions/import-from-url.ts +6 -5
  89. package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +2 -2
  90. package/corpus/templates/design/app/routes/extensions._index.tsx +1 -1
  91. package/corpus/templates/design/app/routes/settings.$.tsx +1 -0
  92. package/corpus/templates/design/app/routes/team.tsx +1 -1
  93. package/corpus/templates/design/changelog/2026-08-05-missing-design-systems-now-return-a-clear-not-found-response.md +6 -0
  94. package/corpus/templates/design/changelog/2026-08-05-website-url-imports-now-capture-hydrated-browser-computed-de.md +6 -0
  95. package/corpus/templates/design/e2e/global-setup.ts +1 -1
  96. package/corpus/templates/dispatch/app/hooks/use-navigation-state.ts +1 -1
  97. package/corpus/templates/dispatch/app/routes/settings.$.tsx +1 -0
  98. package/corpus/templates/dispatch/app/routes/team.tsx +1 -1
  99. package/corpus/templates/factory/app/hooks/use-navigation-state.ts +1 -1
  100. package/corpus/templates/factory/app/routes/extensions._index.tsx +1 -1
  101. package/corpus/templates/factory/app/routes/settings.$.tsx +1 -0
  102. package/corpus/templates/factory/app/routes/team.tsx +1 -1
  103. package/corpus/templates/forms/app/routes/_app.extensions._index.tsx +1 -1
  104. package/corpus/templates/forms/app/routes/_app.settings.$.tsx +1 -0
  105. package/corpus/templates/forms/app/routes/_app.team.tsx +1 -1
  106. package/corpus/templates/forms/shared/navigation.ts +1 -1
  107. package/corpus/templates/macros/app/routes/extensions._index.tsx +1 -1
  108. package/corpus/templates/macros/app/routes/settings.$.tsx +1 -0
  109. package/corpus/templates/macros/app/routes/team.tsx +1 -1
  110. package/corpus/templates/mail/app/routes/extensions._index.tsx +1 -1
  111. package/corpus/templates/mail/app/routes/settings.$.tsx +1 -0
  112. package/corpus/templates/plan/app/hooks/use-navigation-state.ts +1 -1
  113. package/corpus/templates/plan/app/routes/extensions._index.tsx +1 -1
  114. package/corpus/templates/plan/app/routes/settings.$.tsx +1 -0
  115. package/corpus/templates/plan/app/routes/team.tsx +1 -1
  116. package/corpus/templates/slides/.agents/skills/design-systems/SKILL.md +3 -1
  117. package/corpus/templates/slides/actions/analyze-brand-assets.ts +7 -7
  118. package/corpus/templates/slides/actions/import-from-url.ts +6 -6
  119. package/corpus/templates/slides/app/components/deck/DeckCard.tsx +2 -2
  120. package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +1 -1
  121. package/corpus/templates/slides/app/components/editor/PromptDialog.tsx +3 -0
  122. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +1 -1
  123. package/corpus/templates/slides/app/components/onboarding/FirstDeckOnboardingFlow.tsx +9 -3
  124. package/corpus/templates/slides/app/context/DeckContext.tsx +71 -20
  125. package/corpus/templates/slides/app/global.css +6 -0
  126. package/corpus/templates/slides/app/i18n/en-US.ts +5 -5
  127. package/corpus/templates/slides/app/lib/create-deck-generation.ts +9 -5
  128. package/corpus/templates/slides/app/pages/DeckEditor.tsx +17 -0
  129. package/corpus/templates/slides/app/pages/Index.tsx +16 -5
  130. package/corpus/templates/slides/app/routes/extensions._index.tsx +1 -1
  131. package/corpus/templates/slides/app/routes/settings.$.tsx +1 -0
  132. package/corpus/templates/slides/app/routes/team.tsx +1 -1
  133. package/corpus/templates/slides/changelog/2026-08-05-failed-deck-creation-and-reference-uploads-now-recover-clean.md +6 -0
  134. package/corpus/templates/slides/changelog/2026-08-05-new-presentation-prompts-now-clearly-describe-presentation-g.md +6 -0
  135. package/corpus/templates/slides/changelog/2026-08-05-slides-recover-completed-generation-results-after-a-sync-hic.md +6 -0
  136. package/corpus/templates/slides/changelog/2026-08-05-website-url-imports-now-capture-hydrated-browser-computed-st.md +6 -0
  137. package/corpus/templates/tasks/app/components/layout/Header.tsx +3 -1
  138. package/corpus/templates/tasks/app/routes/extensions._index.tsx +1 -1
  139. package/corpus/templates/tasks/app/routes/settings.$.tsx +1 -0
  140. package/dist/agent/engine/ai-sdk-engine.js +49 -3
  141. package/dist/agent/production-agent.d.ts +1 -0
  142. package/dist/agent/production-agent.js +8 -0
  143. package/dist/agent/types.d.ts +7 -0
  144. package/dist/client/AssistantChat.js +1 -1
  145. package/dist/client/agent-chat-adapter.d.ts +1 -1
  146. package/dist/client/agent-chat-adapter.js +30 -3
  147. package/dist/client/agent-page/AgentWorkspaceContent.d.ts +1 -1
  148. package/dist/client/agent-page/AgentWorkspaceContent.js +13 -13
  149. package/dist/client/chat/run-recovery.d.ts +2 -1
  150. package/dist/client/chat/run-recovery.js +14 -3
  151. package/dist/client/embed-auth.js +6 -2
  152. package/dist/client/error-format.d.ts +1 -0
  153. package/dist/client/error-format.js +16 -2
  154. package/dist/client/integrations/IntegrationsPanel.js +40 -6
  155. package/dist/client/org/OrgSwitcher.js +2 -2
  156. package/dist/client/org/TeamPage.js +92 -72
  157. package/dist/client/require-session.d.ts +1 -1
  158. package/dist/client/require-session.js +3 -3
  159. package/dist/client/resources/use-mcp-servers.d.ts +5 -0
  160. package/dist/client/resources/use-mcp-servers.js +36 -0
  161. package/dist/client/settings/AccountSettingsCard.js +40 -35
  162. package/dist/client/settings/AutomationsSection.js +1 -1
  163. package/dist/client/settings/DemoModeSection.d.ts +3 -1
  164. package/dist/client/settings/DemoModeSection.js +4 -1
  165. package/dist/client/settings/ExtensionsSettingsContent.js +1 -1
  166. package/dist/client/settings/SchedulingTimezoneField.d.ts +3 -1
  167. package/dist/client/settings/SchedulingTimezoneField.js +9 -5
  168. package/dist/client/settings/SettingsPanel.d.ts +4 -1
  169. package/dist/client/settings/SettingsPanel.js +21 -21
  170. package/dist/client/settings/SettingsTabsPage.d.ts +2 -2
  171. package/dist/client/settings/SettingsTabsPage.js +98 -45
  172. package/dist/client/settings/VoiceTranscriptionSection.js +6 -1
  173. package/dist/client/settings/useSettingsPanelController.js +47 -8
  174. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  175. package/dist/ingestion/website.d.ts +30 -0
  176. package/dist/localization/default-messages.d.ts +5 -0
  177. package/dist/localization/default-messages.js +5 -0
  178. package/dist/mcp-client/oauth-routes.js +1 -1
  179. package/dist/mcp-client/routes.d.ts +1 -0
  180. package/dist/mcp-client/routes.js +49 -0
  181. package/dist/navigation/index.js +16 -5
  182. package/dist/observability/routes.d.ts +3 -3
  183. package/dist/provider-api/actions/custom-provider-registration.d.ts +13 -13
  184. package/dist/provider-api/actions/provider-api.d.ts +11 -11
  185. package/dist/scripts/call-agent.js +11 -0
  186. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  187. package/dist/server/auth.js +9 -7
  188. package/dist/server/identity-sso.js +2 -1
  189. package/dist/server/onboarding-html.js +57 -34
  190. package/dist/server/realtime-token.d.ts +1 -1
  191. package/dist/server/transcribe-voice.d.ts +1 -1
  192. package/dist/server/workspace-provider-oauth.js +1 -1
  193. package/dist/setup-connections/onboarding.js +1 -1
  194. package/dist/shared/index.d.ts +1 -1
  195. package/dist/shared/index.js +1 -1
  196. package/dist/shared/sign-in-journey.d.ts +5 -2
  197. package/dist/shared/sign-in-journey.js +10 -4
  198. package/dist/templates/chat/app/components/layout/Layout.tsx +1 -1
  199. package/dist/templates/chat/app/hooks/use-navigation-state.ts +1 -1
  200. package/dist/templates/chat/app/root.tsx +1 -1
  201. package/dist/templates/chat/app/routes/agent.tsx +7 -7
  202. package/dist/templates/chat/app/routes/extensions._index.tsx +1 -1
  203. package/dist/templates/chat/app/routes/settings.$.tsx +1 -0
  204. package/dist/templates/chat/app/routes/team.tsx +1 -1
  205. package/dist/templates/workspace-core/.agents/skills/address-feedback/SKILL.md +37 -6
  206. package/dist/templates/workspace-core/.agents/skills/authentication/SKILL.md +4 -2
  207. package/docs/content/authentication.mdx +4 -5
  208. package/docs/content/template-analytics-developers.mdx +1 -1
  209. package/docs/content/template-calendar-developers.mdx +1 -1
  210. package/docs/content/template-chat-developers.mdx +2 -2
  211. package/docs/content/template-content-developers.mdx +1 -1
  212. package/docs/content/template-mail-developers.mdx +1 -1
  213. package/docs/content/template-plan-developers.mdx +1 -1
  214. package/docs/content/toolkit-command-navigation.mdx +6 -6
  215. package/docs/content/toolkit-org-team.mdx +1 -1
  216. package/docs/content/toolkit-settings.mdx +8 -8
  217. package/docs/content/toolkit-setup-connections.mdx +1 -1
  218. package/package.json +1 -1
  219. package/src/templates/chat/app/components/layout/Layout.tsx +1 -1
  220. package/src/templates/chat/app/hooks/use-navigation-state.ts +1 -1
  221. package/src/templates/chat/app/root.tsx +1 -1
  222. package/src/templates/chat/app/routes/agent.tsx +7 -7
  223. package/src/templates/chat/app/routes/extensions._index.tsx +1 -1
  224. package/src/templates/chat/app/routes/settings.$.tsx +1 -0
  225. package/src/templates/chat/app/routes/team.tsx +1 -1
  226. package/src/templates/workspace-core/.agents/skills/address-feedback/SKILL.md +37 -6
  227. package/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +4 -2
  228. package/corpus/templates/analytics/.agents/skills/dashboard-management/references/template-catalog-and-demo.md +0 -62
  229. package/corpus/templates/analytics/actions/install-dashboard-template.ts +0 -369
  230. package/corpus/templates/analytics/actions/list-dashboard-templates.ts +0 -47
  231. package/corpus/templates/analytics/actions/list-native-v2-dashboard-templates.ts +0 -34
  232. package/corpus/templates/analytics/app/routes/catalog.tsx +0 -240
  233. package/corpus/templates/analytics/changelog/2026-08-05-historical-analytics-rollups-stay-complete-and-atomic.md +0 -6
  234. package/corpus/templates/analytics/changelog/2026-08-05-historical-rollups-stay-complete-while-live-events-arrive.md +0 -6
  235. package/src/templates/default/actions/navigate.spec.ts +0 -53
@@ -1,9 +1,7 @@
1
1
  import {
2
2
  deferMigration,
3
3
  ensureAdditiveColumns,
4
- getDialect,
5
4
  getDbExec,
6
- isPostgres,
7
5
  runMigrations,
8
6
  } from "@agent-native/core/db";
9
7
  import { isInBackgroundFunctionRuntime } from "@agent-native/core/server";
@@ -12,7 +10,7 @@ import { isInBackgroundFunctionRuntime } from "@agent-native/core/server";
12
10
  // startup so the dashboard / analysis share actions know where to dispatch.
13
11
  import "../db/index.js";
14
12
  import * as schema from "../db/schema.js";
15
- import { FIRST_PARTY_ANALYTICS_ROLLUP_LOCK_KEY } from "../lib/first-party-analytics-rollups.js";
13
+ import { isHistoricalAnalyticsRollupBackfillComplete } from "../jobs/analytics-rollup-backfill.js";
16
14
  import { repairPersistedFirstPartyDashboardQueries } from "../lib/first-party-dashboard-repair.js";
17
15
 
18
16
  /**
@@ -33,182 +31,6 @@ function isDrizzleTable(value: unknown): value is object {
33
31
 
34
32
  const schemaTables = Object.values(schema).filter(isDrizzleTable);
35
33
 
36
- const ANALYTICS_ROLLUP_BACKFILL_STATEMENTS = {
37
- postgres: [
38
- `
39
- INSERT INTO analytics_event_daily_rollups (
40
- id, tenant_key, owner_email, org_id, event_date, event_name,
41
- app, template, event_count
42
- )
43
- SELECT
44
- md5(random()::text || clock_timestamp()::text), tenant_key,
45
- MIN(owner_email), MIN(org_id), event_date, event_name, app, template,
46
- COUNT(*)::INTEGER
47
- FROM (
48
- SELECT
49
- CASE
50
- WHEN org_id IS NOT NULL AND org_id <> '' THEN 'org:' || org_id
51
- ELSE 'user:' || owner_email
52
- END AS tenant_key,
53
- owner_email,
54
- org_id,
55
- COALESCE(NULLIF(event_date, ''), substr(timestamp, 1, 10)) AS event_date,
56
- event_name,
57
- COALESCE(app, '') AS app,
58
- COALESCE(template, '') AS template
59
- FROM analytics_events
60
- ) AS historical_events
61
- WHERE event_date <> ''
62
- GROUP BY tenant_key, event_date, event_name, app, template
63
- ON CONFLICT (tenant_key, event_date, event_name, app, template)
64
- DO UPDATE SET event_count = GREATEST(
65
- analytics_event_daily_rollups.event_count,
66
- EXCLUDED.event_count
67
- )
68
- `,
69
- `
70
- INSERT INTO analytics_user_days (
71
- id, tenant_key, owner_email, org_id, event_date, user_key
72
- )
73
- SELECT
74
- md5(random()::text || clock_timestamp()::text), tenant_key,
75
- owner_email, org_id, event_date, user_key
76
- FROM (
77
- SELECT DISTINCT
78
- CASE
79
- WHEN org_id IS NOT NULL AND org_id <> '' THEN 'org:' || org_id
80
- ELSE 'user:' || owner_email
81
- END AS tenant_key,
82
- owner_email,
83
- org_id,
84
- COALESCE(NULLIF(event_date, ''), substr(timestamp, 1, 10)) AS event_date,
85
- user_key
86
- FROM analytics_events
87
- WHERE user_key IS NOT NULL AND TRIM(user_key) <> ''
88
- ) AS historical_user_days
89
- WHERE event_date <> ''
90
- ON CONFLICT (tenant_key, event_date, user_key) DO NOTHING
91
- `,
92
- ],
93
- sqlite: [
94
- `
95
- INSERT INTO analytics_event_daily_rollups (
96
- id, tenant_key, owner_email, org_id, event_date, event_name,
97
- app, template, event_count
98
- )
99
- SELECT
100
- lower(hex(randomblob(16))), tenant_key, MIN(owner_email), MIN(org_id),
101
- event_date, event_name, app, template, COUNT(*)
102
- FROM (
103
- SELECT
104
- CASE
105
- WHEN org_id IS NOT NULL AND org_id <> '' THEN 'org:' || org_id
106
- ELSE 'user:' || owner_email
107
- END AS tenant_key,
108
- owner_email,
109
- org_id,
110
- COALESCE(NULLIF(event_date, ''), substr(timestamp, 1, 10)) AS event_date,
111
- event_name,
112
- COALESCE(app, '') AS app,
113
- COALESCE(template, '') AS template
114
- FROM analytics_events
115
- ) AS historical_events
116
- WHERE event_date <> ''
117
- GROUP BY tenant_key, event_date, event_name, app, template
118
- ON CONFLICT (tenant_key, event_date, event_name, app, template)
119
- DO UPDATE SET event_count = excluded.event_count
120
- `,
121
- `
122
- INSERT INTO analytics_user_days (
123
- id, tenant_key, owner_email, org_id, event_date, user_key
124
- )
125
- SELECT
126
- lower(hex(randomblob(16))), tenant_key, owner_email, org_id,
127
- event_date, user_key
128
- FROM (
129
- SELECT DISTINCT
130
- CASE
131
- WHEN org_id IS NOT NULL AND org_id <> '' THEN 'org:' || org_id
132
- ELSE 'user:' || owner_email
133
- END AS tenant_key,
134
- owner_email,
135
- org_id,
136
- COALESCE(NULLIF(event_date, ''), substr(timestamp, 1, 10)) AS event_date,
137
- user_key
138
- FROM analytics_events
139
- WHERE user_key IS NOT NULL AND TRIM(user_key) <> ''
140
- ) AS historical_user_days
141
- WHERE event_date <> ''
142
- ON CONFLICT (tenant_key, event_date, user_key) DO NOTHING
143
- `,
144
- ],
145
- } as const;
146
-
147
- async function runHistoricalAnalyticsRollupBackfill() {
148
- // Durable agent workers share this plugin bundle with the regular server,
149
- // but they must not become a second migration runner for an expensive
150
- // historical scan. The keep-warm/regular server path owns this work.
151
- if (isInBackgroundFunctionRuntime()) {
152
- console.info(
153
- "[db] Deferring historical Analytics rollup backfill from durable background runtime",
154
- );
155
- return deferMigration();
156
- }
157
-
158
- const db = getDbExec();
159
-
160
- if (isPostgres()) {
161
- if (!db.transaction) {
162
- throw new Error(
163
- "Analytics rollup backfill requires a Postgres transaction",
164
- );
165
- }
166
-
167
- const result = await db.transaction(async (tx) => {
168
- // Only one serverless instance should perform the expensive scan. A
169
- // A try-lock keeps competing cold starts from holding pooled Neon
170
- // connections while waiting for the winner. A lock loser defers without
171
- // recording the migration so a failed winner can be retried safely.
172
- // The monotonic rollup upsert below keeps a live increment from being
173
- // overwritten if ingest commits during the historical snapshot. Live
174
- // ingest takes this same lock before inserting its raw event.
175
- const lockResult = await tx.execute({
176
- sql: "SELECT pg_try_advisory_xact_lock(hashtextextended(?, 0::bigint)) AS acquired",
177
- args: [FIRST_PARTY_ANALYTICS_ROLLUP_LOCK_KEY],
178
- });
179
- const acquired = lockResult.rows[0]?.acquired;
180
- if (acquired !== true && acquired !== "t") {
181
- console.info(
182
- "[db] Deferring historical Analytics rollup backfill; another instance owns the advisory lock",
183
- );
184
- return deferMigration();
185
- }
186
- for (const statement of ANALYTICS_ROLLUP_BACKFILL_STATEMENTS.postgres) {
187
- await tx.execute(statement);
188
- }
189
- });
190
- return result;
191
- }
192
-
193
- const statements = ANALYTICS_ROLLUP_BACKFILL_STATEMENTS.sqlite;
194
- if (getDialect() === "d1") {
195
- if (!db.atomicBatch) {
196
- throw new Error("D1 analytics rollup backfill requires an atomic batch");
197
- }
198
- await db.atomicBatch(statements);
199
- return;
200
- }
201
-
202
- if (!db.transaction) {
203
- throw new Error(
204
- "Analytics rollup backfill requires a database transaction",
205
- );
206
- }
207
- await db.transaction(async (tx) => {
208
- for (const statement of statements) await tx.execute(statement);
209
- });
210
- }
211
-
212
34
  // Convention: every new migration below MUST set a unique `name:` slug (see
213
35
  // packages/core/src/db/migrations.ts for the full rationale). Version numbers
214
36
  // alone are not a safe identity across parallel branches that each extend
@@ -1548,14 +1370,56 @@ const runAnalyticsMigrations = runMigrations(
1548
1370
  name: "analytics-query-pressure-daily-key-idx",
1549
1371
  sql: `CREATE UNIQUE INDEX IF NOT EXISTS analytics_query_pressure_daily_key_idx ON analytics_query_pressure_daily (tenant_key, event_date, query_class)`,
1550
1372
  },
1551
- // Rebuild the compact analytics rollups from raw history once when the
1552
- // tables first ship. The run-only migration takes the same write lock as
1553
- // ingest so a live increment cannot be overwritten by its snapshot.
1373
+ // Keep this historical migration name reserved, but record it without a
1374
+ // boot-time run. Scanning analytics_events here makes every concurrent
1375
+ // serverless migration runner hold a database connection for the full
1376
+ // history; v126-v131 continue to maintain the compact tables incrementally.
1377
+ // Any future one-shot backfill must use a new migration identity or an
1378
+ // explicit out-of-band job because this marker is permanently applied.
1554
1379
  {
1555
1380
  version: 132,
1556
1381
  name: "analytics-rollups-historical-backfill",
1557
1382
  sql: {},
1558
- run: runHistoricalAnalyticsRollupBackfill,
1383
+ },
1384
+ {
1385
+ version: 133,
1386
+ name: "analytics-rollups-historical-backfill-state",
1387
+ sql: {
1388
+ postgres: `CREATE TABLE IF NOT EXISTS analytics_rollup_backfill_state (
1389
+ id TEXT PRIMARY KEY,
1390
+ status TEXT NOT NULL DEFAULT 'pending',
1391
+ completed_at TEXT,
1392
+ lease_token TEXT,
1393
+ lease_expires_at TEXT,
1394
+ updated_at TEXT NOT NULL DEFAULT (now()::text)
1395
+ )`,
1396
+ sqlite: `CREATE TABLE IF NOT EXISTS analytics_rollup_backfill_state (
1397
+ id TEXT PRIMARY KEY,
1398
+ status TEXT NOT NULL DEFAULT 'pending',
1399
+ completed_at TEXT,
1400
+ lease_token TEXT,
1401
+ lease_expires_at TEXT,
1402
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
1403
+ )`,
1404
+ },
1405
+ },
1406
+ {
1407
+ version: 134,
1408
+ name: "analytics-rollups-historical-backfill-repair",
1409
+ sql: {},
1410
+ run: async () => {
1411
+ if (!(await isHistoricalAnalyticsRollupBackfillComplete())) {
1412
+ return deferMigration();
1413
+ }
1414
+ },
1415
+ },
1416
+ {
1417
+ version: 135,
1418
+ name: "analytics-rollups-historical-backfill-lease",
1419
+ sql: `
1420
+ ALTER TABLE analytics_rollup_backfill_state ADD COLUMN IF NOT EXISTS lease_token TEXT;
1421
+ ALTER TABLE analytics_rollup_backfill_state ADD COLUMN IF NOT EXISTS lease_expires_at TEXT;
1422
+ `,
1559
1423
  },
1560
1424
  ],
1561
1425
  { table: "analytics_migrations" },
@@ -1572,10 +1436,17 @@ const runAnalyticsMigrations = runMigrations(
1572
1436
  * swallowed so it can never fail boot.
1573
1437
  */
1574
1438
  export default async (nitroApp: any): Promise<void> => {
1575
- if (isInBackgroundFunctionRuntime()) {
1576
- // Durable workers execute signed internal routes against a schema owned by
1577
- // the regular server. A second migration runner only adds a Neon pool
1578
- // probe to every worker cold start.
1439
+ const isScheduledRollupRuntime =
1440
+ (
1441
+ globalThis as typeof globalThis & {
1442
+ __AGENT_NATIVE_ANALYTICS_ROLLUP_BACKFILL_SCHEDULED_RUNTIME__?: boolean;
1443
+ }
1444
+ ).__AGENT_NATIVE_ANALYTICS_ROLLUP_BACKFILL_SCHEDULED_RUNTIME__ === true;
1445
+ if (isInBackgroundFunctionRuntime() && !isScheduledRollupRuntime) {
1446
+ // Most durable workers execute signed internal routes against a schema
1447
+ // owned by the regular server. A second migration runner only adds a Neon
1448
+ // pool probe to every worker cold start. The scheduled rollup worker is
1449
+ // the exception because it can be the first post-deploy invocation.
1579
1450
  console.info(
1580
1451
  "[db] Skipping Analytics migrations in durable background runtime",
1581
1452
  );
@@ -0,0 +1,53 @@
1
+ import { timingSafeEqual } from "node:crypto";
2
+
3
+ import { createError, defineEventHandler, getHeader } from "h3";
4
+
5
+ // guard:allow-action-twin — external cron endpoint authenticates scheduled callers before invoking the rollup job.
6
+ import { runAnalyticsRollupBackfillOnce } from "../../../jobs/analytics-rollup-backfill";
7
+
8
+ declare global {
9
+ var __AGENT_NATIVE_ANALYTICS_ROLLUP_BACKFILL_SCHEDULED_RUNTIME__:
10
+ | boolean
11
+ | undefined;
12
+ }
13
+
14
+ function scheduledFunctionRuntime(): boolean {
15
+ return (
16
+ globalThis.__AGENT_NATIVE_ANALYTICS_ROLLUP_BACKFILL_SCHEDULED_RUNTIME__ ===
17
+ true
18
+ );
19
+ }
20
+
21
+ function cronSecret(): string | null {
22
+ const secret = process.env.ANALYTICS_ROLLUP_BACKFILL_CRON_SECRET?.trim();
23
+ return secret ? secret : null;
24
+ }
25
+
26
+ function headerMatchesSecret(
27
+ header: string | undefined,
28
+ secret: string,
29
+ ): boolean {
30
+ const expected = `Bearer ${secret}`;
31
+ const value = header?.trim() ?? "";
32
+ if (value.length !== expected.length) return false;
33
+ return timingSafeEqual(Buffer.from(value), Buffer.from(expected));
34
+ }
35
+
36
+ export default defineEventHandler(async (event) => {
37
+ const secret = cronSecret();
38
+ const scheduledRuntime = scheduledFunctionRuntime();
39
+ const authenticatedHeader =
40
+ secret !== null &&
41
+ headerMatchesSecret(getHeader(event, "authorization"), secret);
42
+ if (!scheduledRuntime && !authenticatedHeader) {
43
+ throw createError({
44
+ statusCode: secret ? 401 : 503,
45
+ statusMessage: secret
46
+ ? "Unauthorized"
47
+ : "ANALYTICS_ROLLUP_BACKFILL_CRON_SECRET is required",
48
+ });
49
+ }
50
+
51
+ const result = await runAnalyticsRollupBackfillOnce();
52
+ return { ok: true, ...result };
53
+ });
@@ -27,8 +27,9 @@ or generated image/video assets that another app can reference by ID and URL.
27
27
  Pass `mediaType: "image"` by default, or `mediaType: "video"` for video
28
28
  libraries.
29
29
  - Use unattended actions when the agent already knows what to do:
30
- `search-assets`, `list-assets`, `generate-image`, `generate-image-batch`,
31
- `generate-video`, `refresh-generation-run`, and `export-asset`.
30
+ `search-assets`, `list-assets`, `import-style-from-url`, `generate-image`,
31
+ `generate-image-batch`, `generate-video`, `refresh-generation-run`, and
32
+ `export-asset`.
32
33
  - In chat, consume composer `@` references as structured generation inputs:
33
34
  `brand-kit` maps to `libraryId`, `preset` maps to `presetId`, and
34
35
  `media-type` chooses image generation versus video generation. If no mention
@@ -94,10 +95,11 @@ or generated image/video assets that another app can reference by ID and URL.
94
95
  when they want a chat preloaded with the session context.
95
96
 
96
97
  For short vague prompts, enhance conservatively with library style context while
97
- preserving the user's original prompt in run metadata. Use
98
- `analyze-collection-style` when a collection needs upgraded vision brand
99
- analysis before generation. Brand QA scoring and best-of-N selection are
100
- deferred.
98
+ preserving the user's original prompt in run metadata. If a public website is
99
+ the style source, call `import-style-from-url` first so the library keeps the
100
+ hydrated browser-derived design brief. Use `analyze-collection-style` when a
101
+ collection needs upgraded vision brand analysis from image references before
102
+ generation. Brand QA scoring and best-of-N selection are deferred.
101
103
 
102
104
  ## Video Workflows
103
105
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: library-management
3
- description: Schema, CRUD, sharing, and cascade-delete patterns for asset libraries, collections, and assets.
3
+ description: Schema, CRUD, sharing, URL style imports, and cascade-delete patterns for asset libraries, collections, and assets. Use when managing a library or importing visual brand evidence from a URL.
4
4
  ---
5
5
 
6
6
  # Library management
@@ -118,6 +118,21 @@ for the exact logo. Imported assets are stored as `status: "reference"` with
118
118
  `sourceUrl` provenance, so downstream generation, preset boards, and logo
119
119
  compositing can use them like uploaded reference assets.
120
120
 
121
+ ## Importing a rendered visual system
122
+
123
+ Use `import-style-from-url` when a public website should contribute more than
124
+ images to a library or collection. It uses the same layered browser extractor
125
+ as Design and Slides, so CSS-in-JS, Tailwind, hydration, web fonts, computed
126
+ colors, component styles, spacing, radii, shadows, CSS variables, and logo
127
+ references are captured from the live cascade. The action persists a bounded
128
+ `designMd` summary plus structured fields in `styleBrief`, with the source URL
129
+ and any browser/static-fallback warnings preserved for provenance.
130
+
131
+ Prefer this action over copying raw HTML into a style brief. If the result is
132
+ `partial`, the structured values are still usable but the warnings must remain
133
+ visible to the agent; `failed` is an error and must not be treated as an empty
134
+ style brief.
135
+
121
136
  ## When to add a collection
122
137
 
123
138
  Collections are optional. Most users won't create them. Use them when:
@@ -84,6 +84,7 @@ Uncommon actions stay discoverable through `tool-search`.
84
84
  | `duplicate-library` | Make a private copy of a Brand Kit |
85
85
  | `list-assets` / `search-assets` | Browse or search assets in accessible kits |
86
86
  | `import-asset-from-url` | Ingest external brand or blog imagery as a reference asset |
87
+ | `import-style-from-url` | Render a website and merge its design.md-style visual language into a library or collection |
87
88
  | `set-canonical-logo` | Pin the kit's pixel-perfect logo |
88
89
  | `list-generation-presets` | List repeatable output formats before generating |
89
90
  | `create-generation-preset` / `update-generation-preset` | Author preset format, references, `includeLogo`, skeleton |
@@ -97,6 +97,18 @@ export function assetUrls(asset: {
97
97
  };
98
98
  }
99
99
 
100
+ export function imageArtifactLinks(input: {
101
+ id: string;
102
+ runId: string;
103
+ previewUrl: string;
104
+ downloadUrl: string;
105
+ }) {
106
+ return [
107
+ `previewUrl: ${input.previewUrl} (ID: ${input.id}, Run: ${input.runId})`,
108
+ `downloadUrl: ${input.downloadUrl}`,
109
+ ];
110
+ }
111
+
100
112
  export function serializeLibrary(row: any) {
101
113
  return {
102
114
  id: row.id,
@@ -69,6 +69,7 @@ import {
69
69
  type StyleBrief,
70
70
  } from "../shared/api.js";
71
71
  import {
72
+ imageArtifactLinks,
72
73
  requireGenerationSessionInLibrary,
73
74
  serializeAsset,
74
75
  } from "./_helpers.js";
@@ -1109,9 +1110,14 @@ export default defineAction({
1109
1110
  ...serialized,
1110
1111
  runId,
1111
1112
  artifactType: "image",
1112
- Artifacts: [
1113
- `Image: ${serialized.url} (ID: ${asset.id}, Run: ${runId})`,
1114
- ],
1113
+ // Cross-app callers embed the artifact in HTML. `url` is the Assets
1114
+ // detail page; previewUrl is the actual media response.
1115
+ Artifacts: imageArtifactLinks({
1116
+ id: asset.id,
1117
+ runId,
1118
+ previewUrl: serialized.previewUrl,
1119
+ downloadUrl: serialized.downloadUrl,
1120
+ }),
1115
1121
  ...creativeContextProvenance,
1116
1122
  };
1117
1123
  } catch (err) {
@@ -0,0 +1,115 @@
1
+ import { defineAction } from "@agent-native/core";
2
+ import { assertAccess } from "@agent-native/core/sharing";
3
+ import {
4
+ extractRenderedDesignSystemFromUrl,
5
+ styleBriefFromRenderedDesign,
6
+ } from "@agent-native/creative-context/server";
7
+ import { eq } from "drizzle-orm";
8
+ import { z } from "zod";
9
+
10
+ import { getDb, schema } from "../server/db/index.js";
11
+ import { nowIso, parseJson, stringifyJson } from "../server/lib/json.js";
12
+ import type { StyleBrief } from "../shared/api.js";
13
+
14
+ /**
15
+ * Render a public website once and merge its computed visual language into an
16
+ * Assets library or collection style brief. This intentionally shares the
17
+ * exact browser extractor used by Design and Slides instead of maintaining a
18
+ * second image-only or static-HTML interpretation of the page.
19
+ */
20
+ export default defineAction({
21
+ description:
22
+ "Render a website in a real browser and merge its design.md-style visual " +
23
+ "language into an asset library or collection style brief. Captures " +
24
+ "computed colors, typography, spacing, radii, shadows, components, CSS " +
25
+ "variables, and logo references. Uses a static SSRF-safe fallback only " +
26
+ "when browser rendering is unavailable.",
27
+ schema: z.object({
28
+ libraryId: z.string().describe("Asset library ID"),
29
+ collectionId: z
30
+ .string()
31
+ .optional()
32
+ .describe("Optional collection to update instead of the library"),
33
+ url: z.string().describe("Public website URL to render and analyze"),
34
+ }),
35
+ run: async ({ libraryId, collectionId, url }) => {
36
+ await assertAccess("asset-library", libraryId, "editor");
37
+ const db = getDb();
38
+ const [library] = await db
39
+ .select()
40
+ .from(schema.assetLibraries)
41
+ .where(eq(schema.assetLibraries.id, libraryId))
42
+ .limit(1);
43
+ if (!library) throw new Error("Asset library not found.");
44
+
45
+ const [collection] = collectionId
46
+ ? await db
47
+ .select()
48
+ .from(schema.assetCollections)
49
+ .where(eq(schema.assetCollections.id, collectionId))
50
+ .limit(1)
51
+ : [null];
52
+ if (collectionId && !collection) throw new Error("Collection not found.");
53
+ if (collection && collection.libraryId !== libraryId) {
54
+ throw new Error("Collection does not belong to this asset library.");
55
+ }
56
+
57
+ const extraction = await extractRenderedDesignSystemFromUrl(url);
58
+ if (extraction.status === "failed") {
59
+ throw new Error(
60
+ `Rendered website extraction failed: ${extraction.error ?? "unknown error"}`,
61
+ );
62
+ }
63
+
64
+ const previous = parseJson<StyleBrief>(
65
+ collection?.styleBrief ?? library.styleBrief,
66
+ {},
67
+ );
68
+ const styleBrief = {
69
+ ...previous,
70
+ ...styleBriefFromRenderedDesign(extraction),
71
+ } satisfies StyleBrief;
72
+ const updatedAt = nowIso();
73
+
74
+ if (collection) {
75
+ await db
76
+ .update(schema.assetCollections)
77
+ .set({ styleBrief: stringifyJson(styleBrief), updatedAt })
78
+ .where(eq(schema.assetCollections.id, collection.id));
79
+ } else {
80
+ const settings = parseJson<Record<string, unknown>>(library.settings, {});
81
+ settings.brandAnalysis = {
82
+ ...(typeof settings.brandAnalysis === "object" &&
83
+ settings.brandAnalysis !== null
84
+ ? settings.brandAnalysis
85
+ : {}),
86
+ analyzedAt: updatedAt,
87
+ sourceUrl: extraction.finalUrl ?? extraction.url,
88
+ mode: extraction.rendered ? "rendered-browser" : "static-fallback",
89
+ method: extraction.method,
90
+ status: extraction.status,
91
+ warnings: extraction.warnings,
92
+ };
93
+ await db
94
+ .update(schema.assetLibraries)
95
+ .set({
96
+ styleBrief: stringifyJson(styleBrief),
97
+ settings: stringifyJson(settings),
98
+ updatedAt,
99
+ })
100
+ .where(eq(schema.assetLibraries.id, libraryId));
101
+ }
102
+
103
+ return {
104
+ libraryId,
105
+ collectionId: collection?.id ?? null,
106
+ status: extraction.status,
107
+ rendered: extraction.rendered,
108
+ sourceUrl: extraction.finalUrl ?? extraction.url,
109
+ styleBrief,
110
+ designMd: extraction.designMd,
111
+ warnings: extraction.warnings,
112
+ diagnostics: extraction.diagnostics,
113
+ };
114
+ },
115
+ });
@@ -109,7 +109,9 @@ function navigationFromPath(pathname: string, search = "") {
109
109
  const extension = pathname.match(/^\/extensions\/([^/]+)/);
110
110
  if (extension) return { view: "extensions", extensionId: extension[1] };
111
111
  if (pathname === "/audit") return { view: "audit" };
112
- if (pathname === "/settings") return { view: "settings" };
112
+ if (pathname === "/settings" || pathname.startsWith("/settings/")) {
113
+ return { view: "settings" };
114
+ }
113
115
  return { view: "create" };
114
116
  }
115
117
 
@@ -7,5 +7,5 @@ export function meta() {
7
7
  }
8
8
 
9
9
  export default function ExtensionsRoute() {
10
- return <Navigate to="/settings#extensions" replace />;
10
+ return <Navigate to="/settings/extensions" replace />;
11
11
  }
@@ -0,0 +1 @@
1
+ export { default, meta } from "./settings.js";
@@ -61,10 +61,15 @@ import {
61
61
  } from "@/components/ui/select";
62
62
  import { Switch } from "@/components/ui/switch";
63
63
  import { useAssetsPrefs } from "@/hooks/use-assets-prefs";
64
+ import { messagesByLocale } from "@/i18n-data";
64
65
  import { cn } from "@/lib/utils";
65
66
 
66
67
  import changelog from "../../CHANGELOG.md?raw";
67
68
 
69
+ export function meta() {
70
+ return [{ title: messagesByLocale["en-US"].settings.title }];
71
+ }
72
+
68
73
  type ImageGenerationConfig = {
69
74
  builderEnabled?: boolean;
70
75
  builderConnected?: boolean;
@@ -7,5 +7,5 @@ export function meta() {
7
7
  }
8
8
 
9
9
  export default function TeamRoute() {
10
- return <Navigate to="/settings#organization" replace />;
10
+ return <Navigate to="/settings/organization" replace />;
11
11
  }
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: added
3
+ date: 2026-08-05
4
+ ---
5
+
6
+ Brand kits can import a website's hydrated visual language and reuse it in generated imagery.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-08-05
4
+ ---
5
+
6
+ Generated images now hand off embeddable previews to other workspace apps.