@agent-native/core 0.131.9 → 0.132.1

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 (295) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +105 -0
  3. package/corpus/core/docs/content/durable-background-runs.mdx +15 -6
  4. package/corpus/core/docs/design/durable-agent-runs.md +15 -11
  5. package/corpus/core/package.json +1 -1
  6. package/corpus/core/src/a2a/activity.ts +127 -10
  7. package/corpus/core/src/a2a/index.ts +3 -0
  8. package/corpus/core/src/action.ts +52 -11
  9. package/corpus/core/src/agent/durable-background.ts +38 -28
  10. package/corpus/core/src/agent/harness/background.ts +12 -2
  11. package/corpus/core/src/agent/production-agent.ts +210 -43
  12. package/corpus/core/src/agent/run-store.ts +45 -3
  13. package/corpus/core/src/application-state/handlers.ts +45 -0
  14. package/corpus/core/src/application-state/index.ts +3 -0
  15. package/corpus/core/src/application-state/store.ts +28 -11
  16. package/corpus/core/src/audit/redact.ts +104 -18
  17. package/corpus/core/src/client/AgentPanel.tsx +2 -7
  18. package/corpus/core/src/client/MultiTabAssistantChat.tsx +3 -0
  19. package/corpus/core/src/client/RunStuckBanner.tsx +29 -6
  20. package/corpus/core/src/client/agent-chat.ts +4 -9
  21. package/corpus/core/src/client/application-state.ts +115 -12
  22. package/corpus/core/src/client/dynamic-suggestions.ts +4 -12
  23. package/corpus/core/src/client/guided-questions.tsx +12 -23
  24. package/corpus/core/src/db/client.ts +148 -28
  25. package/corpus/core/src/db/create-get-db.ts +39 -6
  26. package/corpus/core/src/db/runtime-diagnostics.ts +41 -6
  27. package/corpus/core/src/deploy/build.ts +26 -33
  28. package/corpus/core/src/feature-flags/store.ts +10 -5
  29. package/corpus/core/src/integrations/a2a-continuations-store.ts +11 -0
  30. package/corpus/core/src/integrations/config-store.ts +13 -0
  31. package/corpus/core/src/integrations/google-docs-poller.ts +26 -2
  32. package/corpus/core/src/mcp-client/routes.ts +33 -2
  33. package/corpus/core/src/onboarding/plugin.ts +31 -25
  34. package/corpus/core/src/org/accept-pending.ts +2 -0
  35. package/corpus/core/src/org/auto-join-domain.ts +2 -0
  36. package/corpus/core/src/org/context.ts +35 -10
  37. package/corpus/core/src/org/handlers.ts +6 -0
  38. package/corpus/core/src/org/request-org-cache.ts +71 -0
  39. package/corpus/core/src/resources/store.ts +10 -9
  40. package/corpus/core/src/scripts/runner.ts +6 -3
  41. package/corpus/core/src/secrets/storage.ts +97 -5
  42. package/corpus/core/src/server/action-change.ts +33 -0
  43. package/corpus/core/src/server/action-routes.ts +11 -9
  44. package/corpus/core/src/server/agent-teams.ts +10 -2
  45. package/corpus/core/src/server/better-auth-instance.ts +39 -12
  46. package/corpus/core/src/server/core-routes-plugin.ts +19 -2
  47. package/corpus/core/src/server/credential-provider.ts +57 -14
  48. package/corpus/core/src/server/http-response-telemetry.ts +1 -0
  49. package/corpus/core/src/server/poll.ts +95 -57
  50. package/corpus/core/src/server/voice-providers-status.ts +19 -4
  51. package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +38 -0
  52. package/corpus/templates/analytics/actions/dashboard-mutation-api.ts +1 -1
  53. package/corpus/templates/analytics/actions/ensure-native-v2-dashboards.ts +299 -0
  54. package/corpus/templates/analytics/actions/list-native-v2-dashboard-templates.ts +34 -0
  55. package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +82 -0
  56. package/corpus/templates/analytics/app/components/layout/NewDashboardDialog.tsx +2 -2
  57. package/corpus/templates/analytics/app/hooks/use-dashboard-chat-context.ts +40 -34
  58. package/corpus/templates/analytics/app/hooks/use-user-pref.ts +3 -3
  59. package/corpus/templates/analytics/app/i18n-data.ts +40 -0
  60. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/PanelEditorDialog.tsx +18 -10
  61. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +14 -0
  62. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/types.ts +1 -0
  63. package/corpus/templates/analytics/app/routes/chart.tsx +3 -0
  64. package/corpus/templates/analytics/changelog/2026-07-30-analytics-dashboards-now-expose-native-funnel-heatmap-callou.md +6 -0
  65. package/corpus/templates/analytics/changelog/2026-07-30-analytics-now-ships-reusable-native-v2-dashboard-manifests-w.md +6 -0
  66. package/corpus/templates/analytics/server/lib/dashboard-report-render.ts +81 -1
  67. package/corpus/templates/analytics/server/lib/native-v2-dashboards.ts +457 -0
  68. package/corpus/templates/analytics/server/lib/report-chart-svg.ts +73 -2
  69. package/corpus/templates/analytics/server/plugins/agent-chat.ts +1 -1
  70. package/corpus/templates/analytics/shared/dashboard-funnel.ts +83 -0
  71. package/corpus/templates/content/changelog/2026-07-30-long-running-delegated-requests-now-continue-reliably.md +6 -0
  72. package/corpus/templates/content/netlify.toml +3 -0
  73. package/corpus/templates/content/server/plugins/agent-chat.ts +1 -0
  74. package/corpus/templates/slides/.agents/skills/create-deck/SKILL.md +2 -1
  75. package/corpus/templates/slides/actions/patch-deck.ts +25 -1
  76. package/corpus/templates/slides/app/components/deck/DeckCard.tsx +17 -4
  77. package/corpus/templates/slides/app/components/deck/SlideRenderer.tsx +9 -1
  78. package/corpus/templates/slides/app/components/layout/Layout.tsx +1 -1
  79. package/corpus/templates/slides/app/context/DeckContext.tsx +11 -3
  80. package/corpus/templates/slides/app/lib/sanitize-slide-html.ts +39 -11
  81. package/corpus/templates/slides/app/pages/Index.tsx +2 -2
  82. package/corpus/templates/slides/changelog/2026-07-30-slide-previews-keep-their-aspect-ratio-isolate-embedded-styl.md +6 -0
  83. package/corpus/templates/slides/changelog/2026-07-30-slides-no-longer-lose-decks-while-organization-access-is-loa.md +6 -0
  84. package/corpus/toolkit/CHANGELOG.md +8 -0
  85. package/corpus/toolkit/package.json +1 -1
  86. package/corpus/toolkit/src/composer/runtime-adapters.tsx +16 -11
  87. package/dist/a2a/activity.d.ts +22 -4
  88. package/dist/a2a/activity.d.ts.map +1 -1
  89. package/dist/a2a/activity.js +97 -11
  90. package/dist/a2a/activity.js.map +1 -1
  91. package/dist/a2a/index.d.ts +1 -1
  92. package/dist/a2a/index.d.ts.map +1 -1
  93. package/dist/a2a/index.js +1 -1
  94. package/dist/a2a/index.js.map +1 -1
  95. package/dist/action.d.ts.map +1 -1
  96. package/dist/action.js +41 -11
  97. package/dist/action.js.map +1 -1
  98. package/dist/agent/durable-background.d.ts +13 -8
  99. package/dist/agent/durable-background.d.ts.map +1 -1
  100. package/dist/agent/durable-background.js +36 -28
  101. package/dist/agent/durable-background.js.map +1 -1
  102. package/dist/agent/harness/background.d.ts.map +1 -1
  103. package/dist/agent/harness/background.js +12 -2
  104. package/dist/agent/harness/background.js.map +1 -1
  105. package/dist/agent/production-agent.d.ts.map +1 -1
  106. package/dist/agent/production-agent.js +195 -44
  107. package/dist/agent/production-agent.js.map +1 -1
  108. package/dist/agent/run-store.d.ts +2 -0
  109. package/dist/agent/run-store.d.ts.map +1 -1
  110. package/dist/agent/run-store.js +44 -3
  111. package/dist/agent/run-store.js.map +1 -1
  112. package/dist/application-state/handlers.d.ts +15 -0
  113. package/dist/application-state/handlers.d.ts.map +1 -1
  114. package/dist/application-state/handlers.js +42 -2
  115. package/dist/application-state/handlers.js.map +1 -1
  116. package/dist/application-state/index.d.ts +2 -2
  117. package/dist/application-state/index.d.ts.map +1 -1
  118. package/dist/application-state/index.js +2 -2
  119. package/dist/application-state/index.js.map +1 -1
  120. package/dist/application-state/store.d.ts +11 -0
  121. package/dist/application-state/store.d.ts.map +1 -1
  122. package/dist/application-state/store.js +23 -10
  123. package/dist/application-state/store.js.map +1 -1
  124. package/dist/audit/redact.d.ts +25 -2
  125. package/dist/audit/redact.d.ts.map +1 -1
  126. package/dist/audit/redact.js +85 -21
  127. package/dist/audit/redact.js.map +1 -1
  128. package/dist/client/AgentPanel.d.ts.map +1 -1
  129. package/dist/client/AgentPanel.js +2 -7
  130. package/dist/client/AgentPanel.js.map +1 -1
  131. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  132. package/dist/client/MultiTabAssistantChat.js +1 -1
  133. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  134. package/dist/client/RunStuckBanner.d.ts +11 -1
  135. package/dist/client/RunStuckBanner.d.ts.map +1 -1
  136. package/dist/client/RunStuckBanner.js +20 -7
  137. package/dist/client/RunStuckBanner.js.map +1 -1
  138. package/dist/client/agent-chat.d.ts.map +1 -1
  139. package/dist/client/agent-chat.js +4 -6
  140. package/dist/client/agent-chat.js.map +1 -1
  141. package/dist/client/application-state.d.ts +11 -0
  142. package/dist/client/application-state.d.ts.map +1 -1
  143. package/dist/client/application-state.js +87 -9
  144. package/dist/client/application-state.js.map +1 -1
  145. package/dist/client/dynamic-suggestions.d.ts.map +1 -1
  146. package/dist/client/dynamic-suggestions.js +4 -13
  147. package/dist/client/dynamic-suggestions.js.map +1 -1
  148. package/dist/client/guided-questions.d.ts.map +1 -1
  149. package/dist/client/guided-questions.js +6 -22
  150. package/dist/client/guided-questions.js.map +1 -1
  151. package/dist/collab/routes.d.ts +1 -1
  152. package/dist/collab/struct-routes.d.ts +1 -1
  153. package/dist/db/client.d.ts +57 -10
  154. package/dist/db/client.d.ts.map +1 -1
  155. package/dist/db/client.js +122 -31
  156. package/dist/db/client.js.map +1 -1
  157. package/dist/db/create-get-db.d.ts.map +1 -1
  158. package/dist/db/create-get-db.js +31 -7
  159. package/dist/db/create-get-db.js.map +1 -1
  160. package/dist/db/runtime-diagnostics.d.ts +1 -0
  161. package/dist/db/runtime-diagnostics.d.ts.map +1 -1
  162. package/dist/db/runtime-diagnostics.js +31 -6
  163. package/dist/db/runtime-diagnostics.js.map +1 -1
  164. package/dist/deploy/build.d.ts +5 -20
  165. package/dist/deploy/build.d.ts.map +1 -1
  166. package/dist/deploy/build.js +18 -30
  167. package/dist/deploy/build.js.map +1 -1
  168. package/dist/feature-flags/store.d.ts.map +1 -1
  169. package/dist/feature-flags/store.js +10 -5
  170. package/dist/feature-flags/store.js.map +1 -1
  171. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  172. package/dist/integrations/a2a-continuations-store.d.ts.map +1 -1
  173. package/dist/integrations/a2a-continuations-store.js +12 -0
  174. package/dist/integrations/a2a-continuations-store.js.map +1 -1
  175. package/dist/integrations/config-store.d.ts +1 -0
  176. package/dist/integrations/config-store.d.ts.map +1 -1
  177. package/dist/integrations/config-store.js +11 -0
  178. package/dist/integrations/config-store.js.map +1 -1
  179. package/dist/integrations/google-docs-poller.d.ts.map +1 -1
  180. package/dist/integrations/google-docs-poller.js +19 -2
  181. package/dist/integrations/google-docs-poller.js.map +1 -1
  182. package/dist/mcp/screen-memory-stdio.d.ts +7 -7
  183. package/dist/mcp/screen-memory-stdio.d.ts.map +1 -1
  184. package/dist/mcp-client/routes.d.ts.map +1 -1
  185. package/dist/mcp-client/routes.js +30 -2
  186. package/dist/mcp-client/routes.js.map +1 -1
  187. package/dist/notifications/routes.d.ts +3 -3
  188. package/dist/observability/routes.d.ts +1 -1
  189. package/dist/onboarding/plugin.d.ts.map +1 -1
  190. package/dist/onboarding/plugin.js +12 -8
  191. package/dist/onboarding/plugin.js.map +1 -1
  192. package/dist/org/accept-pending.d.ts.map +1 -1
  193. package/dist/org/accept-pending.js +2 -0
  194. package/dist/org/accept-pending.js.map +1 -1
  195. package/dist/org/auto-join-domain.d.ts.map +1 -1
  196. package/dist/org/auto-join-domain.js +2 -0
  197. package/dist/org/auto-join-domain.js.map +1 -1
  198. package/dist/org/context.d.ts.map +1 -1
  199. package/dist/org/context.js +29 -9
  200. package/dist/org/context.js.map +1 -1
  201. package/dist/org/handlers.d.ts.map +1 -1
  202. package/dist/org/handlers.js +5 -0
  203. package/dist/org/handlers.js.map +1 -1
  204. package/dist/org/request-org-cache.d.ts +16 -0
  205. package/dist/org/request-org-cache.d.ts.map +1 -0
  206. package/dist/org/request-org-cache.js +62 -0
  207. package/dist/org/request-org-cache.js.map +1 -0
  208. package/dist/resources/store.d.ts.map +1 -1
  209. package/dist/resources/store.js +11 -9
  210. package/dist/resources/store.js.map +1 -1
  211. package/dist/scripts/runner.d.ts.map +1 -1
  212. package/dist/scripts/runner.js +3 -3
  213. package/dist/scripts/runner.js.map +1 -1
  214. package/dist/secrets/storage.d.ts.map +1 -1
  215. package/dist/secrets/storage.js +82 -5
  216. package/dist/secrets/storage.js.map +1 -1
  217. package/dist/server/action-change.d.ts +20 -0
  218. package/dist/server/action-change.d.ts.map +1 -1
  219. package/dist/server/action-change.js +29 -0
  220. package/dist/server/action-change.js.map +1 -1
  221. package/dist/server/action-routes.d.ts.map +1 -1
  222. package/dist/server/action-routes.js +7 -8
  223. package/dist/server/action-routes.js.map +1 -1
  224. package/dist/server/agent-teams.d.ts.map +1 -1
  225. package/dist/server/agent-teams.js +10 -2
  226. package/dist/server/agent-teams.js.map +1 -1
  227. package/dist/server/better-auth-instance.d.ts.map +1 -1
  228. package/dist/server/better-auth-instance.js +31 -14
  229. package/dist/server/better-auth-instance.js.map +1 -1
  230. package/dist/server/core-routes-plugin.d.ts +1 -0
  231. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  232. package/dist/server/core-routes-plugin.js +18 -4
  233. package/dist/server/core-routes-plugin.js.map +1 -1
  234. package/dist/server/credential-provider.d.ts +16 -0
  235. package/dist/server/credential-provider.d.ts.map +1 -1
  236. package/dist/server/credential-provider.js +49 -13
  237. package/dist/server/credential-provider.js.map +1 -1
  238. package/dist/server/http-response-telemetry.d.ts.map +1 -1
  239. package/dist/server/http-response-telemetry.js +1 -0
  240. package/dist/server/http-response-telemetry.js.map +1 -1
  241. package/dist/server/poll.d.ts +6 -0
  242. package/dist/server/poll.d.ts.map +1 -1
  243. package/dist/server/poll.js +84 -47
  244. package/dist/server/poll.js.map +1 -1
  245. package/dist/server/voice-providers-status.d.ts.map +1 -1
  246. package/dist/server/voice-providers-status.js +12 -3
  247. package/dist/server/voice-providers-status.js.map +1 -1
  248. package/docs/content/durable-background-runs.mdx +15 -6
  249. package/docs/design/durable-agent-runs.md +15 -11
  250. package/package.json +2 -2
  251. package/src/a2a/activity.ts +127 -10
  252. package/src/a2a/index.ts +3 -0
  253. package/src/action.ts +52 -11
  254. package/src/agent/durable-background.ts +38 -28
  255. package/src/agent/harness/background.ts +12 -2
  256. package/src/agent/production-agent.ts +210 -43
  257. package/src/agent/run-store.ts +45 -3
  258. package/src/application-state/handlers.ts +45 -0
  259. package/src/application-state/index.ts +3 -0
  260. package/src/application-state/store.ts +28 -11
  261. package/src/audit/redact.ts +104 -18
  262. package/src/client/AgentPanel.tsx +2 -7
  263. package/src/client/MultiTabAssistantChat.tsx +3 -0
  264. package/src/client/RunStuckBanner.tsx +29 -6
  265. package/src/client/agent-chat.ts +4 -9
  266. package/src/client/application-state.ts +115 -12
  267. package/src/client/dynamic-suggestions.ts +4 -12
  268. package/src/client/guided-questions.tsx +12 -23
  269. package/src/db/client.ts +148 -28
  270. package/src/db/create-get-db.ts +39 -6
  271. package/src/db/runtime-diagnostics.ts +41 -6
  272. package/src/deploy/build.ts +26 -33
  273. package/src/feature-flags/store.ts +10 -5
  274. package/src/integrations/a2a-continuations-store.ts +11 -0
  275. package/src/integrations/config-store.ts +13 -0
  276. package/src/integrations/google-docs-poller.ts +26 -2
  277. package/src/mcp-client/routes.ts +33 -2
  278. package/src/onboarding/plugin.ts +31 -25
  279. package/src/org/accept-pending.ts +2 -0
  280. package/src/org/auto-join-domain.ts +2 -0
  281. package/src/org/context.ts +35 -10
  282. package/src/org/handlers.ts +6 -0
  283. package/src/org/request-org-cache.ts +71 -0
  284. package/src/resources/store.ts +10 -9
  285. package/src/scripts/runner.ts +6 -3
  286. package/src/secrets/storage.ts +97 -5
  287. package/src/server/action-change.ts +33 -0
  288. package/src/server/action-routes.ts +11 -9
  289. package/src/server/agent-teams.ts +10 -2
  290. package/src/server/better-auth-instance.ts +39 -12
  291. package/src/server/core-routes-plugin.ts +19 -2
  292. package/src/server/credential-provider.ts +57 -14
  293. package/src/server/http-response-telemetry.ts +1 -0
  294. package/src/server/poll.ts +95 -57
  295. package/src/server/voice-providers-status.ts +19 -4
