@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
@@ -57,6 +57,10 @@ const CHART_TYPES: { value: ChartType; labelKey: string }[] = [
57
57
  { value: "pie", labelKey: "panelEditor.chartTypePie" },
58
58
  { value: "metric", labelKey: "panelEditor.chartTypeMetric" },
59
59
  { value: "table", labelKey: "panelEditor.chartTypeTable" },
60
+ { value: "funnel", labelKey: "panelEditor.chartTypeFunnel" },
61
+ { value: "heatmap", labelKey: "panelEditor.chartTypeHeatmap" },
62
+ { value: "callout", labelKey: "panelEditor.chartTypeCallout" },
63
+ { value: "section", labelKey: "panelEditor.chartTypeSection" },
60
64
  { value: "extension", labelKey: "panelEditor.chartTypeExtension" },
61
65
  ];
62
66
 
@@ -290,6 +294,7 @@ function PanelEditorContent({
290
294
 
291
295
  const isEdit = !!panel;
292
296
  const isExtensionPanel = form.chartType === "extension";
297
+ const isSectionPanel = form.chartType === "section";
293
298
  const isProgramSource = form.source === "program";
294
299
  const { programId, paramsText: initialParamsText } = useMemo(
295
300
  () =>
@@ -332,10 +337,13 @@ function PanelEditorContent({
332
337
  const canSave = isExtensionPanel
333
338
  ? form.title.trim().length > 0 &&
334
339
  (form.extensionMode === "slot" || form.extensionId.trim().length > 0)
335
- : isProgramSource
336
- ? form.title.trim().length > 0 && selectedProgramId.trim().length > 0
337
- : form.title.trim().length > 0 && form.sql.trim().length > 0;
338
- const canFormat = !isExtensionPanel && canFormatPanelSql(form.source);
340
+ : isSectionPanel
341
+ ? form.title.trim().length > 0
342
+ : isProgramSource
343
+ ? form.title.trim().length > 0 && selectedProgramId.trim().length > 0
344
+ : form.title.trim().length > 0 && form.sql.trim().length > 0;
345
+ const canFormat =
346
+ !isExtensionPanel && !isSectionPanel && canFormatPanelSql(form.source);
339
347
 
340
348
  const handleSubmit = async () => {
341
349
  if (!canSave || saving) return;
@@ -343,7 +351,7 @@ function PanelEditorContent({
343
351
  setError(null);
344
352
  try {
345
353
  let sqlForSave = form.sql;
346
- if (isProgramSource) {
354
+ if (isProgramSource && !isSectionPanel) {
347
355
  try {
348
356
  sqlForSave = serializeProgramDescriptor(
349
357
  selectedProgramId,
@@ -388,7 +396,7 @@ function PanelEditorContent({
388
396
  `If no source can answer, report the exact unavailable/error result instead of saving a panel with guessed schema or metrics. ` +
389
397
  `Use the \`mutate-dashboard\` action with code like \`dashboard.insertPanel({"id":"new-panel","title":"New Panel","source":"first-party","chartType":"metric","width":1,"sql":"SELECT COUNT(*) AS value FROM analytics_events"}).atBottom();\` ` +
390
398
  `to append, or \`.nextTo("panel-id")\`, \`.atRow(2)\`, \`.atRowStart(2)\`, \`.before("panel-id")\`, \`.after("panel-id")\`, or \`.atIndex(n)\` to place the panel. Prefer \`.nextTo("panel-id")\` or \`.atRow(rowNumber)\` for visible row placement requests; they keep the chart in the intended rendered row and expand/rebalance that row when needed. ` +
391
- `Panel shape: { id (unique slug), title, sql, source ('bigquery'|'ga4'|'amplitude'|'first-party'|'demo'|'prometheus'|'program'), chartType ('line'|'area'|'bar'|'metric'|'table'|'pie'|'section'|'extension'), width (legacy integer 1..6; set to 1 unless editing existing data), tab? (use 'Group / Tab' for grouped tabs), columns? (section panels only - 1..6 max panels per row for panels following this section), config? }. ` +
399
+ `Panel shape: { id (unique slug), title, sql, source ('bigquery'|'ga4'|'amplitude'|'first-party'|'demo'|'prometheus'|'program'), chartType ('line'|'area'|'bar'|'metric'|'table'|'pie'|'funnel'|'heatmap'|'callout'|'section'|'extension'), width (legacy integer 1..6; set to 1 unless editing existing data), tab? (use 'Group / Tab' for grouped tabs), columns? (section panels only - 1..6 max panels per row for panels following this section), config? }. ` +
392
400
  `Visible layout auto-fits by row: one panel in a row spans the row, two split it, three split it into thirds, up to the section column limit. ` +
393
401
  `For amplitude panels, sql is a JSON descriptor: {"event":"event name","groupBy":"property","days":30}. ` +
394
402
  `For first-party panels, sql is read-only SQL over analytics_events only; use source 'first-party' and do not call db-query for this datasource. ` +
@@ -396,8 +404,8 @@ function PanelEditorContent({
396
404
  `For prometheus panels, sql is a JSON descriptor: {"promql":"rate(http_requests_total[5m])","mode":"range","range":"1h","step":"30s"}. mode defaults to "range"; range defaults to "1h"; step is auto if omitted. Returned rows have shape {timestamp, series, value} — set config.xKey="timestamp", config.yKey="value", and a single series in config.yKeys for clean charting. ` +
397
405
  `For program panels (arbitrary provider data joins/cohorts not expressible in the other sources), first save-data-program (or reuse an existing one via list-data-programs), then set sql to a JSON descriptor: {"programId":"<id>","params":{...}}. See the data-programs skill for the emit(rows, schema) contract and the Risk Meeting worked example. ` +
398
406
  `Native dashboard panels and Data Programs come first. Add an extension panel only when the user explicitly asks for a genuinely bespoke, one-off Custom Block for this dashboard and native panels cannot represent it faithfully. For a reusable/native capability call connect-builder instead. New agent-authored Custom Blocks use config.extensionId plus config.customBlock={authoredBy:"agent",intent:"one-off",scope:"dashboard",nativeGapReason:"custom-visualization"|"custom-interaction"|"custom-layout"|"other"}; never put prompt/customer text in that metadata. Use config.extensionSlotId only when the user explicitly asks for a personal/per-viewer slot; slot installs are per-user and automated report identities may have no install. ` +
399
- `Config is optional: { xKey, yKey, yKeys, yFormatter ('number'|'currency'|'percent'), rightYKeys, rightYFormatter, description, columns, pivot, limit, color, colors, stacked, legend, valueLabels }. ` +
400
- `For line/area/bar series that share an x-axis but not a unit (a count next to a rate), put the smaller-unit series on a second y-axis with config.rightYKeys (a subset of yKeys) and an optional config.rightYFormatter — do not build an extension for a dual-axis chart. ` +
407
+ `Config is optional: { xKey, yKey, yKeys, yFormatter ('number'|'currency'|'percent'), rightYKeys, rightYFormatter, description, columns, pivot, limit, color, colors, stacked, legend, valueLabels }. For funnel panels, use config.xKey for the stage label, config.yKey for the non-negative count/value, and keep the SQL ORDER BY in the intended funnel order. ` +
408
+ `For line/area/bar series that share an x-axis but not a unit (a count next to a rate), put the smaller-unit series on a second y-axis with config.rightYKeys (a subset of yKeys) and an optional config.rightYFormatter — do not build an extension for a dual-axis chart. Use heatmap, callout, and section panels when their native contracts fit; do not create a Custom Block for a supported native panel. ` +
401
409
  `Chart legends render automatically; set config.legend=false only when the user explicitly asks to hide the legend. ` +
402
410
  `Use \`get-sql-dashboard.layout.groups[].rows[].rowNumber/panelIds\` to identify and verify visible rows. ` +
403
411
  `Consult the data dictionary first via \`list-data-dictionary --search <topic>\`, then use AGENTS.md, .agents/skills, and connected data-source instructions before writing SQL. ` +
@@ -455,7 +463,7 @@ function PanelEditorContent({
455
463
  </Select>
456
464
  </div>
457
465
 
458
- {!isExtensionPanel ? (
466
+ {!isExtensionPanel && !isSectionPanel ? (
459
467
  <div className="grid gap-1.5">
460
468
  <Label htmlFor="panel-source">{t("panelEditor.source")}</Label>
461
469
  <Select
@@ -600,7 +608,7 @@ function PanelEditorContent({
600
608
  </div>
601
609
  )}
602
610
  </div>
603
- ) : isProgramSource ? (
611
+ ) : isSectionPanel ? null : isProgramSource ? (
604
612
  <div className="grid gap-4">
605
613
  <div className="grid gap-1.5">
606
614
  <Label htmlFor="panel-program">
@@ -178,6 +178,15 @@ type DashboardTabGroup = {
178
178
  tabs: Array<{ value: string; label: string }>;
179
179
  };
180
180
 
181
+ function sameFilterMap(
182
+ a: Record<string, string> | undefined,
183
+ b: Record<string, string>,
184
+ ): boolean {
185
+ const aKeys = Object.keys(a ?? {});
186
+ if (aKeys.length !== Object.keys(b).length) return false;
187
+ return aKeys.every((key) => a![key] === b[key]);
188
+ }
189
+
181
190
  function groupDashboardTabs(tabs: string[]): {
182
191
  groups: DashboardTabGroup[];
183
192
  hasNestedTabs: boolean;
@@ -1013,6 +1022,10 @@ function SqlDashboardPageContent({
1013
1022
  currentFilters[k] = v;
1014
1023
  }
1015
1024
  });
1025
+ // Opening a dashboard restores the saved filters into the URL, so
1026
+ // without this the mere act of loading a page writes the value back —
1027
+ // one round-trip plus a sync event that invalidates every mounted query.
1028
+ if (sameFilterMap(savedFilters?.filters, currentFilters)) return;
1016
1029
  saveFilterPref({ filters: currentFilters });
1017
1030
  }, 1500);
1018
1031
  return () => clearTimeout(saveTimer.current);
@@ -1021,6 +1034,7 @@ function SqlDashboardPageContent({
1021
1034
  loaded,
1022
1035
  dashboard?.filters,
1023
1036
  dashboardId,
1037
+ savedFilters,
1024
1038
  saveFilterPref,
1025
1039
  reportScreenshot,
1026
1040
  ]);
@@ -15,6 +15,7 @@ export type ChartType =
15
15
  | "table"
16
16
  | "pie"
17
17
  | "section"
18
+ | "funnel"
18
19
  | "heatmap"
19
20
  | "callout"
20
21
  | "extension";
@@ -16,6 +16,9 @@ const VALID_CHART_TYPES = new Set([
16
16
  "metric",
17
17
  "table",
18
18
  "pie",
19
+ "funnel",
20
+ "heatmap",
21
+ "callout",
19
22
  ]);
20
23
  // Embed URLs accept external sources plus the restricted first-party analytics
21
24
  // source. They intentionally do not expose arbitrary app database querying.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: added
3
+ date: 2026-07-30
4
+ ---
5
+
6
+ Analytics dashboards now expose native funnel, heatmap, callout, and section panels.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: added
3
+ date: 2026-07-30
4
+ ---
5
+
6
+ Analytics now ships reusable native v2 dashboard manifests with an admin provisioning action for real Data Program bindings.
@@ -13,6 +13,7 @@ import type {
13
13
  SqlPanel,
14
14
  TableColumnConfig,
15
15
  } from "../../app/pages/adhoc/sql-dashboard/types";
16
+ import { resolveDashboardFunnelRows } from "../../shared/dashboard-funnel";
16
17
  import { DASHBOARD_REPORT_ACTION_TIMEOUT_MS } from "../../shared/dashboard-report-timeouts.js";
17
18
  import {
18
19
  MAX_CONCURRENT_FIRST_PARTY_SQL_QUERIES,
@@ -25,6 +26,7 @@ import {
25
26
  import { resolveAnalyticsPanelSource } from "./dashboard-panel-source-resolver";
26
27
  import {
27
28
  renderReportChartSvg,
29
+ renderFunnelChartSvg,
28
30
  REPORT_CHART_FONT_FAMILY,
29
31
  type ReportChartSeries,
30
32
  type ReportChartType,
@@ -534,6 +536,7 @@ const REPORT_CHART_TYPES: Record<string, ReportChartType> = {
534
536
  line: "line",
535
537
  area: "area",
536
538
  pie: "pie",
539
+ funnel: "funnel",
537
540
  "stacked-bar": "bar",
538
541
  "stacked-area": "area",
539
542
  };
@@ -923,7 +926,7 @@ async function renderChartBlock(args: {
923
926
  panel: SqlPanel;
924
927
  rows: Array<Record<string, unknown>>;
925
928
  forcedYKeys?: string[];
926
- chartType: ReportChartType;
929
+ chartType: Exclude<ReportChartType, "funnel">;
927
930
  index: number;
928
931
  description: string;
929
932
  }): Promise<PanelBlock> {
@@ -997,6 +1000,72 @@ async function renderChartBlock(args: {
997
1000
  }
998
1001
  }
999
1002
 
1003
+ async function renderFunnelChartBlock(args: {
1004
+ panel: SqlPanel;
1005
+ rows: Array<Record<string, unknown>>;
1006
+ index: number;
1007
+ description: string;
1008
+ }): Promise<PanelBlock> {
1009
+ const { panel, rows, index, description } = args;
1010
+ const funnel = resolveDashboardFunnelRows(
1011
+ rows,
1012
+ panel.config?.xKey,
1013
+ panel.config?.yKey,
1014
+ );
1015
+ if (funnel.items.length === 0) {
1016
+ const table = renderTableHtml(panel, rows);
1017
+ return {
1018
+ panelId: panel.id,
1019
+ html: panelCardHtml(
1020
+ panel,
1021
+ `${table.html}${noteHtml("No funnel stages were returned, so the rows are shown as a table.")}`,
1022
+ ),
1023
+ text: `${panel.title}\n${table.text}`,
1024
+ };
1025
+ }
1026
+
1027
+ const subtitle = description;
1028
+ const alt = `funnel chart of ${funnel.items.map((item) => item.label).join(", ")}`;
1029
+ try {
1030
+ const svg = renderFunnelChartSvg({
1031
+ title: "",
1032
+ subtitle,
1033
+ rows: funnel.items,
1034
+ width: CHART_WIDTH,
1035
+ height: CHART_HEIGHT,
1036
+ formatter: panel.config?.yFormatter,
1037
+ colors: panel.config?.colors,
1038
+ });
1039
+ const png = await rasterizeChartPng(svg, CHART_WIDTH * CHART_RASTER_SCALE);
1040
+ const slug =
1041
+ panel.id.replace(/[^A-Za-z0-9_-]/g, "-").slice(0, 40) || "panel";
1042
+ const contentId = `dashboard-report-panel-${index}-${slug}`;
1043
+ return {
1044
+ panelId: panel.id,
1045
+ html: panelCardHtml(
1046
+ panel,
1047
+ `<img src="cid:${contentId}" alt="${escapeHtml(alt)}" width="${CHART_WIDTH}" style="display:block;width:100%;max-width:${CHART_WIDTH}px;height:auto;border:0;" />`,
1048
+ ),
1049
+ text: `${panel.title}: chart attached${subtitle ? ` (${subtitle})` : ""}`,
1050
+ attachment: {
1051
+ filename: `${slug}.png`,
1052
+ content: png,
1053
+ contentType: "image/png",
1054
+ contentId,
1055
+ disposition: "inline",
1056
+ },
1057
+ };
1058
+ } catch (error) {
1059
+ return failureBlock(
1060
+ panel,
1061
+ "Chart rendering failed",
1062
+ error instanceof Error
1063
+ ? error.message
1064
+ : "The funnel image could not be rendered.",
1065
+ );
1066
+ }
1067
+ }
1068
+
1000
1069
  function enforceAttachmentBudget(blocks: PanelBlock[]): void {
1001
1070
  const totalBytes = () =>
1002
1071
  blocks.reduce(
@@ -1093,6 +1162,17 @@ export async function renderReportEmail(args: {
1093
1162
  const chartType: ReportChartType | undefined =
1094
1163
  REPORT_CHART_TYPES[panel.chartType];
1095
1164
  if (chartType) {
1165
+ if (chartType === "funnel") {
1166
+ const block = await renderFunnelChartBlock({
1167
+ panel,
1168
+ rows,
1169
+ index: chartIndex++,
1170
+ description,
1171
+ });
1172
+ if (truncatedNote) block.html += truncatedNote;
1173
+ blocks.push(block);
1174
+ continue;
1175
+ }
1096
1176
  const block = await renderChartBlock({
1097
1177
  panel,
1098
1178
  rows,