@agent-native/core 0.189.0 → 0.190.0-nightly-20260925215540

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 (234) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/analysis-workspace/SKILL.md +10 -0
  3. package/corpus/templates/analytics/.agents/skills/data-querying/SKILL.md +1 -1
  4. package/corpus/templates/analytics/AGENTS.md +11 -22
  5. package/corpus/templates/analytics/actions/list-data-dictionary.ts +7 -9
  6. package/corpus/templates/analytics/actions/search-analytics-query-catalog.ts +1 -1
  7. package/corpus/templates/analytics/app/components/AnalyticsReviewArtifactPreview.tsx +201 -0
  8. package/corpus/templates/analytics/app/i18n/en-US.ts +2 -0
  9. package/corpus/templates/analytics/app/pages/Settings.tsx +50 -3
  10. package/corpus/templates/analytics/changelog/2026-09-25-first-run-onboarding-records-setup-choices-and-builder-conne.md +6 -0
  11. package/corpus/templates/analytics/changelog/2026-09-25-human-review-previews-analytics-dashboards-as-real-charts.md +6 -0
  12. package/corpus/templates/analytics/changelog/2026-09-25-org-admins-can-review-agent-runs-from-settings.md +6 -0
  13. package/corpus/templates/analytics/changelog/2026-09-25-proactive-query-context.md +6 -0
  14. package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +98 -2
  15. package/corpus/templates/analytics/server/lib/analytics-agent-context.ts +557 -0
  16. package/corpus/templates/analytics/server/lib/analytics-query-catalog.ts +217 -46
  17. package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +2 -2
  18. package/corpus/templates/analytics/server/lib/dashboards-store.ts +39 -5
  19. package/corpus/templates/analytics/server/lib/data-dictionary-context.ts +4 -0
  20. package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +268 -2
  21. package/corpus/templates/analytics/server/plugins/agent-chat.ts +44 -9
  22. package/corpus/templates/calendar/AGENTS.md +5 -0
  23. package/corpus/templates/calendar/CHANGELOG.md +6 -0
  24. package/corpus/templates/calendar/actions/get-event-rules-status.ts +47 -0
  25. package/corpus/templates/calendar/actions/list-events.ts +50 -4
  26. package/corpus/templates/calendar/actions/undo-calendar-event-rule.ts +229 -0
  27. package/corpus/templates/calendar/actions/update-settings.ts +8 -0
  28. package/corpus/templates/calendar/app/i18n/en-US.ts +27 -0
  29. package/corpus/templates/calendar/app/pages/Settings.tsx +196 -1
  30. package/corpus/templates/calendar/changelog/2026-09-25-calendar-feeds-with-the-same-url-keep-their-own-names-and-co.md +6 -0
  31. package/corpus/templates/calendar/changelog/2026-09-25-calendar-invitation-rules-surface-expired-connections-and-un.md +6 -0
  32. package/corpus/templates/calendar/changelog/2026-09-25-restore-locally-hidden-events-from-activity-after-disconnect.md +6 -0
  33. package/corpus/templates/calendar/server/lib/calendar-settings.ts +44 -10
  34. package/corpus/templates/calendar/server/lib/google-api.ts +15 -1
  35. package/corpus/templates/calendar/server/lib/google-calendar.ts +1 -1
  36. package/corpus/templates/calendar/server/plugins/event-rules.ts +628 -0
  37. package/corpus/templates/calendar/shared/api.ts +13 -0
  38. package/corpus/templates/calendar/shared/settings.ts +71 -1
  39. package/corpus/templates/clips/actions/create-recording.ts +83 -2
  40. package/corpus/templates/clips/actions/finalize-recording.ts +610 -110
  41. package/corpus/templates/clips/actions/get-recording-player-data.ts +6 -0
  42. package/corpus/templates/clips/actions/import-loom-recording.ts +2 -0
  43. package/corpus/templates/clips/actions/lib/create-recording-schema.ts +7 -0
  44. package/corpus/templates/clips/actions/lib/loom-import-job.ts +15 -1
  45. package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +126 -26
  46. package/corpus/templates/clips/app/hooks/use-drop-video-upload.ts +75 -15
  47. package/corpus/templates/clips/app/routes/_app.r.$recordingId.tsx +5 -1
  48. package/corpus/templates/clips/app/routes/record.tsx +134 -40
  49. package/corpus/templates/clips/app/routes/share.$shareId.tsx +13 -1
  50. package/corpus/templates/clips/changelog/2026-09-25-signed-in-viewers-who-hit-an-unavailable-expired-or-private-.md +6 -0
  51. package/corpus/templates/clips/chrome-extension/src/background.ts +3 -0
  52. package/corpus/templates/clips/chrome-extension/src/offscreen.ts +73 -16
  53. package/corpus/templates/clips/desktop/src/lib/recorder.ts +188 -18
  54. package/corpus/templates/clips/desktop/src/lib/recording-request.ts +1 -0
  55. package/corpus/templates/clips/server/db/schema.ts +4 -0
  56. package/corpus/templates/clips/server/jobs/media-verification.ts +78 -4
  57. package/corpus/templates/clips/server/jobs/recording-failure-backfill.ts +142 -0
  58. package/corpus/templates/clips/server/lib/media-verification-state.ts +29 -0
  59. package/corpus/templates/clips/server/lib/post-finalize-dispatch.ts +2 -0
  60. package/corpus/templates/clips/server/lib/recording-failures.ts +111 -0
  61. package/corpus/templates/clips/server/lib/recordings.ts +6 -1
  62. package/corpus/templates/clips/server/lib/s3-upload-provider.ts +8 -3
  63. package/corpus/templates/clips/server/lib/upload-lease.ts +25 -6
  64. package/corpus/templates/clips/server/plugins/calendar-jobs.ts +2 -0
  65. package/corpus/templates/clips/server/plugins/db.ts +18 -0
  66. package/corpus/templates/clips/server/routes/api/_agent-native-background/post-finalize-worker.post.ts +48 -5
  67. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/abort.post.ts +322 -31
  68. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +223 -61
  69. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/interrupt.post.ts +62 -4
  70. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/reset-chunks.post.ts +74 -7
  71. package/corpus/templates/clips/shared/recording-core.ts +27 -0
  72. package/corpus/templates/design/actions/get-design.ts +35 -6
  73. package/corpus/templates/mail/.agents/skills/inbox-automations/SKILL.md +4 -0
  74. package/corpus/templates/mail/actions/consolidate-ai-filter-rules.ts +41 -0
  75. package/corpus/templates/mail/actions/get-ai-priority.ts +48 -8
  76. package/corpus/templates/mail/actions/record-ai-priority-feedback.ts +69 -0
  77. package/corpus/templates/mail/actions/update-mail-preferences.ts +2 -0
  78. package/corpus/templates/mail/app/components/email/EmailList.tsx +179 -32
  79. package/corpus/templates/mail/app/components/email/EmailListItem.tsx +97 -9
  80. package/corpus/templates/mail/app/components/email/EmailThread.tsx +55 -1
  81. package/corpus/templates/mail/app/components/email/ImportanceFeedbackMenu.tsx +52 -0
  82. package/corpus/templates/mail/app/components/email/compose-autocomplete.ts +2 -1
  83. package/corpus/templates/mail/app/components/layout/AppLayout.tsx +219 -188
  84. package/corpus/templates/mail/app/components/onboarding/AiInboxSetup.tsx +338 -0
  85. package/corpus/templates/mail/app/components/settings/AiFilterSection.tsx +597 -781
  86. package/corpus/templates/mail/app/components/settings/AiRulePromptField.tsx +37 -0
  87. package/corpus/templates/mail/app/global.css +42 -26
  88. package/corpus/templates/mail/app/hooks/use-ai-priority-feedback.ts +43 -0
  89. package/corpus/templates/mail/app/hooks/use-automations.ts +23 -0
  90. package/corpus/templates/mail/app/i18n/en-US.ts +58 -3
  91. package/corpus/templates/mail/app/pages/InboxPage.tsx +47 -10
  92. package/corpus/templates/mail/changelog/2026-09-25-editing-importance-rules-preserves-disabled-instructions-and.md +6 -0
  93. package/corpus/templates/mail/changelog/2026-09-25-handle-astral-unicode-letters-in-autocomplete-word-boundarie.md +6 -0
  94. package/corpus/templates/mail/changelog/2026-09-25-jev-priority-feedback-and-inbox-settings.md +6 -0
  95. package/corpus/templates/mail/changelog/2026-09-25-keep-existing-ai-filter-rules-intact-when-saving-a-prompt.md +6 -0
  96. package/corpus/templates/mail/server/lib/automation-engine.ts +21 -12
  97. package/corpus/templates/mail/server/lib/automations.ts +100 -1
  98. package/corpus/templates/mail/shared/types.ts +3 -0
  99. package/corpus/templates/plan/app/i18n/en-US.ts +6 -0
  100. package/corpus/templates/plan/app/lib/plan-invite-suggestion.ts +43 -0
  101. package/corpus/templates/plan/app/pages/PlansPage.tsx +249 -4
  102. package/corpus/templates/slides/actions/create-deck.ts +11 -1
  103. package/corpus/templates/slides/app/components/deck/SlideRenderer.tsx +117 -1
  104. package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +373 -330
  105. package/corpus/templates/slides/app/components/design-system/builder-index-response.ts +36 -5
  106. package/corpus/templates/slides/app/components/editor/BlockBubbleMenu.tsx +21 -53
  107. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +347 -671
  108. package/corpus/templates/slides/app/components/editor/bullet-editing.ts +55 -8
  109. package/corpus/templates/slides/app/components/editor/in-place-text-session.ts +2082 -0
  110. package/corpus/templates/slides/app/components/editor/list-editing.ts +174 -16
  111. package/corpus/templates/slides/app/components/editor/rich-text-selection.ts +497 -62
  112. package/corpus/templates/slides/app/components/editor/slide-text-targets.ts +102 -20
  113. package/corpus/templates/slides/app/context/DeckContext.tsx +9 -4
  114. package/corpus/templates/slides/app/global.css +7 -219
  115. package/corpus/templates/slides/app/i18n/en-US.ts +3 -0
  116. package/corpus/templates/slides/app/lib/sanitize-slide-html.ts +7 -0
  117. package/corpus/templates/slides/app/lib/slide-image-replacement.ts +203 -1
  118. package/corpus/templates/slides/app/lib/slide-source-map.ts +43 -0
  119. package/corpus/templates/slides/app/pages/DeckEditor.tsx +182 -4
  120. package/corpus/templates/slides/app/routes/settings.tsx +33 -2
  121. package/corpus/templates/slides/changelog/2026-09-25-improve-slides-design-system-setup-feedback.md +6 -0
  122. package/corpus/templates/slides/changelog/2026-09-25-org-admins-can-review-agent-runs-from-settings.md +6 -0
  123. package/corpus/templates/slides/changelog/2026-09-25-text-on-a-slide-is-now-edited-in-place-clicking-into-it-chan.md +6 -0
  124. package/corpus/templates/slides/package.json +0 -12
  125. package/corpus/templates/slides/scripts/edit-fidelity/README.md +4 -2
  126. package/corpus/templates/slides/scripts/edit-fidelity/run.ts +12 -3
  127. package/corpus/templates/slides/vite.config.ts +1 -10
  128. package/dist/agent/jev-tool-prefetch.d.ts +29 -3
  129. package/dist/agent/jev-tool-prefetch.js +161 -34
  130. package/dist/agent/production-agent.d.ts +11 -0
  131. package/dist/agent/production-agent.js +39 -8
  132. package/dist/app-config/runtime.d.ts +1 -0
  133. package/dist/app-config/runtime.js +4 -0
  134. package/dist/app-config/schema.d.ts +1 -0
  135. package/dist/client/AgentSidebar.d.ts +3 -1
  136. package/dist/client/AgentSidebar.js +8 -12
  137. package/dist/client/analytics/index.d.ts +1 -1
  138. package/dist/client/analytics/index.js +1 -1
  139. package/dist/client/analytics.d.ts +2 -0
  140. package/dist/client/analytics.js +9 -0
  141. package/dist/client/api-path.js +5 -2
  142. package/dist/client/chat/run-recovery.d.ts +0 -3
  143. package/dist/client/chat/run-recovery.js +13 -23
  144. package/dist/client/index.d.ts +1 -1
  145. package/dist/client/index.js +1 -1
  146. package/dist/client/observability/ObservabilityDashboard.d.ts +3 -0
  147. package/dist/client/observability/ObservabilityDashboard.js +366 -71
  148. package/dist/client/observability/ObservabilityReviewSummaryButton.d.ts +4 -1
  149. package/dist/client/observability/ObservabilityReviewSummaryButton.js +13 -9
  150. package/dist/client/observability/OutputPreview.d.ts +9 -4
  151. package/dist/client/observability/OutputPreview.js +230 -52
  152. package/dist/client/onboarding/FirstRunOnboarding.js +67 -4
  153. package/dist/client/onboarding/use-onboarding.js +2 -0
  154. package/dist/client/sharing/ShareButton.d.ts +2 -0
  155. package/dist/client/sharing/ShareButton.js +1 -0
  156. package/dist/client/sharing/useShareButtonController.d.ts +1 -0
  157. package/dist/client/sharing/useShareButtonController.js +29 -8
  158. package/dist/collab/awareness.d.ts +2 -2
  159. package/dist/collab/struct-routes.d.ts +1 -1
  160. package/dist/deploy/build.js +9 -1
  161. package/dist/embeddings/index.d.ts +3 -1
  162. package/dist/embeddings/index.js +20 -10
  163. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  164. package/dist/jobs/sweep-hooks.d.ts +7 -0
  165. package/dist/jobs/sweep-hooks.js +28 -0
  166. package/dist/localization/default-messages.d.ts +1 -0
  167. package/dist/localization/default-messages.js +1 -0
  168. package/dist/notifications/routes.d.ts +6 -6
  169. package/dist/observability/actions/authorization.d.ts +1 -0
  170. package/dist/observability/actions/authorization.js +22 -0
  171. package/dist/observability/actions/get-observability-review-detail.d.ts +5 -0
  172. package/dist/observability/actions/get-observability-review-detail.js +10 -2
  173. package/dist/observability/actions/get-observability-review-summary-source.d.ts +2 -0
  174. package/dist/observability/actions/get-observability-review-summary-source.js +3 -2
  175. package/dist/observability/actions/save-observability-instruction-update.js +2 -1
  176. package/dist/observability/actions/save-observability-review-summary.js +21 -10
  177. package/dist/observability/migrations.js +25 -0
  178. package/dist/observability/reviews.d.ts +3 -1
  179. package/dist/observability/reviews.js +410 -53
  180. package/dist/observability/routes.d.ts +6 -6
  181. package/dist/observability/store.d.ts +15 -0
  182. package/dist/observability/store.js +131 -11
  183. package/dist/observability/types.d.ts +18 -0
  184. package/dist/org/handlers.d.ts +1 -1
  185. package/dist/org/index.d.ts +1 -1
  186. package/dist/progress/routes.d.ts +1 -1
  187. package/dist/provider-api/actions/custom-provider-registration.d.ts +5 -5
  188. package/dist/provider-api/actions/provider-api.d.ts +8 -8
  189. package/dist/server/agent-chat/plugin-options.d.ts +4 -20
  190. package/dist/server/agent-chat/prompt-resources.d.ts +16 -0
  191. package/dist/server/agent-chat/prompt-resources.js +385 -22
  192. package/dist/server/agent-chat-plugin.d.ts +3 -1
  193. package/dist/server/agent-chat-plugin.js +149 -27
  194. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  195. package/dist/server/agent-engine-ollama-models-route.d.ts +1 -1
  196. package/dist/server/index.d.ts +3 -0
  197. package/dist/server/index.js +3 -0
  198. package/dist/server/prompts/framework-core-compact.js +1 -1
  199. package/dist/server/prompts/framework-core.js +1 -1
  200. package/dist/server/request-context.d.ts +6 -3
  201. package/dist/settings/org-settings.d.ts +4 -2
  202. package/dist/settings/org-settings.js +3 -3
  203. package/dist/settings/store.d.ts +3 -1
  204. package/dist/settings/store.js +8 -3
  205. package/dist/styles/agent-native.css +5 -0
  206. package/dist/templates/default/app/i18n/ar-SA.ts +1 -0
  207. package/dist/templates/default/app/i18n/de-DE.ts +1 -0
  208. package/dist/templates/default/app/i18n/en-US.ts +1 -0
  209. package/dist/templates/default/app/i18n/es-ES.ts +1 -0
  210. package/dist/templates/default/app/i18n/fr-FR.ts +1 -0
  211. package/dist/templates/default/app/i18n/hi-IN.ts +1 -0
  212. package/dist/templates/default/app/i18n/ja-JP.ts +1 -0
  213. package/dist/templates/default/app/i18n/ko-KR.ts +1 -0
  214. package/dist/templates/default/app/i18n/pt-BR.ts +1 -0
  215. package/dist/templates/default/app/i18n/zh-CN.ts +1 -0
  216. package/dist/templates/default/app/i18n/zh-TW.ts +1 -0
  217. package/dist/tracking/registry.js +11 -4
  218. package/dist/vite/client.js +6 -0
  219. package/package.json +4 -3
  220. package/src/templates/default/app/i18n/ar-SA.ts +1 -0
  221. package/src/templates/default/app/i18n/de-DE.ts +1 -0
  222. package/src/templates/default/app/i18n/en-US.ts +1 -0
  223. package/src/templates/default/app/i18n/es-ES.ts +1 -0
  224. package/src/templates/default/app/i18n/fr-FR.ts +1 -0
  225. package/src/templates/default/app/i18n/hi-IN.ts +1 -0
  226. package/src/templates/default/app/i18n/ja-JP.ts +1 -0
  227. package/src/templates/default/app/i18n/ko-KR.ts +1 -0
  228. package/src/templates/default/app/i18n/pt-BR.ts +1 -0
  229. package/src/templates/default/app/i18n/zh-CN.ts +1 -0
  230. package/src/templates/default/app/i18n/zh-TW.ts +1 -0
  231. package/corpus/templates/slides/app/components/editor/SlideBubbleMenu.tsx +0 -488
  232. package/corpus/templates/slides/app/components/editor/SlideInlineEditor.tsx +0 -589
  233. package/corpus/templates/slides/app/components/editor/SlideRichTextEditor.tsx +0 -1347
  234. package/corpus/templates/slides/app/components/editor/SlideSlashMenu.tsx +0 -373