@@ -0,0 +1,299 @@
1
+ import { createHash } from "node:crypto";
2
+
3
+ import { defineAction, embedApp } from "@agent-native/core";
4
+ import { getDataProgram } from "@agent-native/core/data-programs";
5
+ import {
6
+ buildDeepLink,
7
+ getRequestOrgId,
8
+ getRequestUserEmail,
9
+ } from "@agent-native/core/server";
10
+ import { resolveAccess } from "@agent-native/core/sharing";
11
+ import setResourceVisibilityAction from "@agent-native/core/sharing/actions/set-resource-visibility";
12
+ import { z } from "zod";
13
+
14
+ import { getDashboard, upsertDashboard } from "../server/lib/dashboards-store";
15
+ import { requireAnalyticsAdminContext } from "../server/lib/db-admin-connections";
16
+ import {
17
+ compileNativeV2Dashboard,
18
+ getNativeV2DashboardManifest,
19
+ nativeV2DashboardManifests,
20
+ NATIVE_V2_DASHBOARD_IDS,
21
+ type NativeV2Binding,
22
+ type NativeV2BindingKey,
23
+ type NativeV2DashboardId,
24
+ } from "../server/lib/native-v2-dashboards";
25
+
26
+ const ANALYTICS_APP_ID = "analytics";
27
+
28
+ const bindingSchema = z.object({
29
+ programId: z.string().min(1),
30
+ params: z.record(z.string(), z.unknown()).optional(),
31
+ });
32
+
33
+ function orgDashboardId(
34
+ orgId: string,
35
+ templateId: NativeV2DashboardId,
36
+ ): string {
37
+ const suffix = createHash("sha256").update(orgId).digest("hex").slice(0, 10);
38
+ return `native-${templateId}-${suffix}`;
39
+ }
40
+
41
+ function parseOutputColumns(value: string | null): Set<string> {
42
+ if (!value) return new Set();
43
+ try {
44
+ const parsed = JSON.parse(value);
45
+ if (!Array.isArray(parsed)) return new Set();
46
+ return new Set(
47
+ parsed.flatMap((column) =>
48
+ column && typeof column === "object" && typeof column.name === "string"
49
+ ? [column.name]
50
+ : [],
51
+ ),
52
+ );
53
+ } catch {
54
+ return new Set();
55
+ }
56
+ }
57
+
58
+ function templateIdFromDashboard(
59
+ config: Record<string, unknown>,
60
+ ): string | null {
61
+ const catalog = config.catalog;
62
+ if (!catalog || typeof catalog !== "object" || Array.isArray(catalog)) {
63
+ return null;
64
+ }
65
+ const templateId = (catalog as Record<string, unknown>).templateId;
66
+ return typeof templateId === "string" ? templateId : null;
67
+ }
68
+
69
+ async function validateAndShareBindings(
70
+ bindings: Record<string, NativeV2Binding>,
71
+ requiredKeys: NativeV2BindingKey[],
72
+ requiredColumnsByBinding: Partial<Record<NativeV2BindingKey, string[]>>,
73
+ admin: { userEmail: string; orgId: string },
74
+ ): Promise<void> {
75
+ const missing = requiredKeys.filter((key) => !bindings[key]?.programId);
76
+ if (missing.length > 0) {
77
+ throw new Error(`Missing required v2 bindings: ${missing.join(", ")}`);
78
+ }
79
+
80
+ const uniqueProgramIds = new Set(
81
+ requiredKeys.map((key) => bindings[key]?.programId).filter(Boolean),
82
+ );
83
+ const programsToShare: string[] = [];
84
+ for (const programId of uniqueProgramIds) {
85
+ const program = await getDataProgram(programId, ANALYTICS_APP_ID);
86
+ if (!program) {
87
+ throw new Error(`Data program not found: ${programId}`);
88
+ }
89
+ if (program.orgId && program.orgId !== admin.orgId) {
90
+ throw new Error(
91
+ `Data program belongs to another organization: ${programId}`,
92
+ );
93
+ }
94
+ if (program.archivedAt) {
95
+ throw new Error(`Data program is archived: ${programId}`);
96
+ }
97
+ const access = await resolveAccess("data_program", programId, {
98
+ userEmail: admin.userEmail,
99
+ orgId: admin.orgId,
100
+ });
101
+ if (!access) {
102
+ throw new Error(`No access to data program: ${programId}`);
103
+ }
104
+ const outputColumns = parseOutputColumns(program.outputColumns);
105
+ const missingColumns = [
106
+ ...new Set(
107
+ requiredKeys.flatMap((key) =>
108
+ bindings[key]?.programId === programId
109
+ ? (requiredColumnsByBinding[key] ?? [])
110
+ : [],
111
+ ),
112
+ ),
113
+ ].filter((column) => !outputColumns.has(column));
114
+ if (missingColumns.length > 0) {
115
+ throw new Error(
116
+ `Data program ${programId} is missing required output columns: ${missingColumns.join(", ")}. Re-save it after a successful dry-run.`,
117
+ );
118
+ }
119
+
120
+ if (program.visibility !== "org" || program.orgId !== admin.orgId) {
121
+ programsToShare.push(programId);
122
+ }
123
+ }
124
+
125
+ for (const programId of programsToShare) {
126
+ await setResourceVisibilityAction.run({
127
+ resourceType: "data_program",
128
+ resourceId: programId,
129
+ visibility: "org",
130
+ });
131
+ }
132
+ }
133
+
134
+ export interface EnsureNativeV2DashboardsResult {
135
+ orgId: string;
136
+ dashboards: Array<{
137
+ templateId: NativeV2DashboardId;
138
+ dashboardId: string;
139
+ status: "created" | "updated" | "preserved";
140
+ requiredBindings: NativeV2BindingKey[];
141
+ }>;
142
+ }
143
+
144
+ export async function ensureNativeV2Dashboards(
145
+ args: {
146
+ templateIds?: NativeV2DashboardId[];
147
+ bindings: Record<string, NativeV2Binding>;
148
+ overwrite: boolean;
149
+ },
150
+ ctx?: { userEmail?: string; orgId?: string | null },
151
+ ): Promise<EnsureNativeV2DashboardsResult> {
152
+ const admin = await requireAnalyticsAdminContext(ctx);
153
+ const templateIds = args.templateIds?.length
154
+ ? args.templateIds
155
+ : [...NATIVE_V2_DASHBOARD_IDS];
156
+ const manifests = templateIds.map(getNativeV2DashboardManifest);
157
+ const allRequiredBindings = [
158
+ ...new Set(manifests.flatMap((manifest) => manifest.requiredBindings)),
159
+ ];
160
+ const requiredColumnsByBinding: Partial<
161
+ Record<NativeV2BindingKey, string[]>
162
+ > = Object.fromEntries(
163
+ manifests.flatMap((manifest) =>
164
+ manifest.panels
165
+ .filter(
166
+ (panel): panel is typeof panel & { bindingKey: NativeV2BindingKey } =>
167
+ Boolean(panel.bindingKey),
168
+ )
169
+ .map((panel) => [panel.bindingKey, panel.requiredColumns ?? []]),
170
+ ),
171
+ );
172
+
173
+ await validateAndShareBindings(
174
+ args.bindings,
175
+ allRequiredBindings,
176
+ requiredColumnsByBinding,
177
+ admin,
178
+ );
179
+
180
+ const installedAt = new Date().toISOString();
181
+ const dashboards: EnsureNativeV2DashboardsResult["dashboards"] = [];
182
+ for (const manifest of manifests) {
183
+ const dashboardId = orgDashboardId(admin.orgId, manifest.id);
184
+ const existing = await getDashboard(dashboardId, {
185
+ email: admin.userEmail,
186
+ orgId: admin.orgId,
187
+ });
188
+ if (existing && templateIdFromDashboard(existing.config) !== manifest.id) {
189
+ throw new Error(
190
+ `Refusing to overwrite unrelated dashboard at deterministic id ${dashboardId}.`,
191
+ );
192
+ }
193
+
194
+ if (existing && !args.overwrite) {
195
+ dashboards.push({
196
+ templateId: manifest.id,
197
+ dashboardId,
198
+ status: "preserved",
199
+ requiredBindings: manifest.requiredBindings,
200
+ });
201
+ continue;
202
+ }
203
+
204
+ const config = compileNativeV2Dashboard(
205
+ manifest,
206
+ args.bindings,
207
+ installedAt,
208
+ );
209
+ await upsertDashboard(
210
+ dashboardId,
211
+ "sql",
212
+ config as unknown as Record<string, unknown>,
213
+ {
214
+ email: admin.userEmail,
215
+ orgId: admin.orgId,
216
+ },
217
+ );
218
+ if (!existing) {
219
+ await setResourceVisibilityAction.run({
220
+ resourceType: "dashboard",
221
+ resourceId: dashboardId,
222
+ visibility: "org",
223
+ });
224
+ }
225
+ dashboards.push({
226
+ templateId: manifest.id,
227
+ dashboardId,
228
+ status: existing ? "updated" : "created",
229
+ requiredBindings: manifest.requiredBindings,
230
+ });
231
+ }
232
+
233
+ return { orgId: admin.orgId, dashboards };
234
+ }
235
+
236
+ export default defineAction({
237
+ description:
238
+ "Provision the four reusable native Analytics v2 dashboard patterns for the active organization from explicit Data Program bindings. " +
239
+ "This is an admin-only post-deploy action: it validates every program exists and exposes the manifest's required output columns, shares the bound programs with the organization, creates deterministic native-* dashboard copies, and never changes the legacy extension dashboards. " +
240
+ "Re-running preserves existing v2 dashboard edits unless overwrite=true.",
241
+ schema: z.object({
242
+ templateIds: z
243
+ .array(z.enum(NATIVE_V2_DASHBOARD_IDS))
244
+ .min(1)
245
+ .optional()
246
+ .describe("Optional subset of native v2 templates to provision."),
247
+ bindings: z
248
+ .record(z.string(), bindingSchema)
249
+ .describe(
250
+ 'Binding key to stored Analytics Data Program, e.g. {"roi.kpi": {"programId": "dp_..."}}.',
251
+ ),
252
+ overwrite: z
253
+ .boolean()
254
+ .optional()
255
+ .default(false)
256
+ .describe("Replace an existing v2 copy only when explicitly true."),
257
+ }),
258
+ mcpApp: {
259
+ compactCatalog: true,
260
+ resource: embedApp({
261
+ title: "Native Analytics v2 dashboards",
262
+ description: "Open the provisioned native v2 dashboard in Analytics.",
263
+ iframeTitle: "Agent-Native Analytics",
264
+ openLabel: "Open native v2 dashboard",
265
+ height: 680,
266
+ }),
267
+ },
268
+ run: async (args, ctx) => {
269
+ const userEmail = ctx?.userEmail ?? getRequestUserEmail();
270
+ const orgId = ctx?.orgId ?? getRequestOrgId() ?? null;
271
+ return ensureNativeV2Dashboards(
272
+ {
273
+ templateIds: args.templateIds,
274
+ bindings: args.bindings,
275
+ overwrite: args.overwrite,
276
+ },
277
+ { userEmail, orgId },
278
+ );
279
+ },
280
+ link: ({ result }) => {
281
+ const dashboardId =
282
+ result && typeof result === "object"
283
+ ? (result as EnsureNativeV2DashboardsResult).dashboards?.[0]
284
+ ?.dashboardId
285
+ : null;
286
+ if (!dashboardId) return null;
287
+ return {
288
+ url: buildDeepLink({
289
+ app: "analytics",
290
+ view: "adhoc",
291
+ params: { dashboardId },
292
+ }),
293
+ label: "Open native v2 dashboard",
294
+ view: "adhoc",
295
+ };
296
+ },
297
+ });
298
+
299
+ export { nativeV2DashboardManifests };
@@ -0,0 +1,34 @@
1
+ import { defineAction } from "@agent-native/core";
2
+ import { z } from "zod";
3
+
4
+ import {
5
+ nativeV2DashboardManifests,
6
+ NATIVE_V2_DASHBOARD_VERSION,
7
+ } from "../server/lib/native-v2-dashboards";
8
+
9
+ export default defineAction({
10
+ description:
11
+ "List the source-controlled native Analytics v2 dashboard manifests, including required Data Program binding keys and output columns. These provider-free manifests are compiled into org dashboards by ensure-native-v2-dashboards.",
12
+ schema: z.object({}),
13
+ http: { method: "GET" },
14
+ readOnly: true,
15
+ publicAgent: { expose: true, readOnly: true, requiresAuth: true },
16
+ run: async () => ({
17
+ version: NATIVE_V2_DASHBOARD_VERSION,
18
+ templates: nativeV2DashboardManifests.map((manifest) => ({
19
+ id: manifest.id,
20
+ name: manifest.name,
21
+ description: manifest.description,
22
+ category: manifest.category,
23
+ tags: manifest.tags,
24
+ requiredBindings: manifest.requiredBindings,
25
+ panels: manifest.panels.map((panel) => ({
26
+ id: panel.id,
27
+ title: panel.title,
28
+ chartType: panel.chartType,
29
+ bindingKey: panel.bindingKey ?? null,
30
+ requiredColumns: panel.requiredColumns ?? [],
31
+ })),
32
+ })),
33
+ }),
34
+ });
@@ -64,6 +64,8 @@ import {
64
64
  import { useChartTooltipPortalPosition } from "@/hooks/use-chart-tooltip-portal";
65
65
 
66
66
  const PAGE_SIZE_OPTIONS = [10, 25, 50, 100];
67
+ import { resolveDashboardFunnelRows } from "@shared/dashboard-funnel";
68
+
67
69
  import { createDemoChartTrendRows } from "@/lib/demo-chart-trend";
68
70
  import { useSqlQuery } from "@/lib/sql-query";
69
71
  import {
@@ -1454,6 +1456,12 @@ export function SqlChart({
1454
1456
  );
1455
1457
  }
1456
1458
 
1459
+ if (chartType === "funnel") {
1460
+ return withConfigWarning(
1461
+ <FunnelRenderer rows={rows} panel={panel} colors={colors} />,
1462
+ );
1463
+ }
1464
+
1457
1465
  if (chartType === "heatmap") {
1458
1466
  return withConfigWarning(<HeatmapRenderer rows={rows} panel={panel} />);
1459
1467
  }
@@ -2350,6 +2358,80 @@ function TimeSeriesRenderer({
2350
2358
  );
2351
2359
  }
2352
2360
 
2361
+ function FunnelRenderer({
2362
+ rows,
2363
+ panel,
2364
+ colors,
2365
+ }: {
2366
+ rows: Record<string, unknown>[];
2367
+ panel: SqlPanel;
2368
+ colors: string[];
2369
+ }) {
2370
+ const t = useT();
2371
+ const funnel = useMemo(
2372
+ () =>
2373
+ resolveDashboardFunnelRows(rows, panel.config?.xKey, panel.config?.yKey),
2374
+ [rows, panel.config?.xKey, panel.config?.yKey],
2375
+ );
2376
+
2377
+ if (funnel.items.length === 0) {
2378
+ return (
2379
+ <div
2380
+ className={`flex items-center justify-center py-8 ${TABLE_PANEL_MIN_HEIGHT_CLASS}`}
2381
+ >
2382
+ <p className="text-sm text-muted-foreground text-center">
2383
+ {t("common.noData")}
2384
+ </p>
2385
+ </div>
2386
+ );
2387
+ }
2388
+
2389
+ const maxValue = Math.max(...funnel.items.map((item) => item.value), 1);
2390
+ const formatter = panel.config?.yFormatter;
2391
+ const funnelColors = colors.length > 0 ? colors : DEFAULT_COLORS;
2392
+
2393
+ return (
2394
+ <div
2395
+ className="space-y-3 py-2"
2396
+ role="img"
2397
+ aria-label={panel.title}
2398
+ data-dashboard-funnel="true"
2399
+ >
2400
+ {funnel.items.map((item, index) => {
2401
+ const width =
2402
+ item.value > 0 ? Math.max(2, (item.value / maxValue) * 100) : 0;
2403
+ const dropOff =
2404
+ item.dropOffPercent === null
2405
+ ? null
2406
+ : `${item.dropOffPercent < 0 ? "↑" : "↓"} ${Math.abs(item.dropOffPercent).toFixed(1)}%`;
2407
+ return (
2408
+ <div key={`${item.label}-${index}`} className="space-y-1">
2409
+ <div className="flex items-baseline justify-between gap-3 text-xs">
2410
+ <span className="min-w-0 truncate font-medium text-foreground">
2411
+ {item.label}
2412
+ </span>
2413
+ <span className="shrink-0 tabular-nums text-muted-foreground">
2414
+ {formatYValue(item.value, formatter)} ·{" "}
2415
+ {item.percentOfFirst.toFixed(1)}%
2416
+ {dropOff ? ` · ${dropOff}` : ""}
2417
+ </span>
2418
+ </div>
2419
+ <div className="h-5 overflow-hidden rounded-sm bg-muted/60">
2420
+ <div
2421
+ className="h-full rounded-sm transition-[width]"
2422
+ style={{
2423
+ width: `${width}%`,
2424
+ backgroundColor: funnelColors[index % funnelColors.length],
2425
+ }}
2426
+ />
2427
+ </div>
2428
+ </div>
2429
+ );
2430
+ })}
2431
+ </div>
2432
+ );
2433
+ }
2434
+
2353
2435
  // Heatmap config: `xKey` = x-axis column, `yKey` = numeric value column,
2354
2436
  // `color` = optional row-label column. If `color` is omitted, the renderer
2355
2437
  // auto-detects the row-label as the first non-x non-value string column.
@@ -22,9 +22,9 @@ const DASHBOARD_CONTEXT =
22
22
  "NATIVE PANELS — Create a SQL-driven dashboard by calling the `update-dashboard` action with `dashboardId` and `config`; use a Data Program first when reusable fetching or transformation is the missing layer. " +
23
23
  "The config shape is: { name: string, panels: [{ id, title, sql, source, chartType, width, tab?, config? }] }. " +
24
24
  "Each panel needs: id (unique string), title, sql (the query), source ('bigquery' | 'ga4' | 'amplitude' | 'first-party' | 'demo' | 'prometheus'), " +
25
- "chartType ('line' | 'area' | 'bar' | 'metric' | 'table' | 'pie'), width (1 or 2). " +
25
+ "chartType ('line' | 'area' | 'bar' | 'metric' | 'table' | 'pie' | 'funnel' | 'heatmap' | 'callout' | 'section'), width (1 or 2). " +
26
26
  "Optional tab labels can use 'Group / Tab' for primary and secondary dashboard tabs. " +
27
- "Optional config: { xKey, yKey, yKeys, color, colors, yFormatter ('number'|'currency'|'percent'), description, valueLabels }. " +
27
+ "Optional config: { xKey, yKey, yKeys, color, colors, yFormatter ('number'|'currency'|'percent'), description, valueLabels }. For funnel panels, xKey is the stage label, yKey is the non-negative value, and SQL ORDER BY defines stage order. " +
28
28
  "For first-party analytics, source is 'first-party' and sql may read analytics_events only; do not use db-query for datasource panels. " +
29
29
  "For the built-in demo dashboard, source is 'demo' and sql uses the same Prometheus JSON descriptor shape as source 'prometheus': { promql, mode, range, step }. " +
30
30
  "Call `data-source-status` if you need to see which data sources are connected. " +
@@ -18,6 +18,7 @@ const DASHBOARD_CONTEXT_KEY = "analytics-selected-dashboard";
18
18
  const DASHBOARD_PANEL_CONTEXT_KEY = "analytics-selected-dashboard-panel";
19
19
  const SELECTED_OBJECT_STATE_KEY = "selected-object";
20
20
  const SELECTED_OBJECT_SOURCE_FIELD = "__agentNativeSelectedObjectSource";
21
+ const CONTEXT_PUBLISH_DELAY_MS = 250;
21
22
 
22
23
  export interface DashboardChatContextArgs {
23
24
  id: string | null | undefined;
@@ -233,56 +234,61 @@ export function useDashboardChatContext(
233
234
  );
234
235
  }, []);
235
236
 
237
+ // Dashboard metadata lands in pieces — title first, then panel count, then
238
+ // the access role. Publishing each piece costs a round-trip and a sync event
239
+ // that invalidates every mounted query, so only the settled value is sent.
236
240
  useEffect(() => {
237
241
  if (!id) return;
238
242
  const displayTitle = title?.trim() || id;
239
-
240
- setAgentChatContextItem({
241
- key: DASHBOARD_CONTEXT_KEY,
242
- title: `Dashboard: ${displayTitle}`,
243
- context: dashboardContext({
244
- id,
245
- kind,
246
- title: displayTitle,
247
- panelCount,
248
- canEdit,
249
- }),
250
- openSidebar: false,
251
- focus: false,
252
- });
253
-
254
- return () => {
255
- removeAgentChatContextItem({
243
+ const timer = setTimeout(() => {
244
+ setAgentChatContextItem({
256
245
  key: DASHBOARD_CONTEXT_KEY,
246
+ title: `Dashboard: ${displayTitle}`,
247
+ context: dashboardContext({
248
+ id,
249
+ kind,
250
+ title: displayTitle,
251
+ panelCount,
252
+ canEdit,
253
+ }),
257
254
  openSidebar: false,
255
+ focus: false,
258
256
  });
259
- };
257
+ }, CONTEXT_PUBLISH_DELAY_MS);
258
+ return () => clearTimeout(timer);
260
259
  }, [canEdit, id, kind, panelCount, title]);
261
260
 
262
261
  useEffect(() => {
263
262
  if (!id || hasSelectedPanel) return;
264
263
  const displayTitle = title?.trim() || id;
265
- setClientAppState(
266
- SELECTED_OBJECT_STATE_KEY,
267
- {
268
- type: "dashboard",
269
- id,
270
- kind,
271
- title: displayTitle,
272
- panelCount,
273
- canEdit,
274
- [SELECTED_OBJECT_SOURCE_FIELD]: TAB_ID,
275
- },
276
- {
277
- keepalive: true,
278
- requestSource: TAB_ID,
279
- },
280
- ).catch(() => {});
264
+ const timer = setTimeout(() => {
265
+ setClientAppState(
266
+ SELECTED_OBJECT_STATE_KEY,
267
+ {
268
+ type: "dashboard",
269
+ id,
270
+ kind,
271
+ title: displayTitle,
272
+ panelCount,
273
+ canEdit,
274
+ [SELECTED_OBJECT_SOURCE_FIELD]: TAB_ID,
275
+ },
276
+ {
277
+ keepalive: true,
278
+ requestSource: TAB_ID,
279
+ },
280
+ ).catch(() => {});
281
+ }, CONTEXT_PUBLISH_DELAY_MS);
282
+ return () => clearTimeout(timer);
281
283
  }, [canEdit, hasSelectedPanel, id, kind, panelCount, title]);
282
284
 
283
285
  useEffect(() => {
284
286
  if (!id) return;
285
287
  return () => {
288
+ removeAgentChatContextItem({
289
+ key: DASHBOARD_CONTEXT_KEY,
290
+ openSidebar: false,
291
+ });
286
292
  removeAgentChatContextItem({
287
293
  key: DASHBOARD_PANEL_CONTEXT_KEY,
288
294
  openSidebar: false,
@@ -32,13 +32,13 @@ export function useUserPref<T extends Record<string, unknown>>(key: string) {
32
32
  queryClient.setQueryData(queryKey, value);
33
33
  return { previousValue };
34
34
  },
35
+ // No invalidate on success: the optimistic value above is exactly what the
36
+ // server stored, so refetching it only buys a round-trip. `onError` is the
37
+ // path that restores truth.
35
38
  onError: (_err, _value, context) => {
36
39
  queryClient.setQueryData(queryKey, context?.previousValue);
37
40
  toast.error("Failed to save preference");
38
41
  },
39
- onSettled: () => {
40
- queryClient.invalidateQueries({ queryKey });
41
- },
42
42
  });
43
43
 
44
44
  const { mutate: remove } = useMutation({
@@ -947,6 +947,10 @@ const enUS = {
947
947
  chartTypePie: "Pie",
948
948
  chartTypeMetric: "Metric",
949
949
  chartTypeTable: "Table",
950
+ chartTypeFunnel: "Funnel",
951
+ chartTypeHeatmap: "Heatmap",
952
+ chartTypeCallout: "Callout",
953
+ chartTypeSection: "Section",
950
954
  chartTypeExtension: "Custom visualization",
951
955
  failedToSavePanel: "Failed to save panel",
952
956
  failedToFormatSql: "Failed to format SQL",
@@ -1416,6 +1420,10 @@ const analyticsSliceTranslations: {
1416
1420
  chartTypePie: "馅饼",
1417
1421
  chartTypeMetric: "公制",
1418
1422
  chartTypeTable: "桌子",
1423
+ chartTypeFunnel: "漏斗",
1424
+ chartTypeHeatmap: "热力图",
1425
+ chartTypeCallout: "提示",
1426
+ chartTypeSection: "分区",
1419
1427
  chartTypeExtension: "自定义可视化",
1420
1428
  failedToSavePanel: "保存面板失败",
1421
1429
  failedToFormatSql: "格式化SQL失败",
@@ -1716,6 +1724,10 @@ const analyticsSliceTranslations: {
1716
1724
  chartTypePie: "Pastel",
1717
1725
  chartTypeMetric: "Métrico",
1718
1726
  chartTypeTable: "Mesa",
1727
+ chartTypeFunnel: "Embudo",
1728
+ chartTypeHeatmap: "Mapa de calor",
1729
+ chartTypeCallout: "Aviso",
1730
+ chartTypeSection: "Sección",
1719
1731
  chartTypeExtension: "Visualización personalizada",
1720
1732
  failedToSavePanel: "No se pudo guardar el panel",
1721
1733
  failedToFormatSql: "No se pudo formatear SQL",
@@ -2033,6 +2045,10 @@ const analyticsSliceTranslations: {
2033
2045
  chartTypePie: "Tarte",
2034
2046
  chartTypeMetric: "Métrique",
2035
2047
  chartTypeTable: "Tableau",
2048
+ chartTypeFunnel: "Entonnoir",
2049
+ chartTypeHeatmap: "Carte thermique",
2050
+ chartTypeCallout: "Alerte",
2051
+ chartTypeSection: "Section",
2036
2052
  chartTypeExtension: "Visualisation personnalisée",
2037
2053
  failedToSavePanel: "Échec de l'enregistrement du panneau",
2038
2054
  failedToFormatSql: "Échec du formatage du SQL",
@@ -2348,6 +2364,10 @@ const analyticsSliceTranslations: {
2348
2364
  chartTypePie: "Torte",
2349
2365
  chartTypeMetric: "Metrisch",
2350
2366
  chartTypeTable: "Tisch",
2367
+ chartTypeFunnel: "Trichter",
2368
+ chartTypeHeatmap: "Heatmap",
2369
+ chartTypeCallout: "Hinweis",
2370
+ chartTypeSection: "Abschnitt",
2351
2371
  chartTypeExtension: "Benutzerdefinierte Visualisierung",
2352
2372
  failedToSavePanel: "Das Panel konnte nicht gespeichert werden",
2353
2373
  failedToFormatSql: "SQL konnte nicht formatiert werden",
@@ -2658,6 +2678,10 @@ const analyticsSliceTranslations: {
2658
2678
  chartTypePie: "パイ",
2659
2679
  chartTypeMetric: "メトリック",
2660
2680
  chartTypeTable: "テーブル",
2681
+ chartTypeFunnel: "ファネル",
2682
+ chartTypeHeatmap: "ヒートマップ",
2683
+ chartTypeCallout: "コールアウト",
2684
+ chartTypeSection: "セクション",
2661
2685
  chartTypeExtension: "カスタム可視化",
2662
2686
  failedToSavePanel: "パネルの保存に失敗しました",
2663
2687
  failedToFormatSql: "SQLのフォーマットに失敗しました",
@@ -2962,6 +2986,10 @@ const analyticsSliceTranslations: {
2962
2986
  chartTypePie: "파이",
2963
2987
  chartTypeMetric: "미터법",
2964
2988
  chartTypeTable: "테이블",
2989
+ chartTypeFunnel: "퍼널",
2990
+ chartTypeHeatmap: "히트맵",
2991
+ chartTypeCallout: "콜아웃",
2992
+ chartTypeSection: "섹션",
2965
2993
  chartTypeExtension: "사용자 지정 시각화",
2966
2994
  failedToSavePanel: "패널을 저장하지 못했습니다.",
2967
2995
  failedToFormatSql: "SQL 포맷 실패",
@@ -3266,6 +3294,10 @@ const analyticsSliceTranslations: {
3266
3294
  chartTypePie: "Torta",
3267
3295
  chartTypeMetric: "Métrica",
3268
3296
  chartTypeTable: "Mesa",
3297
+ chartTypeFunnel: "Funil",
3298
+ chartTypeHeatmap: "Mapa de calor",
3299
+ chartTypeCallout: "Destaque",
3300
+ chartTypeSection: "Seção",
3269
3301
  chartTypeExtension: "Visualização personalizada",
3270
3302
  failedToSavePanel: "Falha ao salvar painel",
3271
3303
  failedToFormatSql: "Falha ao formatar SQL",
@@ -3572,6 +3604,10 @@ const analyticsSliceTranslations: {
3572
3604
  chartTypePie: "पाई",
3573
3605
  chartTypeMetric: "मीट्रिक",
3574
3606
  chartTypeTable: "मेज़",
3607
+ chartTypeFunnel: "फ़नल",
3608
+ chartTypeHeatmap: "हीटमैप",
3609
+ chartTypeCallout: "कॉलआउट",
3610
+ chartTypeSection: "अनुभाग",
3575
3611
  chartTypeExtension: "कस्टम विज़ुअलाइज़ेशन",
3576
3612
  failedToSavePanel: "पैनल सहेजने में विफल",
3577
3613
  failedToFormatSql: "SQL को फ़ॉर्मेट करने में विफल",
@@ -3871,6 +3907,10 @@ const analyticsSliceTranslations: {
3871
3907
  chartTypePie: "فطيرة",
3872
3908
  chartTypeMetric: "متري",
3873
3909
  chartTypeTable: "طاولة",
3910
+ chartTypeFunnel: "مسار تحويل",
3911
+ chartTypeHeatmap: "خريطة حرارية",
3912
+ chartTypeCallout: "تنبيه",
3913
+ chartTypeSection: "قسم",
3874
3914
  chartTypeExtension: "عرض مرئي مخصص",
3875
3915
  failedToSavePanel: "فشل حفظ اللوحة",
3876
3916
  failedToFormatSql: "فشل تهيئة SQL",