@agent-native/core 0.95.1 → 0.97.0

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 (655) hide show
  1. package/README.md +6 -0
  2. package/corpus/README.md +2 -2
  3. package/corpus/core/CHANGELOG.md +64 -0
  4. package/corpus/core/docs/content/audit-log.mdx +2 -1
  5. package/corpus/core/docs/content/automations.mdx +7 -7
  6. package/corpus/core/docs/content/creating-templates.mdx +1 -1
  7. package/corpus/core/docs/content/internationalization.mdx +2 -1
  8. package/corpus/core/docs/content/locales/ar-SA/audit-log.mdx +2 -1
  9. package/corpus/core/docs/content/locales/ar-SA/automations.mdx +7 -7
  10. package/corpus/core/docs/content/locales/ar-SA/getting-started.mdx +7 -1
  11. package/corpus/core/docs/content/locales/ar-SA/internationalization.mdx +1 -1
  12. package/corpus/core/docs/content/locales/ar-SA/package-lifecycle.mdx +19 -0
  13. package/corpus/core/docs/content/locales/ar-SA/sandbox-adapters.mdx +18 -7
  14. package/corpus/core/docs/content/locales/ar-SA/security.mdx +8 -0
  15. package/corpus/core/docs/content/locales/de-DE/audit-log.mdx +2 -1
  16. package/corpus/core/docs/content/locales/de-DE/automations.mdx +7 -7
  17. package/corpus/core/docs/content/locales/de-DE/getting-started.mdx +7 -1
  18. package/corpus/core/docs/content/locales/de-DE/internationalization.mdx +1 -1
  19. package/corpus/core/docs/content/locales/de-DE/package-lifecycle.mdx +22 -0
  20. package/corpus/core/docs/content/locales/de-DE/sandbox-adapters.mdx +18 -7
  21. package/corpus/core/docs/content/locales/de-DE/security.mdx +8 -0
  22. package/corpus/core/docs/content/locales/es-ES/audit-log.mdx +2 -1
  23. package/corpus/core/docs/content/locales/es-ES/automations.mdx +7 -7
  24. package/corpus/core/docs/content/locales/es-ES/getting-started.mdx +7 -1
  25. package/corpus/core/docs/content/locales/es-ES/internationalization.mdx +1 -1
  26. package/corpus/core/docs/content/locales/es-ES/package-lifecycle.mdx +22 -0
  27. package/corpus/core/docs/content/locales/es-ES/sandbox-adapters.mdx +18 -7
  28. package/corpus/core/docs/content/locales/es-ES/security.mdx +8 -0
  29. package/corpus/core/docs/content/locales/fr-FR/audit-log.mdx +2 -1
  30. package/corpus/core/docs/content/locales/fr-FR/automations.mdx +7 -7
  31. package/corpus/core/docs/content/locales/fr-FR/getting-started.mdx +7 -1
  32. package/corpus/core/docs/content/locales/fr-FR/internationalization.mdx +1 -1
  33. package/corpus/core/docs/content/locales/fr-FR/package-lifecycle.mdx +21 -0
  34. package/corpus/core/docs/content/locales/fr-FR/sandbox-adapters.mdx +19 -8
  35. package/corpus/core/docs/content/locales/fr-FR/security.mdx +8 -0
  36. package/corpus/core/docs/content/locales/hi-IN/audit-log.mdx +2 -1
  37. package/corpus/core/docs/content/locales/hi-IN/automations.mdx +7 -7
  38. package/corpus/core/docs/content/locales/hi-IN/getting-started.mdx +7 -1
  39. package/corpus/core/docs/content/locales/hi-IN/internationalization.mdx +1 -1
  40. package/corpus/core/docs/content/locales/hi-IN/package-lifecycle.mdx +20 -0
  41. package/corpus/core/docs/content/locales/hi-IN/sandbox-adapters.mdx +18 -7
  42. package/corpus/core/docs/content/locales/hi-IN/security.mdx +8 -0
  43. package/corpus/core/docs/content/locales/ja-JP/audit-log.mdx +2 -1
  44. package/corpus/core/docs/content/locales/ja-JP/automations.mdx +7 -7
  45. package/corpus/core/docs/content/locales/ja-JP/getting-started.mdx +7 -1
  46. package/corpus/core/docs/content/locales/ja-JP/internationalization.mdx +1 -1
  47. package/corpus/core/docs/content/locales/ja-JP/package-lifecycle.mdx +21 -0
  48. package/corpus/core/docs/content/locales/ja-JP/sandbox-adapters.mdx +17 -6
  49. package/corpus/core/docs/content/locales/ja-JP/security.mdx +8 -0
  50. package/corpus/core/docs/content/locales/ko-KR/audit-log.mdx +2 -1
  51. package/corpus/core/docs/content/locales/ko-KR/automations.mdx +7 -7
  52. package/corpus/core/docs/content/locales/ko-KR/getting-started.mdx +7 -1
  53. package/corpus/core/docs/content/locales/ko-KR/internationalization.mdx +1 -1
  54. package/corpus/core/docs/content/locales/ko-KR/package-lifecycle.mdx +20 -0
  55. package/corpus/core/docs/content/locales/ko-KR/sandbox-adapters.mdx +17 -6
  56. package/corpus/core/docs/content/locales/ko-KR/security.mdx +8 -0
  57. package/corpus/core/docs/content/locales/pt-BR/audit-log.mdx +2 -1
  58. package/corpus/core/docs/content/locales/pt-BR/automations.mdx +7 -7
  59. package/corpus/core/docs/content/locales/pt-BR/getting-started.mdx +7 -1
  60. package/corpus/core/docs/content/locales/pt-BR/internationalization.mdx +1 -1
  61. package/corpus/core/docs/content/locales/pt-BR/package-lifecycle.mdx +21 -0
  62. package/corpus/core/docs/content/locales/pt-BR/sandbox-adapters.mdx +18 -7
  63. package/corpus/core/docs/content/locales/pt-BR/security.mdx +8 -0
  64. package/corpus/core/docs/content/locales/zh-CN/audit-log.mdx +2 -1
  65. package/corpus/core/docs/content/locales/zh-CN/automations.mdx +7 -7
  66. package/corpus/core/docs/content/locales/zh-CN/getting-started.mdx +6 -4
  67. package/corpus/core/docs/content/locales/zh-CN/internationalization.mdx +1 -1
  68. package/corpus/core/docs/content/locales/zh-CN/package-lifecycle.mdx +19 -0
  69. package/corpus/core/docs/content/locales/zh-CN/sandbox-adapters.mdx +17 -6
  70. package/corpus/core/docs/content/locales/zh-CN/security.mdx +8 -0
  71. package/corpus/core/docs/content/locales/zh-TW/audit-log.mdx +2 -1
  72. package/corpus/core/docs/content/locales/zh-TW/automations.mdx +7 -7
  73. package/corpus/core/docs/content/locales/zh-TW/getting-started.mdx +6 -4
  74. package/corpus/core/docs/content/locales/zh-TW/internationalization.mdx +1 -1
  75. package/corpus/core/docs/content/locales/zh-TW/package-lifecycle.mdx +19 -0
  76. package/corpus/core/docs/content/locales/zh-TW/sandbox-adapters.mdx +18 -7
  77. package/corpus/core/docs/content/locales/zh-TW/security.mdx +8 -0
  78. package/corpus/core/docs/content/package-lifecycle.mdx +51 -0
  79. package/corpus/core/docs/content/sandbox-adapters.mdx +16 -5
  80. package/corpus/core/package.json +11 -2
  81. package/corpus/core/src/a2a/auth-policy.ts +33 -0
  82. package/corpus/core/src/a2a/client.ts +11 -1
  83. package/corpus/core/src/agent/durable-background.ts +15 -7
  84. package/corpus/core/src/agent/production-agent.ts +87 -27
  85. package/corpus/core/src/agent/run-store.ts +13 -4
  86. package/corpus/core/src/agent/thread-data-builder.ts +7 -5
  87. package/corpus/core/src/audit/actions/export-audit-events.ts +162 -0
  88. package/corpus/core/src/audit/store.ts +8 -3
  89. package/corpus/core/src/audit/types.ts +3 -0
  90. package/corpus/core/src/chat-threads/store.ts +20 -1
  91. package/corpus/core/src/cli/code-agent-executor.ts +1 -0
  92. package/corpus/core/src/cli/code-agent-runs.ts +14 -0
  93. package/corpus/core/src/cli/doctor.ts +396 -0
  94. package/corpus/core/src/cli/index.ts +56 -0
  95. package/corpus/core/src/cli/info.ts +3 -3
  96. package/corpus/core/src/cli/package-lifecycle.ts +821 -0
  97. package/corpus/core/src/client/AgentPanel.tsx +32 -25
  98. package/corpus/core/src/client/AssistantChat.tsx +36 -20
  99. package/corpus/core/src/client/MultiTabAssistantChat.tsx +141 -90
  100. package/corpus/core/src/client/agent-chat-adapter.ts +9 -9
  101. package/corpus/core/src/client/chat/ChatHistoryList.tsx +404 -0
  102. package/corpus/core/src/client/chat/index.ts +6 -0
  103. package/corpus/core/src/client/chat/tool-call-display.tsx +37 -3
  104. package/corpus/core/src/client/code-agent-chat-adapter.ts +72 -4
  105. package/corpus/core/src/client/composer/RealtimeVoiceMode.tsx +52 -11
  106. package/corpus/core/src/client/composer/TiptapComposer.tsx +157 -20
  107. package/corpus/core/src/client/composer/VoiceButton.tsx +28 -0
  108. package/corpus/core/src/client/composer/useRealtimeVoiceMode.tsx +5 -0
  109. package/corpus/core/src/client/conversation/code-agent-transcript.ts +4 -5
  110. package/corpus/core/src/client/guided-questions.tsx +2 -1
  111. package/corpus/core/src/client/index.ts +11 -0
  112. package/corpus/core/src/client/use-chat-threads.ts +10 -1
  113. package/corpus/core/src/client/voice-provider-status.ts +44 -0
  114. package/corpus/core/src/code-agents/transcript-normalizer.ts +76 -0
  115. package/corpus/core/src/deploy/build.ts +12 -11
  116. package/corpus/core/src/guards/db-tool-scoping.ts +129 -0
  117. package/corpus/core/src/guards/index.ts +23 -0
  118. package/corpus/core/src/guards/no-drizzle-push.ts +116 -0
  119. package/corpus/core/src/guards/no-env-credentials.ts +229 -0
  120. package/corpus/core/src/guards/no-env-mutation.ts +114 -0
  121. package/corpus/core/src/guards/no-localhost-fallback.ts +124 -0
  122. package/corpus/core/src/guards/no-unscoped-credentials.ts +227 -0
  123. package/corpus/core/src/guards/no-unscoped-queries.ts +834 -0
  124. package/corpus/core/src/guards/scan-utils.ts +105 -0
  125. package/corpus/core/src/guards/types.ts +30 -0
  126. package/corpus/core/src/integrations/a2a-continuation-processor.ts +35 -5
  127. package/corpus/core/src/integrations/adapters/slack.ts +74 -21
  128. package/corpus/core/src/integrations/webhook-handler.ts +14 -0
  129. package/corpus/core/src/localization/default-messages.ts +6 -0
  130. package/corpus/core/src/observability/hosted-model-experiment.ts +118 -0
  131. package/corpus/core/src/observability/routes.ts +17 -0
  132. package/corpus/core/src/observability/store.ts +4 -1
  133. package/corpus/core/src/observability/traces.ts +41 -2
  134. package/corpus/core/src/org/accept-pending.ts +9 -1
  135. package/corpus/core/src/org/migrations.ts +41 -0
  136. package/corpus/core/src/package-lifecycle/index.ts +1 -0
  137. package/corpus/core/src/package-lifecycle/manifest.ts +120 -0
  138. package/corpus/core/src/provider-api/custom-registry.ts +56 -0
  139. package/corpus/core/src/provider-api/index.ts +2 -0
  140. package/corpus/core/src/scripts/agent-engines/list-agent-engines.ts +19 -1
  141. package/corpus/core/src/scripts/call-agent.ts +53 -3
  142. package/corpus/core/src/scripts/chat/search-chats.ts +11 -8
  143. package/corpus/core/src/secrets/register-framework-secrets.ts +1 -1
  144. package/corpus/core/src/secrets/storage.ts +47 -27
  145. package/corpus/core/src/server/action-discovery.ts +4 -0
  146. package/corpus/core/src/server/agent-chat-plugin.ts +95 -18
  147. package/corpus/core/src/server/core-routes-plugin.ts +45 -14
  148. package/corpus/core/src/server/csrf.ts +24 -5
  149. package/corpus/core/src/server/framework-request-handler.ts +34 -0
  150. package/corpus/core/src/server/google-realtime-session.ts +1 -36
  151. package/corpus/core/src/server/poll.ts +24 -2
  152. package/corpus/core/src/server/realtime-voice.ts +80 -24
  153. package/corpus/core/src/server/request-origin.ts +63 -0
  154. package/corpus/core/src/server/transcribe-voice.ts +1 -50
  155. package/corpus/core/src/server/voice-providers-status.ts +4 -3
  156. package/corpus/core/src/shared/cache-control.ts +2 -8
  157. package/corpus/core/src/styles/agent-native.css +1 -0
  158. package/corpus/core/src/styles/chat-history-list.css +313 -0
  159. package/corpus/core/src/templates/default/package.json +2 -2
  160. package/corpus/core/src/templates/headless/package.json +0 -1
  161. package/corpus/core/src/templates/workspace-core/.agents/skills/audit-log/SKILL.md +3 -0
  162. package/corpus/core/src/templates/workspace-core/.agents/skills/automations/SKILL.md +1 -1
  163. package/corpus/core/src/templates/workspace-core/.agents/skills/observability/SKILL.md +20 -0
  164. package/corpus/core/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +8 -4
  165. package/corpus/core/src/templates/workspace-core/package.json +0 -1
  166. package/corpus/core/src/templates/workspace-root/package.json +0 -1
  167. package/corpus/core/src/triggers/actions.ts +11 -3
  168. package/corpus/core/src/triggers/types.ts +7 -1
  169. package/corpus/core/src/vite/client.ts +1 -0
  170. package/corpus/templates/analytics/AGENTS.md +7 -0
  171. package/corpus/templates/analytics/actions/bigquery.ts +26 -2
  172. package/corpus/templates/analytics/actions/compose-dashboard.ts +65 -37
  173. package/corpus/templates/analytics/actions/data-source-status.ts +18 -0
  174. package/corpus/templates/analytics/actions/mutate-dashboard.ts +73 -24
  175. package/corpus/templates/analytics/actions/reorder-dashboard-panels.ts +16 -13
  176. package/corpus/templates/analytics/actions/update-dashboard.ts +53 -42
  177. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +5 -4
  178. package/corpus/templates/analytics/app/global.css +10 -0
  179. package/corpus/templates/analytics/app/pages/Ask.tsx +25 -2
  180. package/corpus/templates/analytics/changelog/2026-07-10-analytics-answers-simple-time-bounded-metrics-without-unnece.md +6 -0
  181. package/corpus/templates/analytics/changelog/2026-07-10-analytics-chat-reliably-uses-connected-data-sources-across-s.md +6 -0
  182. package/corpus/templates/analytics/changelog/2026-07-10-chat-threads-now-stay-clear-of-top-actions-and-fade-smoothly.md +6 -0
  183. package/corpus/templates/analytics/changelog/2026-07-10-context-chips-can-now-be-selected-and-removed-one-at-a-time-.md +6 -0
  184. package/corpus/templates/analytics/changelog/2026-07-10-dashboard-edits-from-the-agent-and-the-ui-at-the-same-time-n.md +6 -0
  185. package/corpus/templates/analytics/changelog/2026-07-10-the-ask-composer-no-longer-carries-dashboard-or-panel-contex.md +6 -0
  186. package/corpus/templates/analytics/changelog/2026-07-10-the-ask-section-stays-highlighted-while-its-chat-history-is-.md +6 -0
  187. package/corpus/templates/analytics/changelog/2026-07-10-timed-out-bigquery-jobs-are-now-cancelled-so-they-stop-consu.md +6 -0
  188. package/corpus/templates/analytics/package.json +1 -2
  189. package/corpus/templates/analytics/server/lib/bigquery.ts +91 -14
  190. package/corpus/templates/analytics/server/lib/dashboards-store.ts +154 -11
  191. package/corpus/templates/analytics/server/plugins/agent-chat.ts +82 -7
  192. package/corpus/templates/assets/package.json +1 -2
  193. package/corpus/templates/brain/package.json +1 -2
  194. package/corpus/templates/calendar/package.json +1 -2
  195. package/corpus/templates/chat/package.json +1 -2
  196. package/corpus/templates/clips/changelog/2026-07-10-atomic-view-tracking.md +6 -0
  197. package/corpus/templates/clips/chrome-extension/package.json +3 -2
  198. package/corpus/templates/clips/desktop/package.json +6 -4
  199. package/corpus/templates/clips/desktop/src/lib/recorder.ts +26 -13
  200. package/corpus/templates/clips/desktop/src/lib/voice-dictation.ts +4 -1
  201. package/corpus/templates/clips/package.json +1 -2
  202. package/corpus/templates/clips/server/db/schema.ts +29 -17
  203. package/corpus/templates/clips/server/plugins/db.ts +8 -0
  204. package/corpus/templates/clips/server/routes/api/view-event.post.ts +260 -105
  205. package/corpus/templates/content/.agents/skills/notion-integration/SKILL.md +16 -0
  206. package/corpus/templates/content/AGENTS.md +72 -62
  207. package/corpus/templates/content/actions/_content-database-source-adapters.ts +84 -0
  208. package/corpus/templates/content/actions/_database-source-utils.ts +206 -96
  209. package/corpus/templates/content/actions/_notion-database-source-adapter.ts +216 -0
  210. package/corpus/templates/content/actions/_position-utils.ts +76 -0
  211. package/corpus/templates/content/actions/_property-utils.ts +31 -20
  212. package/corpus/templates/content/actions/add-database-item.ts +52 -39
  213. package/corpus/templates/content/actions/attach-content-database-source.ts +95 -11
  214. package/corpus/templates/content/actions/change-content-database-source-role.ts +24 -1
  215. package/corpus/templates/content/actions/configure-document-property.ts +36 -27
  216. package/corpus/templates/content/actions/create-content-database.ts +40 -30
  217. package/corpus/templates/content/actions/create-document.ts +39 -32
  218. package/corpus/templates/content/actions/duplicate-document-property.ts +37 -28
  219. package/corpus/templates/content/actions/list-notion-database-sources.ts +80 -0
  220. package/corpus/templates/content/actions/move-document.ts +64 -48
  221. package/corpus/templates/content/actions/refresh-content-database-source.ts +42 -3
  222. package/corpus/templates/content/actions/share-local-file-document.ts +36 -27
  223. package/corpus/templates/content/actions/suggest-source-join-key.ts +39 -16
  224. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +89 -23
  225. package/corpus/templates/content/app/components/editor/database/settings.tsx +1 -1
  226. package/corpus/templates/content/app/hooks/use-content-database.ts +18 -1
  227. package/corpus/templates/content/app/i18n/zh-TW.ts +4 -0
  228. package/corpus/templates/content/app/i18n-data.ts +40 -0
  229. package/corpus/templates/content/changelog/2026-07-10-failed-row-creation-and-source-attach-now-show-an-error-inst.md +6 -0
  230. package/corpus/templates/content/changelog/2026-07-10-items-added-at-the-same-time-no-longer-end-up-with-identical.md +6 -0
  231. package/corpus/templates/content/changelog/2026-07-10-notion-databases-can-add-read-only-details-to-content-tables.md +6 -0
  232. package/corpus/templates/content/package.json +1 -2
  233. package/corpus/templates/content/parity/matrix.md +25 -25
  234. package/corpus/templates/content/parity/matrix.ts +1 -0
  235. package/corpus/templates/content/server/routes/api/documents/[id]/move.patch.ts +43 -26
  236. package/corpus/templates/content/shared/api.ts +16 -1
  237. package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +8 -4
  238. package/corpus/templates/design/AGENTS.md +12 -2
  239. package/corpus/templates/design/DEVELOPING.md +10 -6
  240. package/corpus/templates/design/actions/apply-visual-edit.ts +191 -6
  241. package/corpus/templates/design/actions/duplicate-design.ts +31 -25
  242. package/corpus/templates/design/app/pages/design-editor/pending-edits.ts +5 -1
  243. package/corpus/templates/design/changelog/2026-07-10-duplicating-a-design-can-no-longer-produce-a-copy-with-missi.md +6 -0
  244. package/corpus/templates/design/changelog/2026-07-10-local-visual-edits-can-now-save-safe-leaf-text-class-and.md +6 -0
  245. package/corpus/templates/design/package.json +1 -1
  246. package/corpus/templates/design/shared/local-jsx-visual-edit.ts +388 -0
  247. package/corpus/templates/dispatch/package.json +1 -2
  248. package/corpus/templates/forms/actions/patch-form-fields.ts +82 -44
  249. package/corpus/templates/forms/changelog/2026-07-10-simultaneous-edits-to-different-form-fields-no-longer-overwr.md +6 -0
  250. package/corpus/templates/forms/package.json +1 -2
  251. package/corpus/templates/macros/package.json +1 -2
  252. package/corpus/templates/mail/app/components/email/EmailThread.tsx +3 -1
  253. package/corpus/templates/mail/changelog/2026-07-10-fixed-calendar-rsvp-buttons-in-emails-firing-duplicate-respo.md +6 -0
  254. package/corpus/templates/mail/package.json +1 -2
  255. package/corpus/templates/plan/actions/restore-plan-version.ts +126 -96
  256. package/corpus/templates/plan/changelog/2026-07-10-restoring-a-plan-version-can-no-longer-leave-a-plan-half-res.md +6 -0
  257. package/corpus/templates/plan/package.json +1 -2
  258. package/corpus/templates/plan/server/plans.ts +9 -2
  259. package/corpus/templates/slides/actions/list-decks.ts +28 -0
  260. package/corpus/templates/slides/app/components/deck/MermaidRenderer.tsx +41 -26
  261. package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +4 -8
  262. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +2 -4
  263. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +4 -8
  264. package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +5 -12
  265. package/corpus/templates/slides/app/components/editor/HistoryPanel.tsx +3 -6
  266. package/corpus/templates/slides/app/components/editor/ImageDropPromptPopover.tsx +3 -6
  267. package/corpus/templates/slides/app/components/editor/PromptDialog.tsx +2 -5
  268. package/corpus/templates/slides/app/context/DeckContext.tsx +47 -4
  269. package/corpus/templates/slides/app/pages/DeckEditor.tsx +11 -30
  270. package/corpus/templates/slides/app/pages/Index.tsx +2 -3
  271. package/corpus/templates/slides/app/root.tsx +0 -5
  272. package/corpus/templates/slides/changelog/2026-07-10-deck-lists-load-much-faster-and-no-longer-re-download-slide-.md +6 -0
  273. package/corpus/templates/slides/package.json +1 -2
  274. package/dist/a2a/auth-policy.d.ts +19 -0
  275. package/dist/a2a/auth-policy.d.ts.map +1 -1
  276. package/dist/a2a/auth-policy.js +32 -0
  277. package/dist/a2a/auth-policy.js.map +1 -1
  278. package/dist/a2a/client.d.ts +7 -0
  279. package/dist/a2a/client.d.ts.map +1 -1
  280. package/dist/a2a/client.js +2 -1
  281. package/dist/a2a/client.js.map +1 -1
  282. package/dist/agent/durable-background.d.ts +8 -5
  283. package/dist/agent/durable-background.d.ts.map +1 -1
  284. package/dist/agent/durable-background.js +15 -8
  285. package/dist/agent/durable-background.js.map +1 -1
  286. package/dist/agent/production-agent.d.ts +6 -0
  287. package/dist/agent/production-agent.d.ts.map +1 -1
  288. package/dist/agent/production-agent.js +63 -26
  289. package/dist/agent/production-agent.js.map +1 -1
  290. package/dist/agent/run-store.d.ts.map +1 -1
  291. package/dist/agent/run-store.js +9 -4
  292. package/dist/agent/run-store.js.map +1 -1
  293. package/dist/agent/thread-data-builder.d.ts +1 -0
  294. package/dist/agent/thread-data-builder.d.ts.map +1 -1
  295. package/dist/agent/thread-data-builder.js +6 -5
  296. package/dist/agent/thread-data-builder.js.map +1 -1
  297. package/dist/audit/actions/export-audit-events.d.ts +27 -0
  298. package/dist/audit/actions/export-audit-events.d.ts.map +1 -0
  299. package/dist/audit/actions/export-audit-events.js +144 -0
  300. package/dist/audit/actions/export-audit-events.js.map +1 -0
  301. package/dist/audit/store.d.ts +1 -0
  302. package/dist/audit/store.d.ts.map +1 -1
  303. package/dist/audit/store.js +8 -3
  304. package/dist/audit/store.js.map +1 -1
  305. package/dist/audit/types.d.ts +3 -0
  306. package/dist/audit/types.d.ts.map +1 -1
  307. package/dist/audit/types.js.map +1 -1
  308. package/dist/chat-threads/store.d.ts +10 -0
  309. package/dist/chat-threads/store.d.ts.map +1 -1
  310. package/dist/chat-threads/store.js +6 -0
  311. package/dist/chat-threads/store.js.map +1 -1
  312. package/dist/cli/code-agent-executor.d.ts.map +1 -1
  313. package/dist/cli/code-agent-executor.js +1 -0
  314. package/dist/cli/code-agent-executor.js.map +1 -1
  315. package/dist/cli/code-agent-runs.d.ts +12 -0
  316. package/dist/cli/code-agent-runs.d.ts.map +1 -1
  317. package/dist/cli/code-agent-runs.js +1 -0
  318. package/dist/cli/code-agent-runs.js.map +1 -1
  319. package/dist/cli/doctor.d.ts +83 -0
  320. package/dist/cli/doctor.d.ts.map +1 -0
  321. package/dist/cli/doctor.js +277 -0
  322. package/dist/cli/doctor.js.map +1 -0
  323. package/dist/cli/index.js +50 -0
  324. package/dist/cli/index.js.map +1 -1
  325. package/dist/cli/info.js +3 -3
  326. package/dist/cli/info.js.map +1 -1
  327. package/dist/cli/package-lifecycle.d.ts +50 -0
  328. package/dist/cli/package-lifecycle.d.ts.map +1 -0
  329. package/dist/cli/package-lifecycle.js +560 -0
  330. package/dist/cli/package-lifecycle.js.map +1 -0
  331. package/dist/client/AgentPanel.d.ts.map +1 -1
  332. package/dist/client/AgentPanel.js +3 -3
  333. package/dist/client/AgentPanel.js.map +1 -1
  334. package/dist/client/AssistantChat.d.ts +11 -0
  335. package/dist/client/AssistantChat.d.ts.map +1 -1
  336. package/dist/client/AssistantChat.js +7 -4
  337. package/dist/client/AssistantChat.js.map +1 -1
  338. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  339. package/dist/client/MultiTabAssistantChat.js +83 -10
  340. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  341. package/dist/client/agent-chat-adapter.js +7 -3
  342. package/dist/client/agent-chat-adapter.js.map +1 -1
  343. package/dist/client/chat/ChatHistoryList.d.ts +88 -0
  344. package/dist/client/chat/ChatHistoryList.d.ts.map +1 -0
  345. package/dist/client/chat/ChatHistoryList.js +83 -0
  346. package/dist/client/chat/ChatHistoryList.js.map +1 -0
  347. package/dist/client/chat/index.d.ts +1 -0
  348. package/dist/client/chat/index.d.ts.map +1 -1
  349. package/dist/client/chat/index.js +1 -0
  350. package/dist/client/chat/index.js.map +1 -1
  351. package/dist/client/chat/tool-call-display.d.ts +13 -1
  352. package/dist/client/chat/tool-call-display.d.ts.map +1 -1
  353. package/dist/client/chat/tool-call-display.js +10 -2
  354. package/dist/client/chat/tool-call-display.js.map +1 -1
  355. package/dist/client/code-agent-chat-adapter.d.ts +17 -1
  356. package/dist/client/code-agent-chat-adapter.d.ts.map +1 -1
  357. package/dist/client/code-agent-chat-adapter.js +47 -3
  358. package/dist/client/code-agent-chat-adapter.js.map +1 -1
  359. package/dist/client/composer/RealtimeVoiceMode.d.ts +11 -1
  360. package/dist/client/composer/RealtimeVoiceMode.d.ts.map +1 -1
  361. package/dist/client/composer/RealtimeVoiceMode.js +5 -3
  362. package/dist/client/composer/RealtimeVoiceMode.js.map +1 -1
  363. package/dist/client/composer/TiptapComposer.d.ts +25 -2
  364. package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
  365. package/dist/client/composer/TiptapComposer.js +96 -5
  366. package/dist/client/composer/TiptapComposer.js.map +1 -1
  367. package/dist/client/composer/VoiceButton.d.ts.map +1 -1
  368. package/dist/client/composer/VoiceButton.js +21 -1
  369. package/dist/client/composer/VoiceButton.js.map +1 -1
  370. package/dist/client/composer/useRealtimeVoiceMode.d.ts.map +1 -1
  371. package/dist/client/composer/useRealtimeVoiceMode.js +5 -0
  372. package/dist/client/composer/useRealtimeVoiceMode.js.map +1 -1
  373. package/dist/client/conversation/code-agent-transcript.d.ts +2 -0
  374. package/dist/client/conversation/code-agent-transcript.d.ts.map +1 -1
  375. package/dist/client/conversation/code-agent-transcript.js +3 -5
  376. package/dist/client/conversation/code-agent-transcript.js.map +1 -1
  377. package/dist/client/guided-questions.d.ts.map +1 -1
  378. package/dist/client/guided-questions.js +2 -1
  379. package/dist/client/guided-questions.js.map +1 -1
  380. package/dist/client/index.d.ts +3 -1
  381. package/dist/client/index.d.ts.map +1 -1
  382. package/dist/client/index.js +3 -1
  383. package/dist/client/index.js.map +1 -1
  384. package/dist/client/use-chat-threads.d.ts.map +1 -1
  385. package/dist/client/use-chat-threads.js +9 -1
  386. package/dist/client/use-chat-threads.js.map +1 -1
  387. package/dist/client/voice-provider-status.d.ts +10 -0
  388. package/dist/client/voice-provider-status.d.ts.map +1 -0
  389. package/dist/client/voice-provider-status.js +32 -0
  390. package/dist/client/voice-provider-status.js.map +1 -0
  391. package/dist/code-agents/transcript-normalizer.d.ts +29 -0
  392. package/dist/code-agents/transcript-normalizer.d.ts.map +1 -1
  393. package/dist/code-agents/transcript-normalizer.js +55 -0
  394. package/dist/code-agents/transcript-normalizer.js.map +1 -1
  395. package/dist/collab/routes.d.ts +1 -1
  396. package/dist/deploy/build.d.ts.map +1 -1
  397. package/dist/deploy/build.js +13 -11
  398. package/dist/deploy/build.js.map +1 -1
  399. package/dist/guards/db-tool-scoping.d.ts +38 -0
  400. package/dist/guards/db-tool-scoping.d.ts.map +1 -0
  401. package/dist/guards/db-tool-scoping.js +114 -0
  402. package/dist/guards/db-tool-scoping.js.map +1 -0
  403. package/dist/guards/index.d.ts +23 -0
  404. package/dist/guards/index.d.ts.map +1 -0
  405. package/dist/guards/index.js +19 -0
  406. package/dist/guards/index.js.map +1 -0
  407. package/dist/guards/no-drizzle-push.d.ts +14 -0
  408. package/dist/guards/no-drizzle-push.d.ts.map +1 -0
  409. package/dist/guards/no-drizzle-push.js +97 -0
  410. package/dist/guards/no-drizzle-push.js.map +1 -0
  411. package/dist/guards/no-env-credentials.d.ts +36 -0
  412. package/dist/guards/no-env-credentials.d.ts.map +1 -0
  413. package/dist/guards/no-env-credentials.js +204 -0
  414. package/dist/guards/no-env-credentials.js.map +1 -0
  415. package/dist/guards/no-env-mutation.d.ts +23 -0
  416. package/dist/guards/no-env-mutation.d.ts.map +1 -0
  417. package/dist/guards/no-env-mutation.js +90 -0
  418. package/dist/guards/no-env-mutation.js.map +1 -0
  419. package/dist/guards/no-localhost-fallback.d.ts +30 -0
  420. package/dist/guards/no-localhost-fallback.d.ts.map +1 -0
  421. package/dist/guards/no-localhost-fallback.js +105 -0
  422. package/dist/guards/no-localhost-fallback.js.map +1 -0
  423. package/dist/guards/no-unscoped-credentials.d.ts +28 -0
  424. package/dist/guards/no-unscoped-credentials.d.ts.map +1 -0
  425. package/dist/guards/no-unscoped-credentials.js +199 -0
  426. package/dist/guards/no-unscoped-credentials.js.map +1 -0
  427. package/dist/guards/no-unscoped-queries.d.ts +32 -0
  428. package/dist/guards/no-unscoped-queries.d.ts.map +1 -0
  429. package/dist/guards/no-unscoped-queries.js +720 -0
  430. package/dist/guards/no-unscoped-queries.js.map +1 -0
  431. package/dist/guards/scan-utils.d.ts +28 -0
  432. package/dist/guards/scan-utils.d.ts.map +1 -0
  433. package/dist/guards/scan-utils.js +92 -0
  434. package/dist/guards/scan-utils.js.map +1 -0
  435. package/dist/guards/types.d.ts +28 -0
  436. package/dist/guards/types.d.ts.map +1 -0
  437. package/dist/guards/types.js +11 -0
  438. package/dist/guards/types.js.map +1 -0
  439. package/dist/integrations/a2a-continuation-processor.d.ts.map +1 -1
  440. package/dist/integrations/a2a-continuation-processor.js +25 -5
  441. package/dist/integrations/a2a-continuation-processor.js.map +1 -1
  442. package/dist/integrations/adapters/slack.d.ts.map +1 -1
  443. package/dist/integrations/adapters/slack.js +55 -21
  444. package/dist/integrations/adapters/slack.js.map +1 -1
  445. package/dist/integrations/webhook-handler.d.ts.map +1 -1
  446. package/dist/integrations/webhook-handler.js +11 -0
  447. package/dist/integrations/webhook-handler.js.map +1 -1
  448. package/dist/localization/default-messages.d.ts +5 -0
  449. package/dist/localization/default-messages.d.ts.map +1 -1
  450. package/dist/localization/default-messages.js +5 -0
  451. package/dist/localization/default-messages.js.map +1 -1
  452. package/dist/notifications/routes.d.ts +2 -2
  453. package/dist/observability/hosted-model-experiment.d.ts +39 -0
  454. package/dist/observability/hosted-model-experiment.d.ts.map +1 -0
  455. package/dist/observability/hosted-model-experiment.js +90 -0
  456. package/dist/observability/hosted-model-experiment.js.map +1 -0
  457. package/dist/observability/routes.d.ts +1 -1
  458. package/dist/observability/routes.d.ts.map +1 -1
  459. package/dist/observability/routes.js +16 -0
  460. package/dist/observability/routes.js.map +1 -1
  461. package/dist/observability/store.d.ts.map +1 -1
  462. package/dist/observability/store.js +4 -1
  463. package/dist/observability/store.js.map +1 -1
  464. package/dist/observability/traces.d.ts +6 -0
  465. package/dist/observability/traces.d.ts.map +1 -1
  466. package/dist/observability/traces.js +28 -2
  467. package/dist/observability/traces.js.map +1 -1
  468. package/dist/org/accept-pending.d.ts.map +1 -1
  469. package/dist/org/accept-pending.js +9 -1
  470. package/dist/org/accept-pending.js.map +1 -1
  471. package/dist/org/migrations.d.ts +7 -2
  472. package/dist/org/migrations.d.ts.map +1 -1
  473. package/dist/org/migrations.js +41 -0
  474. package/dist/org/migrations.js.map +1 -1
  475. package/dist/package-lifecycle/index.d.ts +2 -0
  476. package/dist/package-lifecycle/index.d.ts.map +1 -0
  477. package/dist/package-lifecycle/index.js +2 -0
  478. package/dist/package-lifecycle/index.js.map +1 -0
  479. package/dist/package-lifecycle/manifest.d.ts +24 -0
  480. package/dist/package-lifecycle/manifest.d.ts.map +1 -0
  481. package/dist/package-lifecycle/manifest.js +81 -0
  482. package/dist/package-lifecycle/manifest.js.map +1 -0
  483. package/dist/progress/routes.d.ts +1 -1
  484. package/dist/provider-api/custom-registry.d.ts +32 -1
  485. package/dist/provider-api/custom-registry.d.ts.map +1 -1
  486. package/dist/provider-api/custom-registry.js +41 -1
  487. package/dist/provider-api/custom-registry.js.map +1 -1
  488. package/dist/provider-api/index.d.ts +1 -1
  489. package/dist/provider-api/index.d.ts.map +1 -1
  490. package/dist/provider-api/index.js +1 -1
  491. package/dist/provider-api/index.js.map +1 -1
  492. package/dist/resources/handlers.d.ts +1 -1
  493. package/dist/scripts/agent-engines/list-agent-engines.d.ts.map +1 -1
  494. package/dist/scripts/agent-engines/list-agent-engines.js +19 -1
  495. package/dist/scripts/agent-engines/list-agent-engines.js.map +1 -1
  496. package/dist/scripts/call-agent.d.ts.map +1 -1
  497. package/dist/scripts/call-agent.js +43 -3
  498. package/dist/scripts/call-agent.js.map +1 -1
  499. package/dist/scripts/chat/search-chats.d.ts +1 -1
  500. package/dist/scripts/chat/search-chats.d.ts.map +1 -1
  501. package/dist/scripts/chat/search-chats.js +11 -8
  502. package/dist/scripts/chat/search-chats.js.map +1 -1
  503. package/dist/secrets/register-framework-secrets.js +1 -1
  504. package/dist/secrets/register-framework-secrets.js.map +1 -1
  505. package/dist/secrets/storage.d.ts.map +1 -1
  506. package/dist/secrets/storage.js +45 -27
  507. package/dist/secrets/storage.js.map +1 -1
  508. package/dist/server/action-discovery.d.ts.map +1 -1
  509. package/dist/server/action-discovery.js +4 -0
  510. package/dist/server/action-discovery.js.map +1 -1
  511. package/dist/server/agent-chat-plugin.d.ts +11 -0
  512. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  513. package/dist/server/agent-chat-plugin.js +74 -19
  514. package/dist/server/agent-chat-plugin.js.map +1 -1
  515. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  516. package/dist/server/core-routes-plugin.d.ts +3 -0
  517. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  518. package/dist/server/core-routes-plugin.js +36 -12
  519. package/dist/server/core-routes-plugin.js.map +1 -1
  520. package/dist/server/csrf.d.ts.map +1 -1
  521. package/dist/server/csrf.js +18 -5
  522. package/dist/server/csrf.js.map +1 -1
  523. package/dist/server/framework-request-handler.d.ts.map +1 -1
  524. package/dist/server/framework-request-handler.js +34 -0
  525. package/dist/server/framework-request-handler.js.map +1 -1
  526. package/dist/server/google-realtime-session.d.ts.map +1 -1
  527. package/dist/server/google-realtime-session.js +2 -34
  528. package/dist/server/google-realtime-session.js.map +1 -1
  529. package/dist/server/poll.d.ts.map +1 -1
  530. package/dist/server/poll.js +24 -2
  531. package/dist/server/poll.js.map +1 -1
  532. package/dist/server/realtime-voice.d.ts.map +1 -1
  533. package/dist/server/realtime-voice.js +64 -21
  534. package/dist/server/realtime-voice.js.map +1 -1
  535. package/dist/server/request-origin.d.ts +10 -0
  536. package/dist/server/request-origin.d.ts.map +1 -0
  537. package/dist/server/request-origin.js +60 -0
  538. package/dist/server/request-origin.js.map +1 -0
  539. package/dist/server/transcribe-voice.d.ts.map +1 -1
  540. package/dist/server/transcribe-voice.js +2 -48
  541. package/dist/server/transcribe-voice.js.map +1 -1
  542. package/dist/server/voice-providers-status.d.ts.map +1 -1
  543. package/dist/server/voice-providers-status.js +2 -3
  544. package/dist/server/voice-providers-status.js.map +1 -1
  545. package/dist/shared/cache-control.d.ts +4 -6
  546. package/dist/shared/cache-control.d.ts.map +1 -1
  547. package/dist/shared/cache-control.js +2 -7
  548. package/dist/shared/cache-control.js.map +1 -1
  549. package/dist/styles/agent-native.css +1 -0
  550. package/dist/styles/chat-history-list.css +313 -0
  551. package/dist/templates/default/package.json +2 -2
  552. package/dist/templates/headless/package.json +0 -1
  553. package/dist/templates/workspace-core/.agents/skills/audit-log/SKILL.md +3 -0
  554. package/dist/templates/workspace-core/.agents/skills/automations/SKILL.md +1 -1
  555. package/dist/templates/workspace-core/.agents/skills/observability/SKILL.md +20 -0
  556. package/dist/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +8 -4
  557. package/dist/templates/workspace-core/package.json +0 -1
  558. package/dist/templates/workspace-root/package.json +0 -1
  559. package/dist/triggers/actions.d.ts.map +1 -1
  560. package/dist/triggers/actions.js +8 -3
  561. package/dist/triggers/actions.js.map +1 -1
  562. package/dist/triggers/types.d.ts +7 -1
  563. package/dist/triggers/types.d.ts.map +1 -1
  564. package/dist/triggers/types.js.map +1 -1
  565. package/dist/vite/client.d.ts.map +1 -1
  566. package/dist/vite/client.js +1 -0
  567. package/dist/vite/client.js.map +1 -1
  568. package/docs/content/audit-log.mdx +2 -1
  569. package/docs/content/automations.mdx +7 -7
  570. package/docs/content/creating-templates.mdx +1 -1
  571. package/docs/content/internationalization.mdx +2 -1
  572. package/docs/content/locales/ar-SA/audit-log.mdx +2 -1
  573. package/docs/content/locales/ar-SA/automations.mdx +7 -7
  574. package/docs/content/locales/ar-SA/getting-started.mdx +7 -1
  575. package/docs/content/locales/ar-SA/internationalization.mdx +1 -1
  576. package/docs/content/locales/ar-SA/package-lifecycle.mdx +19 -0
  577. package/docs/content/locales/ar-SA/sandbox-adapters.mdx +18 -7
  578. package/docs/content/locales/ar-SA/security.mdx +8 -0
  579. package/docs/content/locales/de-DE/audit-log.mdx +2 -1
  580. package/docs/content/locales/de-DE/automations.mdx +7 -7
  581. package/docs/content/locales/de-DE/getting-started.mdx +7 -1
  582. package/docs/content/locales/de-DE/internationalization.mdx +1 -1
  583. package/docs/content/locales/de-DE/package-lifecycle.mdx +22 -0
  584. package/docs/content/locales/de-DE/sandbox-adapters.mdx +18 -7
  585. package/docs/content/locales/de-DE/security.mdx +8 -0
  586. package/docs/content/locales/es-ES/audit-log.mdx +2 -1
  587. package/docs/content/locales/es-ES/automations.mdx +7 -7
  588. package/docs/content/locales/es-ES/getting-started.mdx +7 -1
  589. package/docs/content/locales/es-ES/internationalization.mdx +1 -1
  590. package/docs/content/locales/es-ES/package-lifecycle.mdx +22 -0
  591. package/docs/content/locales/es-ES/sandbox-adapters.mdx +18 -7
  592. package/docs/content/locales/es-ES/security.mdx +8 -0
  593. package/docs/content/locales/fr-FR/audit-log.mdx +2 -1
  594. package/docs/content/locales/fr-FR/automations.mdx +7 -7
  595. package/docs/content/locales/fr-FR/getting-started.mdx +7 -1
  596. package/docs/content/locales/fr-FR/internationalization.mdx +1 -1
  597. package/docs/content/locales/fr-FR/package-lifecycle.mdx +21 -0
  598. package/docs/content/locales/fr-FR/sandbox-adapters.mdx +19 -8
  599. package/docs/content/locales/fr-FR/security.mdx +8 -0
  600. package/docs/content/locales/hi-IN/audit-log.mdx +2 -1
  601. package/docs/content/locales/hi-IN/automations.mdx +7 -7
  602. package/docs/content/locales/hi-IN/getting-started.mdx +7 -1
  603. package/docs/content/locales/hi-IN/internationalization.mdx +1 -1
  604. package/docs/content/locales/hi-IN/package-lifecycle.mdx +20 -0
  605. package/docs/content/locales/hi-IN/sandbox-adapters.mdx +18 -7
  606. package/docs/content/locales/hi-IN/security.mdx +8 -0
  607. package/docs/content/locales/ja-JP/audit-log.mdx +2 -1
  608. package/docs/content/locales/ja-JP/automations.mdx +7 -7
  609. package/docs/content/locales/ja-JP/getting-started.mdx +7 -1
  610. package/docs/content/locales/ja-JP/internationalization.mdx +1 -1
  611. package/docs/content/locales/ja-JP/package-lifecycle.mdx +21 -0
  612. package/docs/content/locales/ja-JP/sandbox-adapters.mdx +17 -6
  613. package/docs/content/locales/ja-JP/security.mdx +8 -0
  614. package/docs/content/locales/ko-KR/audit-log.mdx +2 -1
  615. package/docs/content/locales/ko-KR/automations.mdx +7 -7
  616. package/docs/content/locales/ko-KR/getting-started.mdx +7 -1
  617. package/docs/content/locales/ko-KR/internationalization.mdx +1 -1
  618. package/docs/content/locales/ko-KR/package-lifecycle.mdx +20 -0
  619. package/docs/content/locales/ko-KR/sandbox-adapters.mdx +17 -6
  620. package/docs/content/locales/ko-KR/security.mdx +8 -0
  621. package/docs/content/locales/pt-BR/audit-log.mdx +2 -1
  622. package/docs/content/locales/pt-BR/automations.mdx +7 -7
  623. package/docs/content/locales/pt-BR/getting-started.mdx +7 -1
  624. package/docs/content/locales/pt-BR/internationalization.mdx +1 -1
  625. package/docs/content/locales/pt-BR/package-lifecycle.mdx +21 -0
  626. package/docs/content/locales/pt-BR/sandbox-adapters.mdx +18 -7
  627. package/docs/content/locales/pt-BR/security.mdx +8 -0
  628. package/docs/content/locales/zh-CN/audit-log.mdx +2 -1
  629. package/docs/content/locales/zh-CN/automations.mdx +7 -7
  630. package/docs/content/locales/zh-CN/getting-started.mdx +6 -4
  631. package/docs/content/locales/zh-CN/internationalization.mdx +1 -1
  632. package/docs/content/locales/zh-CN/package-lifecycle.mdx +19 -0
  633. package/docs/content/locales/zh-CN/sandbox-adapters.mdx +17 -6
  634. package/docs/content/locales/zh-CN/security.mdx +8 -0
  635. package/docs/content/locales/zh-TW/audit-log.mdx +2 -1
  636. package/docs/content/locales/zh-TW/automations.mdx +7 -7
  637. package/docs/content/locales/zh-TW/getting-started.mdx +6 -4
  638. package/docs/content/locales/zh-TW/internationalization.mdx +1 -1
  639. package/docs/content/locales/zh-TW/package-lifecycle.mdx +19 -0
  640. package/docs/content/locales/zh-TW/sandbox-adapters.mdx +18 -7
  641. package/docs/content/locales/zh-TW/security.mdx +8 -0
  642. package/docs/content/package-lifecycle.mdx +51 -0
  643. package/docs/content/sandbox-adapters.mdx +16 -5
  644. package/package.json +12 -3
  645. package/src/templates/default/package.json +2 -2
  646. package/src/templates/headless/package.json +0 -1
  647. package/src/templates/workspace-core/.agents/skills/audit-log/SKILL.md +3 -0
  648. package/src/templates/workspace-core/.agents/skills/automations/SKILL.md +1 -1
  649. package/src/templates/workspace-core/.agents/skills/observability/SKILL.md +20 -0
  650. package/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +8 -4
  651. package/src/templates/workspace-core/package.json +0 -1
  652. package/src/templates/workspace-root/package.json +0 -1
  653. package/corpus/templates/slides/app/components/ui/toast.tsx +0 -1
  654. package/corpus/templates/slides/app/components/ui/toaster.tsx +0 -1
  655. package/corpus/templates/slides/app/hooks/use-toast.ts +0 -1