@@ -1,7 +1,7 @@
1
1
  import {
2
- getAllSettings,
3
2
  getUserSetting,
4
3
  listOrgSettings,
4
+ listSettingsByPrefix,
5
5
  } from "@agent-native/core/settings";
6
6
 
7
7
  import { dashboardCatalogEntries } from "./dashboard-catalog";
@@ -19,6 +19,9 @@ import {
19
19
  const DATA_DICTIONARY_KEY_PREFIX = "data-dict-";
20
20
  const MAX_QUERY_LENGTH = 12_000;
21
21
  const MAX_CATALOG_DASHBOARD_HYDRATION = 24;
22
+ // ponytail: scan the 200 most recently updated summaries; a search-backed index is the upgrade path if larger workspaces need older references.
23
+ const MAX_CATALOG_DASHBOARD_SUMMARIES = 200;
24
+ const MAX_CATALOG_DICTIONARY_ENTRIES = 200;
22
25
  const RETIRED_CATALOG_STATES = new Set([
23
26
  "deprecated",
24
27
  "obsolete",
@@ -118,11 +121,29 @@ export type AnalyticsQueryCatalogCandidate =
118
121
  columnsUsed?: string;
119
122
  queryTemplate?: string;
120
123
  knownGotchas?: string;
124
+ commonQuestions?: string;
125
+ cuts?: string;
126
+ joinPattern?: string;
127
+ updateFrequency?: string;
128
+ dataLag?: string;
129
+ dependencies?: string;
130
+ validDateRange?: string;
131
+ owner?: string;
121
132
  approved?: boolean;
122
133
  aiGenerated?: boolean;
123
134
  sourceUrl?: string;
124
135
  };
125
136
 
137
+ export type AnalyticsQueryCatalogSearchResult = {
138
+ candidates: AnalyticsQueryCatalogCandidate[];
139
+ searchedDashboardCount: number;
140
+ dashboardSearchTruncated: boolean;
141
+ dashboardSearchStatus: "available" | "unavailable";
142
+ searchedDictionaryEntryCount: number;
143
+ dictionarySearchTruncated: boolean;
144
+ dictionarySearchStatus: "available" | "partial" | "unavailable";
145
+ };
146
+
126
147
  function text(value: unknown): string {
127
148
  return typeof value === "string" ? value.trim() : "";
128
149
  }
@@ -452,7 +473,7 @@ function dictionaryCandidates(
452
473
  entries: DictionaryEntry[],
453
474
  search: string,
454
475
  ): AnalyticsQueryCatalogCandidate[] {
455
- return entries.flatMap((entry) => {
476
+ const candidates = entries.flatMap((entry) => {
456
477
  if (isRetiredCatalogReference(entry)) return [];
457
478
  const { score, matchedTerms } = matchScore(search, [
458
479
  { value: entry.metric, weight: 28 },
@@ -493,6 +514,24 @@ function dictionaryCandidates(
493
514
  ...(text(entry.knownGotchas)
494
515
  ? { knownGotchas: text(entry.knownGotchas) }
495
516
  : {}),
517
+ ...(text(entry.commonQuestions)
518
+ ? { commonQuestions: text(entry.commonQuestions) }
519
+ : {}),
520
+ ...(text(entry.cuts) ? { cuts: text(entry.cuts) } : {}),
521
+ ...(text(entry.joinPattern)
522
+ ? { joinPattern: text(entry.joinPattern) }
523
+ : {}),
524
+ ...(text(entry.updateFrequency)
525
+ ? { updateFrequency: text(entry.updateFrequency) }
526
+ : {}),
527
+ ...(text(entry.dataLag) ? { dataLag: text(entry.dataLag) } : {}),
528
+ ...(text(entry.dependencies)
529
+ ? { dependencies: text(entry.dependencies) }
530
+ : {}),
531
+ ...(text(entry.validDateRange)
532
+ ? { validDateRange: text(entry.validDateRange) }
533
+ : {}),
534
+ ...(text(entry.owner) ? { owner: text(entry.owner) } : {}),
496
535
  ...(typeof entry.approved === "boolean"
497
536
  ? { approved: entry.approved }
498
537
  : {}),
@@ -503,6 +542,19 @@ function dictionaryCandidates(
503
542
  },
504
543
  ];
505
544
  });
545
+ const strongestHumanOrApprovedScore = candidates.reduce(
546
+ (strongest, candidate) =>
547
+ candidate.approved === true || candidate.aiGenerated !== true
548
+ ? Math.max(strongest, candidate.score)
549
+ : strongest,
550
+ 0,
551
+ );
552
+ return candidates.filter(
553
+ (candidate) =>
554
+ candidate.aiGenerated !== true ||
555
+ candidate.approved === true ||
556
+ candidate.score > strongestHumanOrApprovedScore,
557
+ );
506
558
  }
507
559
 
508
560
  function candidateIsRunnable(
@@ -525,7 +577,9 @@ function candidateDedupeKey(candidate: AnalyticsQueryCatalogCandidate): string {
525
577
  .toLowerCase()}`;
526
578
  }
527
579
 
528
- function candidateTrustTier(candidate: AnalyticsQueryCatalogCandidate): number {
580
+ export function candidateTrustTier(
581
+ candidate: AnalyticsQueryCatalogCandidate,
582
+ ): number {
529
583
  if (candidate.kind !== "dashboard-panel") return 0;
530
584
  if (candidate.dashboardCertified) return 2;
531
585
  return candidate.favorite ? 1 : 0;
@@ -591,7 +645,12 @@ export function rankAnalyticsQueryCatalog(args: {
591
645
  async function listDictionaryEntries(args: {
592
646
  email: string;
593
647
  orgId: string | null;
594
- }): Promise<DictionaryEntry[]> {
648
+ }): Promise<{
649
+ entries: DictionaryEntry[];
650
+ searchedEntryCount: number;
651
+ truncated: boolean;
652
+ status: "available" | "partial" | "unavailable";
653
+ }> {
595
654
  const entries: DictionaryEntry[] = [];
596
655
  const seen = new Set<string>();
597
656
  const collect = (raw: unknown) => {
@@ -603,20 +662,76 @@ async function listDictionaryEntries(args: {
603
662
  entries.push(entry);
604
663
  };
605
664
 
606
- if (args.orgId) {
607
- const orgEntries = await listOrgSettings(
608
- args.orgId,
609
- DATA_DICTIONARY_KEY_PREFIX,
665
+ const userPrefix = `u:${args.email}:${DATA_DICTIONARY_KEY_PREFIX}`;
666
+ const [orgResult, userResult] = await Promise.allSettled([
667
+ args.orgId
668
+ ? listOrgSettings(args.orgId, DATA_DICTIONARY_KEY_PREFIX, {
669
+ limit: MAX_CATALOG_DICTIONARY_ENTRIES + 1,
670
+ })
671
+ : Promise.resolve(null),
672
+ listSettingsByPrefix(userPrefix, {
673
+ limit: MAX_CATALOG_DICTIONARY_ENTRIES + 1,
674
+ }),
675
+ ]);
676
+ if (orgResult.status === "fulfilled" && orgResult.value) {
677
+ const orgEntries = Object.entries(orgResult.value);
678
+ for (const [, value] of orgEntries.slice(
679
+ 0,
680
+ MAX_CATALOG_DICTIONARY_ENTRIES,
681
+ )) {
682
+ collect(value);
683
+ }
684
+ } else if (orgResult.status === "rejected") {
685
+ console.warn(
686
+ "[analytics-query-catalog] Organization dictionary lookup failed:",
687
+ orgResult.reason,
610
688
  );
611
- for (const value of Object.values(orgEntries)) collect(value);
612
689
  }
613
-
614
- const userPrefix = `u:${args.email}:${DATA_DICTIONARY_KEY_PREFIX}`;
615
- const all = await getAllSettings();
616
- for (const [key, value] of Object.entries(all)) {
617
- if (key.startsWith(userPrefix)) collect(value);
690
+ if (userResult.status === "fulfilled") {
691
+ for (const { value } of userResult.value.slice(
692
+ 0,
693
+ MAX_CATALOG_DICTIONARY_ENTRIES,
694
+ )) {
695
+ collect(value);
696
+ }
697
+ } else {
698
+ console.warn(
699
+ "[analytics-query-catalog] User dictionary lookup failed:",
700
+ userResult.reason,
701
+ );
618
702
  }
619
- return entries;
703
+ const orgCount =
704
+ orgResult.status === "fulfilled"
705
+ ? Object.keys(orgResult.value ?? {}).length
706
+ : 0;
707
+ const userCount =
708
+ userResult.status === "fulfilled" ? userResult.value.length : 0;
709
+ const scopedResults = [
710
+ ...(args.orgId ? [orgResult.status === "fulfilled"] : []),
711
+ userResult.status === "fulfilled",
712
+ ];
713
+ const availableScopes = scopedResults.filter(Boolean).length;
714
+ const scopeCount = args.orgId ? 2 : 1;
715
+ return {
716
+ entries,
717
+ searchedEntryCount: entries.length,
718
+ truncated:
719
+ orgCount > MAX_CATALOG_DICTIONARY_ENTRIES ||
720
+ userCount > MAX_CATALOG_DICTIONARY_ENTRIES,
721
+ status:
722
+ availableScopes === scopeCount
723
+ ? "available"
724
+ : availableScopes > 0
725
+ ? "partial"
726
+ : "unavailable",
727
+ };
728
+ }
729
+
730
+ function warnCatalogReadFailure(source: string, error: unknown): void {
731
+ console.warn(
732
+ `[analytics-query-catalog] ${source} lookup failed; continuing with available references:`,
733
+ error,
734
+ );
620
735
  }
621
736
 
622
737
  function savedDashboardInput(
@@ -640,8 +755,10 @@ export async function searchAnalyticsQueryCatalog(args: {
640
755
  email: string;
641
756
  orgId: string | null;
642
757
  limit: number;
643
- }): Promise<AnalyticsQueryCatalogCandidate[]> {
644
- const [savedSummariesResult, dictionaryEntriesResult, favoriteIdsResult] =
758
+ signal?: AbortSignal;
759
+ }): Promise<AnalyticsQueryCatalogSearchResult> {
760
+ args.signal?.throwIfAborted();
761
+ const [summariesResult, dictionaryResult, favoritesResult] =
645
762
  await Promise.allSettled([
646
763
  listDashboardSummaries(
647
764
  { email: args.email, orgId: args.orgId },
@@ -650,38 +767,83 @@ export async function searchAnalyticsQueryCatalog(args: {
650
767
  archived: "active",
651
768
  hidden: "visible",
652
769
  includeCatalogMetadata: true,
770
+ limit: MAX_CATALOG_DASHBOARD_SUMMARIES + 1,
653
771
  },
654
772
  ),
655
773
  listDictionaryEntries({ email: args.email, orgId: args.orgId }),
656
774
  listFavoriteDashboardIds(args.email),
657
775
  ]);
658
- const favoriteIds =
659
- favoriteIdsResult.status === "fulfilled"
660
- ? favoriteIdsResult.value
661
- : new Set<string>();
776
+ args.signal?.throwIfAborted();
662
777
  const savedSummaries =
663
- savedSummariesResult.status === "fulfilled"
664
- ? savedSummariesResult.value
665
- : [];
778
+ summariesResult.status === "fulfilled" ? summariesResult.value : [];
779
+ const searchedSummaries = savedSummaries.slice(
780
+ 0,
781
+ MAX_CATALOG_DASHBOARD_SUMMARIES,
782
+ );
783
+ const dashboardSearchTruncated =
784
+ savedSummaries.length > MAX_CATALOG_DASHBOARD_SUMMARIES;
785
+ if (dashboardSearchTruncated) {
786
+ console.warn("[analytics] Dashboard reference search truncated.", {
787
+ searchedDashboardCount: searchedSummaries.length,
788
+ dashboardSearchTruncated,
789
+ });
790
+ }
666
791
  const dictionaryEntries =
667
- dictionaryEntriesResult.status === "fulfilled"
668
- ? dictionaryEntriesResult.value
792
+ dictionaryResult.status === "fulfilled"
793
+ ? dictionaryResult.value.entries
669
794
  : [];
795
+ const searchedDictionaryEntryCount =
796
+ dictionaryResult.status === "fulfilled"
797
+ ? dictionaryResult.value.searchedEntryCount
798
+ : 0;
799
+ const dictionarySearchTruncated =
800
+ dictionaryResult.status === "fulfilled" && dictionaryResult.value.truncated;
801
+ let dashboardSearchStatus: "available" | "unavailable" =
802
+ summariesResult.status === "fulfilled" ? "available" : "unavailable";
803
+ const dictionarySearchStatus =
804
+ dictionaryResult.status === "fulfilled"
805
+ ? dictionaryResult.value.status
806
+ : "unavailable";
807
+ if (dictionarySearchTruncated) {
808
+ console.warn("[analytics] Data dictionary search truncated.", {
809
+ searchedDictionaryEntryCount,
810
+ dictionarySearchTruncated,
811
+ });
812
+ }
813
+ const favoriteIds =
814
+ favoritesResult.status === "fulfilled"
815
+ ? favoritesResult.value
816
+ : new Set<string>();
817
+ if (summariesResult.status === "rejected") {
818
+ warnCatalogReadFailure("Dashboard summary", summariesResult.reason);
819
+ }
820
+ if (dictionaryResult.status === "rejected") {
821
+ warnCatalogReadFailure("Data dictionary", dictionaryResult.reason);
822
+ }
823
+ if (favoritesResult.status === "rejected") {
824
+ warnCatalogReadFailure("Favorite dashboard", favoritesResult.reason);
825
+ }
670
826
  const savedSummaryIds = new Set(
671
827
  savedSummaries.map((dashboard) => dashboard.id),
672
828
  );
673
829
 
674
830
  const shortlistedSummaries = shortlistDashboardSummaries(
675
831
  args.search,
676
- savedSummaries,
832
+ searchedSummaries,
677
833
  args.limit,
678
834
  favoriteIds,
679
835
  );
680
836
  const shortlistedIds = shortlistedSummaries.map((dashboard) => dashboard.id);
837
+ args.signal?.throwIfAborted();
681
838
  const savedDashboardsResult = await loadDashboardCatalogDashboards(
682
839
  { email: args.email, orgId: args.orgId },
683
840
  shortlistedIds,
684
- );
841
+ ).catch((error: unknown) => {
842
+ warnCatalogReadFailure("Dashboard detail", error);
843
+ dashboardSearchStatus = "unavailable";
844
+ return [];
845
+ });
846
+ args.signal?.throwIfAborted();
685
847
  const savedDashboards = new Map(
686
848
  savedDashboardsResult.map((dashboard) => [dashboard.id, dashboard]),
687
849
  );
@@ -707,22 +869,31 @@ export async function searchAnalyticsQueryCatalog(args: {
707
869
  }
708
870
  });
709
871
 
710
- return rankAnalyticsQueryCatalog({
711
- search: args.search,
712
- dashboards: [
713
- ...shortlistedSummaries.flatMap((summary) => {
714
- const dashboard = savedDashboards.get(summary.id);
715
- if (!dashboard) return [];
716
- return [
717
- savedDashboardInput(
718
- { ...summary, favorite: favoriteIds.has(summary.id) },
719
- dashboard,
720
- ),
721
- ];
722
- }),
723
- ...templateDashboards,
724
- ],
725
- dictionaryEntries,
726
- limit: args.limit,
727
- });
872
+ return {
873
+ candidates: rankAnalyticsQueryCatalog({
874
+ search: args.search,
875
+ dashboards: [
876
+ ...shortlistedSummaries.flatMap((summary) => {
877
+ const dashboard = savedDashboards.get(summary.id);
878
+ if (!dashboard) return [];
879
+ return [
880
+ savedDashboardInput(
881
+ { ...summary, favorite: favoriteIds.has(summary.id) },
882
+ dashboard,
883
+ ),
884
+ ];
885
+ }),
886
+ // A truncated window cannot prove a shipped default has no saved version.
887
+ ...(dashboardSearchTruncated ? [] : templateDashboards),
888
+ ],
889
+ dictionaryEntries,
890
+ limit: args.limit,
891
+ }),
892
+ searchedDashboardCount: searchedSummaries.length,
893
+ dashboardSearchTruncated,
894
+ dashboardSearchStatus,
895
+ searchedDictionaryEntryCount,
896
+ dictionarySearchTruncated,
897
+ dictionarySearchStatus,
898
+ };
728
899
  }
@@ -931,7 +931,7 @@ export const dashboardCatalogEntries: DashboardCatalogEntry[] = [
931
931
  id: "first-party-template-traffic",
932
932
  name: "First-party Template Traffic",
933
933
  description:
934
- "Template signups, activation and onboarding funnel, sharing actions, clicks, demo starts, CLI copies, activity, top URLs, top clips, retention by template, and active users over time.",
934
+ "Template signups, activation and onboarding step/choice funnels, sharing actions, clicks, demo starts, CLI copies, activity, top URLs, top clips, retention by template, and active users over time.",
935
935
  category: "Product",
936
936
  defaultDashboardId: "agent-native-templates-first-party",
937
937
  dataSources: ["first-party"],
@@ -948,7 +948,7 @@ export const dashboardCatalogEntries: DashboardCatalogEntry[] = [
948
948
  "referrals",
949
949
  "virality",
950
950
  ],
951
- panelCount: 42,
951
+ panelCount: 43,
952
952
  version: CATALOG_VERSION,
953
953
  recommended: true,
954
954
  visibleInCatalog: false,
@@ -31,6 +31,7 @@ import {
31
31
  } from "@agent-native/core/sharing";
32
32
  import {
33
33
  and,
34
+ asc,
34
35
  desc,
35
36
  eq,
36
37
  inArray,
@@ -220,18 +221,29 @@ interface AccessCtx {
220
221
  */
221
222
  async function getScopedLegacySettings(
222
223
  ctx: Pick<AccessCtx, "email" | "orgId">,
224
+ options?: { dashboardKind?: DashboardKind; limit?: number },
223
225
  ): Promise<Record<string, Record<string, unknown>>> {
224
226
  // User scope first, then org: callers append these to the SQL rows in
225
227
  // iteration order and never re-sort, so the order is user-visible. The
226
228
  // previous full-table read inherited whatever order the settings table
227
229
  // returned, which no query pinned.
228
230
  const prefixes: string[] = [];
229
- if (ctx.email) prefixes.push(`u:${ctx.email}:`);
230
- if (ctx.orgId) prefixes.push(`o:${ctx.orgId}:`);
231
+ if (options?.dashboardKind === "sql") {
232
+ if (ctx.email) prefixes.push(`u:${ctx.email}:${SQL_PREFIX}`);
233
+ if (ctx.orgId) prefixes.push(`o:${ctx.orgId}:${SQL_PREFIX}`);
234
+ } else {
235
+ if (ctx.email) prefixes.push(`u:${ctx.email}:`);
236
+ if (ctx.orgId) prefixes.push(`o:${ctx.orgId}:`);
237
+ }
231
238
  if (prefixes.length === 0) return {};
232
239
  const scoped: Record<string, Record<string, unknown>> = {};
233
240
  for (const entries of await Promise.all(
234
- prefixes.map((prefix) => listSettingsByPrefix(prefix)),
241
+ prefixes.map((prefix) =>
242
+ listSettingsByPrefix(
243
+ prefix,
244
+ options?.limit === undefined ? undefined : { limit: options.limit },
245
+ ),
246
+ ),
235
247
  )) {
236
248
  for (const { key, value } of entries) scoped[key] = value;
237
249
  }
@@ -956,6 +968,7 @@ export async function listDashboardSummaries(
956
968
  hidden?: DashboardHiddenFilter;
957
969
  includeCatalogMetadata?: boolean;
958
970
  legacyScan?: "best-effort" | "strict";
971
+ limit?: number;
959
972
  },
960
973
  dbOverride?: any,
961
974
  ): Promise<DashboardSummaryRecord[]> {
@@ -963,6 +976,15 @@ export async function listDashboardSummaries(
963
976
  const archived = filter?.archived ?? "active";
964
977
  const hidden = filter?.hidden ?? "visible";
965
978
  const includeCatalogMetadata = filter?.includeCatalogMetadata === true;
979
+ const summaryLimit = filter?.limit;
980
+ if (
981
+ summaryLimit !== undefined &&
982
+ (!Number.isSafeInteger(summaryLimit) || summaryLimit < 0)
983
+ ) {
984
+ throw new RangeError(
985
+ "Dashboard summary limit must be a non-negative integer.",
986
+ );
987
+ }
966
988
  const conditions: any[] = [
967
989
  accessFilter(schema.dashboards, schema.dashboardShares, {
968
990
  userEmail: ctx.email,
@@ -993,7 +1015,7 @@ export async function listDashboardSummaries(
993
1015
  const demoId = sql<
994
1016
  string | null
995
1017
  >`(${schema.dashboards.config}::jsonb -> 'demo' ->> 'id')`;
996
- const rows = await db
1018
+ const rowsQuery = db
997
1019
  .select({
998
1020
  id: schema.dashboards.id,
999
1021
  kind: schema.dashboards.kind,
@@ -1016,6 +1038,11 @@ export async function listDashboardSummaries(
1016
1038
  })
1017
1039
  .from(schema.dashboards)
1018
1040
  .where(where);
1041
+ const rows = await (summaryLimit === undefined
1042
+ ? rowsQuery
1043
+ : rowsQuery
1044
+ .orderBy(desc(schema.dashboards.updatedAt), asc(schema.dashboards.id))
1045
+ .limit(summaryLimit));
1019
1046
  const out: DashboardSummaryRecord[] = rows.map((row: any) => {
1020
1047
  const certification = parseDashboardCertification(row.certification);
1021
1048
  const { certification: _rawCertification, ...summaryRow } = row;
@@ -1041,10 +1068,17 @@ export async function listDashboardSummaries(
1041
1068
  });
1042
1069
  const seen = new Set(out.map((row) => row.id));
1043
1070
 
1071
+ if (summaryLimit !== undefined && out.length >= summaryLimit) return out;
1044
1072
  if (archived === "archived" || hidden === "hidden") return out;
1045
1073
  try {
1046
- const all = await getScopedLegacySettings(ctx);
1074
+ const all = await getScopedLegacySettings(
1075
+ ctx,
1076
+ summaryLimit === undefined
1077
+ ? undefined
1078
+ : { dashboardKind: filter?.kind, limit: summaryLimit },
1079
+ );
1047
1080
  for (const [key, value] of Object.entries(all)) {
1081
+ if (summaryLimit !== undefined && out.length >= summaryLimit) break;
1048
1082
  let id: string | null = null;
1049
1083
  let kind: DashboardKind | null = null;
1050
1084
  let orgId: string | null = null;
@@ -59,6 +59,10 @@ function renderEntry(
59
59
 
60
60
  const table = compact(entry.table, 240);
61
61
  if (table) lines.push(` - table: ${table}`);
62
+ const source = compact(entry.source, 240);
63
+ if (source) lines.push(` - source: ${source}`);
64
+ const action = compact(entry.action, 160);
65
+ if (action) lines.push(` - query action: ${action}`);
62
66
  const columns = compact(entry.columnsUsed, 360);
63
67
  if (columns) lines.push(` - columns: ${columns}`);
64
68
  const cuts = compact(entry.cuts, 240);