@agent-native/core 0.145.5 → 0.145.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 (393) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/actions/SKILL.md +18 -0
  3. package/corpus/templates/analytics/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  4. package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +17 -1
  5. package/corpus/templates/analytics/.agents/skills/delegate-to-agent/SKILL.md +45 -47
  6. package/corpus/templates/analytics/.agents/skills/frontend-design/SKILL.md +1 -0
  7. package/corpus/templates/analytics/.agents/skills/gong/SKILL.md +22 -3
  8. package/corpus/templates/analytics/.agents/skills/turn-into-app/SKILL.md +76 -23
  9. package/corpus/templates/analytics/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  10. package/corpus/templates/analytics/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  11. package/corpus/templates/analytics/AGENTS.md +2 -1
  12. package/corpus/templates/analytics/actions/account-deep-dive.ts +83 -47
  13. package/corpus/templates/analytics/actions/create-dashboard-folder.ts +27 -0
  14. package/corpus/templates/analytics/actions/gong-calls.ts +3 -3
  15. package/corpus/templates/analytics/actions/list-dashboard-folders.ts +26 -0
  16. package/corpus/templates/analytics/actions/list-sql-dashboards.ts +1 -0
  17. package/corpus/templates/analytics/actions/set-dashboard-folder.ts +26 -0
  18. package/corpus/templates/analytics/app/components/ProviderCorpusJobNotifier.tsx +18 -3
  19. package/corpus/templates/analytics/app/components/layout/CommandPalette.tsx +6 -4
  20. package/corpus/templates/analytics/app/components/layout/Header.tsx +1 -0
  21. package/corpus/templates/analytics/app/components/layout/NewDashboardDialog.tsx +12 -2
  22. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +35 -20
  23. package/corpus/templates/analytics/app/global.css +17 -0
  24. package/corpus/templates/analytics/app/hooks/use-dashboard-revisions.ts +29 -15
  25. package/corpus/templates/analytics/app/hooks/use-dashboard-views.ts +15 -3
  26. package/corpus/templates/analytics/app/i18n-data.ts +296 -0
  27. package/corpus/templates/analytics/app/lib/prefetch-keys.ts +23 -2
  28. package/corpus/templates/analytics/app/pages/DashboardOverview.tsx +642 -0
  29. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +66 -31
  30. package/corpus/templates/analytics/app/root.tsx +4 -0
  31. package/corpus/templates/analytics/app/routes/dashboards._index.tsx +5 -1
  32. package/corpus/templates/analytics/changelog/2026-08-07-added-a-dashboards-overview-with-personal-and-shared-folders.md +6 -0
  33. package/corpus/templates/analytics/changelog/2026-08-07-analytics-no-longer-stalls-under-bursts-of-event-ingest-key-.md +6 -0
  34. package/corpus/templates/analytics/changelog/2026-08-07-analytics-tracking-and-chat-startup-no-longer-emit-avoidable.md +6 -0
  35. package/corpus/templates/analytics/changelog/2026-08-07-dashboard-panels-now-load-custom-api-data-programs-reliably.md +6 -0
  36. package/corpus/templates/analytics/changelog/2026-08-07-gong-searches-conserve-api-quota-and-report-incomplete-coverage.md +6 -0
  37. package/corpus/templates/analytics/changelog/2026-08-07-searchable-dashboards-make-personal-and-shared-folders-easie.md +6 -0
  38. package/corpus/templates/analytics/netlify.toml +3 -0
  39. package/corpus/templates/analytics/scripts/emit-netlify-dashboard-report-cron.ts +112 -0
  40. package/corpus/templates/analytics/server/db/index.ts +12 -0
  41. package/corpus/templates/analytics/server/db/schema.ts +14 -0
  42. package/corpus/templates/analytics/server/jobs/analytics-rollup-backfill.ts +4 -0
  43. package/corpus/templates/analytics/server/lib/agent-chat-plan-mode.ts +1 -0
  44. package/corpus/templates/analytics/server/lib/analytics-query-catalog.ts +85 -15
  45. package/corpus/templates/analytics/server/lib/dashboard-folders-store.ts +168 -0
  46. package/corpus/templates/analytics/server/lib/dashboard-panel-query.ts +9 -4
  47. package/corpus/templates/analytics/server/lib/dashboards-store.ts +116 -1
  48. package/corpus/templates/analytics/server/lib/first-party-analytics-rollups.ts +4 -8
  49. package/corpus/templates/analytics/server/lib/first-party-analytics.ts +65 -10
  50. package/corpus/templates/analytics/server/lib/gong.ts +139 -78
  51. package/corpus/templates/analytics/server/lib/session-replay.ts +5 -5
  52. package/corpus/templates/analytics/server/plugins/db.ts +55 -15
  53. package/corpus/templates/analytics/server/plugins/session-replay-retention-jobs.ts +6 -2
  54. package/corpus/templates/analytics/server/routes/api/session-replay/retention.post.ts +65 -0
  55. package/corpus/templates/assets/.agents/skills/actions/SKILL.md +18 -0
  56. package/corpus/templates/assets/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  57. package/corpus/templates/assets/.agents/skills/delegate-to-agent/SKILL.md +45 -47
  58. package/corpus/templates/assets/.agents/skills/frontend-design/SKILL.md +1 -0
  59. package/corpus/templates/assets/.agents/skills/turn-into-app/SKILL.md +76 -23
  60. package/corpus/templates/assets/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  61. package/corpus/templates/assets/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  62. package/corpus/templates/brain/.agents/skills/actions/SKILL.md +18 -0
  63. package/corpus/templates/brain/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  64. package/corpus/templates/brain/.agents/skills/delegate-to-agent/SKILL.md +45 -47
  65. package/corpus/templates/brain/.agents/skills/frontend-design/SKILL.md +1 -0
  66. package/corpus/templates/brain/.agents/skills/turn-into-app/SKILL.md +76 -23
  67. package/corpus/templates/brain/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  68. package/corpus/templates/brain/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  69. package/corpus/templates/calendar/.agents/skills/actions/SKILL.md +18 -0
  70. package/corpus/templates/calendar/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  71. package/corpus/templates/calendar/.agents/skills/delegate-to-agent/SKILL.md +45 -47
  72. package/corpus/templates/calendar/.agents/skills/frontend-design/SKILL.md +1 -0
  73. package/corpus/templates/calendar/.agents/skills/turn-into-app/SKILL.md +76 -23
  74. package/corpus/templates/calendar/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  75. package/corpus/templates/calendar/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  76. package/corpus/templates/chat/.agents/skills/actions/SKILL.md +18 -0
  77. package/corpus/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  78. package/corpus/templates/chat/.agents/skills/delegate-to-agent/SKILL.md +45 -47
  79. package/corpus/templates/chat/.agents/skills/frontend-design/SKILL.md +1 -0
  80. package/corpus/templates/chat/.agents/skills/turn-into-app/SKILL.md +76 -23
  81. package/corpus/templates/chat/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  82. package/corpus/templates/chat/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  83. package/corpus/templates/chat/.env.example +3 -0
  84. package/corpus/templates/chat/AGENTS.md +4 -0
  85. package/corpus/templates/clips/.agents/skills/actions/SKILL.md +18 -0
  86. package/corpus/templates/clips/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  87. package/corpus/templates/clips/.agents/skills/delegate-to-agent/SKILL.md +45 -47
  88. package/corpus/templates/clips/.agents/skills/frontend-design/SKILL.md +1 -0
  89. package/corpus/templates/clips/.agents/skills/turn-into-app/SKILL.md +76 -23
  90. package/corpus/templates/clips/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  91. package/corpus/templates/clips/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  92. package/corpus/templates/clips/actions/lib/audio-only-transcription.ts +4 -0
  93. package/corpus/templates/clips/actions/request-transcript.ts +52 -3
  94. package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +3 -10
  95. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +1 -0
  96. package/corpus/templates/clips/changelog/2026-08-07-screen-recordings-now-capture-tab-and-system-audio-even-when.md +6 -0
  97. package/corpus/templates/clips/changelog/2026-08-07-transcription-now-retries-transient-cloud-failures-automatic.md +6 -0
  98. package/corpus/templates/clips/server/db/schema.ts +7 -0
  99. package/corpus/templates/clips/server/plugins/db.ts +8 -0
  100. package/corpus/templates/clips/shared/recording-capture.ts +38 -0
  101. package/corpus/templates/clips/shared/transcript-failure.ts +89 -0
  102. package/corpus/templates/content/.agents/skills/actions/SKILL.md +18 -0
  103. package/corpus/templates/content/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  104. package/corpus/templates/content/.agents/skills/delegate-to-agent/SKILL.md +45 -47
  105. package/corpus/templates/content/.agents/skills/frontend-design/SKILL.md +1 -0
  106. package/corpus/templates/content/.agents/skills/turn-into-app/SKILL.md +76 -23
  107. package/corpus/templates/content/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  108. package/corpus/templates/content/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  109. package/corpus/templates/content/.env.example +9 -3
  110. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +1 -0
  111. package/corpus/templates/content/changelog/2026-08-07-collapsed-sidebar-utility-buttons-stay-anchored-at-the-botto.md +6 -0
  112. package/corpus/templates/crm/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  113. package/corpus/templates/crm/.agents/skills/turn-into-app/SKILL.md +76 -23
  114. package/corpus/templates/crm/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  115. package/corpus/templates/crm/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  116. package/corpus/templates/design/.agents/skills/actions/SKILL.md +18 -0
  117. package/corpus/templates/design/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  118. package/corpus/templates/design/.agents/skills/delegate-to-agent/SKILL.md +45 -47
  119. package/corpus/templates/design/.agents/skills/frontend-design/SKILL.md +1 -0
  120. package/corpus/templates/design/.agents/skills/turn-into-app/SKILL.md +76 -23
  121. package/corpus/templates/design/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  122. package/corpus/templates/design/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  123. package/corpus/templates/dispatch/.agents/skills/actions/SKILL.md +18 -0
  124. package/corpus/templates/dispatch/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  125. package/corpus/templates/dispatch/.agents/skills/delegate-to-agent/SKILL.md +45 -47
  126. package/corpus/templates/dispatch/.agents/skills/frontend-design/SKILL.md +1 -0
  127. package/corpus/templates/dispatch/.agents/skills/turn-into-app/SKILL.md +76 -23
  128. package/corpus/templates/dispatch/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  129. package/corpus/templates/dispatch/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  130. package/corpus/templates/factory/.agents/skills/actions/SKILL.md +18 -0
  131. package/corpus/templates/factory/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  132. package/corpus/templates/factory/.agents/skills/delegate-to-agent/SKILL.md +45 -47
  133. package/corpus/templates/factory/.agents/skills/frontend-design/SKILL.md +1 -0
  134. package/corpus/templates/factory/.agents/skills/turn-into-app/SKILL.md +76 -23
  135. package/corpus/templates/factory/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  136. package/corpus/templates/factory/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  137. package/corpus/templates/factory/actions/babysit-agent-native-pull-request.ts +279 -0
  138. package/corpus/templates/factory/actions/get-factory-automation-health.ts +23 -4
  139. package/corpus/templates/factory/app/components/factory/FactoryCanvas.tsx +1 -1
  140. package/corpus/templates/factory/app/routes/factory.tsx +21 -3
  141. package/corpus/templates/factory/changelog/2026-08-07-factory-can-keep-builder-bot-pull-requests-moving-by-request.md +6 -0
  142. package/corpus/templates/factory/server/lib/require-factory-automation.ts +1 -0
  143. package/corpus/templates/factory/server/plugins/agent-chat.ts +4 -1
  144. package/corpus/templates/factory/server/plugins/factory-scheduler-job.ts +81 -12
  145. package/corpus/templates/factory/server/triage/github-client.ts +31 -0
  146. package/corpus/templates/factory/server/triage/pr-babysit.ts +75 -1
  147. package/corpus/templates/forms/.agents/skills/actions/SKILL.md +18 -0
  148. package/corpus/templates/forms/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  149. package/corpus/templates/forms/.agents/skills/delegate-to-agent/SKILL.md +45 -47
  150. package/corpus/templates/forms/.agents/skills/frontend-design/SKILL.md +1 -0
  151. package/corpus/templates/forms/.agents/skills/turn-into-app/SKILL.md +76 -23
  152. package/corpus/templates/forms/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  153. package/corpus/templates/forms/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  154. package/corpus/templates/macros/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  155. package/corpus/templates/macros/.agents/skills/frontend-design/SKILL.md +1 -0
  156. package/corpus/templates/macros/.agents/skills/turn-into-app/SKILL.md +76 -23
  157. package/corpus/templates/macros/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  158. package/corpus/templates/macros/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  159. package/corpus/templates/mail/.agents/skills/actions/SKILL.md +18 -0
  160. package/corpus/templates/mail/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  161. package/corpus/templates/mail/.agents/skills/delegate-to-agent/SKILL.md +45 -47
  162. package/corpus/templates/mail/.agents/skills/frontend-design/SKILL.md +1 -0
  163. package/corpus/templates/mail/.agents/skills/turn-into-app/SKILL.md +76 -23
  164. package/corpus/templates/mail/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  165. package/corpus/templates/mail/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  166. package/corpus/templates/plan/.agents/skills/actions/SKILL.md +18 -0
  167. package/corpus/templates/plan/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  168. package/corpus/templates/plan/.agents/skills/delegate-to-agent/SKILL.md +45 -47
  169. package/corpus/templates/plan/.agents/skills/frontend-design/SKILL.md +1 -0
  170. package/corpus/templates/plan/.agents/skills/turn-into-app/SKILL.md +76 -23
  171. package/corpus/templates/plan/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  172. package/corpus/templates/plan/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  173. package/corpus/templates/plan/app/pages/PlansPage.tsx +2 -1
  174. package/corpus/templates/plan/changelog/2026-08-07-inline-help-icons-now-stay-visually-balanced-with-nearby-tex.md +6 -0
  175. package/corpus/templates/slides/.agents/skills/actions/SKILL.md +18 -0
  176. package/corpus/templates/slides/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  177. package/corpus/templates/slides/.agents/skills/delegate-to-agent/SKILL.md +45 -47
  178. package/corpus/templates/slides/.agents/skills/frontend-design/SKILL.md +1 -0
  179. package/corpus/templates/slides/.agents/skills/turn-into-app/SKILL.md +76 -23
  180. package/corpus/templates/slides/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  181. package/corpus/templates/slides/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  182. package/corpus/templates/slides/.env.example +5 -0
  183. package/corpus/templates/slides/changelog/2026-08-07-fixed-generated-slides-briefly-disappearing-while-they-are-b.md +6 -0
  184. package/corpus/templates/slides/server/plugins/collab.ts +20 -8
  185. package/corpus/templates/tasks/.agents/skills/actions/SKILL.md +18 -0
  186. package/corpus/templates/tasks/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  187. package/corpus/templates/tasks/.agents/skills/delegate-to-agent/SKILL.md +45 -47
  188. package/corpus/templates/tasks/.agents/skills/frontend-design/SKILL.md +1 -0
  189. package/corpus/templates/tasks/.agents/skills/turn-into-app/SKILL.md +76 -23
  190. package/corpus/templates/tasks/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  191. package/corpus/templates/tasks/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  192. package/corpus/templates/tasks/.env.example +3 -0
  193. package/dist/agent/production-agent.js +24 -3
  194. package/dist/client/AgentPanel.d.ts +6 -0
  195. package/dist/client/AgentPanel.js +176 -30
  196. package/dist/client/AssistantChat.d.ts +2 -0
  197. package/dist/client/AssistantChat.js +4 -1
  198. package/dist/client/ConnectBuilderCard.js +6 -11
  199. package/dist/client/MultiTabAssistantChat.js +1 -1
  200. package/dist/client/RuntimeConfigNotice.d.ts +1 -0
  201. package/dist/client/RuntimeConfigNotice.js +94 -0
  202. package/dist/client/agent-chat/index.d.ts +1 -1
  203. package/dist/client/agent-chat/index.js +1 -1
  204. package/dist/client/agent-chat-adapter.js +85 -4
  205. package/dist/client/agent-page/AgentTabsPage.js +1 -1
  206. package/dist/client/agent-sidebar-state.d.ts +6 -0
  207. package/dist/client/agent-sidebar-state.js +25 -0
  208. package/dist/client/app-providers.js +2 -1
  209. package/dist/client/assistant-ui-recovery.js +1 -1
  210. package/dist/client/chat/index.d.ts +1 -1
  211. package/dist/client/chat/index.js +1 -1
  212. package/dist/client/chat/markdown-renderer.d.ts +2 -1
  213. package/dist/client/chat/markdown-renderer.js +48 -2
  214. package/dist/client/chat/message-components.d.ts +6 -0
  215. package/dist/client/chat/message-components.js +182 -53
  216. package/dist/client/chat/tool-call-display.d.ts +16 -4
  217. package/dist/client/chat/tool-call-display.js +225 -18
  218. package/dist/client/extensions/ExtensionsSidebarSection.js +1 -1
  219. package/dist/client/frame-protocol.d.ts +4 -0
  220. package/dist/client/i18n.js +9 -0
  221. package/dist/client/integrations/IntegrationsPanel.js +2 -5
  222. package/dist/client/onboarding/FirstRunOnboarding.js +2 -3
  223. package/dist/client/org/TeamPage.js +49 -6
  224. package/dist/client/org/hooks.d.ts +7 -2
  225. package/dist/client/org/hooks.js +11 -4
  226. package/dist/client/resources/McpConnectionSuggestion.js +2 -3
  227. package/dist/client/resources/McpIntegrationDialog.js +2 -16
  228. package/dist/client/resources/McpIntegrationLogo.d.ts +11 -0
  229. package/dist/client/resources/McpIntegrationLogo.js +14 -0
  230. package/dist/client/resources/ResourceTree.js +1 -1
  231. package/dist/client/resources/mcp-integration-logos.d.ts +1 -0
  232. package/dist/client/resources/mcp-integration-logos.js +47 -18
  233. package/dist/client/settings/useBuilderStatus.js +6 -0
  234. package/dist/client/ui/index.d.ts +1 -0
  235. package/dist/client/ui/index.js +1 -0
  236. package/dist/client/use-db-sync.js +4 -0
  237. package/dist/collab/struct-routes.d.ts +1 -1
  238. package/dist/config.d.ts +26 -3
  239. package/dist/config.js +106 -14
  240. package/dist/db/ddl-guard.d.ts +15 -2
  241. package/dist/db/ddl-guard.js +53 -5
  242. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  243. package/dist/jobs/scheduler.js +7 -0
  244. package/dist/localization/default-messages.d.ts +17 -0
  245. package/dist/localization/default-messages.js +17 -0
  246. package/dist/mcp/oauth-client-metadata.js +5 -1
  247. package/dist/mcp/oauth-route.d.ts +5 -4
  248. package/dist/mcp/oauth-route.js +25 -9
  249. package/dist/mcp/screen-memory-stdio.d.ts +7 -7
  250. package/dist/mcp/server.d.ts +1 -1
  251. package/dist/mcp/server.js +9 -10
  252. package/dist/notifications/routes.d.ts +2 -2
  253. package/dist/observability/routes.d.ts +3 -3
  254. package/dist/org/handlers.d.ts +1 -0
  255. package/dist/org/handlers.js +17 -2
  256. package/dist/provider-api/actions/custom-provider-registration.d.ts +5 -5
  257. package/dist/provider-api/actions/provider-api.d.ts +7 -7
  258. package/dist/provider-api/corpus-jobs.d.ts +12 -2
  259. package/dist/provider-api/corpus-jobs.js +31 -1
  260. package/dist/provider-api/index.d.ts +2 -0
  261. package/dist/provider-api/index.js +7 -1
  262. package/dist/secrets/routes.d.ts +3 -3
  263. package/dist/server/agent-chat/recurring-jobs-runtime.d.ts +1 -1
  264. package/dist/server/agent-chat/recurring-jobs-runtime.js +14 -2
  265. package/dist/server/agent-chat/script-entries.js +12 -1
  266. package/dist/server/agent-chat-plugin.js +2 -2
  267. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  268. package/dist/server/auth.d.ts +1 -0
  269. package/dist/server/auth.js +85 -1
  270. package/dist/server/better-auth-instance.d.ts +9 -0
  271. package/dist/server/better-auth-instance.js +25 -11
  272. package/dist/server/core-routes-plugin.d.ts +7 -0
  273. package/dist/server/core-routes-plugin.js +51 -5
  274. package/dist/server/create-server.js +23 -2
  275. package/dist/server/db-pressure.d.ts +38 -0
  276. package/dist/server/db-pressure.js +147 -0
  277. package/dist/server/index.d.ts +1 -0
  278. package/dist/server/index.js +1 -0
  279. package/dist/server/legacy-auth-migration.d.ts +10 -0
  280. package/dist/server/legacy-auth-migration.js +37 -0
  281. package/dist/server/onboarding-html.js +206 -2
  282. package/dist/server/poll.d.ts +26 -0
  283. package/dist/server/poll.js +108 -13
  284. package/dist/server/ssr-handler.js +4 -1
  285. package/dist/server/transcribe-voice.d.ts +1 -1
  286. package/dist/shared/index.d.ts +1 -0
  287. package/dist/shared/index.js +1 -0
  288. package/dist/shared/runtime-config.d.ts +44 -0
  289. package/dist/shared/runtime-config.js +284 -0
  290. package/dist/styles/agent-native.css +48 -1
  291. package/dist/templates/chat/.agents/skills/actions/SKILL.md +18 -0
  292. package/dist/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  293. package/dist/templates/chat/.agents/skills/delegate-to-agent/SKILL.md +45 -47
  294. package/dist/templates/chat/.agents/skills/frontend-design/SKILL.md +1 -0
  295. package/dist/templates/chat/.agents/skills/turn-into-app/SKILL.md +76 -23
  296. package/dist/templates/chat/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  297. package/dist/templates/chat/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  298. package/dist/templates/chat/.env.example +3 -0
  299. package/dist/templates/chat/AGENTS.md +4 -0
  300. package/dist/templates/default/.agents/skills/actions/SKILL.md +18 -0
  301. package/dist/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  302. package/dist/templates/default/.agents/skills/delegate-to-agent/SKILL.md +45 -47
  303. package/dist/templates/default/.agents/skills/frontend-design/SKILL.md +1 -0
  304. package/dist/templates/default/.agents/skills/turn-into-app/SKILL.md +76 -23
  305. package/dist/templates/default/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  306. package/dist/templates/default/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  307. package/dist/templates/default/AGENTS.md +6 -1
  308. package/dist/templates/default/app/i18n/ar-SA.ts +6 -0
  309. package/dist/templates/default/app/i18n/de-DE.ts +6 -0
  310. package/dist/templates/default/app/i18n/en-US.ts +6 -0
  311. package/dist/templates/default/app/i18n/es-ES.ts +6 -0
  312. package/dist/templates/default/app/i18n/fr-FR.ts +6 -0
  313. package/dist/templates/default/app/i18n/hi-IN.ts +6 -0
  314. package/dist/templates/default/app/i18n/ja-JP.ts +6 -0
  315. package/dist/templates/default/app/i18n/ko-KR.ts +6 -0
  316. package/dist/templates/default/app/i18n/pt-BR.ts +6 -0
  317. package/dist/templates/default/app/i18n/zh-CN.ts +6 -0
  318. package/dist/templates/default/app/i18n/zh-TW.ts +6 -0
  319. package/dist/templates/headless/.agents/skills/actions/SKILL.md +18 -0
  320. package/dist/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  321. package/dist/templates/headless/.agents/skills/turn-into-app/SKILL.md +76 -23
  322. package/dist/templates/headless/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  323. package/dist/templates/headless/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  324. package/dist/templates/workspace-core/.agents/skills/actions/SKILL.md +18 -0
  325. package/dist/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  326. package/dist/templates/workspace-core/.agents/skills/delegate-to-agent/SKILL.md +45 -47
  327. package/dist/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +1 -0
  328. package/dist/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +76 -23
  329. package/dist/templates/workspace-core/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  330. package/dist/templates/workspace-core/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  331. package/dist/templates/workspace-core/.agents/skills/workspace-conventions/SKILL.md +4 -0
  332. package/dist/templates/workspace-core/.agents/skills/writing-agent-instructions/SKILL.md +8 -0
  333. package/dist/templates/workspace-core/AGENTS.md +7 -2
  334. package/dist/templates/workspace-root/AGENTS.md +5 -0
  335. package/dist/vite/client.d.ts +3 -3
  336. package/dist/vite/client.js +47 -1
  337. package/dist/vite/index.d.ts +1 -1
  338. package/docs/content/agent-native-config.mdx +240 -139
  339. package/docs/content/authentication.mdx +17 -0
  340. package/docs/content/deployment.mdx +19 -14
  341. package/docs/content/drop-in-agent.mdx +19 -2
  342. package/docs/content/environment-variables.mdx +63 -38
  343. package/docs/content/getting-started-actions.mdx +15 -6
  344. package/docs/content/getting-started-database.mdx +14 -10
  345. package/docs/content/getting-started-pages.mdx +32 -35
  346. package/docs/content/getting-started.mdx +7 -6
  347. package/package.json +1 -1
  348. package/src/templates/chat/.agents/skills/actions/SKILL.md +18 -0
  349. package/src/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  350. package/src/templates/chat/.agents/skills/delegate-to-agent/SKILL.md +45 -47
  351. package/src/templates/chat/.agents/skills/frontend-design/SKILL.md +1 -0
  352. package/src/templates/chat/.agents/skills/turn-into-app/SKILL.md +76 -23
  353. package/src/templates/chat/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  354. package/src/templates/chat/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  355. package/src/templates/chat/.env.example +3 -0
  356. package/src/templates/chat/AGENTS.md +4 -0
  357. package/src/templates/default/.agents/skills/actions/SKILL.md +18 -0
  358. package/src/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  359. package/src/templates/default/.agents/skills/delegate-to-agent/SKILL.md +45 -47
  360. package/src/templates/default/.agents/skills/frontend-design/SKILL.md +1 -0
  361. package/src/templates/default/.agents/skills/turn-into-app/SKILL.md +76 -23
  362. package/src/templates/default/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  363. package/src/templates/default/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  364. package/src/templates/default/AGENTS.md +6 -1
  365. package/src/templates/default/app/i18n/ar-SA.ts +6 -0
  366. package/src/templates/default/app/i18n/de-DE.ts +6 -0
  367. package/src/templates/default/app/i18n/en-US.ts +6 -0
  368. package/src/templates/default/app/i18n/es-ES.ts +6 -0
  369. package/src/templates/default/app/i18n/fr-FR.ts +6 -0
  370. package/src/templates/default/app/i18n/hi-IN.ts +6 -0
  371. package/src/templates/default/app/i18n/ja-JP.ts +6 -0
  372. package/src/templates/default/app/i18n/ko-KR.ts +6 -0
  373. package/src/templates/default/app/i18n/pt-BR.ts +6 -0
  374. package/src/templates/default/app/i18n/zh-CN.ts +6 -0
  375. package/src/templates/default/app/i18n/zh-TW.ts +6 -0
  376. package/src/templates/headless/.agents/skills/actions/SKILL.md +18 -0
  377. package/src/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  378. package/src/templates/headless/.agents/skills/turn-into-app/SKILL.md +76 -23
  379. package/src/templates/headless/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  380. package/src/templates/headless/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  381. package/src/templates/workspace-core/.agents/skills/actions/SKILL.md +18 -0
  382. package/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +5 -0
  383. package/src/templates/workspace-core/.agents/skills/delegate-to-agent/SKILL.md +45 -47
  384. package/src/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +1 -0
  385. package/src/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +76 -23
  386. package/src/templates/workspace-core/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  387. package/src/templates/workspace-core/.agents/skills/turn-into-app/references/fresh-project.md +64 -0
  388. package/src/templates/workspace-core/.agents/skills/workspace-conventions/SKILL.md +4 -0
  389. package/src/templates/workspace-core/.agents/skills/writing-agent-instructions/SKILL.md +8 -0
  390. package/src/templates/workspace-core/AGENTS.md +7 -2
  391. package/src/templates/workspace-root/AGENTS.md +5 -0
  392. package/corpus/templates/dispatch/app/routes/admin.apps.$appId.tsx +0 -1
  393. package/corpus/templates/dispatch/app/routes/admin.apps.tsx +0 -1