@@ -13,12 +13,16 @@ import { z } from "zod";
13
13
 
14
14
  import { interpolate } from "../app/pages/adhoc/sql-dashboard/interpolate";
15
15
  import { dryRunQuery } from "../server/lib/bigquery";
16
- import { getDashboard, upsertDashboard } from "../server/lib/dashboards-store";
16
+ import {
17
+ upsertDashboard,
18
+ upsertDashboardWithRetry,
19
+ } from "../server/lib/dashboards-store";
17
20
  import { parseDemoDescriptor } from "../server/lib/demo-source";
18
21
  import { validateFirstPartyAnalyticsSql } from "../server/lib/first-party-analytics.js";
19
22
  import {
20
23
  applyPanelOrder,
21
24
  compactDashboardResult,
25
+ type PanelOrderResult,
22
26
  } from "./dashboard-panel-order";
23
27
 
24
28
  /**
@@ -627,29 +631,27 @@ export default defineAction({
627
631
  );
628
632
  }
629
633
 
630
- const existing = await getDashboard(dashboardId, ctx);
631
- if (!existing) {
632
- throw new Error(
633
- `dashboard "${dashboardId}" not found (or you don't have access).`,
634
- );
635
- }
636
-
637
- const root = existing.config as any;
638
-
639
634
  if (args.panelOrder) {
640
- const orderDetails = applyPanelOrder(root, args.panelOrder);
641
- const validation = validateDashboardConfig(root);
642
- if (validation) throw new Error(validation);
643
- await upsertDashboard(
635
+ // Recomputed on every retry attempt from the freshest dashboard config,
636
+ // so a concurrent writer's edit is never silently overwritten by this
637
+ // move.
638
+ let orderDetails!: PanelOrderResult;
639
+ const saved = await upsertDashboardWithRetry(
644
640
  dashboardId,
645
- existing.kind,
646
- root as Record<string, unknown>,
647
641
  ctx,
642
+ (existing) => {
643
+ const root = existing.config as Record<string, unknown>;
644
+ orderDetails = applyPanelOrder(root, args.panelOrder!);
645
+ const validation = validateDashboardConfig(root);
646
+ if (validation) throw new Error(validation);
647
+ return { kind: existing.kind, body: root };
648
+ },
648
649
  );
649
- await syncToCollab(dashboardId, root as Record<string, unknown>);
650
+ const root = saved.config as Record<string, unknown>;
651
+ await syncToCollab(dashboardId, root);
650
652
  return dashboardResult(
651
653
  dashboardId,
652
- root as Record<string, unknown>,
654
+ root,
653
655
  1,
654
656
  `Moved ${orderDetails.movedPanelIds.length} panel id(s) to the front of dashboard "${dashboardId}"; it now has ${orderDetails.panelCount} panel(s).`,
655
657
  orderDetails.movedPanelIds,
@@ -657,36 +659,45 @@ export default defineAction({
657
659
  );
658
660
  }
659
661
 
660
- const details: string[] = [];
661
- for (const op of args.ops!) {
662
- try {
663
- details.push(applyJsonOp(root, op as JsonOp));
664
- } catch (err: any) {
665
- throw new Error(`applying op ${JSON.stringify(op)}: ${err.message}`);
666
- }
667
- }
668
-
669
- const validation = validateDashboardConfig(root);
670
- if (validation) throw new Error(validation);
671
- if (args.ops!.some((op) => opCanChangePanelSql(op as JsonOp))) {
672
- const sqlError = await validatePanelSql(root);
673
- if (sqlError) throw new Error(sqlError);
674
- }
675
-
676
- await upsertDashboard(
662
+ // Recomputed on every retry attempt from the freshest dashboard config —
663
+ // JSON-pointer ops are replayed against fresh state, not the stale config
664
+ // that produced the first (lost) attempt.
665
+ let appliedDetails: string[] = [];
666
+ const saved = await upsertDashboardWithRetry(
677
667
  dashboardId,
678
- existing.kind,
679
- root as Record<string, unknown>,
680
668
  ctx,
669
+ async (existing) => {
670
+ const root = existing.config as Record<string, unknown>;
671
+ const details: string[] = [];
672
+ for (const op of args.ops!) {
673
+ try {
674
+ details.push(applyJsonOp(root, op as JsonOp));
675
+ } catch (err: any) {
676
+ throw new Error(
677
+ `applying op ${JSON.stringify(op)}: ${err.message}`,
678
+ );
679
+ }
680
+ }
681
+
682
+ const validation = validateDashboardConfig(root);
683
+ if (validation) throw new Error(validation);
684
+ if (args.ops!.some((op) => opCanChangePanelSql(op as JsonOp))) {
685
+ const sqlError = await validatePanelSql(root);
686
+ if (sqlError) throw new Error(sqlError);
687
+ }
688
+ appliedDetails = details;
689
+ return { kind: existing.kind, body: root };
690
+ },
681
691
  );
682
- await syncToCollab(dashboardId, root as Record<string, unknown>);
692
+ const root = saved.config as Record<string, unknown>;
693
+ await syncToCollab(dashboardId, root);
683
694
 
684
- const panelCount = countPanels(root as Record<string, unknown>);
695
+ const panelCount = countPanels(root);
685
696
  return dashboardResult(
686
697
  dashboardId,
687
- root as Record<string, unknown>,
688
- details.length,
689
- `Applied ${details.length} op(s); dashboard "${dashboardId}" now has ${panelCount} panel(s).`,
698
+ root,
699
+ appliedDetails.length,
700
+ `Applied ${appliedDetails.length} op(s); dashboard "${dashboardId}" now has ${panelCount} panel(s).`,
690
701
  [],
691
702
  args.returnConfig === true,
692
703
  );
@@ -1295,7 +1295,7 @@ function persistedAnalyticsThreadId() {
1295
1295
  }
1296
1296
  }
1297
1297
 
1298
- function AnalyticsChatsSection() {
1298
+ function AnalyticsChatsSection({ isAskRoute }: { isAskRoute: boolean }) {
1299
1299
  const navigate = useNavigate();
1300
1300
  const t = useT();
1301
1301
  const {
@@ -1439,8 +1439,9 @@ function AnalyticsChatsSection() {
1439
1439
  {visibleThreads.map((thread) => {
1440
1440
  const title = threadTitle(thread, t("chat.untitledChat"));
1441
1441
  const isActive =
1442
- thread.id === activeThreadId ||
1443
- thread.id === persistedAnalyticsThreadId();
1442
+ isAskRoute &&
1443
+ (thread.id === activeThreadId ||
1444
+ thread.id === persistedAnalyticsThreadId());
1444
1445
  const isRenaming = thread.id === renamingThreadId;
1445
1446
  return (
1446
1447
  <div
@@ -2592,7 +2593,7 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
2592
2593
  />
2593
2594
  </button>
2594
2595
  </div>
2595
- {askOpen && <AnalyticsChatsSection />}
2596
+ {askOpen && <AnalyticsChatsSection isAskRoute={isAskRoute} />}
2596
2597
  </div>
2597
2598
 
2598
2599
  {/* Sessions link */}
