@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
@@ -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,
@@ -22,7 +22,7 @@
22
22
  "@libsql/client": "^0.15.8",
23
23
  "@tabler/icons-react": "catalog:",
24
24
  "drizzle-orm": "^0.45.2",
25
- "h3": "^2.0.1-rc.20",
25
+ "h3": "catalog:",
26
26
  "isbot": "^5",
27
27
  "nanoid": "^5.1.9",
28
28
  "p-limit": "^7.3.0",
@@ -66,7 +66,6 @@
66
66
  "tailwind-merge": "^3.5.0",
67
67
  "tailwindcss": "catalog:",
68
68
  "tsx": "^4.20.3",
69
- "typescript": "catalog:",
70
69
  "typescript-7": "catalog:",
71
70
  "vite": "catalog:",
72
71
  "vitest": "catalog:"
@@ -23,7 +23,7 @@
23
23
  "@fontsource-variable/inter": "^5.2.8",
24
24
  "@libsql/client": "^0.15.0",
25
25
  "drizzle-orm": "^0.45.2",
26
- "h3": "^2.0.1-rc.22",
26
+ "h3": "catalog:",
27
27
  "isbot": "^5.1.40",
28
28
  "react": "catalog:",
29
29
  "react-dom": "catalog:",
@@ -57,7 +57,6 @@
57
57
  "sonner": "^2.0.7",
58
58
  "tailwind-merge": "^3.6.0",
59
59
  "tailwindcss": "catalog:",
60
- "typescript": "catalog:",
61
60
  "typescript-7": "catalog:",
62
61
  "vite": "catalog:",
63
62
  "vitest": "catalog:"
@@ -26,7 +26,7 @@
26
26
  "chrono-node": "2.9.1",
27
27
  "dotenv": "^17.2.1",
28
28
  "drizzle-orm": "^0.45.2",
29
- "h3": "^2.0.1-rc.20",
29
+ "h3": "catalog:",
30
30
  "isbot": "^5",
31
31
  "nanoid": "^5.1.9",
32
32
  "zod": "^4.3.6"
@@ -93,7 +93,6 @@
93
93
  "tailwind-merge": "^3.5.0",
94
94
  "tailwindcss": "catalog:",
95
95
  "tsx": "^4.20.3",
96
- "typescript": "catalog:",
97
96
  "typescript-7": "catalog:",
98
97
  "vaul": "^1.1.2",
99
98
  "vite": "catalog:",
@@ -19,7 +19,7 @@
19
19
  "@fontsource-variable/inter": "^5.2.8",
20
20
  "@libsql/client": "^0.15.8",
21
21
  "@tabler/icons-react": "catalog:",
22
- "h3": "^2.0.1-rc.20",
22
+ "h3": "catalog:",
23
23
  "isbot": "^5",
24
24
  "node-pty": "^1.1.0",
25
25
  "zod": "^4.4.3"
@@ -81,7 +81,6 @@
81
81
  "tailwind-merge": "^3.5.0",
82
82
  "tailwindcss": "catalog:",
83
83
  "tsx": "^4.20.3",
84
- "typescript": "catalog:",
85
84
  "typescript-7": "catalog:",
86
85
  "vaul": "^1.1.2",
87
86
  "vite": "catalog:",
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-10
4
+ ---
5
+
6
+ Viewer analytics stay accurate when multiple playback events arrive at the same time.
@@ -8,6 +8,7 @@
8
8
  "dev:hot": "tsx scripts/dev.ts",
9
9
  "build": "vite build",
10
10
  "typecheck": "tsc --noEmit",
11
+ "test": "vitest --run",
11
12
  "package": "pnpm build && tsx scripts/package.ts"
12
13
  },
13
14
  "dependencies": {
@@ -16,8 +17,8 @@
16
17
  "devDependencies": {
17
18
  "@types/chrome": "^0.2.0",
18
19
  "tsx": "catalog:",
19
- "typescript": "catalog:",
20
20
  "typescript-7": "catalog:",
21
- "vite": "catalog:"
21
+ "vite": "catalog:",
22
+ "vitest": "catalog:"
22
23
  }
23
24
  }
@@ -12,13 +12,15 @@
12
12
  "build:tauri": "CLIPS_DESKTOP_LOCAL_BUILD=1 pnpm run check:prereqs && CLIPS_DESKTOP_LOCAL_BUILD=1 tauri build --config '{\"bundle\":{\"createUpdaterArtifacts\":false},\"plugins\":{\"updater\":{\"active\":false}}}' && node ./scripts/sign-macos-local.ts",
13
13
  "tauri": "tauri",
14
14
  "vite:dev": "vite",