@@ -1,10 +1,16 @@
1
1
  import { getAllSettings, listOrgSettings } from "@agent-native/core/settings";
2
2
 
3
3
  import { dashboardCatalogEntries } from "./dashboard-catalog";
4
- import { listDashboards, type DashboardRecord } from "./dashboards-store";
4
+ import {
5
+ listDashboardSummaries,
6
+ loadDashboardCatalogDashboards,
7
+ type DashboardCatalogRecord,
8
+ type DashboardSummaryRecord,
9
+ } from "./dashboards-store";
5
10
 
6
11
  const DATA_DICTIONARY_KEY_PREFIX = "data-dict-";
7
12
  const MAX_QUERY_LENGTH = 12_000;
13
+ const MAX_CATALOG_DASHBOARD_HYDRATION = 24;
8
14
  const STOP_WORDS = new Set([
9
15
  "a",
10
16
  "all",
@@ -106,6 +112,44 @@ function compactQuery(value: unknown): string | Record<string, unknown> | null {
106
112
  return null;
107
113
  }
108
114
 
115
+ function summaryScore(
116
+ search: string,
117
+ dashboard: DashboardSummaryRecord,
118
+ ): number {
119
+ const { score } = matchScore(search, [
120
+ { value: dashboard.name, weight: 24 },
121
+ { value: dashboard.description, weight: 12 },
122
+ { value: dashboard.configName, weight: 10 },
123
+ { value: dashboard.catalogTemplateId, weight: 6 },
124
+ { value: dashboard.demoId, weight: 6 },
125
+ ]);
126
+ return score;
127
+ }
128
+
129
+ function shortlistDashboardSummaries(
130
+ search: string,
131
+ dashboards: DashboardSummaryRecord[],
132
+ limit: number,
133
+ ): DashboardSummaryRecord[] {
134
+ const maxHydration = Math.min(
135
+ Math.max(limit * 4, 12),
136
+ MAX_CATALOG_DASHBOARD_HYDRATION,
137
+ );
138
+ return dashboards
139
+ .map((dashboard) => ({
140
+ dashboard,
141
+ score: summaryScore(search, dashboard),
142
+ }))
143
+ .sort((a, b) => {
144
+ if (b.score !== a.score) return b.score - a.score;
145
+ return JSON.stringify(a.dashboard).localeCompare(
146
+ JSON.stringify(b.dashboard),
147
+ );
148
+ })
149
+ .slice(0, maxHydration)
150
+ .map(({ dashboard }) => dashboard);
151
+ }
152
+
109
153
  // Analytics vocabulary the corpus spells out but users abbreviate (or vice versa).
110
154
  // Expansions match at reduced weight so they break ties without outranking a literal hit.
111
155
  const SYNONYM_EXPANSIONS: Record<string, string[]> = {
@@ -497,13 +541,15 @@ async function listDictionaryEntries(args: {
497
541
  return entries;
498
542
  }
499
543
 
500
- function savedDashboardInput(dashboard: DashboardRecord) {
501
- const config = dashboard.config as Record<string, unknown>;
544
+ function savedDashboardInput(
545
+ summary: DashboardSummaryRecord,
546
+ dashboard: DashboardCatalogRecord,
547
+ ) {
502
548
  return {
503
549
  id: dashboard.id,
504
- title: text(config.name) || dashboard.title,
505
- description: text(config.description) || undefined,
506
- config,
550
+ title: summary.name,
551
+ description: summary.description ?? undefined,
552
+ config: dashboard.config,
507
553
  origin: "saved-dashboard" as const,
508
554
  };
509
555
  }
@@ -514,26 +560,46 @@ export async function searchAnalyticsQueryCatalog(args: {
514
560
  orgId: string | null;
515
561
  limit: number;
516
562
  }): Promise<AnalyticsQueryCatalogCandidate[]> {
517
- const [savedDashboardsResult, dictionaryEntriesResult] =
563
+ const [savedSummariesResult, dictionaryEntriesResult] =
518
564
  await Promise.allSettled([
519
- listDashboards(
565
+ listDashboardSummaries(
520
566
  { email: args.email, orgId: args.orgId },
521
- { kind: "sql", archived: "active", hidden: "visible" },
567
+ {
568
+ kind: "sql",
569
+ archived: "active",
570
+ hidden: "visible",
571
+ includeCatalogMetadata: true,
572
+ },
522
573
  ),
523
574
  listDictionaryEntries({ email: args.email, orgId: args.orgId }),
524
575
  ]);
525
- const savedDashboards =
526
- savedDashboardsResult.status === "fulfilled"
527
- ? savedDashboardsResult.value
576
+ const savedSummaries =
577
+ savedSummariesResult.status === "fulfilled"
578
+ ? savedSummariesResult.value
528
579
  : [];
529
580
  const dictionaryEntries =
530
581
  dictionaryEntriesResult.status === "fulfilled"
531
582
  ? dictionaryEntriesResult.value
532
583
  : [];
584
+ const savedSummaryIds = new Set(
585
+ savedSummaries.map((dashboard) => dashboard.id),
586
+ );
533
587
 
534
- const savedIds = new Set(savedDashboards.map((dashboard) => dashboard.id));
588
+ const shortlistedSummaries = shortlistDashboardSummaries(
589
+ args.search,
590
+ savedSummaries,
591
+ args.limit,
592
+ );
593
+ const shortlistedIds = shortlistedSummaries.map((dashboard) => dashboard.id);
594
+ const savedDashboardsResult = await loadDashboardCatalogDashboards(
595
+ { email: args.email, orgId: args.orgId },
596
+ shortlistedIds,
597
+ );
598
+ const savedDashboards = new Map(
599
+ savedDashboardsResult.map((dashboard) => [dashboard.id, dashboard]),
600
+ );
535
601
  const templateDashboards = dashboardCatalogEntries
536
- .filter((entry) => !savedIds.has(entry.defaultDashboardId))
602
+ .filter((entry) => !savedSummaryIds.has(entry.defaultDashboardId))
537
603
  .flatMap((entry) => {
538
604
  try {
539
605
  const config = entry.buildConfig() as unknown as Record<
@@ -557,7 +623,11 @@ export async function searchAnalyticsQueryCatalog(args: {
557
623
  return rankAnalyticsQueryCatalog({
558
624
  search: args.search,
559
625
  dashboards: [
560
- ...savedDashboards.map(savedDashboardInput),
626
+ ...shortlistedSummaries.flatMap((summary) => {
627
+ const dashboard = savedDashboards.get(summary.id);
628
+ if (!dashboard) return [];
629
+ return [savedDashboardInput(summary, dashboard)];
630
+ }),
561
631
  ...templateDashboards,
562
632
  ],
563
633
  dictionaryEntries,
@@ -0,0 +1,168 @@
1
+ import { recordChange } from "@agent-native/core/server";
2
+ import { accessFilter, assertAccess } from "@agent-native/core/sharing";
3
+ import { desc, eq } from "drizzle-orm";
4
+
5
+ import { getDb, schema } from "../db/index.js";
6
+ import { type DashboardRecord, getDashboard } from "./dashboards-store.js";
7
+
8
+ export type DashboardFolderScope = "personal" | "shared";
9
+
10
+ export interface DashboardFolderContext {
11
+ email: string;
12
+ orgId: string | null;
13
+ }
14
+
15
+ export interface DashboardFolderRecord {
16
+ id: string;
17
+ name: string;
18
+ scope: DashboardFolderScope;
19
+ ownerEmail: string;
20
+ orgId: string | null;
21
+ visibility: "private" | "org" | "public";
22
+ createdAt: string;
23
+ updatedAt: string;
24
+ }
25
+
26
+ function nowIso(): string {
27
+ return new Date().toISOString();
28
+ }
29
+
30
+ function toFolder(row: any): DashboardFolderRecord {
31
+ return {
32
+ id: row.id,
33
+ name: row.name,
34
+ scope: row.scope,
35
+ ownerEmail: row.ownerEmail,
36
+ orgId: row.orgId ?? null,
37
+ visibility: row.visibility,
38
+ createdAt: row.createdAt,
39
+ updatedAt: row.updatedAt,
40
+ };
41
+ }
42
+
43
+ export async function listDashboardFolders(
44
+ ctx: DashboardFolderContext,
45
+ ): Promise<DashboardFolderRecord[]> {
46
+ const rows = await (getDb() as any)
47
+ .select()
48
+ .from(schema.dashboardFolders)
49
+ .where(
50
+ accessFilter(schema.dashboardFolders, schema.dashboardFolderShares, {
51
+ userEmail: ctx.email,
52
+ orgId: ctx.orgId ?? undefined,
53
+ }),
54
+ )
55
+ .orderBy(desc(schema.dashboardFolders.updatedAt));
56
+ return rows.map(toFolder);
57
+ }
58
+
59
+ export async function createDashboardFolder(
60
+ name: string,
61
+ scope: DashboardFolderScope,
62
+ ctx: DashboardFolderContext,
63
+ ): Promise<DashboardFolderRecord> {
64
+ const trimmedName = name.trim();
65
+ if (!trimmedName) throw new Error("folder name is required");
66
+ if (scope === "shared" && !ctx.orgId) {
67
+ throw new Error("an active organization is required for shared folders");
68
+ }
69
+ const id = crypto.randomUUID();
70
+ const now = nowIso();
71
+ const folderOrgId = scope === "shared" ? ctx.orgId : null;
72
+ await (getDb() as any).insert(schema.dashboardFolders).values({
73
+ id,
74
+ name: trimmedName,
75
+ scope,
76
+ createdAt: now,
77
+ updatedAt: now,
78
+ ownerEmail: ctx.email,
79
+ orgId: folderOrgId,
80
+ visibility: scope === "shared" ? "org" : "private",
81
+ });
82
+ recordChange({
83
+ source: "dashboard-folders",
84
+ type: "change",
85
+ key: id,
86
+ ...(scope === "shared" && ctx.orgId
87
+ ? { orgId: ctx.orgId }
88
+ : { owner: ctx.email }),
89
+ });
90
+ return {
91
+ id,
92
+ name: trimmedName,
93
+ scope,
94
+ ownerEmail: ctx.email,
95
+ orgId: folderOrgId,
96
+ visibility: scope === "shared" ? "org" : "private",
97
+ createdAt: now,
98
+ updatedAt: now,
99
+ };
100
+ }
101
+
102
+ async function getFolder(
103
+ id: string,
104
+ ctx: DashboardFolderContext,
105
+ ): Promise<DashboardFolderRecord> {
106
+ await assertAccess("dashboard-folder", id, "viewer", {
107
+ userEmail: ctx.email,
108
+ orgId: ctx.orgId ?? undefined,
109
+ });
110
+ const [row] = await (getDb() as any)
111
+ .select()
112
+ .from(schema.dashboardFolders)
113
+ .where(eq(schema.dashboardFolders.id, id))
114
+ .limit(1);
115
+ if (!row) throw new Error("dashboard folder not found");
116
+ return toFolder(row);
117
+ }
118
+
119
+ export async function assignDashboardToFolder(
120
+ dashboardId: string,
121
+ folderId: string | null,
122
+ ctx: DashboardFolderContext,
123
+ ): Promise<DashboardRecord> {
124
+ const dashboard = await getDashboard(dashboardId, ctx);
125
+ if (!dashboard) throw new Error("dashboard not found or inaccessible");
126
+ await assertAccess("dashboard", dashboardId, "editor", {
127
+ userEmail: ctx.email,
128
+ orgId: ctx.orgId ?? undefined,
129
+ });
130
+
131
+ if (folderId) {
132
+ const folder = await getFolder(folderId, ctx);
133
+ if (folder.scope === "personal") {
134
+ if (
135
+ dashboard.visibility !== "private" ||
136
+ dashboard.ownerEmail.toLowerCase() !== ctx.email.toLowerCase()
137
+ ) {
138
+ throw new Error("personal folders require an owned private dashboard");
139
+ }
140
+ } else if (
141
+ dashboard.visibility !== "org" ||
142
+ !ctx.orgId ||
143
+ dashboard.orgId !== ctx.orgId ||
144
+ folder.orgId !== ctx.orgId
145
+ ) {
146
+ throw new Error("shared folders require an org-visible dashboard");
147
+ }
148
+ }
149
+
150
+ await (getDb() as any)
151
+ .update(schema.dashboards)
152
+ .set({ folderId, updatedAt: nowIso(), updatedBy: ctx.email })
153
+ .where(eq(schema.dashboards.id, dashboardId));
154
+ recordChange({
155
+ source: "dashboards",
156
+ type: "change",
157
+ key: dashboardId,
158
+ ...((dashboard.visibility === "org" && dashboard.orgId
159
+ ? { orgId: dashboard.orgId }
160
+ : { owner: dashboard.ownerEmail }) as {
161
+ owner?: string;
162
+ orgId?: string;
163
+ }),
164
+ });
165
+ const updated = await getDashboard(dashboardId, ctx);
166
+ if (!updated) throw new Error("dashboard disappeared after folder update");
167
+ return updated;
168
+ }
@@ -1,6 +1,9 @@
1
1
  import type { CredentialContext } from "@agent-native/core/credentials";
2
2
  import { resolveCredential } from "@agent-native/core/credentials";
3
- import { runDataProgram } from "@agent-native/core/data-programs";
3
+ import {
4
+ getInitializedDataProgramsAppId,
5
+ runDataProgram,
6
+ } from "@agent-native/core/data-programs";
4
7
  import type { MissingKeyResponse } from "@agent-native/core/server";
5
8
 
6
9
  import { getUserSegmentation, queryEvents } from "./amplitude";
@@ -12,6 +15,7 @@ import {
12
15
  runPrometheusPanel,
13
16
  serializePanelDescriptorInput,
14
17
  } from "./prometheus";
18
+ import { ANALYTICS_APP_ID } from "./provider-credentials";
15
19
 
16
20
  export const DASHBOARD_PANEL_SOURCES = [
17
21
  "bigquery",
@@ -25,8 +29,6 @@ export const DASHBOARD_PANEL_SOURCES = [
25
29
 
26
30
  export type DashboardPanelSource = (typeof DASHBOARD_PANEL_SOURCES)[number];
27
31
 
28
- const ANALYTICS_DATA_PROGRAM_APP_ID = "analytics";
29
-
30
32
  export interface DashboardPanelQueryResult {
31
33
  rows: Record<string, unknown>[];
32
34
  schema: { name: string; type: string }[];
@@ -360,7 +362,10 @@ async function runProgramPanel(
360
362
  const descriptor = parseProgramDescriptor(raw);
361
363
  const result = await runDataProgram({
362
364
  programId: descriptor.programId,
363
- appId: ANALYTICS_DATA_PROGRAM_APP_ID,
365
+ // Use the same app scope that registered the agent's data-program actions.
366
+ // Standalone template dev can derive that scope from the runtime package
367
+ // even when APP_NAME is not explicitly set.
368
+ appId: getInitializedDataProgramsAppId() ?? ANALYTICS_APP_ID,
364
369
  params: descriptor.params,
365
370
  ctx: { userEmail: ctx.userEmail, orgId: ctx.orgId ?? null },
366
371
  triggeredBy: "panel_view",
@@ -28,7 +28,7 @@ import {
28
28
  resolveAccess,
29
29
  type ShareRole,
30
30
  } from "@agent-native/core/sharing";
31
- import { and, desc, eq, isNotNull, isNull, sql } from "drizzle-orm";
31
+ import { and, desc, eq, inArray, isNotNull, isNull, sql } from "drizzle-orm";
32
32
 
33
33
  import { getDb, schema } from "../db/index.js";
34
34
 
@@ -62,10 +62,12 @@ export interface DashboardSummaryRecord {
62
62
  id: string;
63
63
  kind: DashboardKind;
64
64
  name: string;
65
+ description: string | null;
65
66
  configName: string | null;
66
67
  catalogTemplateId: string | null;
67
68
  demoId: string | null;
68
69
  parentId: string | null;
70
+ folderId: string | null;
69
71
  ownerEmail: string;
70
72
  orgId: string | null;
71
73
  visibility: "private" | "org" | "public";
@@ -76,6 +78,17 @@ export interface DashboardSummaryRecord {
76
78
  hiddenBy: string | null;
77
79
  }
78
80
 
81
+ /** Hydrated dashboard row for catalog ranking only. */
82
+ export interface DashboardCatalogRecord {
83
+ id: string;
84
+ kind: DashboardKind;
85
+ title: string;
86
+ description: string | null;
87
+ config: Record<string, unknown>;
88
+ }
89
+
90
+ const MAX_CATALOG_DASHBOARD_HYDRATION = 24;
91
+
79
92
  export interface DashboardRevisionRecord {
80
93
  id: string;
81
94
  dashboardId: string;
@@ -281,6 +294,13 @@ function rowToDashboardRevision(row: any): DashboardRevisionRecord {
281
294
  };
282
295
  }
283
296
 
297
+ function configDescriptionFromValue(
298
+ value: Record<string, unknown>,
299
+ ): string | null {
300
+ const description = value["description"];
301
+ return typeof description === "string" ? description : null;
302
+ }
303
+
284
304
  function configFromSettings(data: Record<string, unknown>): {
285
305
  title: string;
286
306
  config: Record<string, unknown>;
@@ -564,6 +584,11 @@ export async function listDashboardSummaries(
564
584
  : sql<
565
585
  string | null
566
586
  >`json_extract(${schema.dashboards.config}, '$.parentId')`;
587
+ const description = isPostgres()
588
+ ? sql<string | null>`(${schema.dashboards.config}::jsonb ->> 'description')`
589
+ : sql<
590
+ string | null
591
+ >`json_extract(${schema.dashboards.config}, '$.description')`;
567
592
  const configName = isPostgres()
568
593
  ? sql<string | null>`(${schema.dashboards.config}::jsonb ->> 'name')`
569
594
  : sql<string | null>`json_extract(${schema.dashboards.config}, '$.name')`;
@@ -586,10 +611,12 @@ export async function listDashboardSummaries(
586
611
  id: schema.dashboards.id,
587
612
  kind: schema.dashboards.kind,
588
613
  name: schema.dashboards.title,
614
+ description,
589
615
  ...(includeCatalogMetadata
590
616
  ? { configName, catalogTemplateId, demoId }
591
617
  : {}),
592
618
  parentId,
619
+ folderId: schema.dashboards.folderId,
593
620
  ownerEmail: schema.dashboards.ownerEmail,
594
621
  orgId: schema.dashboards.orgId,
595
622
  visibility: schema.dashboards.visibility,
@@ -603,11 +630,13 @@ export async function listDashboardSummaries(
603
630
  .where(where);
604
631
  const out: DashboardSummaryRecord[] = rows.map((row: any) => ({
605
632
  ...row,
633
+ description: typeof row.description === "string" ? row.description : null,
606
634
  configName: typeof row.configName === "string" ? row.configName : null,
607
635
  catalogTemplateId:
608
636
  typeof row.catalogTemplateId === "string" ? row.catalogTemplateId : null,
609
637
  demoId: typeof row.demoId === "string" ? row.demoId : null,
610
638
  parentId: typeof row.parentId === "string" ? row.parentId : null,
639
+ folderId: typeof row.folderId === "string" ? row.folderId : null,
611
640
  orgId: row.orgId ?? null,
612
641
  archivedAt: row.archivedAt ?? null,
613
642
  hiddenAt: row.hiddenAt ?? null,
@@ -656,8 +685,10 @@ export async function listDashboardSummaries(
656
685
  id,
657
686
  kind,
658
687
  name: title,
688
+ description: configDescriptionFromValue(config),
659
689
  ...catalogMetadata,
660
690
  parentId: typeof config.parentId === "string" ? config.parentId : null,
691
+ folderId: null,
661
692
  ownerEmail: ctx.email,
662
693
  orgId,
663
694
  visibility,
@@ -676,6 +707,90 @@ export async function listDashboardSummaries(
676
707
  return out;
677
708
  }
678
709
 
710
+ /**
711
+ * Hydrate a bounded set of dashboard ids for catalog ranking.
712
+ *
713
+ * This is the catalog-specific path: it reads only the id, kind, title,
714
+ * description, and config needed for ranking, and only for explicit ids that
715
+ * were already shortlisted from the metadata path.
716
+ */
717
+ export async function loadDashboardCatalogDashboards(
718
+ ctx: AccessCtx,
719
+ ids: readonly string[],
720
+ dbOverride?: any,
721
+ ): Promise<DashboardCatalogRecord[]> {
722
+ const db = (dbOverride ?? getDb()) as any;
723
+ const uniqueIds = [
724
+ ...new Set(ids.map((id) => id.trim()).filter(Boolean)),
725
+ ].slice(0, MAX_CATALOG_DASHBOARD_HYDRATION);
726
+ if (!uniqueIds.length) return [];
727
+
728
+ const description = isPostgres()
729
+ ? sql<string | null>`(${schema.dashboards.config}::jsonb ->> 'description')`
730
+ : sql<
731
+ string | null
732
+ >`json_extract(${schema.dashboards.config}, '$.description')`;
733
+ const archived = isNull(schema.dashboards.archivedAt);
734
+ const visible = isNull(schema.dashboards.hiddenAt);
735
+ const where = and(
736
+ accessFilter(schema.dashboards, schema.dashboardShares, {
737
+ userEmail: ctx.email,
738
+ orgId: ctx.orgId ?? undefined,
739
+ }),
740
+ inArray(schema.dashboards.id, uniqueIds),
741
+ archived,
742
+ visible,
743
+ );
744
+
745
+ // guard:allow-heavy-dashboard-list-read - bounded explicit ids shortlisted from metadata
746
+ const sqlRows = await db
747
+ .select({
748
+ id: schema.dashboards.id,
749
+ kind: schema.dashboards.kind,
750
+ title: schema.dashboards.title,
751
+ description,
752
+ config: schema.dashboards.config,
753
+ })
754
+ .from(schema.dashboards)
755
+ .where(where);
756
+
757
+ const byId = new Map<string, DashboardCatalogRecord>(
758
+ sqlRows.map((row: any) => {
759
+ const catalogRow: DashboardCatalogRecord = {
760
+ id: row.id,
761
+ kind: row.kind,
762
+ title: row.title,
763
+ description:
764
+ typeof row.description === "string" ? row.description : null,
765
+ config:
766
+ typeof row.config === "string" ? JSON.parse(row.config) : row.config,
767
+ };
768
+ return [row.id, catalogRow];
769
+ }),
770
+ );
771
+
772
+ const out: DashboardCatalogRecord[] = [];
773
+ for (const id of uniqueIds) {
774
+ const row = byId.get(id);
775
+ if (row) {
776
+ out.push(row);
777
+ continue;
778
+ }
779
+
780
+ const legacy = await findLegacyDashboard(id, ctx);
781
+ if (!legacy) continue;
782
+ const { title, config } = configFromSettings(legacy.data);
783
+ out.push({
784
+ id,
785
+ kind: legacy.kind,
786
+ title,
787
+ description: configDescriptionFromValue(config),
788
+ config,
789
+ });
790
+ }
791
+ return out;
792
+ }
793
+
679
794
  /**
680
795
  * Reject a name that would collide with another dashboard visible to the
681
796
  * caller. Archived and hidden dashboards are intentionally excluded because
@@ -2,10 +2,7 @@ import { isPostgres } from "@agent-native/core/db";
2
2
  import { sql } from "@agent-native/core/db/schema";
3
3
 
4
4
  import { getDb, schema } from "../db/index.js";
5
- import {
6
- FIRST_PARTY_ANALYTICS_ROLLUP_LOCK_KEY,
7
- FIRST_PARTY_ANALYTICS_ROLLUP_LOCK_SQL,
8
- } from "./analytics-rollup-lock.js";
5
+ import { FIRST_PARTY_ANALYTICS_ROLLUP_LOCK_KEY } from "./analytics-rollup-lock.js";
9
6
 
10
7
  /**
11
8
  * The normalized subset emitted by first-party analytics ingest. Keeping this
@@ -151,10 +148,9 @@ export async function upsertFirstPartyAnalyticsRollups(
151
148
  // snapshot. Holding it through ingest's rollup increment prevents the
152
149
  // backfill's monotonic upsert from losing a concurrently committed
153
150
  // event.
154
- await tx.execute({
155
- sql: FIRST_PARTY_ANALYTICS_ROLLUP_LOCK_SQL,
156
- args: [FIRST_PARTY_ANALYTICS_ROLLUP_LOCK_KEY],
157
- });
151
+ await tx.execute(
152
+ sql`SELECT pg_advisory_xact_lock(hashtextextended(${FIRST_PARTY_ANALYTICS_ROLLUP_LOCK_KEY}, 0::bigint))`,
153
+ );
158
154
  }
159
155
  const dailyRows = [...dailyRollups.values()];
160
156
  await tx
@@ -1,6 +1,6 @@
1
1
  import { getDbExec } from "@agent-native/core/db";
2
2
  import { runWithRequestContext } from "@agent-native/core/server";
3
- import { and, eq, isNull, or } from "drizzle-orm";
3
+ import { and, eq, isNull, lt, or } from "drizzle-orm";
4
4
 
5
5
  import { FIRST_PARTY_ANALYTICS_QUERY_TIMEOUT_MS } from "../../shared/dashboard-report-timeouts.js";
6
6
  import { getDb, schema } from "../db/index.js";
@@ -181,6 +181,68 @@ export async function listAnalyticsPublicKeys(
181
181
  }));
182
182
  }
183
183
 
184
+ /** How stale the last-used stamp must be before a request pays to refresh it. */
185
+ const LAST_USED_AT_REFRESH_MS = 60_000;
186
+
187
+ /**
188
+ * Refresh a public key's last-used stamp without serializing ingest behind it.
189
+ *
190
+ * This UPDATE used to sit inside the ingest transaction, so every concurrent
191
+ * request for one public key took an exclusive row lock on that key's row and
192
+ * held it until the transaction committed — which meant through the rollup
193
+ * upsert. Production stacked 36 writers on three hot rows waiting 38-57s each;
194
+ * that exhausted the connection pool, and the whole app stopped loading while
195
+ * Postgres reported "no server connection available, client being queued". The
196
+ * stamp is bookkeeping: it needs neither atomicity with the events nor
197
+ * second-precision, and losing one is harmless.
198
+ *
199
+ * The staleness predicate throttles in SQL rather than in the caller, because
200
+ * Postgres only locks rows an UPDATE actually matches — a request whose key was
201
+ * stamped seconds ago matches nothing and takes no lock at all. Doing the same
202
+ * check in JS would reintroduce the convoy, since every racing request would
203
+ * still issue its own unconditional write.
204
+ *
205
+ * `last_used_at` is TEXT holding ISO-8601 UTC (always `Z`-suffixed), so `lt` is
206
+ * a lexicographic comparison that happens to be chronological. Storing a local
207
+ * or offset-bearing timestamp here would silently break this ordering.
208
+ */
209
+ export async function touchPublicKeyLastUsedAt(
210
+ keyId: string,
211
+ receivedAt: string,
212
+ ): Promise<void> {
213
+ const parsed = Date.parse(receivedAt);
214
+ if (!Number.isFinite(parsed)) {
215
+ console.warn(
216
+ "[first-party-analytics] Skipping last-used stamp: unparseable receivedAt",
217
+ receivedAt,
218
+ );
219
+ return;
220
+ }
221
+ const staleBefore = new Date(parsed - LAST_USED_AT_REFRESH_MS).toISOString();
222
+ try {
223
+ const db = await getDb();
224
+ await db
225
+ .update(schema.analyticsPublicKeys)
226
+ .set({ lastUsedAt: receivedAt })
227
+ .where(
228
+ and(
229
+ eq(schema.analyticsPublicKeys.id, keyId),
230
+ or(
231
+ isNull(schema.analyticsPublicKeys.lastUsedAt),
232
+ lt(schema.analyticsPublicKeys.lastUsedAt, staleBefore),
233
+ ),
234
+ ),
235
+ );
236
+ } catch (error) {
237
+ // Best-effort by design: a failed stamp must not reject an ingest whose
238
+ // events already committed. Loud enough to see if it starts failing always.
239
+ console.warn(
240
+ "[first-party-analytics] Failed to refresh key last-used stamp:",
241
+ error,
242
+ );
243
+ }
244
+ }
245
+
184
246
  function parseReplayAllowedOrigins(value: unknown): string[] {
185
247
  if (Array.isArray(value)) {
186
248
  return value.filter((item): item is string => typeof item === "string");
@@ -539,18 +601,11 @@ export async function recordAnalyticsEvents(
539
601
  if (rows.length && backend.sink !== "bigquery") {
540
602
  await db.transaction(async (tx: any) => {
541
603
  await tx.insert(schema.analyticsEvents).values(rows);
542
- await tx
543
- .update(schema.analyticsPublicKeys)
544
- .set({ lastUsedAt: receivedAt })
545
- .where(eq(schema.analyticsPublicKeys.id, key.id));
546
604
  await upsertFirstPartyAnalyticsRollups(rows, tx);
547
605
  });
548
606
  }
549
- if (rows.length && backend.sink === "bigquery") {
550
- await db
551
- .update(schema.analyticsPublicKeys)
552
- .set({ lastUsedAt: receivedAt })
553
- .where(eq(schema.analyticsPublicKeys.id, key.id));
607
+ if (rows.length) {
608
+ await touchPublicKeyLastUsedAt(key.id, receivedAt);
554
609
  }
555
610
 
556
611
  // Fork captured exceptions into the dedicated error-capture tables. This is