@@ -149,6 +149,16 @@
149
149
  background-color: hsl(var(--background)) !important;
150
150
  }
151
151
 
152
+ .analytics-chat-panel .agent-thread-content {
153
+ padding-top: 4.5rem;
154
+ }
155
+
156
+ .analytics-chat-panel
157
+ [data-agent-page-chat-scrolled]
158
+ [data-agent-page-chat-fade] {
159
+ opacity: 1;
160
+ }
161
+
152
162
  @media (max-width: 767px) {
153
163
  .analytics-chat-panel [data-agent-page-new-chat] {
154
164
  display: none;
@@ -1,5 +1,9 @@
1
- import { AgentChatSurface, useT } from "@agent-native/core/client";
2
- import { useState } from "react";
1
+ import {
2
+ AgentChatSurface,
3
+ useAgentChatContext,
4
+ useT,
5
+ } from "@agent-native/core/client";
6
+ import { useEffect, useMemo, useState } from "react";
3
7
 
4
8
  import {
5
9
  ANALYTICS_CHAT_STORAGE_KEY,
@@ -7,9 +11,28 @@ import {
7
11
  } from "@/lib/chat-handoff";
8
12
  import { TAB_ID } from "@/lib/tab-id";
9
13
 
14
+ const DASHBOARD_CONTEXT_KEYS = new Set([
15
+ "analytics-selected-dashboard",
16
+ "analytics-selected-dashboard-panel",
17
+ ]);
18
+
10
19
  export default function AskPage() {
11
20
  const t = useT();
12
21
  const [restoreActiveThread] = useState(() => hasRecentAnalyticsChat());
22
+ const { items: chatContextItems, remove: removeChatContextItem } =
23
+ useAgentChatContext();
24
+ const staleDashboardContextKey = useMemo(
25
+ () =>
26
+ chatContextItems.find((item) => DASHBOARD_CONTEXT_KEYS.has(item.key))
27
+ ?.key ?? null,
28
+ [chatContextItems],
29
+ );
30
+
31
+ useEffect(() => {
32
+ if (staleDashboardContextKey) {
33
+ removeChatContextItem(staleDashboardContextKey);
34
+ }
35
+ }, [removeChatContextItem, staleDashboardContextKey]);
13
36
 
14
37
  return (
15
38
  <div className="analytics-ask-page flex h-full min-h-0 flex-col bg-background">
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-07-10
4
+ ---
5
+
6
+ Analytics answers simple time-bounded metrics without unnecessary follow-up queries.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-10
4
+ ---
5
+
6
+ Analytics chat reliably uses connected data sources across supported models
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-10
4
+ ---
5
+
6
+ Chat threads now stay clear of top actions and fade smoothly beneath them while scrolling.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-07-10
4
+ ---
5
+
6
+ Context chips can now be selected and removed one at a time with Backspace from the start of the Ask composer.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-10
4
+ ---
5
+
6
+ Dashboard edits from the agent and the UI at the same time no longer overwrite each other
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-10
4
+ ---
5
+
6
+ The Ask composer no longer carries dashboard or panel context after leaving a dashboard.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-10
4
+ ---
5
+
6
+ The Ask section stays highlighted while its chat history is expanded.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-10
4
+ ---
5
+
6
+ Timed-out BigQuery jobs are now cancelled so they stop consuming warehouse quota.
@@ -25,7 +25,7 @@
25
25
  "@sparticuz/chromium-min": "^149.0.0",
26
26
  "@tabler/icons-react": "catalog:",
27
27
  "drizzle-orm": "^0.45.2",
28
- "h3": "^2.0.1-rc.20",
28
+ "h3": "catalog:",
29
29
  "isbot": "^5",
30
30
  "playwright-core": "^1.61.1",
31
31
  "postgres": "^3.4.9",
@@ -100,7 +100,6 @@
100
100
  "tailwindcss": "catalog:",
101
101
  "three": "^0.176.0",
102
102
  "tsx": "^4.20.3",
103
- "typescript": "catalog:",
104
103
  "typescript-7": "catalog:",
105
104
  "vaul": "^1.1.2",
106
105
  "vite": "catalog:",
@@ -241,6 +241,15 @@ export interface QueryResult {
241
241
  cached?: boolean;
242
242
  }
243
243
 
244
+ export interface RunQueryOptions {
245
+ /**
246
+ * The current agent run's abort signal. This cancels in-flight BigQuery
247
+ * requests and, importantly, stops the one-second job polling wait without
248
+ * starting another request after the parent run has ended.
249
+ */
250
+ signal?: AbortSignal;
251
+ }
252
+
244
253
  interface BigQueryField {
245
254
  name: string;
246
255
  type: string;
@@ -265,6 +274,57 @@ interface BigQueryGetQueryResultsResponse {
265
274
  totalBytesProcessed?: string;
266
275
  }
267
276
 
277
+ function createAbortError(): Error {
278
+ if (typeof DOMException !== "undefined") {
279
+ return new DOMException("BigQuery query aborted", "AbortError");
280
+ }
281
+ const error = new Error("BigQuery query aborted");
282
+ error.name = "AbortError";
283
+ return error;
284
+ }
285
+
286
+ function throwIfAborted(signal?: AbortSignal): void {
287
+ if (signal?.aborted) throw createAbortError();
288
+ }
289
+
290
+ function waitForPollInterval(signal?: AbortSignal): Promise<void> {
291
+ throwIfAborted(signal);
292
+ return new Promise((resolve, reject) => {
293
+ const timeout = setTimeout(() => {
294
+ signal?.removeEventListener("abort", onAbort);
295
+ resolve();
296
+ }, 1000);
297
+ const onAbort = () => {
298
+ clearTimeout(timeout);
299
+ signal?.removeEventListener("abort", onAbort);
300
+ reject(createAbortError());
301
+ };
302
+ signal?.addEventListener("abort", onAbort, { once: true });
303
+ });
304
+ }
305
+
306
+ async function cancelQueryJob(
307
+ projectId: string,
308
+ jobId: string,
309
+ token: string,
310
+ ): Promise<void> {
311
+ try {
312
+ await fetch(
313
+ `https://bigquery.googleapis.com/bigquery/v2/projects/${projectId}/jobs/${jobId}/cancel`,
314
+ {
315
+ method: "POST",
316
+ headers: {
317
+ Authorization: `Bearer ${token}`,
318
+ "Content-Type": "application/json",
319
+ },
320
+ },
321
+ );
322
+ } catch {
323
+ // Cancellation is best-effort and must not hide the original abort or
324
+ // timeout reason if BigQuery or the network is unavailable.
325
+ }
326
+ }
327
+
268
328
  /**
269
329
  * Convert BigQuery REST API row format to plain objects.
270
330
  * BigQuery returns rows as { f: [{ v: value }, ...] } arrays
@@ -361,7 +421,12 @@ export async function dryRunQuery(sql: string): Promise<string | null> {
361
421
  return `BigQuery validation failed (${res.status})`;
362
422
  }
363
423
 
364
- export async function runQuery(sql: string): Promise<QueryResult> {
424
+ export async function runQuery(
425
+ sql: string,
426
+ options: RunQueryOptions = {},
427
+ ): Promise<QueryResult> {
428
+ const { signal } = options;
429
+ throwIfAborted(signal);
365
430
  const { projectId, cacheScope, appEventsTable } = await getProjectInfo();
366
431
  const resolvedSql = await resolveTablePlaceholder(
367
432
  sql,
@@ -383,8 +448,10 @@ export async function runQuery(sql: string): Promise<QueryResult> {
383
448
  const token = await getAccessToken();
384
449
  const url = `https://bigquery.googleapis.com/bigquery/v2/projects/${projectId}/queries`;
385
450
 
451
+ throwIfAborted(signal);
386
452
  const res = await fetch(url, {
387
453
  method: "POST",
454
+ ...(signal ? { signal } : {}),
388
455
  headers: {
389
456
  Authorization: `Bearer ${token}`,
390
457
  "Content-Type": "application/json",
@@ -409,23 +476,33 @@ export async function runQuery(sql: string): Promise<QueryResult> {
409
476
  const resultsUrl = `https://bigquery.googleapis.com/bigquery/v2/projects/${projectId}/queries/${jobId}`;
410
477
 
411
478
  let attempts = 0;
412
- while (!data.jobComplete && attempts < 60) {
413
- await new Promise((resolve) => setTimeout(resolve, 1000));
414
- const pollRes = await fetch(resultsUrl, {
415
- headers: {
416
- Authorization: `Bearer ${token}`,
417
- "Content-Type": "application/json",
418
- },
419
- });
420
- if (!pollRes.ok) {
421
- const text = await pollRes.text();
422
- throw new Error(`BigQuery poll error ${pollRes.status}: ${text}`);
479
+ try {
480
+ while (!data.jobComplete && attempts < 60) {
481
+ await waitForPollInterval(signal);
482
+ throwIfAborted(signal);
483
+ const pollRes = await fetch(resultsUrl, {
484
+ ...(signal ? { signal } : {}),
485
+ headers: {
486
+ Authorization: `Bearer ${token}`,
487
+ "Content-Type": "application/json",
488
+ },
489
+ });
490
+ if (!pollRes.ok) {
491
+ const text = await pollRes.text();
492
+ throw new Error(`BigQuery poll error ${pollRes.status}: ${text}`);
493
+ }
494
+ data = (await pollRes.json()) as BigQueryGetQueryResultsResponse;
495
+ attempts++;
496
+ }
497
+ } catch (error) {
498
+ if (signal?.aborted) {
499
+ await cancelQueryJob(projectId, jobId, token);
423
500
  }
424
- data = (await pollRes.json()) as BigQueryGetQueryResultsResponse;
425
- attempts++;
501
+ throw error;
426
502
  }
427
503
 
428
504
  if (!data.jobComplete) {
505
+ await cancelQueryJob(projectId, jobId, token);
429
506
  throw new Error("BigQuery query timed out after 60 seconds");
430
507
  }
431
508
  }
@@ -129,6 +129,45 @@ function nanoidFallback(): string {
129
129
  );
130
130
  }
131
131
 
132
+ /**
133
+ * Normalize affected-row metadata from every createGetDb backend: libSQL,
134
+ * PGlite, Neon, postgres.js, better-sqlite3, and D1. Mirrors
135
+ * templates/design/actions/update-design.ts's `affectedRowCount`.
136
+ */
137
+ function affectedRowCount(result: unknown): number | undefined {
138
+ const candidate = result as
139
+ | {
140
+ rowsAffected?: unknown;
141
+ affectedRows?: unknown;
142
+ rowCount?: unknown;
143
+ count?: unknown;
144
+ changes?: unknown;
145
+ meta?: { changes?: unknown };
146
+ }
147
+ | undefined;
148
+ const value =
149
+ candidate?.rowsAffected ??
150
+ candidate?.affectedRows ??
151
+ candidate?.rowCount ??
152
+ candidate?.count ??
153
+ candidate?.changes ??
154
+ candidate?.meta?.changes;
155
+ return typeof value === "number" ? value : undefined;
156
+ }
157
+
158
+ /**
159
+ * Thrown when a fenced `upsertDashboard` write (an `expectedUpdatedAt` was
160
+ * supplied) loses its compare-and-swap because another writer changed the
161
+ * row first. Callers should re-read the dashboard and re-apply their mutation
162
+ * against the fresh config — see `upsertDashboardWithRetry`.
163
+ */
164
+ export class DashboardConflictError extends Error {
165
+ constructor(id: string) {
166
+ super(`Dashboard "${id}" changed between read and write.`);
167
+ this.name = "DashboardConflictError";
168
+ }
169
+ }
170
+
132
171
  function changeScope(
133
172
  ownerEmail: string,
134
173
  orgId: string | null,
@@ -466,15 +505,33 @@ async function snapshotDashboardRevision(
466
505
  * Upsert a dashboard. On create, caller becomes owner and visibility defaults
467
506
  * to `private`; users explicitly promote useful dashboards to org/public via
468
507
  * sharing. On update, `assertAccess` requires `editor`.
508
+ *
509
+ * `expectedUpdatedAt` fences the update against concurrent writers: pass the
510
+ * `updatedAt` observed by an earlier `getDashboard` call and the UPDATE only
511
+ * applies `WHERE id = ? AND updated_at = ?`. If another writer already saved
512
+ * in between, the fenced UPDATE affects zero rows and this throws
513
+ * `DashboardConflictError` instead of silently clobbering their write. Omit
514
+ * it (the default) to keep the prior unconditional last-write-wins behavior,
515
+ * which existing callers (legacy migration, revision restore, and any
516
+ * one-shot write that isn't a read-modify-write) still rely on.
469
517
  */
470
518
  export async function upsertDashboard(
471
519
  id: string,
472
520
  kind: DashboardKind,
473
521
  body: Record<string, unknown>,
474
522
  ctx: AccessCtx,
523
+ expectedUpdatedAt?: string,
475
524
  ): Promise<DashboardRecord> {
476
525
  // If the row exists (or legacy-migrates), require editor.
477
526
  const existing = await getDashboard(id, ctx);
527
+ if (!existing && expectedUpdatedAt !== undefined) {
528
+ // A fence was supplied against a specific prior version, but the row is
529
+ // gone (deleted, or a legacy key that failed to migrate) by the time we
530
+ // looked. Treat this as a conflict rather than silently creating a fresh
531
+ // row — the caller's mutation was computed against state that no longer
532
+ // exists.
533
+ throw new DashboardConflictError(id);
534
+ }
478
535
  const db = getDb() as any;
479
536
  const { title, config } = configFromSettings(body);
480
537
  const configJson = JSON.stringify(config);
@@ -487,17 +544,43 @@ export async function upsertDashboard(
487
544
  existing.kind !== kind ||
488
545
  existing.title !== title ||
489
546
  JSON.stringify(existing.config) !== configJson;
490
- if (changed) await snapshotDashboardRevision(db, existing, ctx);
491
- await db
492
- .update(schema.dashboards)
493
- .set({
494
- kind,
495
- title,
496
- config: configJson,
497
- updatedAt: nowIso(),
498
- updatedBy: ctx.email,
499
- })
500
- .where(eq(schema.dashboards.id, id));
547
+ const setValues = {
548
+ kind,
549
+ title,
550
+ config: configJson,
551
+ updatedAt: nowIso(),
552
+ updatedBy: ctx.email,
553
+ };
554
+ if (expectedUpdatedAt !== undefined) {
555
+ // Fenced write. Snapshot the revision only after we know this exact
556
+ // write actually landed — otherwise a lost race would record a
557
+ // revision for a save that never happened.
558
+ const updateResult = await db
559
+ .update(schema.dashboards)
560
+ .set(setValues)
561
+ .where(
562
+ and(
563
+ eq(schema.dashboards.id, id),
564
+ eq(schema.dashboards.updatedAt, expectedUpdatedAt),
565
+ ),
566
+ );
567
+ const affected = affectedRowCount(updateResult);
568
+ if (affected === undefined) {
569
+ throw new Error(
570
+ "The database driver did not report an affected-row count for the fenced dashboard update.",
571
+ );
572
+ }
573
+ if (affected === 0) {
574
+ throw new DashboardConflictError(id);
575
+ }
576
+ if (changed) await snapshotDashboardRevision(db, existing, ctx);
577
+ } else {
578
+ if (changed) await snapshotDashboardRevision(db, existing, ctx);
579
+ await db
580
+ .update(schema.dashboards)
581
+ .set(setValues)
582
+ .where(eq(schema.dashboards.id, id));
583
+ }
501
584
  } else {
502
585
  await db.insert(schema.dashboards).values({
503
586
  id,
@@ -528,6 +611,66 @@ export async function upsertDashboard(
528
611
  return dashboard;
529
612
  }
530
613
 
614
+ /** Max attempts (first try + retries) for `upsertDashboardWithRetry`. */
615
+ export const DASHBOARD_SAVE_MAX_ATTEMPTS = 3;
616
+
617
+ /**
618
+ * Read-modify-write helper for the four action call sites that fetch a
619
+ * dashboard, mutate its config in memory, then save it back. Fences every
620
+ * save with the `updatedAt` of the record `mutate` was given, so a
621
+ * concurrent writer (agent adds a panel while a human drags one, or two
622
+ * agent calls race) never gets silently clobbered.
623
+ *
624
+ * `mutate` is invoked with the freshest `DashboardRecord` on every attempt —
625
+ * re-fetched from `getDashboard` each time — and must recompute the
626
+ * `{ kind, body }` to save FROM THAT RECORD, not from a closure over an
627
+ * earlier read; only then does a retry actually merge both writers' changes
628
+ * instead of re-deriving the same stale result. `mutate` may throw a
629
+ * non-conflict error (e.g. validation) to abort immediately without
630
+ * retrying.
631
+ *
632
+ * On a lost race, this re-reads and re-invokes `mutate` up to `maxAttempts`
633
+ * times before failing loud with a clear error so callers never silently
634
+ * drop a write or loop forever.
635
+ */
636
+ export async function upsertDashboardWithRetry(
637
+ id: string,
638
+ ctx: AccessCtx,
639
+ mutate: (
640
+ existing: DashboardRecord,
641
+ ) =>
642
+ | { kind: DashboardKind; body: Record<string, unknown> }
643
+ | Promise<{ kind: DashboardKind; body: Record<string, unknown> }>,
644
+ maxAttempts: number = DASHBOARD_SAVE_MAX_ATTEMPTS,
645
+ ): Promise<DashboardRecord> {
646
+ let lastConflict: unknown;
647
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
648
+ const existing = await getDashboard(id, ctx);
649
+ if (!existing) {
650
+ throw new Error(
651
+ `dashboard "${id}" not found (or you don't have access).`,
652
+ );
653
+ }
654
+ const { kind, body } = await mutate(existing);
655
+ try {
656
+ return await upsertDashboard(id, kind, body, ctx, existing.updatedAt);
657
+ } catch (err) {
658
+ if (err instanceof DashboardConflictError) {
659
+ lastConflict = err;
660
+ continue;
661
+ }
662
+ throw err;
663
+ }
664
+ }
665
+ const finalError = new Error(
666
+ `Could not save dashboard "${id}" after ${maxAttempts} attempt(s); it kept changing concurrently. Re-read the dashboard and try again.`,
667
+ );
668
+ if (lastConflict !== undefined) {
669
+ (finalError as Error & { cause?: unknown }).cause = lastConflict;
670
+ }
671
+ throw finalError;
672
+ }
673
+
531
674
  export async function listDashboardRevisions(
532
675
  dashboardId: string,
533
676
  ctx: AccessCtx,