15
- "vite:build": "vite build"
15
+ "vite:build": "vite build",
16
+ "typecheck": "tsc --noEmit",
17
+ "test": "vitest --run"
16
18
  },
17
19
  "dependencies": {
18
20
  "@radix-ui/react-popover": "^1.1.15",
19
21
  "@radix-ui/react-tooltip": "^1.2.8",
20
22
  "@sentry/browser": "10.62.0",
21
- "@tabler/icons-react": "^3.26.0",
23
+ "@tabler/icons-react": "catalog:",
22
24
  "@tauri-apps/api": "^2.11.0",
23
25
  "@tauri-apps/plugin-fs": "2.5.1",
24
26
  "@tauri-apps/plugin-process": "^2.3.1",
@@ -32,8 +34,8 @@
32
34
  "@types/react": "^19.2.14",
33
35
  "@types/react-dom": "^19.2.3",
34
36
  "@vitejs/plugin-react": "catalog:",
35
- "typescript": "catalog:",
36
37
  "typescript-7": "catalog:",
37
- "vite": "catalog:"
38
+ "vite": "catalog:",
39
+ "vitest": "catalog:"
38
40
  }
39
41
  }
@@ -2147,12 +2147,18 @@ async function startNativeFullscreenRecording(
2147
2147
  console.log(
2148
2148
  `[clips-recorder] native begin durationMs=${Date.now() - beginStartedAt} clickToLiveMs=${Date.now() - clickStartedAt}`,
2149
2149
  );
2150
- await transcriptionCapture?.resetTimeline().catch((err) => {
2151
- console.warn(
2152
- "[clips-recorder] transcription timeline reset failed:",
2153
- err,
2154
- );
2155
- });
2150
+ // Cast: `transcriptionCapture` is only ever reassigned inside the
2151
+ // `startNativeTranscriptionBeforeRecording` closure above, so TS's
2152
+ // control-flow analysis can't see past that call and narrows this
2153
+ // read to `null`. Restate the variable's own declared type.
2154
+ await (transcriptionCapture as TranscriptionCapture | null)
2155
+ ?.resetTimeline()
2156
+ .catch((err) => {
2157
+ console.warn(
2158
+ "[clips-recorder] transcription timeline reset failed:",
2159
+ err,
2160
+ );
2161
+ });
2156
2162
  // Capture is now live — after rebasing the transcript timeline, stamp the
2157
2163
  // timer baseline so the toolbar clock lines up with the real start.
2158
2164
  startedAt = Date.now();
@@ -2164,12 +2170,15 @@ async function startNativeFullscreenRecording(
2164
2170
  localCameraExport?.start(2_000);
2165
2171
  } catch (err) {
2166
2172
  await localCameraExport?.cancel().catch(() => {});
2167
- await transcriptionCapture?.cancel().catch((cancelErr) => {
2168
- console.warn(
2169
- "[clips-recorder] native transcription cancel after start failure failed:",
2170
- cancelErr,
2171
- );
2172
- });
2173
+ // Same TS narrowing gap as above: reassert the declared type.
2174
+ await (transcriptionCapture as TranscriptionCapture | null)
2175
+ ?.cancel()
2176
+ .catch((cancelErr) => {
2177
+ console.warn(
2178
+ "[clips-recorder] native transcription cancel after start failure failed:",
2179
+ cancelErr,
2180
+ );
2181
+ });
2173
2182
  // Tear down any capture started by the warm phase — on a countdown cancel
2174
2183
  // (or a `begin` failure) the SCStream is already running with the mic live,
2175
2184
  // and without this it would keep capturing after the aborted start.
@@ -2629,7 +2638,11 @@ async function startNativeFullscreenRecording(
2629
2638
  console.log(
2630
2639
  "[clips-recorder] native: paused during startup, pausing transcription",
2631
2640
  );
2632
- void transcriptionCapture.pause().catch(() => {});
2641
+ // The `if` above already proves non-null at runtime; TS just can't see
2642
+ // it (same closure-narrowing gap as above).
2643
+ void (transcriptionCapture as TranscriptionCapture)
2644
+ .pause()
2645
+ .catch(() => {});
2633
2646
  }
2634
2647
  }
2635
2648
 
@@ -298,7 +298,10 @@ function appendFinalTranscript(session: VoiceSession, text: string): void {
298
298
  } else if (clean === committed || clean.startsWith(`${committed} `)) {
299
299
  // Some recognizers send the whole dictation as their final result.
300
300
  session.finalTranscriptParts = [clean];
301
- } else if (session.finalTranscriptParts.at(-1) !== clean) {
301
+ } else if (
302
+ session.finalTranscriptParts[session.finalTranscriptParts.length - 1] !==
303
+ clean
304
+ ) {
302
305
  session.finalTranscriptParts.push(clean);
303
306
  }
304
307
  session.interimTranscript = "";
@@ -30,7 +30,7 @@
30
30
  "@tabler/icons-react": "catalog:",
31
31
  "drizzle-orm": "^0.45.2",
32
32
  "ffmpeg-static": "^5.3.0",
33
- "h3": "^2.0.1-rc.20",
33
+ "h3": "catalog:",
34
34
  "isbot": "^5",
35
35
  "postgres": "^3.4.9",
36
36
  "zod": "^4.3.6"
@@ -92,7 +92,6 @@
92
92
  "tailwind-merge": "^3.5.0",
93
93
  "tailwindcss": "catalog:",
94
94
  "tsx": "^4.20.3",
95
- "typescript": "catalog:",
96
95
  "typescript-7": "catalog:",
97
96
  "vaul": "^1.1.2",
98
97
  "vite": "catalog:",
@@ -6,6 +6,7 @@ import {
6
6
  now,
7
7
  ownableColumns,
8
8
  createSharesTable,
9
+ uniqueIndex,
9
10
  } from "@agent-native/core/db/schema";
10
11
 
11
12
  // -----------------------------------------------------------------------------
@@ -332,23 +333,34 @@ export const recordingReactions = table("recording_reactions", {
332
333
  // Analytics: viewers + granular events
333
334
  // -----------------------------------------------------------------------------
334
335
 
335
- export const recordingViewers = table("recording_viewers", {
336
- id: text("id").primaryKey(),
337
- recordingId: text("recording_id").notNull(),
338
- viewerEmail: text("viewer_email"), // null = anonymous
339
- viewerName: text("viewer_name"),
340
- firstViewedAt: text("first_viewed_at").notNull().default(now()),
341
- lastViewedAt: text("last_viewed_at").notNull().default(now()),
342
- totalWatchMs: integer("total_watch_ms").notNull().default(0),
343
- completedPct: integer("completed_pct").notNull().default(0),
344
- // True once they meet the 5s / 75% / end-scrub rule.
345
- countedView: integer("counted_view", { mode: "boolean" })
346
- .notNull()
347
- .default(false),
348
- ctaClicked: integer("cta_clicked", { mode: "boolean" })
349
- .notNull()
350
- .default(false),
351
- });
336
+ export const recordingViewers = table(
337
+ "recording_viewers",
338
+ {
339
+ id: text("id").primaryKey(),
340
+ recordingId: text("recording_id").notNull(),
341
+ // Stable canonical identity for new viewers. Nullable so existing rows can
342
+ // be migrated additively and claimed on their next event.
343
+ viewerKey: text("viewer_key"),
344
+ viewerEmail: text("viewer_email"), // null = anonymous
345
+ viewerName: text("viewer_name"),
346
+ firstViewedAt: text("first_viewed_at").notNull().default(now()),
347
+ lastViewedAt: text("last_viewed_at").notNull().default(now()),
348
+ totalWatchMs: integer("total_watch_ms").notNull().default(0),
349
+ completedPct: integer("completed_pct").notNull().default(0),
350
+ // True once they meet the 5s / 75% / end-scrub rule.
351
+ countedView: integer("counted_view", { mode: "boolean" })
352
+ .notNull()
353
+ .default(false),
354
+ ctaClicked: integer("cta_clicked", { mode: "boolean" })
355
+ .notNull()
356
+ .default(false),
357
+ },
358
+ (viewer) => ({
359
+ recordingViewerKeyUnique: uniqueIndex(
360
+ "recording_viewers_recording_viewer_key_unique_idx",
361
+ ).on(viewer.recordingId, viewer.viewerKey),
362
+ }),
363
+ );
352
364
 
353
365
  // Per-view records — one row per distinct counted view, so the owner can see
354
366
  // *who viewed and when* (not just an aggregate count or a single
@@ -825,6 +825,14 @@ const migrations = runMigrations(
825
825
  `CREATE UNIQUE INDEX IF NOT EXISTS recording_views_session_unique_idx ON recording_views (recording_id, viewer_key, view_session_id)`,
826
826
  ].join("; "),
827
827
  },
828
+ {
829
+ version: 48,
830
+ name: "recording-viewers-canonical-viewer-key",
831
+ sql: [
832
+ `ALTER TABLE recording_viewers ADD COLUMN IF NOT EXISTS viewer_key TEXT`,
833
+ `CREATE UNIQUE INDEX IF NOT EXISTS recording_viewers_recording_viewer_key_unique_idx ON recording_viewers (recording_id, viewer_key)`,
834
+ ].join("; "),
835
+ },
828
836
  ],
829
837
  { table: "clips_migrations" },
830
838
  );