@agent-native/core 0.96.0 → 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 (450) hide show
  1. package/README.md +6 -0
  2. package/corpus/README.md +2 -2
  3. package/corpus/core/CHANGELOG.md +39 -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 +9 -2
  81. package/corpus/core/src/a2a/auth-policy.ts +33 -0
  82. package/corpus/core/src/agent/durable-background.ts +15 -7
  83. package/corpus/core/src/agent/run-store.ts +13 -4
  84. package/corpus/core/src/audit/actions/export-audit-events.ts +162 -0
  85. package/corpus/core/src/audit/store.ts +8 -3
  86. package/corpus/core/src/audit/types.ts +3 -0
  87. package/corpus/core/src/cli/doctor.ts +396 -0
  88. package/corpus/core/src/cli/index.ts +56 -0
  89. package/corpus/core/src/cli/info.ts +3 -3
  90. package/corpus/core/src/cli/package-lifecycle.ts +821 -0
  91. package/corpus/core/src/client/agent-chat-adapter.ts +9 -9
  92. package/corpus/core/src/guards/db-tool-scoping.ts +129 -0
  93. package/corpus/core/src/guards/index.ts +23 -0
  94. package/corpus/core/src/guards/no-drizzle-push.ts +116 -0
  95. package/corpus/core/src/guards/no-env-credentials.ts +229 -0
  96. package/corpus/core/src/guards/no-env-mutation.ts +114 -0
  97. package/corpus/core/src/guards/no-localhost-fallback.ts +124 -0
  98. package/corpus/core/src/guards/no-unscoped-credentials.ts +227 -0
  99. package/corpus/core/src/guards/no-unscoped-queries.ts +834 -0
  100. package/corpus/core/src/guards/scan-utils.ts +105 -0
  101. package/corpus/core/src/guards/types.ts +30 -0
  102. package/corpus/core/src/org/accept-pending.ts +9 -1
  103. package/corpus/core/src/org/migrations.ts +41 -0
  104. package/corpus/core/src/package-lifecycle/index.ts +1 -0
  105. package/corpus/core/src/package-lifecycle/manifest.ts +120 -0
  106. package/corpus/core/src/provider-api/custom-registry.ts +56 -0
  107. package/corpus/core/src/provider-api/index.ts +2 -0
  108. package/corpus/core/src/secrets/storage.ts +47 -27
  109. package/corpus/core/src/server/action-discovery.ts +4 -0
  110. package/corpus/core/src/server/agent-chat-plugin.ts +54 -12
  111. package/corpus/core/src/server/core-routes-plugin.ts +30 -14
  112. package/corpus/core/src/server/csrf.ts +24 -5
  113. package/corpus/core/src/server/framework-request-handler.ts +34 -0
  114. package/corpus/core/src/server/google-realtime-session.ts +1 -36
  115. package/corpus/core/src/server/poll.ts +24 -2
  116. package/corpus/core/src/server/realtime-voice.ts +9 -0
  117. package/corpus/core/src/server/request-origin.ts +63 -0
  118. package/corpus/core/src/server/transcribe-voice.ts +1 -50
  119. package/corpus/core/src/templates/default/package.json +2 -2
  120. package/corpus/core/src/templates/headless/package.json +0 -1
  121. package/corpus/core/src/templates/workspace-core/.agents/skills/audit-log/SKILL.md +3 -0
  122. package/corpus/core/src/templates/workspace-core/.agents/skills/automations/SKILL.md +1 -1
  123. package/corpus/core/src/templates/workspace-core/package.json +0 -1
  124. package/corpus/core/src/templates/workspace-root/package.json +0 -1
  125. package/corpus/core/src/triggers/actions.ts +11 -3
  126. package/corpus/core/src/triggers/types.ts +7 -1
  127. package/corpus/templates/analytics/actions/compose-dashboard.ts +65 -37
  128. package/corpus/templates/analytics/actions/mutate-dashboard.ts +73 -24
  129. package/corpus/templates/analytics/actions/reorder-dashboard-panels.ts +16 -13
  130. package/corpus/templates/analytics/actions/update-dashboard.ts +53 -42
  131. package/corpus/templates/analytics/changelog/2026-07-10-dashboard-edits-from-the-agent-and-the-ui-at-the-same-time-n.md +6 -0
  132. package/corpus/templates/analytics/changelog/2026-07-10-timed-out-bigquery-jobs-are-now-cancelled-so-they-stop-consu.md +6 -0
  133. package/corpus/templates/analytics/package.json +1 -2
  134. package/corpus/templates/analytics/server/lib/bigquery.ts +3 -2
  135. package/corpus/templates/analytics/server/lib/dashboards-store.ts +154 -11
  136. package/corpus/templates/assets/package.json +1 -2
  137. package/corpus/templates/brain/package.json +1 -2
  138. package/corpus/templates/calendar/package.json +1 -2
  139. package/corpus/templates/chat/package.json +1 -2
  140. package/corpus/templates/clips/changelog/2026-07-10-atomic-view-tracking.md +6 -0
  141. package/corpus/templates/clips/chrome-extension/package.json +3 -2
  142. package/corpus/templates/clips/desktop/package.json +6 -4
  143. package/corpus/templates/clips/desktop/src/lib/recorder.ts +26 -13
  144. package/corpus/templates/clips/desktop/src/lib/voice-dictation.ts +4 -1
  145. package/corpus/templates/clips/package.json +1 -2
  146. package/corpus/templates/clips/server/db/schema.ts +29 -17
  147. package/corpus/templates/clips/server/plugins/db.ts +8 -0
  148. package/corpus/templates/clips/server/routes/api/view-event.post.ts +260 -105
  149. package/corpus/templates/content/.agents/skills/notion-integration/SKILL.md +16 -0
  150. package/corpus/templates/content/AGENTS.md +72 -62
  151. package/corpus/templates/content/actions/_content-database-source-adapters.ts +84 -0
  152. package/corpus/templates/content/actions/_database-source-utils.ts +206 -96
  153. package/corpus/templates/content/actions/_notion-database-source-adapter.ts +216 -0
  154. package/corpus/templates/content/actions/_position-utils.ts +76 -0
  155. package/corpus/templates/content/actions/_property-utils.ts +31 -20
  156. package/corpus/templates/content/actions/add-database-item.ts +52 -39
  157. package/corpus/templates/content/actions/attach-content-database-source.ts +95 -11
  158. package/corpus/templates/content/actions/change-content-database-source-role.ts +24 -1
  159. package/corpus/templates/content/actions/configure-document-property.ts +36 -27
  160. package/corpus/templates/content/actions/create-content-database.ts +40 -30
  161. package/corpus/templates/content/actions/create-document.ts +39 -32
  162. package/corpus/templates/content/actions/duplicate-document-property.ts +37 -28
  163. package/corpus/templates/content/actions/list-notion-database-sources.ts +80 -0
  164. package/corpus/templates/content/actions/move-document.ts +64 -48
  165. package/corpus/templates/content/actions/refresh-content-database-source.ts +42 -3
  166. package/corpus/templates/content/actions/share-local-file-document.ts +36 -27
  167. package/corpus/templates/content/actions/suggest-source-join-key.ts +39 -16
  168. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +89 -23
  169. package/corpus/templates/content/app/components/editor/database/settings.tsx +1 -1
  170. package/corpus/templates/content/app/hooks/use-content-database.ts +18 -1
  171. package/corpus/templates/content/app/i18n/zh-TW.ts +4 -0
  172. package/corpus/templates/content/app/i18n-data.ts +40 -0
  173. package/corpus/templates/content/changelog/2026-07-10-failed-row-creation-and-source-attach-now-show-an-error-inst.md +6 -0
  174. package/corpus/templates/content/changelog/2026-07-10-items-added-at-the-same-time-no-longer-end-up-with-identical.md +6 -0
  175. package/corpus/templates/content/changelog/2026-07-10-notion-databases-can-add-read-only-details-to-content-tables.md +6 -0
  176. package/corpus/templates/content/package.json +1 -2
  177. package/corpus/templates/content/parity/matrix.md +25 -25
  178. package/corpus/templates/content/parity/matrix.ts +1 -0
  179. package/corpus/templates/content/server/routes/api/documents/[id]/move.patch.ts +43 -26
  180. package/corpus/templates/content/shared/api.ts +16 -1
  181. package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +8 -4
  182. package/corpus/templates/design/AGENTS.md +12 -2
  183. package/corpus/templates/design/DEVELOPING.md +10 -6
  184. package/corpus/templates/design/actions/apply-visual-edit.ts +191 -6
  185. package/corpus/templates/design/actions/duplicate-design.ts +31 -25
  186. package/corpus/templates/design/app/pages/design-editor/pending-edits.ts +5 -1
  187. package/corpus/templates/design/changelog/2026-07-10-duplicating-a-design-can-no-longer-produce-a-copy-with-missi.md +6 -0
  188. package/corpus/templates/design/changelog/2026-07-10-local-visual-edits-can-now-save-safe-leaf-text-class-and.md +6 -0
  189. package/corpus/templates/design/package.json +1 -1
  190. package/corpus/templates/design/shared/local-jsx-visual-edit.ts +388 -0
  191. package/corpus/templates/dispatch/package.json +1 -2
  192. package/corpus/templates/forms/actions/patch-form-fields.ts +82 -44
  193. package/corpus/templates/forms/changelog/2026-07-10-simultaneous-edits-to-different-form-fields-no-longer-overwr.md +6 -0
  194. package/corpus/templates/forms/package.json +1 -2
  195. package/corpus/templates/macros/package.json +1 -2
  196. package/corpus/templates/mail/app/components/email/EmailThread.tsx +3 -1
  197. package/corpus/templates/mail/changelog/2026-07-10-fixed-calendar-rsvp-buttons-in-emails-firing-duplicate-respo.md +6 -0
  198. package/corpus/templates/mail/package.json +1 -2
  199. package/corpus/templates/plan/actions/restore-plan-version.ts +126 -96
  200. package/corpus/templates/plan/changelog/2026-07-10-restoring-a-plan-version-can-no-longer-leave-a-plan-half-res.md +6 -0
  201. package/corpus/templates/plan/package.json +1 -2
  202. package/corpus/templates/plan/server/plans.ts +9 -2
  203. package/corpus/templates/slides/actions/list-decks.ts +28 -0
  204. package/corpus/templates/slides/app/components/deck/MermaidRenderer.tsx +41 -26
  205. package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +4 -8
  206. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +2 -4
  207. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +4 -8
  208. package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +5 -12
  209. package/corpus/templates/slides/app/components/editor/HistoryPanel.tsx +3 -6
  210. package/corpus/templates/slides/app/components/editor/ImageDropPromptPopover.tsx +3 -6
  211. package/corpus/templates/slides/app/components/editor/PromptDialog.tsx +2 -5
  212. package/corpus/templates/slides/app/context/DeckContext.tsx +47 -4
  213. package/corpus/templates/slides/app/pages/DeckEditor.tsx +11 -30
  214. package/corpus/templates/slides/app/pages/Index.tsx +2 -3
  215. package/corpus/templates/slides/app/root.tsx +0 -5
  216. package/corpus/templates/slides/changelog/2026-07-10-deck-lists-load-much-faster-and-no-longer-re-download-slide-.md +6 -0
  217. package/corpus/templates/slides/package.json +1 -2
  218. package/dist/a2a/auth-policy.d.ts +19 -0
  219. package/dist/a2a/auth-policy.d.ts.map +1 -1
  220. package/dist/a2a/auth-policy.js +32 -0
  221. package/dist/a2a/auth-policy.js.map +1 -1
  222. package/dist/agent/durable-background.d.ts +8 -5
  223. package/dist/agent/durable-background.d.ts.map +1 -1
  224. package/dist/agent/durable-background.js +15 -8
  225. package/dist/agent/durable-background.js.map +1 -1
  226. package/dist/agent/run-store.d.ts.map +1 -1
  227. package/dist/agent/run-store.js +9 -4
  228. package/dist/agent/run-store.js.map +1 -1
  229. package/dist/audit/actions/export-audit-events.d.ts +27 -0
  230. package/dist/audit/actions/export-audit-events.d.ts.map +1 -0
  231. package/dist/audit/actions/export-audit-events.js +144 -0
  232. package/dist/audit/actions/export-audit-events.js.map +1 -0
  233. package/dist/audit/store.d.ts +1 -0
  234. package/dist/audit/store.d.ts.map +1 -1
  235. package/dist/audit/store.js +8 -3
  236. package/dist/audit/store.js.map +1 -1
  237. package/dist/audit/types.d.ts +3 -0
  238. package/dist/audit/types.d.ts.map +1 -1
  239. package/dist/audit/types.js.map +1 -1
  240. package/dist/cli/doctor.d.ts +83 -0
  241. package/dist/cli/doctor.d.ts.map +1 -0
  242. package/dist/cli/doctor.js +277 -0
  243. package/dist/cli/doctor.js.map +1 -0
  244. package/dist/cli/index.js +50 -0
  245. package/dist/cli/index.js.map +1 -1
  246. package/dist/cli/info.js +3 -3
  247. package/dist/cli/info.js.map +1 -1
  248. package/dist/cli/package-lifecycle.d.ts +50 -0
  249. package/dist/cli/package-lifecycle.d.ts.map +1 -0
  250. package/dist/cli/package-lifecycle.js +560 -0
  251. package/dist/cli/package-lifecycle.js.map +1 -0
  252. package/dist/client/agent-chat-adapter.js +7 -3
  253. package/dist/client/agent-chat-adapter.js.map +1 -1
  254. package/dist/collab/struct-routes.d.ts +1 -1
  255. package/dist/guards/db-tool-scoping.d.ts +38 -0
  256. package/dist/guards/db-tool-scoping.d.ts.map +1 -0
  257. package/dist/guards/db-tool-scoping.js +114 -0
  258. package/dist/guards/db-tool-scoping.js.map +1 -0
  259. package/dist/guards/index.d.ts +23 -0
  260. package/dist/guards/index.d.ts.map +1 -0
  261. package/dist/guards/index.js +19 -0
  262. package/dist/guards/index.js.map +1 -0
  263. package/dist/guards/no-drizzle-push.d.ts +14 -0
  264. package/dist/guards/no-drizzle-push.d.ts.map +1 -0
  265. package/dist/guards/no-drizzle-push.js +97 -0
  266. package/dist/guards/no-drizzle-push.js.map +1 -0
  267. package/dist/guards/no-env-credentials.d.ts +36 -0
  268. package/dist/guards/no-env-credentials.d.ts.map +1 -0
  269. package/dist/guards/no-env-credentials.js +204 -0
  270. package/dist/guards/no-env-credentials.js.map +1 -0
  271. package/dist/guards/no-env-mutation.d.ts +23 -0
  272. package/dist/guards/no-env-mutation.d.ts.map +1 -0
  273. package/dist/guards/no-env-mutation.js +90 -0
  274. package/dist/guards/no-env-mutation.js.map +1 -0
  275. package/dist/guards/no-localhost-fallback.d.ts +30 -0
  276. package/dist/guards/no-localhost-fallback.d.ts.map +1 -0
  277. package/dist/guards/no-localhost-fallback.js +105 -0
  278. package/dist/guards/no-localhost-fallback.js.map +1 -0
  279. package/dist/guards/no-unscoped-credentials.d.ts +28 -0
  280. package/dist/guards/no-unscoped-credentials.d.ts.map +1 -0
  281. package/dist/guards/no-unscoped-credentials.js +199 -0
  282. package/dist/guards/no-unscoped-credentials.js.map +1 -0
  283. package/dist/guards/no-unscoped-queries.d.ts +32 -0
  284. package/dist/guards/no-unscoped-queries.d.ts.map +1 -0
  285. package/dist/guards/no-unscoped-queries.js +720 -0
  286. package/dist/guards/no-unscoped-queries.js.map +1 -0
  287. package/dist/guards/scan-utils.d.ts +28 -0
  288. package/dist/guards/scan-utils.d.ts.map +1 -0
  289. package/dist/guards/scan-utils.js +92 -0
  290. package/dist/guards/scan-utils.js.map +1 -0
  291. package/dist/guards/types.d.ts +28 -0
  292. package/dist/guards/types.d.ts.map +1 -0
  293. package/dist/guards/types.js +11 -0
  294. package/dist/guards/types.js.map +1 -0
  295. package/dist/org/accept-pending.d.ts.map +1 -1
  296. package/dist/org/accept-pending.js +9 -1
  297. package/dist/org/accept-pending.js.map +1 -1
  298. package/dist/org/migrations.d.ts +7 -2
  299. package/dist/org/migrations.d.ts.map +1 -1
  300. package/dist/org/migrations.js +41 -0
  301. package/dist/org/migrations.js.map +1 -1
  302. package/dist/package-lifecycle/index.d.ts +2 -0
  303. package/dist/package-lifecycle/index.d.ts.map +1 -0
  304. package/dist/package-lifecycle/index.js +2 -0
  305. package/dist/package-lifecycle/index.js.map +1 -0
  306. package/dist/package-lifecycle/manifest.d.ts +24 -0
  307. package/dist/package-lifecycle/manifest.d.ts.map +1 -0
  308. package/dist/package-lifecycle/manifest.js +81 -0
  309. package/dist/package-lifecycle/manifest.js.map +1 -0
  310. package/dist/progress/routes.d.ts +1 -1
  311. package/dist/provider-api/custom-registry.d.ts +32 -1
  312. package/dist/provider-api/custom-registry.d.ts.map +1 -1
  313. package/dist/provider-api/custom-registry.js +41 -1
  314. package/dist/provider-api/custom-registry.js.map +1 -1
  315. package/dist/provider-api/index.d.ts +1 -1
  316. package/dist/provider-api/index.d.ts.map +1 -1
  317. package/dist/provider-api/index.js +1 -1
  318. package/dist/provider-api/index.js.map +1 -1
  319. package/dist/secrets/storage.d.ts.map +1 -1
  320. package/dist/secrets/storage.js +45 -27
  321. package/dist/secrets/storage.js.map +1 -1
  322. package/dist/server/action-discovery.d.ts.map +1 -1
  323. package/dist/server/action-discovery.js +4 -0
  324. package/dist/server/action-discovery.js.map +1 -1
  325. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  326. package/dist/server/agent-chat-plugin.js +47 -13
  327. package/dist/server/agent-chat-plugin.js.map +1 -1
  328. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  329. package/dist/server/core-routes-plugin.js +23 -12
  330. package/dist/server/core-routes-plugin.js.map +1 -1
  331. package/dist/server/csrf.d.ts.map +1 -1
  332. package/dist/server/csrf.js +18 -5
  333. package/dist/server/csrf.js.map +1 -1
  334. package/dist/server/framework-request-handler.d.ts.map +1 -1
  335. package/dist/server/framework-request-handler.js +34 -0
  336. package/dist/server/framework-request-handler.js.map +1 -1
  337. package/dist/server/google-realtime-session.d.ts.map +1 -1
  338. package/dist/server/google-realtime-session.js +2 -34
  339. package/dist/server/google-realtime-session.js.map +1 -1
  340. package/dist/server/poll.d.ts.map +1 -1
  341. package/dist/server/poll.js +24 -2
  342. package/dist/server/poll.js.map +1 -1
  343. package/dist/server/realtime-voice.d.ts.map +1 -1
  344. package/dist/server/realtime-voice.js +9 -0
  345. package/dist/server/realtime-voice.js.map +1 -1
  346. package/dist/server/request-origin.d.ts +10 -0
  347. package/dist/server/request-origin.d.ts.map +1 -0
  348. package/dist/server/request-origin.js +60 -0
  349. package/dist/server/request-origin.js.map +1 -0
  350. package/dist/server/transcribe-voice.d.ts.map +1 -1
  351. package/dist/server/transcribe-voice.js +2 -48
  352. package/dist/server/transcribe-voice.js.map +1 -1
  353. package/dist/templates/default/package.json +2 -2
  354. package/dist/templates/headless/package.json +0 -1
  355. package/dist/templates/workspace-core/.agents/skills/audit-log/SKILL.md +3 -0
  356. package/dist/templates/workspace-core/.agents/skills/automations/SKILL.md +1 -1
  357. package/dist/templates/workspace-core/package.json +0 -1
  358. package/dist/templates/workspace-root/package.json +0 -1
  359. package/dist/triggers/actions.d.ts.map +1 -1
  360. package/dist/triggers/actions.js +8 -3
  361. package/dist/triggers/actions.js.map +1 -1
  362. package/dist/triggers/types.d.ts +7 -1
  363. package/dist/triggers/types.d.ts.map +1 -1
  364. package/dist/triggers/types.js.map +1 -1
  365. package/docs/content/audit-log.mdx +2 -1
  366. package/docs/content/automations.mdx +7 -7
  367. package/docs/content/creating-templates.mdx +1 -1
  368. package/docs/content/internationalization.mdx +2 -1
  369. package/docs/content/locales/ar-SA/audit-log.mdx +2 -1
  370. package/docs/content/locales/ar-SA/automations.mdx +7 -7
  371. package/docs/content/locales/ar-SA/getting-started.mdx +7 -1
  372. package/docs/content/locales/ar-SA/internationalization.mdx +1 -1
  373. package/docs/content/locales/ar-SA/package-lifecycle.mdx +19 -0
  374. package/docs/content/locales/ar-SA/sandbox-adapters.mdx +18 -7
  375. package/docs/content/locales/ar-SA/security.mdx +8 -0
  376. package/docs/content/locales/de-DE/audit-log.mdx +2 -1
  377. package/docs/content/locales/de-DE/automations.mdx +7 -7
  378. package/docs/content/locales/de-DE/getting-started.mdx +7 -1
  379. package/docs/content/locales/de-DE/internationalization.mdx +1 -1
  380. package/docs/content/locales/de-DE/package-lifecycle.mdx +22 -0
  381. package/docs/content/locales/de-DE/sandbox-adapters.mdx +18 -7
  382. package/docs/content/locales/de-DE/security.mdx +8 -0
  383. package/docs/content/locales/es-ES/audit-log.mdx +2 -1
  384. package/docs/content/locales/es-ES/automations.mdx +7 -7
  385. package/docs/content/locales/es-ES/getting-started.mdx +7 -1
  386. package/docs/content/locales/es-ES/internationalization.mdx +1 -1
  387. package/docs/content/locales/es-ES/package-lifecycle.mdx +22 -0
  388. package/docs/content/locales/es-ES/sandbox-adapters.mdx +18 -7
  389. package/docs/content/locales/es-ES/security.mdx +8 -0
  390. package/docs/content/locales/fr-FR/audit-log.mdx +2 -1
  391. package/docs/content/locales/fr-FR/automations.mdx +7 -7
  392. package/docs/content/locales/fr-FR/getting-started.mdx +7 -1
  393. package/docs/content/locales/fr-FR/internationalization.mdx +1 -1
  394. package/docs/content/locales/fr-FR/package-lifecycle.mdx +21 -0
  395. package/docs/content/locales/fr-FR/sandbox-adapters.mdx +19 -8
  396. package/docs/content/locales/fr-FR/security.mdx +8 -0
  397. package/docs/content/locales/hi-IN/audit-log.mdx +2 -1
  398. package/docs/content/locales/hi-IN/automations.mdx +7 -7
  399. package/docs/content/locales/hi-IN/getting-started.mdx +7 -1
  400. package/docs/content/locales/hi-IN/internationalization.mdx +1 -1
  401. package/docs/content/locales/hi-IN/package-lifecycle.mdx +20 -0
  402. package/docs/content/locales/hi-IN/sandbox-adapters.mdx +18 -7
  403. package/docs/content/locales/hi-IN/security.mdx +8 -0
  404. package/docs/content/locales/ja-JP/audit-log.mdx +2 -1
  405. package/docs/content/locales/ja-JP/automations.mdx +7 -7
  406. package/docs/content/locales/ja-JP/getting-started.mdx +7 -1
  407. package/docs/content/locales/ja-JP/internationalization.mdx +1 -1
  408. package/docs/content/locales/ja-JP/package-lifecycle.mdx +21 -0
  409. package/docs/content/locales/ja-JP/sandbox-adapters.mdx +17 -6
  410. package/docs/content/locales/ja-JP/security.mdx +8 -0
  411. package/docs/content/locales/ko-KR/audit-log.mdx +2 -1
  412. package/docs/content/locales/ko-KR/automations.mdx +7 -7
  413. package/docs/content/locales/ko-KR/getting-started.mdx +7 -1
  414. package/docs/content/locales/ko-KR/internationalization.mdx +1 -1
  415. package/docs/content/locales/ko-KR/package-lifecycle.mdx +20 -0
  416. package/docs/content/locales/ko-KR/sandbox-adapters.mdx +17 -6
  417. package/docs/content/locales/ko-KR/security.mdx +8 -0
  418. package/docs/content/locales/pt-BR/audit-log.mdx +2 -1
  419. package/docs/content/locales/pt-BR/automations.mdx +7 -7
  420. package/docs/content/locales/pt-BR/getting-started.mdx +7 -1
  421. package/docs/content/locales/pt-BR/internationalization.mdx +1 -1
  422. package/docs/content/locales/pt-BR/package-lifecycle.mdx +21 -0
  423. package/docs/content/locales/pt-BR/sandbox-adapters.mdx +18 -7
  424. package/docs/content/locales/pt-BR/security.mdx +8 -0
  425. package/docs/content/locales/zh-CN/audit-log.mdx +2 -1
  426. package/docs/content/locales/zh-CN/automations.mdx +7 -7
  427. package/docs/content/locales/zh-CN/getting-started.mdx +6 -4
  428. package/docs/content/locales/zh-CN/internationalization.mdx +1 -1
  429. package/docs/content/locales/zh-CN/package-lifecycle.mdx +19 -0
  430. package/docs/content/locales/zh-CN/sandbox-adapters.mdx +17 -6
  431. package/docs/content/locales/zh-CN/security.mdx +8 -0
  432. package/docs/content/locales/zh-TW/audit-log.mdx +2 -1
  433. package/docs/content/locales/zh-TW/automations.mdx +7 -7
  434. package/docs/content/locales/zh-TW/getting-started.mdx +6 -4
  435. package/docs/content/locales/zh-TW/internationalization.mdx +1 -1
  436. package/docs/content/locales/zh-TW/package-lifecycle.mdx +19 -0
  437. package/docs/content/locales/zh-TW/sandbox-adapters.mdx +18 -7
  438. package/docs/content/locales/zh-TW/security.mdx +8 -0
  439. package/docs/content/package-lifecycle.mdx +51 -0
  440. package/docs/content/sandbox-adapters.mdx +16 -5
  441. package/package.json +10 -3
  442. package/src/templates/default/package.json +2 -2
  443. package/src/templates/headless/package.json +0 -1
  444. package/src/templates/workspace-core/.agents/skills/audit-log/SKILL.md +3 -0
  445. package/src/templates/workspace-core/.agents/skills/automations/SKILL.md +1 -1
  446. package/src/templates/workspace-core/package.json +0 -1
  447. package/src/templates/workspace-root/package.json +0 -1
  448. package/corpus/templates/slides/app/components/ui/toast.tsx +0 -1
  449. package/corpus/templates/slides/app/components/ui/toaster.tsx +0 -1
  450. package/corpus/templates/slides/app/hooks/use-toast.ts +0 -1
