@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
@@ -0,0 +1,388 @@
1
+ import { previewSourceDiff } from "./source-workspace.js";
2
+
3
+ export interface LocalJsxSourceAnchor {
4
+ line: number;
5
+ column: number;
6
+ runtimeMultiplicity?: number;
7
+ scope?:
8
+ | "single-instance"
9
+ | "repeated-render"
10
+ | "shared-component-definition"
11
+ | "unknown";
12
+ }
13
+
14
+ export type LocalJsxLeafIntent =
15
+ | {
16
+ kind: "textContent";
17
+ value: string;
18
+ }
19
+ | {
20
+ kind: "class";
21
+ operation: "add" | "remove" | "replace" | "set";
22
+ className?: string;
23
+ classNames?: string[];
24
+ from?: string;
25
+ to?: string;
26
+ }
27
+ | { kind: "style"; property: string; value: string };
28
+
29
+ export interface LocalJsxVisualEditResult {
30
+ content: string;
31
+ result: {
32
+ status: "applied" | "conflict" | "needsAgent" | "unsupported";
33
+ changed: boolean;
34
+ message: string;
35
+ };
36
+ proposedDiff?: ReturnType<typeof previewSourceDiff>;
37
+ }
38
+
39
+ interface OpeningTag {
40
+ start: number;
41
+ end: number;
42
+ name: string;
43
+ selfClosing: boolean;
44
+ }
45
+
46
+ function offsetAt(
47
+ content: string,
48
+ line: number,
49
+ column: number,
50
+ ): number | null {
51
+ if (
52
+ !Number.isInteger(line) ||
53
+ !Number.isInteger(column) ||
54
+ line < 1 ||
55
+ column < 1
56
+ )
57
+ return null;
58
+ let offset = 0;
59
+ for (let currentLine = 1; currentLine < line; currentLine += 1) {
60
+ const newline = content.indexOf("\n", offset);
61
+ if (newline === -1) return null;
62
+ offset = newline + 1;
63
+ }
64
+ const result = offset + column - 1;
65
+ return result <= content.length ? result : null;
66
+ }
67
+
68
+ function positionAt(
69
+ content: string,
70
+ offset: number,
71
+ ): { line: number; column: number } {
72
+ let line = 1;
73
+ let lineStart = 0;
74
+ for (let index = 0; index < offset; index += 1) {
75
+ if (content.charCodeAt(index) === 10) {
76
+ line += 1;
77
+ lineStart = index + 1;
78
+ }
79
+ }
80
+ return { line, column: offset - lineStart + 1 };
81
+ }
82
+
83
+ function scanOpeningTags(content: string): OpeningTag[] {
84
+ const tags: OpeningTag[] = [];
85
+ for (let start = 0; start < content.length; start += 1) {
86
+ if (content[start] === "/" && content[start + 1] === "/") {
87
+ const newline = content.indexOf("\n", start + 2);
88
+ start = newline === -1 ? content.length : newline;
89
+ continue;
90
+ }
91
+ if (content[start] === "/" && content[start + 1] === "*") {
92
+ const close = content.indexOf("*/", start + 2);
93
+ start = close === -1 ? content.length : close + 1;
94
+ continue;
95
+ }
96
+ if (
97
+ content[start] === '"' ||
98
+ content[start] === "'" ||
99
+ content[start] === "`"
100
+ ) {
101
+ const quote = content[start];
102
+ for (start += 1; start < content.length; start += 1) {
103
+ if (content[start] === quote && content[start - 1] !== "\\") break;
104
+ }
105
+ continue;
106
+ }
107
+ if (content[start] !== "<" || /[!/?]/.test(content[start + 1] ?? ""))
108
+ continue;
109
+ const nameMatch = /^[A-Za-z][A-Za-z0-9_.:-]*/.exec(
110
+ content.slice(start + 1),
111
+ );
112
+ if (!nameMatch) continue;
113
+ let quote = "";
114
+ let braces = 0;
115
+ for (
116
+ let index = start + 1 + nameMatch[0].length;
117
+ index < content.length;
118
+ index += 1
119
+ ) {
120
+ const char = content[index] ?? "";
121
+ if (quote) {
122
+ if (char === quote && content[index - 1] !== "\\") quote = "";
123
+ continue;
124
+ }
125
+ if (char === '"' || char === "'") {
126
+ quote = char;
127
+ continue;
128
+ }
129
+ if (char === "{") braces += 1;
130
+ else if (char === "}") braces -= 1;
131
+ else if (char === ">" && braces === 0) {
132
+ const before = content.slice(start, index).trimEnd();
133
+ tags.push({
134
+ start,
135
+ end: index + 1,
136
+ name: nameMatch[0],
137
+ selfClosing: before.endsWith("/"),
138
+ });
139
+ start = index;
140
+ break;
141
+ }
142
+ if (braces < 0) break;
143
+ }
144
+ }
145
+ return tags;
146
+ }
147
+
148
+ function fail(
149
+ content: string,
150
+ status: LocalJsxVisualEditResult["result"]["status"],
151
+ message: string,
152
+ ): LocalJsxVisualEditResult {
153
+ return { content, result: { status, changed: false, message } };
154
+ }
155
+
156
+ function replaceRange(
157
+ content: string,
158
+ start: number,
159
+ end: number,
160
+ replacement: string,
161
+ message: string,
162
+ ): LocalJsxVisualEditResult {
163
+ const next = `${content.slice(0, start)}${replacement}${content.slice(end)}`;
164
+ return {
165
+ content: next,
166
+ result: { status: "applied", changed: next !== content, message },
167
+ proposedDiff: previewSourceDiff(content, next),
168
+ };
169
+ }
170
+
171
+ function jsxText(value: string): string {
172
+ return value
173
+ .split("&")
174
+ .join("&amp;")
175
+ .split("<")
176
+ .join("&lt;")
177
+ .split(">")
178
+ .join("&gt;")
179
+ .split("{")
180
+ .join("&#123;")
181
+ .split("}")
182
+ .join("&#125;");
183
+ }
184
+
185
+ function camelProperty(property: string): string | null {
186
+ const value = property
187
+ .trim()
188
+ .replace(/-([a-z])/g, (_, char: string) => char.toUpperCase());
189
+ return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(value) ? value : null;
190
+ }
191
+
192
+ export function planLocalJsxVisualEdit(args: {
193
+ content: string;
194
+ anchor: LocalJsxSourceAnchor;
195
+ intent: LocalJsxLeafIntent;
196
+ }): LocalJsxVisualEditResult {
197
+ const { content, anchor, intent } = args;
198
+ if (
199
+ (anchor.runtimeMultiplicity ?? 1) !== 1 ||
200
+ anchor.scope === "repeated-render" ||
201
+ anchor.scope === "shared-component-definition"
202
+ ) {
203
+ return fail(
204
+ content,
205
+ "needsAgent",
206
+ "The source anchor renders more than one instance or belongs to a shared component; inspect its call sites before editing.",
207
+ );
208
+ }
209
+ const anchorOffset = offsetAt(content, anchor.line, anchor.column);
210
+ if (anchorOffset === null) {
211
+ return fail(
212
+ content,
213
+ "conflict",
214
+ "The source anchor is outside the current file.",
215
+ );
216
+ }
217
+ const candidates = scanOpeningTags(content).filter((tag) => {
218
+ const position = positionAt(content, tag.start);
219
+ return (
220
+ position.line === anchor.line &&
221
+ Math.abs(position.column - anchor.column) <= 1
222
+ );
223
+ });
224
+ if (candidates.length !== 1) {
225
+ return fail(
226
+ content,
227
+ "conflict",
228
+ candidates.length === 0
229
+ ? "No JSX element starts at the supplied source anchor. Re-resolve the live selection."
230
+ : "The supplied source anchor is ambiguous.",
231
+ );
232
+ }
233
+ const tag = candidates[0]!;
234
+ const opening = content.slice(tag.start, tag.end);
235
+ if (/\{\s*\.\.\./.test(opening)) {
236
+ return fail(
237
+ content,
238
+ "needsAgent",
239
+ "JSX spread attributes require semantic source inspection.",
240
+ );
241
+ }
242
+
243
+ if (intent.kind === "textContent") {
244
+ if (tag.selfClosing)
245
+ return fail(
246
+ content,
247
+ "unsupported",
248
+ "A self-closing JSX element has no leaf text to edit.",
249
+ );
250
+ const close = `</${tag.name}>`;
251
+ const closeStart = content.indexOf(close, tag.end);
252
+ if (closeStart === -1)
253
+ return fail(
254
+ content,
255
+ "conflict",
256
+ "The JSX closing tag could not be resolved.",
257
+ );
258
+ const inner = content.slice(tag.end, closeStart);
259
+ if (/[<>{}]/.test(inner)) {
260
+ return fail(
261
+ content,
262
+ "needsAgent",
263
+ "Only leaf JSX text without child elements or expressions can be edited deterministically.",
264
+ );
265
+ }
266
+ return replaceRange(
267
+ content,
268
+ tag.end,
269
+ closeStart,
270
+ jsxText(intent.value),
271
+ "Leaf JSX text updated.",
272
+ );
273
+ }
274
+
275
+ if (intent.kind === "class") {
276
+ const attr = /\s(className|class)\s*=\s*(["'])(.*?)\2/s.exec(opening);
277
+ if (!attr && /\s(?:className|class)\s*=/.test(opening)) {
278
+ return fail(
279
+ content,
280
+ "needsAgent",
281
+ "Dynamic JSX class expressions require semantic source inspection.",
282
+ );
283
+ }
284
+ let classes = attr?.[3]?.split(/\s+/).filter(Boolean) ?? [];
285
+ const requested =
286
+ intent.classNames ?? (intent.className ? [intent.className] : []);
287
+ const replacementTokens = [
288
+ ...requested,
289
+ ...(intent.from ? [intent.from] : []),
290
+ ...(intent.to ? [intent.to] : []),
291
+ ];
292
+ if (
293
+ replacementTokens.some((token) => !token || /[\s"'`<>{}=]/.test(token))
294
+ ) {
295
+ return fail(
296
+ content,
297
+ "unsupported",
298
+ "Class tokens must be plain non-whitespace tokens without JSX delimiters.",
299
+ );
300
+ }
301
+ if (intent.operation === "set") classes = requested;
302
+ else if (intent.operation === "add") classes = [...classes, ...requested];
303
+ else if (intent.operation === "remove")
304
+ classes = classes.filter((item) => !requested.includes(item));
305
+ else {
306
+ if (!intent.from || !intent.to || !classes.includes(intent.from))
307
+ return fail(
308
+ content,
309
+ "conflict",
310
+ "The class replacement guard does not match the current literal class list.",
311
+ );
312
+ classes = classes.map((item) =>
313
+ item === intent.from ? intent.to! : item,
314
+ );
315
+ }
316
+ classes = [...new Set(classes)];
317
+ const replacement = classes.join(" ");
318
+ let nextOpening: string;
319
+ if (attr) {
320
+ const valueStart = attr.index! + attr[0].indexOf(attr[3]!);
321
+ nextOpening = `${opening.slice(0, valueStart)}${replacement}${opening.slice(valueStart + attr[3]!.length)}`;
322
+ } else if (intent.operation === "add" || intent.operation === "set") {
323
+ const insertAt = opening.lastIndexOf(tag.selfClosing ? "/>" : ">");
324
+ nextOpening = `${opening.slice(0, insertAt)} className="${replacement.split('"').join("&quot;")}"${opening.slice(insertAt)}`;
325
+ } else {
326
+ return fail(
327
+ content,
328
+ "conflict",
329
+ "The anchored JSX element has no literal className/class attribute.",
330
+ );
331
+ }
332
+ return replaceRange(
333
+ content,
334
+ tag.start,
335
+ tag.end,
336
+ nextOpening,
337
+ "Literal JSX classes updated.",
338
+ );
339
+ }
340
+
341
+ const property = camelProperty(intent.property);
342
+ if (!property)
343
+ return fail(
344
+ content,
345
+ "unsupported",
346
+ "The CSS property cannot be represented as a JSX style key.",
347
+ );
348
+ const styleAttr = /\sstyle\s*=\s*\{\{([\s\S]*?)\}\}/.exec(opening);
349
+ let nextOpening: string;
350
+ if (!styleAttr) {
351
+ if (/\sstyle\s*=/.test(opening))
352
+ return fail(
353
+ content,
354
+ "needsAgent",
355
+ "Dynamic JSX style expressions require semantic source inspection.",
356
+ );
357
+ const insertAt = opening.lastIndexOf(tag.selfClosing ? "/>" : ">");
358
+ const value = JSON.stringify(intent.value);
359
+ nextOpening = `${opening.slice(0, insertAt)} style={{ ${property}: ${value} }}${opening.slice(insertAt)}`;
360
+ } else {
361
+ const body = styleAttr[1]!;
362
+ const literalStyleObject =
363
+ /^\s*(?:[A-Za-z_$][A-Za-z0-9_$]*\s*:\s*(?:"[^"\n]*"|'[^'\n]*'|-?\d+(?:\.\d+)?)(?:\s*,\s*[A-Za-z_$][A-Za-z0-9_$]*\s*:\s*(?:"[^"\n]*"|'[^'\n]*'|-?\d+(?:\.\d+)?)\s*,?)*|)\s*$/;
364
+ if (!literalStyleObject.test(body))
365
+ return fail(
366
+ content,
367
+ "needsAgent",
368
+ "Only flat literal JSX style objects can be edited deterministically.",
369
+ );
370
+ const propertyPattern = new RegExp(
371
+ `(^|[,\\s])(${property})\\s*:\\s*((?:["'][^"'\\n]*["'])|-?\\d+(?:\\.\\d+)?)`,
372
+ "s",
373
+ );
374
+ const match = propertyPattern.exec(body);
375
+ const nextBody = match
376
+ ? `${body.slice(0, match.index + match[1]!.length)}${property}: ${JSON.stringify(intent.value)}${body.slice(match.index + match[0].length)}`
377
+ : `${body.trimEnd()}${body.trim() ? ", " : ""}${property}: ${JSON.stringify(intent.value)}${body.endsWith(" ") ? " " : ""}`;
378
+ const bodyStart = styleAttr.index! + styleAttr[0].indexOf(body);
379
+ nextOpening = `${opening.slice(0, bodyStart)}${nextBody}${opening.slice(bodyStart + body.length)}`;
380
+ }
381
+ return replaceRange(
382
+ content,
383
+ tag.start,
384
+ tag.end,
385
+ nextOpening,
386
+ "Literal JSX style updated.",
387
+ );
388
+ }
@@ -20,7 +20,7 @@
20
20
  "@radix-ui/react-tooltip": "^1.2.7",
21
21
  "@tabler/icons-react": "catalog:",
22
22
  "@tanstack/react-query": "^5.99.2",
23
- "h3": "^2.0.1-rc.20",
23
+ "h3": "catalog:",
24
24
  "isbot": "^5",
25
25
  "next-themes": "^0.4.6",
26
26
  "sonner": "^2.0.7",
@@ -40,7 +40,6 @@
40
40
  "react-router": "^8.1.0",
41
41
  "tailwindcss": "catalog:",
42
42
  "tsx": "^4.20.3",
43
- "typescript": "catalog:",
44
43
  "typescript-7": "catalog:",
45
44
  "vite": "catalog:",
46
45
  "vitest": "catalog:"
@@ -6,6 +6,12 @@
6
6
  * concurrent edits to DIFFERENT fields both survive instead of the later
7
7
  * client overwriting the earlier one with its stale full-array snapshot.
8
8
  *
9
+ * The read-modify-write runs under a per-form in-process lock (same pattern
10
+ * as `patch-deck` in the slides template) so two concurrent callers (e.g. the
11
+ * form-builder autosave and an agent edit) are serialized instead of racing
12
+ * on the same row — without the lock, the second writer's read would miss
13
+ * the first writer's not-yet-committed update and silently clobber it.
14
+ *
9
15
  * The UI form builder uses this action for all incremental edits.
10
16
  * The legacy `update-form --fields <json>` path remains available for agents
11
17
  * and bulk imports that want to replace the whole fields array at once.
@@ -21,6 +27,36 @@ import { assertValidFields } from "../server/lib/validate-fields.js";
21
27
  import type { FormField } from "../shared/types.js";
22
28
  import { assertPublishableForm } from "./lib/assert-publishable-form.js";
23
29
 
30
+ // ---------------------------------------------------------------------------
31
+ // Per-form write lock — mirrors `withDeckLock` in
32
+ // templates/slides/actions/patch-deck.ts so concurrent client and agent
33
+ // writes to the same form's fields are serialised in-process.
34
+ // ---------------------------------------------------------------------------
35
+ const LOCK_KEY = "__formsFieldPatchLocks" as const;
36
+ type GlobalWithLocks = typeof globalThis & {
37
+ [LOCK_KEY]?: Map<string, Promise<unknown>>;
38
+ };
39
+ const globalRef = globalThis as GlobalWithLocks;
40
+ if (!globalRef[LOCK_KEY]) {
41
+ globalRef[LOCK_KEY] = new Map<string, Promise<unknown>>();
42
+ }
43
+ const formLocks: Map<string, Promise<unknown>> = globalRef[LOCK_KEY]!;
44
+
45
+ export function withFormLock<T>(
46
+ formId: string,
47
+ fn: () => Promise<T>,
48
+ ): Promise<T> {
49
+ const prev = formLocks.get(formId) ?? Promise.resolve();
50
+ const next = prev.then(fn, fn);
51
+ formLocks.set(formId, next);
52
+ next
53
+ .finally(() => {
54
+ if (formLocks.get(formId) === next) formLocks.delete(formId);
55
+ })
56
+ .catch(() => {});
57
+ return next;
58
+ }
59
+
24
60
  const fieldOpSchema = z.union([
25
61
  z.object({
26
62
  op: z.literal("upsert"),
@@ -50,58 +86,60 @@ export default defineAction({
50
86
  run: async (args) => {
51
87
  await assertAccess("form", args.id, "editor");
52
88
 
53
- const db = getDb();
54
- const [existing] = await db
55
- .select()
56
- .from(schema.forms)
57
- .where(eq(schema.forms.id, args.id))
58
- .limit(1);
89
+ return withFormLock(args.id, async () => {
90
+ const db = getDb();
91
+ const [existing] = await db
92
+ .select()
93
+ .from(schema.forms)
94
+ .where(eq(schema.forms.id, args.id))
95
+ .limit(1);
59
96
 
60
- if (!existing) {
61
- throw new Error(`Form ${args.id} not found`);
62
- }
97
+ if (!existing) {
98
+ throw new Error(`Form ${args.id} not found`);
99
+ }
63
100
 
64
- let ops: Array<{ op: string; [k: string]: unknown }>;
65
- if (typeof args.ops === "string") {
66
- try {
67
- ops = JSON.parse(args.ops);
68
- } catch {
69
- throw new Error("--ops must be valid JSON");
101
+ let ops: Array<{ op: string; [k: string]: unknown }>;
102
+ if (typeof args.ops === "string") {
103
+ try {
104
+ ops = JSON.parse(args.ops);
105
+ } catch {
106
+ throw new Error("--ops must be valid JSON");
107
+ }
108
+ } else {
109
+ ops = args.ops as Array<{ op: string; [k: string]: unknown }>;
70
110
  }
71
- } else {
72
- ops = args.ops as Array<{ op: string; [k: string]: unknown }>;
73
- }
74
111
 
75
- if (!Array.isArray(ops)) {
76
- throw new Error("ops must be an array");
77
- }
112
+ if (!Array.isArray(ops)) {
113
+ throw new Error("ops must be an array");
114
+ }
78
115
 
79
- // Parse current fields from the DB row.
80
- let currentFields: FormField[] = [];
81
- try {
82
- currentFields = JSON.parse(existing.fields) as FormField[];
83
- } catch {
84
- currentFields = [];
85
- }
116
+ // Parse current fields from the DB row.
117
+ let currentFields: FormField[] = [];
118
+ try {
119
+ currentFields = JSON.parse(existing.fields) as FormField[];
120
+ } catch {
121
+ currentFields = [];
122
+ }
86
123
 
87
- // Apply ops server-side so concurrent edits on different fields both land.
88
- const nextFields = applyFieldOps(
89
- currentFields,
90
- ops as Parameters<typeof applyFieldOps>[1],
91
- );
124
+ // Apply ops server-side so concurrent edits on different fields both land.
125
+ const nextFields = applyFieldOps(
126
+ currentFields,
127
+ ops as Parameters<typeof applyFieldOps>[1],
128
+ );
92
129
 
93
- // Validate the result before persisting.
94
- assertValidFields(nextFields);
95
- if (existing.status === "published") {
96
- assertPublishableForm(nextFields);
97
- }
130
+ // Validate the result before persisting.
131
+ assertValidFields(nextFields);
132
+ if (existing.status === "published") {
133
+ assertPublishableForm(nextFields);
134
+ }
98
135
 
99
- const now = new Date().toISOString();
100
- await db
101
- .update(schema.forms)
102
- .set({ fields: JSON.stringify(nextFields), updatedAt: now })
103
- .where(eq(schema.forms.id, args.id));
136
+ const now = new Date().toISOString();
137
+ await db
138
+ .update(schema.forms)
139
+ .set({ fields: JSON.stringify(nextFields), updatedAt: now })
140
+ .where(eq(schema.forms.id, args.id));
104
141
 
105
- return { id: args.id, fields: nextFields, updatedAt: now };
142
+ return { id: args.id, fields: nextFields, updatedAt: now };
143
+ });
106
144
  },
107
145
  });
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-10
4
+ ---
5
+
6
+ Simultaneous edits to different form fields no longer overwrite each other
@@ -39,7 +39,7 @@
39
39
  "cmdk": "^1.1.1",
40
40
  "drizzle-orm": "^0.45.2",
41
41
  "embla-carousel-react": "^8.6.0",
42
- "h3": "^2.0.1-rc.20",
42
+ "h3": "catalog:",
43
43
  "input-otp": "^1.4.2",
44
44
  "isbot": "^5",
45
45
  "nanoid": "^5.1.9",
@@ -87,7 +87,6 @@
87
87
  "sonner": "^2.0.7",
88
88
  "tailwind-merge": "^3.5.0",
89
89
  "tailwindcss": "catalog:",
90
- "typescript": "catalog:",
91
90
  "typescript-7": "catalog:",
92
91
  "vite": "catalog:",
93
92
  "vitest": "catalog:"
@@ -24,7 +24,7 @@
24
24
  "@supabase/supabase-js": "^2.101.1",
25
25
  "@tabler/icons-react": "catalog:",
26
26
  "drizzle-orm": "^0.45.2",
27
- "h3": "^2.0.1-rc.20",
27
+ "h3": "catalog:",
28
28
  "isbot": "^5",
29
29
  "recharts": "^3.8.1",
30
30
  "zod": "^4.4.3"
@@ -86,7 +86,6 @@
86
86
  "tailwind-merge": "^3.5.0",
87
87
  "tailwindcss": "catalog:",
88
88
  "tsx": "^4.20.3",
89
- "typescript": "catalog:",
90
89
  "typescript-7": "catalog:",
91
90
  "vaul": "^1.1.2",
92
91
  "vite": "catalog:",
@@ -3042,6 +3042,7 @@ function HtmlEmailBody({
3042
3042
  } catch {}
3043
3043
  });
3044
3044
 
3045
+ let handleRsvpClick: ((e: MouseEvent) => void) | null = null;
3045
3046
  if (calEventId && rsvpLinks.length > 0) {
3046
3047
  const eventId = calEventId;
3047
3048
  rsvpLinks.forEach((a) => {
@@ -3083,7 +3084,7 @@ function HtmlEmailBody({
3083
3084
  });
3084
3085
 
3085
3086
  // Handle RSVP clicks inline
3086
- const handleRsvpClick = async (e: MouseEvent) => {
3087
+ handleRsvpClick = async (e: MouseEvent) => {
3087
3088
  const anchor = (e.target as Element)?.closest?.(
3088
3089
  'a[href*="calendar.google.com/calendar/event"]',
3089
3090
  ) as HTMLElement | null;
@@ -3189,6 +3190,7 @@ function HtmlEmailBody({
3189
3190
 
3190
3191
  return () => {
3191
3192
  doc.removeEventListener("click", handleLinkClick);
3193
+ if (handleRsvpClick) doc.removeEventListener("click", handleRsvpClick);
3192
3194
  doc.removeEventListener("keydown", forwardKey);
3193
3195
  clearTimeout(timer);
3194
3196
  clearTimeout(timer2);
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-10
4
+ ---
5
+
6
+ Fixed calendar RSVP buttons in emails firing duplicate responses after theme changes
@@ -25,7 +25,7 @@
25
25
  "chrono-node": "^2.7.6",
26
26
  "drizzle-orm": "^0.45.2",
27
27
  "embla-carousel-react": "^8.6.0",
28
- "h3": "^2.0.1-rc.20",
28
+ "h3": "catalog:",
29
29
  "input-otp": "^1.4.2",
30
30
  "isbot": "^5",
31
31
  "jose": "^6.2.2",
@@ -102,7 +102,6 @@
102
102
  "tailwindcss": "catalog:",
103
103
  "tiptap-markdown": "^0.9.0",
104
104
  "tsx": "^4.20.3",
105
- "typescript": "catalog:",
106
105
  "typescript-7": "catalog:",
107
106
  "vaul": "^1.1.2",
108
107
  "vite": "catalog:",