@@ -11,7 +11,11 @@ import {
11
11
  } from "@agent-native/core/server";
12
12
  import { z } from "zod";
13
13
 
14
- import { getDashboard, upsertDashboard } from "../server/lib/dashboards-store";
14
+ import {
15
+ getDashboard,
16
+ upsertDashboard,
17
+ upsertDashboardWithRetry,
18
+ } from "../server/lib/dashboards-store";
15
19
  import { validateFirstPartyAnalyticsSql } from "../server/lib/first-party-analytics.js";
16
20
  import {
17
21
  buildFirstPartyDashboardFilters,
@@ -235,61 +239,85 @@ export default defineAction({
235
239
  ? (existing.config.name as string)
236
240
  : args.dashboardId);
237
241
 
242
+ function withFilters(
243
+ config: Record<string, unknown>,
244
+ ): Record<string, unknown> {
245
+ return composedPanels.some((panel) =>
246
+ usesFirstPartyDashboardFilters(panel.sql),
247
+ )
248
+ ? withFirstPartyDashboardFilters(config)
249
+ : config;
250
+ }
251
+
238
252
  let finalConfig: Record<string, unknown>;
239
253
  let appendedCount = composedPanels.length;
240
254
  let skippedExistingIds: string[] = [];
241
255
 
242
256
  if (existing && !args.overwrite) {
243
257
  // Append: preserve existing panels + order, add only new panel ids.
244
- const existingConfig = existing.config as Record<string, unknown>;
245
- const existingPanels = Array.isArray(existingConfig.panels)
246
- ? (existingConfig.panels as Array<Record<string, unknown>>)
247
- : [];
248
- const existingIds = new Set(
249
- existingPanels
250
- .map((p) => (typeof p?.id === "string" ? p.id : null))
251
- .filter((id): id is string => !!id),
258
+ // Recomputed on every retry attempt from the freshest existing config
259
+ // via upsertDashboardWithRetry — so a panel a concurrent writer just
260
+ // added is never silently dropped by this merge.
261
+ const saved = await upsertDashboardWithRetry(
262
+ args.dashboardId,
263
+ ctx,
264
+ (freshExisting) => {
265
+ const existingConfig = freshExisting.config as Record<
266
+ string,
267
+ unknown
268
+ >;
269
+ const existingPanels = Array.isArray(existingConfig.panels)
270
+ ? (existingConfig.panels as Array<Record<string, unknown>>)
271
+ : [];
272
+ const existingIds = new Set(
273
+ existingPanels
274
+ .map((p) => (typeof p?.id === "string" ? p.id : null))
275
+ .filter((id): id is string => !!id),
276
+ );
277
+ const toAppend: ComposedPanel[] = [];
278
+ const skipped: string[] = [];
279
+ for (const panel of composedPanels) {
280
+ if (existingIds.has(panel.id)) {
281
+ skipped.push(panel.id);
282
+ continue;
283
+ }
284
+ toAppend.push(panel);
285
+ existingIds.add(panel.id);
286
+ }
287
+ appendedCount = toAppend.length;
288
+ skippedExistingIds = skipped;
289
+ const merged = withFilters({
290
+ ...existingConfig,
291
+ name:
292
+ typeof existingConfig.name === "string" &&
293
+ existingConfig.name.trim()
294
+ ? existingConfig.name
295
+ : dashboardName,
296
+ panels: [...existingPanels, ...toAppend],
297
+ });
298
+ return { kind: "sql" as const, body: merged };
299
+ },
252
300
  );
253
- const toAppend: ComposedPanel[] = [];
254
- for (const panel of composedPanels) {
255
- if (existingIds.has(panel.id)) {
256
- skippedExistingIds.push(panel.id);
257
- continue;
258
- }
259
- toAppend.push(panel);
260
- existingIds.add(panel.id);
261
- }
262
- appendedCount = toAppend.length;
263
- finalConfig = {
264
- ...existingConfig,
265
- name:
266
- typeof existingConfig.name === "string" && existingConfig.name.trim()
267
- ? existingConfig.name
268
- : dashboardName,
269
- panels: [...existingPanels, ...toAppend],
270
- };
301
+ finalConfig = saved.config as Record<string, unknown>;
271
302
  } else {
272
303
  // Create or overwrite: a fresh config with exactly the composed panels.
273
- finalConfig = {
304
+ // No prior state can be lost here — create has none, and
305
+ // `overwrite: true` is an explicit full-replace request rather than a
306
+ // read-modify-write, so it saves unconditionally like update-dashboard's
307
+ // full-config replace mode.
308
+ finalConfig = withFilters({
274
309
  name: dashboardName,
275
310
  description:
276
311
  "First-party analytics dashboard composed from the metric catalog.",
277
312
  panels: composedPanels,
278
- };
279
- }
280
-
281
- if (
282
- composedPanels.some((panel) => usesFirstPartyDashboardFilters(panel.sql))
283
- ) {
284
- finalConfig = withFirstPartyDashboardFilters(finalConfig);
313
+ });
314
+ await upsertDashboard(args.dashboardId, "sql", finalConfig, ctx);
285
315
  }
286
316
 
287
317
  const panelCount = Array.isArray(finalConfig.panels)
288
318
  ? (finalConfig.panels as unknown[]).length
289
319
  : 0;
290
320
 
291
- // Single atomic save through the same store path update-dashboard uses.
292
- await upsertDashboard(args.dashboardId, "sql", finalConfig, ctx);
293
321
  await syncToCollab(args.dashboardId, finalConfig);
294
322
 
295
323
  const parts: string[] = [];
@@ -11,13 +11,18 @@ import {
11
11
  } from "@agent-native/core/server";
12
12
  import { z } from "zod";
13
13
 
14
- import { getDashboard, upsertDashboard } from "../server/lib/dashboards-store";
14
+ import {
15
+ getDashboard,
16
+ upsertDashboardWithRetry,
17
+ type DashboardRecord,
18
+ } from "../server/lib/dashboards-store";
15
19
  import {
16
20
  applyDashboardMutationOperations,
17
21
  DASHBOARD_MUTATION_API_TYPES,
18
22
  DASHBOARD_MUTATION_EXAMPLES,
19
23
  parseDashboardMutationScript,
20
24
  type DashboardMutationOperation,
25
+ type DashboardMutationResult,
21
26
  } from "./dashboard-mutation-api";
22
27
  import { compactDashboardResult } from "./dashboard-panel-order";
23
28
  import { validateDashboardConfig, validatePanelSql } from "./update-dashboard";
@@ -260,33 +265,77 @@ export default defineAction({
260
265
 
261
266
  const scope = resolveScope();
262
267
  const ctx = { email: scope.email, orgId: scope.orgId };
263
- const existing = await getDashboard(dashboardId, ctx);
264
- if (!existing) {
265
- throw new Error(
266
- `dashboard "${dashboardId}" not found (or you don't have access).`,
267
- );
268
- }
269
- if (existing.kind !== "sql") {
270
- throw new Error(
271
- `mutate-dashboard only supports SQL dashboards; "${dashboardId}" is ${existing.kind}.`,
268
+
269
+ // Recomputes the mutation from whatever dashboard state is passed in.
270
+ // `operations` sourced from `args.operations` are already concrete panel
271
+ // ids, safe to replay verbatim; `args.code` is re-parsed against `existing`
272
+ // every time because selectors like `panelsMatching(...)` resolve against
273
+ // the config at parse time, so a retry must re-resolve them against the
274
+ // fresh state, not reuse ids resolved from a now-stale config.
275
+ function computeMutation(
276
+ existing: Pick<DashboardRecord, "kind" | "config">,
277
+ ) {
278
+ if (existing.kind !== "sql") {
279
+ throw new Error(
280
+ `mutate-dashboard only supports SQL dashboards; "${dashboardId}" is ${existing.kind}.`,
281
+ );
282
+ }
283
+ const nextRoot = cloneConfig(existing.config as Record<string, unknown>);
284
+ const nextOperations = args.operations
285
+ ? (args.operations as DashboardMutationOperation[])
286
+ : parseDashboardMutationScript(nextRoot, args.code!);
287
+ const nextMutation = applyDashboardMutationOperations(
288
+ nextRoot,
289
+ nextOperations,
272
290
  );
291
+ const validation = validateDashboardConfig(nextRoot);
292
+ if (validation) throw new Error(validation);
293
+ return { nextRoot, nextOperations, nextMutation };
273
294
  }
274
295
 
275
- const root = cloneConfig(existing.config as Record<string, unknown>);
276
- const operations = args.operations
277
- ? (args.operations as DashboardMutationOperation[])
278
- : parseDashboardMutationScript(root, args.code!);
279
- const mutation = applyDashboardMutationOperations(root, operations);
296
+ // Assigned inside `computeMutation`'s caller (directly for dry-run, inside
297
+ // the retry callback for a real save); always assigned at least once
298
+ // before use below, since `upsertDashboardWithRetry` only resolves after
299
+ // its callback has run.
300
+ let root: Record<string, unknown>;
301
+ let operations!: DashboardMutationOperation[];
302
+ let mutation!: DashboardMutationResult;
280
303
 
281
- const validation = validateDashboardConfig(root);
282
- if (validation) throw new Error(validation);
283
- if (operations.some(mutationCanChangePanelSql)) {
284
- const sqlError = await validatePanelSql(root);
285
- if (sqlError) throw new Error(sqlError);
286
- }
287
-
288
- if (args.dryRun !== true) {
289
- await upsertDashboard(dashboardId, existing.kind, root, ctx);
304
+ if (args.dryRun === true) {
305
+ const existing = await getDashboard(dashboardId, ctx);
306
+ if (!existing) {
307
+ throw new Error(
308
+ `dashboard "${dashboardId}" not found (or you don't have access).`,
309
+ );
310
+ }
311
+ const computed = computeMutation(existing);
312
+ root = computed.nextRoot;
313
+ operations = computed.nextOperations;
314
+ mutation = computed.nextMutation;
315
+ if (operations.some(mutationCanChangePanelSql)) {
316
+ const sqlError = await validatePanelSql(root);
317
+ if (sqlError) throw new Error(sqlError);
318
+ }
319
+ } else {
320
+ const saved = await upsertDashboardWithRetry(
321
+ dashboardId,
322
+ ctx,
323
+ async (existing) => {
324
+ const computed = computeMutation(existing);
325
+ if (computed.nextOperations.some(mutationCanChangePanelSql)) {
326
+ const sqlError = await validatePanelSql(computed.nextRoot);
327
+ if (sqlError) throw new Error(sqlError);
328
+ }
329
+ root = computed.nextRoot;
330
+ operations = computed.nextOperations;
331
+ mutation = computed.nextMutation;
332
+ return { kind: "sql" as const, body: computed.nextRoot };
333
+ },
334
+ );
335
+ // Use the persisted config as the source of truth for the response —
336
+ // structurally identical to the winning attempt's `root`, but reflects
337
+ // exactly what was saved.
338
+ root = saved.config as Record<string, unknown>;
290
339
  await syncToCollab(dashboardId, root);
291
340
  }
292
341
 
@@ -11,10 +11,11 @@ import {
11
11
  } from "@agent-native/core/server";
12
12
  import { z } from "zod";
13
13
 
14
- import { getDashboard, upsertDashboard } from "../server/lib/dashboards-store";
14
+ import { upsertDashboardWithRetry } from "../server/lib/dashboards-store";
15
15
  import {
16
16
  compactDashboardResult,
17
17
  movePanelsById,
18
+ type PanelOrderResult,
18
19
  type PanelOrderTarget,
19
20
  } from "./dashboard-panel-order";
20
21
 
@@ -155,20 +156,22 @@ export default defineAction({
155
156
  const dashboardId = resolveDashboardId(args);
156
157
  const scope = resolveScope();
157
158
  const ctx = { email: scope.email, orgId: scope.orgId };
158
- const existing = await getDashboard(dashboardId, ctx);
159
- if (!existing) {
160
- throw new Error(
161
- `dashboard "${dashboardId}" not found (or you don't have access).`,
162
- );
163
- }
164
159
 
165
- const root = existing.config as Record<string, unknown>;
166
- const orderResult = movePanelsById(
167
- root,
168
- args.panelIds,
169
- resolveTarget(args),
160
+ // Recomputed on every attempt from whichever dashboard state
161
+ // `upsertDashboardWithRetry` hands us, so a retry after a concurrent
162
+ // writer's save re-applies this move against their fresh panel order
163
+ // instead of silently discarding it.
164
+ let orderResult!: PanelOrderResult;
165
+ const saved = await upsertDashboardWithRetry(
166
+ dashboardId,
167
+ ctx,
168
+ (existing) => {
169
+ const root = existing.config as Record<string, unknown>;
170
+ orderResult = movePanelsById(root, args.panelIds, resolveTarget(args));
171
+ return { kind: existing.kind, body: root };
172
+ },
170
173
  );
171
- await upsertDashboard(dashboardId, existing.kind, root, ctx);
174
+ const root = saved.config as Record<string, unknown>;
172
175
  await syncToCollab(dashboardId, root);
173
176
 
174
177
  const compact = compactDashboardResult(root, orderResult.movedPanelIds);
@@ -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
  );
@@ -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
+ 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:",
@@ -320,8 +320,8 @@ async function cancelQueryJob(
320
320
  },
321
321
  );
322
322
  } catch {
323
- // The caller is already aborting. Cancellation is best-effort and must not
324
- // hide the original abort reason if BigQuery or the network is unavailable.
323
+ // Cancellation is best-effort and must not hide the original abort or
324
+ // timeout reason if BigQuery or the network is unavailable.
325
325
  }
326
326
  }
327
327
 
@@ -502,6 +502,7 @@ export async function runQuery(
502
502
  }
503
503
 
504
504
  if (!data.jobComplete) {
505
+ await cancelQueryJob(projectId, jobId, token);
505
506
  throw new Error("BigQuery query timed out after 60 seconds");
506
507
  }
507
508
  }