@agent-native/core 0.95.1 → 0.97.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (655) hide show
  1. package/README.md +6 -0
  2. package/corpus/README.md +2 -2
  3. package/corpus/core/CHANGELOG.md +64 -0
  4. package/corpus/core/docs/content/audit-log.mdx +2 -1
  5. package/corpus/core/docs/content/automations.mdx +7 -7
  6. package/corpus/core/docs/content/creating-templates.mdx +1 -1
  7. package/corpus/core/docs/content/internationalization.mdx +2 -1
  8. package/corpus/core/docs/content/locales/ar-SA/audit-log.mdx +2 -1
  9. package/corpus/core/docs/content/locales/ar-SA/automations.mdx +7 -7
  10. package/corpus/core/docs/content/locales/ar-SA/getting-started.mdx +7 -1
  11. package/corpus/core/docs/content/locales/ar-SA/internationalization.mdx +1 -1
  12. package/corpus/core/docs/content/locales/ar-SA/package-lifecycle.mdx +19 -0
  13. package/corpus/core/docs/content/locales/ar-SA/sandbox-adapters.mdx +18 -7
  14. package/corpus/core/docs/content/locales/ar-SA/security.mdx +8 -0
  15. package/corpus/core/docs/content/locales/de-DE/audit-log.mdx +2 -1
  16. package/corpus/core/docs/content/locales/de-DE/automations.mdx +7 -7
  17. package/corpus/core/docs/content/locales/de-DE/getting-started.mdx +7 -1
  18. package/corpus/core/docs/content/locales/de-DE/internationalization.mdx +1 -1
  19. package/corpus/core/docs/content/locales/de-DE/package-lifecycle.mdx +22 -0
  20. package/corpus/core/docs/content/locales/de-DE/sandbox-adapters.mdx +18 -7
  21. package/corpus/core/docs/content/locales/de-DE/security.mdx +8 -0
  22. package/corpus/core/docs/content/locales/es-ES/audit-log.mdx +2 -1
  23. package/corpus/core/docs/content/locales/es-ES/automations.mdx +7 -7
  24. package/corpus/core/docs/content/locales/es-ES/getting-started.mdx +7 -1
  25. package/corpus/core/docs/content/locales/es-ES/internationalization.mdx +1 -1
  26. package/corpus/core/docs/content/locales/es-ES/package-lifecycle.mdx +22 -0
  27. package/corpus/core/docs/content/locales/es-ES/sandbox-adapters.mdx +18 -7
  28. package/corpus/core/docs/content/locales/es-ES/security.mdx +8 -0
  29. package/corpus/core/docs/content/locales/fr-FR/audit-log.mdx +2 -1
  30. package/corpus/core/docs/content/locales/fr-FR/automations.mdx +7 -7
  31. package/corpus/core/docs/content/locales/fr-FR/getting-started.mdx +7 -1
  32. package/corpus/core/docs/content/locales/fr-FR/internationalization.mdx +1 -1
  33. package/corpus/core/docs/content/locales/fr-FR/package-lifecycle.mdx +21 -0
  34. package/corpus/core/docs/content/locales/fr-FR/sandbox-adapters.mdx +19 -8
  35. package/corpus/core/docs/content/locales/fr-FR/security.mdx +8 -0
  36. package/corpus/core/docs/content/locales/hi-IN/audit-log.mdx +2 -1
  37. package/corpus/core/docs/content/locales/hi-IN/automations.mdx +7 -7
  38. package/corpus/core/docs/content/locales/hi-IN/getting-started.mdx +7 -1
  39. package/corpus/core/docs/content/locales/hi-IN/internationalization.mdx +1 -1
  40. package/corpus/core/docs/content/locales/hi-IN/package-lifecycle.mdx +20 -0
  41. package/corpus/core/docs/content/locales/hi-IN/sandbox-adapters.mdx +18 -7
  42. package/corpus/core/docs/content/locales/hi-IN/security.mdx +8 -0
  43. package/corpus/core/docs/content/locales/ja-JP/audit-log.mdx +2 -1
  44. package/corpus/core/docs/content/locales/ja-JP/automations.mdx +7 -7
  45. package/corpus/core/docs/content/locales/ja-JP/getting-started.mdx +7 -1
  46. package/corpus/core/docs/content/locales/ja-JP/internationalization.mdx +1 -1
  47. package/corpus/core/docs/content/locales/ja-JP/package-lifecycle.mdx +21 -0
  48. package/corpus/core/docs/content/locales/ja-JP/sandbox-adapters.mdx +17 -6
  49. package/corpus/core/docs/content/locales/ja-JP/security.mdx +8 -0
  50. package/corpus/core/docs/content/locales/ko-KR/audit-log.mdx +2 -1
  51. package/corpus/core/docs/content/locales/ko-KR/automations.mdx +7 -7
  52. package/corpus/core/docs/content/locales/ko-KR/getting-started.mdx +7 -1
  53. package/corpus/core/docs/content/locales/ko-KR/internationalization.mdx +1 -1
  54. package/corpus/core/docs/content/locales/ko-KR/package-lifecycle.mdx +20 -0
  55. package/corpus/core/docs/content/locales/ko-KR/sandbox-adapters.mdx +17 -6
  56. package/corpus/core/docs/content/locales/ko-KR/security.mdx +8 -0
  57. package/corpus/core/docs/content/locales/pt-BR/audit-log.mdx +2 -1
  58. package/corpus/core/docs/content/locales/pt-BR/automations.mdx +7 -7
  59. package/corpus/core/docs/content/locales/pt-BR/getting-started.mdx +7 -1
  60. package/corpus/core/docs/content/locales/pt-BR/internationalization.mdx +1 -1
  61. package/corpus/core/docs/content/locales/pt-BR/package-lifecycle.mdx +21 -0
  62. package/corpus/core/docs/content/locales/pt-BR/sandbox-adapters.mdx +18 -7
  63. package/corpus/core/docs/content/locales/pt-BR/security.mdx +8 -0
  64. package/corpus/core/docs/content/locales/zh-CN/audit-log.mdx +2 -1
  65. package/corpus/core/docs/content/locales/zh-CN/automations.mdx +7 -7
  66. package/corpus/core/docs/content/locales/zh-CN/getting-started.mdx +6 -4
  67. package/corpus/core/docs/content/locales/zh-CN/internationalization.mdx +1 -1
  68. package/corpus/core/docs/content/locales/zh-CN/package-lifecycle.mdx +19 -0
  69. package/corpus/core/docs/content/locales/zh-CN/sandbox-adapters.mdx +17 -6
  70. package/corpus/core/docs/content/locales/zh-CN/security.mdx +8 -0
  71. package/corpus/core/docs/content/locales/zh-TW/audit-log.mdx +2 -1
  72. package/corpus/core/docs/content/locales/zh-TW/automations.mdx +7 -7
  73. package/corpus/core/docs/content/locales/zh-TW/getting-started.mdx +6 -4
  74. package/corpus/core/docs/content/locales/zh-TW/internationalization.mdx +1 -1
  75. package/corpus/core/docs/content/locales/zh-TW/package-lifecycle.mdx +19 -0
  76. package/corpus/core/docs/content/locales/zh-TW/sandbox-adapters.mdx +18 -7
  77. package/corpus/core/docs/content/locales/zh-TW/security.mdx +8 -0
  78. package/corpus/core/docs/content/package-lifecycle.mdx +51 -0
  79. package/corpus/core/docs/content/sandbox-adapters.mdx +16 -5
  80. package/corpus/core/package.json +11 -2
  81. package/corpus/core/src/a2a/auth-policy.ts +33 -0
  82. package/corpus/core/src/a2a/client.ts +11 -1
  83. package/corpus/core/src/agent/durable-background.ts +15 -7
  84. package/corpus/core/src/agent/production-agent.ts +87 -27
  85. package/corpus/core/src/agent/run-store.ts +13 -4
  86. package/corpus/core/src/agent/thread-data-builder.ts +7 -5
  87. package/corpus/core/src/audit/actions/export-audit-events.ts +162 -0
  88. package/corpus/core/src/audit/store.ts +8 -3
  89. package/corpus/core/src/audit/types.ts +3 -0
  90. package/corpus/core/src/chat-threads/store.ts +20 -1
  91. package/corpus/core/src/cli/code-agent-executor.ts +1 -0
  92. package/corpus/core/src/cli/code-agent-runs.ts +14 -0
  93. package/corpus/core/src/cli/doctor.ts +396 -0
  94. package/corpus/core/src/cli/index.ts +56 -0
  95. package/corpus/core/src/cli/info.ts +3 -3
  96. package/corpus/core/src/cli/package-lifecycle.ts +821 -0
  97. package/corpus/core/src/client/AgentPanel.tsx +32 -25
  98. package/corpus/core/src/client/AssistantChat.tsx +36 -20
  99. package/corpus/core/src/client/MultiTabAssistantChat.tsx +141 -90
  100. package/corpus/core/src/client/agent-chat-adapter.ts +9 -9
  101. package/corpus/core/src/client/chat/ChatHistoryList.tsx +404 -0
  102. package/corpus/core/src/client/chat/index.ts +6 -0
  103. package/corpus/core/src/client/chat/tool-call-display.tsx +37 -3
  104. package/corpus/core/src/client/code-agent-chat-adapter.ts +72 -4
  105. package/corpus/core/src/client/composer/RealtimeVoiceMode.tsx +52 -11
  106. package/corpus/core/src/client/composer/TiptapComposer.tsx +157 -20
  107. package/corpus/core/src/client/composer/VoiceButton.tsx +28 -0
  108. package/corpus/core/src/client/composer/useRealtimeVoiceMode.tsx +5 -0
  109. package/corpus/core/src/client/conversation/code-agent-transcript.ts +4 -5
  110. package/corpus/core/src/client/guided-questions.tsx +2 -1
  111. package/corpus/core/src/client/index.ts +11 -0
  112. package/corpus/core/src/client/use-chat-threads.ts +10 -1
  113. package/corpus/core/src/client/voice-provider-status.ts +44 -0
  114. package/corpus/core/src/code-agents/transcript-normalizer.ts +76 -0
  115. package/corpus/core/src/deploy/build.ts +12 -11
  116. package/corpus/core/src/guards/db-tool-scoping.ts +129 -0
  117. package/corpus/core/src/guards/index.ts +23 -0
  118. package/corpus/core/src/guards/no-drizzle-push.ts +116 -0
  119. package/corpus/core/src/guards/no-env-credentials.ts +229 -0
  120. package/corpus/core/src/guards/no-env-mutation.ts +114 -0
  121. package/corpus/core/src/guards/no-localhost-fallback.ts +124 -0
  122. package/corpus/core/src/guards/no-unscoped-credentials.ts +227 -0
  123. package/corpus/core/src/guards/no-unscoped-queries.ts +834 -0
  124. package/corpus/core/src/guards/scan-utils.ts +105 -0
  125. package/corpus/core/src/guards/types.ts +30 -0
  126. package/corpus/core/src/integrations/a2a-continuation-processor.ts +35 -5
  127. package/corpus/core/src/integrations/adapters/slack.ts +74 -21
  128. package/corpus/core/src/integrations/webhook-handler.ts +14 -0
  129. package/corpus/core/src/localization/default-messages.ts +6 -0
  130. package/corpus/core/src/observability/hosted-model-experiment.ts +118 -0
  131. package/corpus/core/src/observability/routes.ts +17 -0
  132. package/corpus/core/src/observability/store.ts +4 -1
  133. package/corpus/core/src/observability/traces.ts +41 -2
  134. package/corpus/core/src/org/accept-pending.ts +9 -1
  135. package/corpus/core/src/org/migrations.ts +41 -0
  136. package/corpus/core/src/package-lifecycle/index.ts +1 -0
  137. package/corpus/core/src/package-lifecycle/manifest.ts +120 -0
  138. package/corpus/core/src/provider-api/custom-registry.ts +56 -0
  139. package/corpus/core/src/provider-api/index.ts +2 -0
  140. package/corpus/core/src/scripts/agent-engines/list-agent-engines.ts +19 -1
  141. package/corpus/core/src/scripts/call-agent.ts +53 -3
  142. package/corpus/core/src/scripts/chat/search-chats.ts +11 -8
  143. package/corpus/core/src/secrets/register-framework-secrets.ts +1 -1
  144. package/corpus/core/src/secrets/storage.ts +47 -27
  145. package/corpus/core/src/server/action-discovery.ts +4 -0
  146. package/corpus/core/src/server/agent-chat-plugin.ts +95 -18
  147. package/corpus/core/src/server/core-routes-plugin.ts +45 -14
  148. package/corpus/core/src/server/csrf.ts +24 -5
  149. package/corpus/core/src/server/framework-request-handler.ts +34 -0
  150. package/corpus/core/src/server/google-realtime-session.ts +1 -36
  151. package/corpus/core/src/server/poll.ts +24 -2
  152. package/corpus/core/src/server/realtime-voice.ts +80 -24
  153. package/corpus/core/src/server/request-origin.ts +63 -0
  154. package/corpus/core/src/server/transcribe-voice.ts +1 -50
  155. package/corpus/core/src/server/voice-providers-status.ts +4 -3
  156. package/corpus/core/src/shared/cache-control.ts +2 -8
  157. package/corpus/core/src/styles/agent-native.css +1 -0
  158. package/corpus/core/src/styles/chat-history-list.css +313 -0
  159. package/corpus/core/src/templates/default/package.json +2 -2
  160. package/corpus/core/src/templates/headless/package.json +0 -1
  161. package/corpus/core/src/templates/workspace-core/.agents/skills/audit-log/SKILL.md +3 -0
  162. package/corpus/core/src/templates/workspace-core/.agents/skills/automations/SKILL.md +1 -1
  163. package/corpus/core/src/templates/workspace-core/.agents/skills/observability/SKILL.md +20 -0
  164. package/corpus/core/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +8 -4
  165. package/corpus/core/src/templates/workspace-core/package.json +0 -1
  166. package/corpus/core/src/templates/workspace-root/package.json +0 -1
  167. package/corpus/core/src/triggers/actions.ts +11 -3
  168. package/corpus/core/src/triggers/types.ts +7 -1
  169. package/corpus/core/src/vite/client.ts +1 -0
  170. package/corpus/templates/analytics/AGENTS.md +7 -0
  171. package/corpus/templates/analytics/actions/bigquery.ts +26 -2
  172. package/corpus/templates/analytics/actions/compose-dashboard.ts +65 -37
  173. package/corpus/templates/analytics/actions/data-source-status.ts +18 -0
  174. package/corpus/templates/analytics/actions/mutate-dashboard.ts +73 -24
  175. package/corpus/templates/analytics/actions/reorder-dashboard-panels.ts +16 -13
  176. package/corpus/templates/analytics/actions/update-dashboard.ts +53 -42
  177. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +5 -4
  178. package/corpus/templates/analytics/app/global.css +10 -0
  179. package/corpus/templates/analytics/app/pages/Ask.tsx +25 -2
  180. package/corpus/templates/analytics/changelog/2026-07-10-analytics-answers-simple-time-bounded-metrics-without-unnece.md +6 -0
  181. package/corpus/templates/analytics/changelog/2026-07-10-analytics-chat-reliably-uses-connected-data-sources-across-s.md +6 -0
  182. package/corpus/templates/analytics/changelog/2026-07-10-chat-threads-now-stay-clear-of-top-actions-and-fade-smoothly.md +6 -0
  183. package/corpus/templates/analytics/changelog/2026-07-10-context-chips-can-now-be-selected-and-removed-one-at-a-time-.md +6 -0
  184. package/corpus/templates/analytics/changelog/2026-07-10-dashboard-edits-from-the-agent-and-the-ui-at-the-same-time-n.md +6 -0
  185. package/corpus/templates/analytics/changelog/2026-07-10-the-ask-composer-no-longer-carries-dashboard-or-panel-contex.md +6 -0
  186. package/corpus/templates/analytics/changelog/2026-07-10-the-ask-section-stays-highlighted-while-its-chat-history-is-.md +6 -0
  187. package/corpus/templates/analytics/changelog/2026-07-10-timed-out-bigquery-jobs-are-now-cancelled-so-they-stop-consu.md +6 -0
  188. package/corpus/templates/analytics/package.json +1 -2
  189. package/corpus/templates/analytics/server/lib/bigquery.ts +91 -14
  190. package/corpus/templates/analytics/server/lib/dashboards-store.ts +154 -11
  191. package/corpus/templates/analytics/server/plugins/agent-chat.ts +82 -7
  192. package/corpus/templates/assets/package.json +1 -2
  193. package/corpus/templates/brain/package.json +1 -2
  194. package/corpus/templates/calendar/package.json +1 -2
  195. package/corpus/templates/chat/package.json +1 -2
  196. package/corpus/templates/clips/changelog/2026-07-10-atomic-view-tracking.md +6 -0
  197. package/corpus/templates/clips/chrome-extension/package.json +3 -2
  198. package/corpus/templates/clips/desktop/package.json +6 -4
  199. package/corpus/templates/clips/desktop/src/lib/recorder.ts +26 -13
  200. package/corpus/templates/clips/desktop/src/lib/voice-dictation.ts +4 -1
  201. package/corpus/templates/clips/package.json +1 -2
  202. package/corpus/templates/clips/server/db/schema.ts +29 -17
  203. package/corpus/templates/clips/server/plugins/db.ts +8 -0
  204. package/corpus/templates/clips/server/routes/api/view-event.post.ts +260 -105
  205. package/corpus/templates/content/.agents/skills/notion-integration/SKILL.md +16 -0
  206. package/corpus/templates/content/AGENTS.md +72 -62
  207. package/corpus/templates/content/actions/_content-database-source-adapters.ts +84 -0
  208. package/corpus/templates/content/actions/_database-source-utils.ts +206 -96
  209. package/corpus/templates/content/actions/_notion-database-source-adapter.ts +216 -0
  210. package/corpus/templates/content/actions/_position-utils.ts +76 -0
  211. package/corpus/templates/content/actions/_property-utils.ts +31 -20
  212. package/corpus/templates/content/actions/add-database-item.ts +52 -39
  213. package/corpus/templates/content/actions/attach-content-database-source.ts +95 -11
  214. package/corpus/templates/content/actions/change-content-database-source-role.ts +24 -1
  215. package/corpus/templates/content/actions/configure-document-property.ts +36 -27
  216. package/corpus/templates/content/actions/create-content-database.ts +40 -30
  217. package/corpus/templates/content/actions/create-document.ts +39 -32
  218. package/corpus/templates/content/actions/duplicate-document-property.ts +37 -28
  219. package/corpus/templates/content/actions/list-notion-database-sources.ts +80 -0
  220. package/corpus/templates/content/actions/move-document.ts +64 -48
  221. package/corpus/templates/content/actions/refresh-content-database-source.ts +42 -3
  222. package/corpus/templates/content/actions/share-local-file-document.ts +36 -27
  223. package/corpus/templates/content/actions/suggest-source-join-key.ts +39 -16
  224. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +89 -23
  225. package/corpus/templates/content/app/components/editor/database/settings.tsx +1 -1
  226. package/corpus/templates/content/app/hooks/use-content-database.ts +18 -1
  227. package/corpus/templates/content/app/i18n/zh-TW.ts +4 -0
  228. package/corpus/templates/content/app/i18n-data.ts +40 -0
  229. package/corpus/templates/content/changelog/2026-07-10-failed-row-creation-and-source-attach-now-show-an-error-inst.md +6 -0
  230. package/corpus/templates/content/changelog/2026-07-10-items-added-at-the-same-time-no-longer-end-up-with-identical.md +6 -0
  231. package/corpus/templates/content/changelog/2026-07-10-notion-databases-can-add-read-only-details-to-content-tables.md +6 -0
  232. package/corpus/templates/content/package.json +1 -2
  233. package/corpus/templates/content/parity/matrix.md +25 -25
  234. package/corpus/templates/content/parity/matrix.ts +1 -0
  235. package/corpus/templates/content/server/routes/api/documents/[id]/move.patch.ts +43 -26
  236. package/corpus/templates/content/shared/api.ts +16 -1
  237. package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +8 -4
  238. package/corpus/templates/design/AGENTS.md +12 -2
  239. package/corpus/templates/design/DEVELOPING.md +10 -6
  240. package/corpus/templates/design/actions/apply-visual-edit.ts +191 -6
  241. package/corpus/templates/design/actions/duplicate-design.ts +31 -25
  242. package/corpus/templates/design/app/pages/design-editor/pending-edits.ts +5 -1
  243. package/corpus/templates/design/changelog/2026-07-10-duplicating-a-design-can-no-longer-produce-a-copy-with-missi.md +6 -0
  244. package/corpus/templates/design/changelog/2026-07-10-local-visual-edits-can-now-save-safe-leaf-text-class-and.md +6 -0
  245. package/corpus/templates/design/package.json +1 -1
  246. package/corpus/templates/design/shared/local-jsx-visual-edit.ts +388 -0
  247. package/corpus/templates/dispatch/package.json +1 -2
  248. package/corpus/templates/forms/actions/patch-form-fields.ts +82 -44
  249. package/corpus/templates/forms/changelog/2026-07-10-simultaneous-edits-to-different-form-fields-no-longer-overwr.md +6 -0
  250. package/corpus/templates/forms/package.json +1 -2
  251. package/corpus/templates/macros/package.json +1 -2
  252. package/corpus/templates/mail/app/components/email/EmailThread.tsx +3 -1
  253. package/corpus/templates/mail/changelog/2026-07-10-fixed-calendar-rsvp-buttons-in-emails-firing-duplicate-respo.md +6 -0
  254. package/corpus/templates/mail/package.json +1 -2
  255. package/corpus/templates/plan/actions/restore-plan-version.ts +126 -96
  256. package/corpus/templates/plan/changelog/2026-07-10-restoring-a-plan-version-can-no-longer-leave-a-plan-half-res.md +6 -0
  257. package/corpus/templates/plan/package.json +1 -2
  258. package/corpus/templates/plan/server/plans.ts +9 -2
  259. package/corpus/templates/slides/actions/list-decks.ts +28 -0
  260. package/corpus/templates/slides/app/components/deck/MermaidRenderer.tsx +41 -26
  261. package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +4 -8
  262. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +2 -4
  263. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +4 -8
  264. package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +5 -12
  265. package/corpus/templates/slides/app/components/editor/HistoryPanel.tsx +3 -6
  266. package/corpus/templates/slides/app/components/editor/ImageDropPromptPopover.tsx +3 -6
  267. package/corpus/templates/slides/app/components/editor/PromptDialog.tsx +2 -5
  268. package/corpus/templates/slides/app/context/DeckContext.tsx +47 -4
  269. package/corpus/templates/slides/app/pages/DeckEditor.tsx +11 -30
  270. package/corpus/templates/slides/app/pages/Index.tsx +2 -3
  271. package/corpus/templates/slides/app/root.tsx +0 -5
  272. package/corpus/templates/slides/changelog/2026-07-10-deck-lists-load-much-faster-and-no-longer-re-download-slide-.md +6 -0
  273. package/corpus/templates/slides/package.json +1 -2
  274. package/dist/a2a/auth-policy.d.ts +19 -0
  275. package/dist/a2a/auth-policy.d.ts.map +1 -1
  276. package/dist/a2a/auth-policy.js +32 -0
  277. package/dist/a2a/auth-policy.js.map +1 -1
  278. package/dist/a2a/client.d.ts +7 -0
  279. package/dist/a2a/client.d.ts.map +1 -1
  280. package/dist/a2a/client.js +2 -1
  281. package/dist/a2a/client.js.map +1 -1
  282. package/dist/agent/durable-background.d.ts +8 -5
  283. package/dist/agent/durable-background.d.ts.map +1 -1
  284. package/dist/agent/durable-background.js +15 -8
  285. package/dist/agent/durable-background.js.map +1 -1
  286. package/dist/agent/production-agent.d.ts +6 -0
  287. package/dist/agent/production-agent.d.ts.map +1 -1
  288. package/dist/agent/production-agent.js +63 -26
  289. package/dist/agent/production-agent.js.map +1 -1
  290. package/dist/agent/run-store.d.ts.map +1 -1
  291. package/dist/agent/run-store.js +9 -4
  292. package/dist/agent/run-store.js.map +1 -1
  293. package/dist/agent/thread-data-builder.d.ts +1 -0
  294. package/dist/agent/thread-data-builder.d.ts.map +1 -1
  295. package/dist/agent/thread-data-builder.js +6 -5
  296. package/dist/agent/thread-data-builder.js.map +1 -1
  297. package/dist/audit/actions/export-audit-events.d.ts +27 -0
  298. package/dist/audit/actions/export-audit-events.d.ts.map +1 -0
  299. package/dist/audit/actions/export-audit-events.js +144 -0
  300. package/dist/audit/actions/export-audit-events.js.map +1 -0
  301. package/dist/audit/store.d.ts +1 -0
  302. package/dist/audit/store.d.ts.map +1 -1
  303. package/dist/audit/store.js +8 -3
  304. package/dist/audit/store.js.map +1 -1
  305. package/dist/audit/types.d.ts +3 -0
  306. package/dist/audit/types.d.ts.map +1 -1
  307. package/dist/audit/types.js.map +1 -1
  308. package/dist/chat-threads/store.d.ts +10 -0
  309. package/dist/chat-threads/store.d.ts.map +1 -1
  310. package/dist/chat-threads/store.js +6 -0
  311. package/dist/chat-threads/store.js.map +1 -1
  312. package/dist/cli/code-agent-executor.d.ts.map +1 -1
  313. package/dist/cli/code-agent-executor.js +1 -0
  314. package/dist/cli/code-agent-executor.js.map +1 -1
  315. package/dist/cli/code-agent-runs.d.ts +12 -0
  316. package/dist/cli/code-agent-runs.d.ts.map +1 -1
  317. package/dist/cli/code-agent-runs.js +1 -0
  318. package/dist/cli/code-agent-runs.js.map +1 -1
  319. package/dist/cli/doctor.d.ts +83 -0
  320. package/dist/cli/doctor.d.ts.map +1 -0
  321. package/dist/cli/doctor.js +277 -0
  322. package/dist/cli/doctor.js.map +1 -0
  323. package/dist/cli/index.js +50 -0
  324. package/dist/cli/index.js.map +1 -1
  325. package/dist/cli/info.js +3 -3
  326. package/dist/cli/info.js.map +1 -1
  327. package/dist/cli/package-lifecycle.d.ts +50 -0
  328. package/dist/cli/package-lifecycle.d.ts.map +1 -0
  329. package/dist/cli/package-lifecycle.js +560 -0
  330. package/dist/cli/package-lifecycle.js.map +1 -0
  331. package/dist/client/AgentPanel.d.ts.map +1 -1
  332. package/dist/client/AgentPanel.js +3 -3
  333. package/dist/client/AgentPanel.js.map +1 -1
  334. package/dist/client/AssistantChat.d.ts +11 -0
  335. package/dist/client/AssistantChat.d.ts.map +1 -1
  336. package/dist/client/AssistantChat.js +7 -4
  337. package/dist/client/AssistantChat.js.map +1 -1
  338. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  339. package/dist/client/MultiTabAssistantChat.js +83 -10
  340. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  341. package/dist/client/agent-chat-adapter.js +7 -3
  342. package/dist/client/agent-chat-adapter.js.map +1 -1
  343. package/dist/client/chat/ChatHistoryList.d.ts +88 -0
  344. package/dist/client/chat/ChatHistoryList.d.ts.map +1 -0
  345. package/dist/client/chat/ChatHistoryList.js +83 -0
  346. package/dist/client/chat/ChatHistoryList.js.map +1 -0
  347. package/dist/client/chat/index.d.ts +1 -0
  348. package/dist/client/chat/index.d.ts.map +1 -1
  349. package/dist/client/chat/index.js +1 -0
  350. package/dist/client/chat/index.js.map +1 -1
  351. package/dist/client/chat/tool-call-display.d.ts +13 -1
  352. package/dist/client/chat/tool-call-display.d.ts.map +1 -1
  353. package/dist/client/chat/tool-call-display.js +10 -2
  354. package/dist/client/chat/tool-call-display.js.map +1 -1
  355. package/dist/client/code-agent-chat-adapter.d.ts +17 -1
  356. package/dist/client/code-agent-chat-adapter.d.ts.map +1 -1
  357. package/dist/client/code-agent-chat-adapter.js +47 -3
  358. package/dist/client/code-agent-chat-adapter.js.map +1 -1
  359. package/dist/client/composer/RealtimeVoiceMode.d.ts +11 -1
  360. package/dist/client/composer/RealtimeVoiceMode.d.ts.map +1 -1
  361. package/dist/client/composer/RealtimeVoiceMode.js +5 -3
  362. package/dist/client/composer/RealtimeVoiceMode.js.map +1 -1
  363. package/dist/client/composer/TiptapComposer.d.ts +25 -2
  364. package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
  365. package/dist/client/composer/TiptapComposer.js +96 -5
  366. package/dist/client/composer/TiptapComposer.js.map +1 -1
  367. package/dist/client/composer/VoiceButton.d.ts.map +1 -1
  368. package/dist/client/composer/VoiceButton.js +21 -1
  369. package/dist/client/composer/VoiceButton.js.map +1 -1
  370. package/dist/client/composer/useRealtimeVoiceMode.d.ts.map +1 -1
  371. package/dist/client/composer/useRealtimeVoiceMode.js +5 -0
  372. package/dist/client/composer/useRealtimeVoiceMode.js.map +1 -1
  373. package/dist/client/conversation/code-agent-transcript.d.ts +2 -0
  374. package/dist/client/conversation/code-agent-transcript.d.ts.map +1 -1
  375. package/dist/client/conversation/code-agent-transcript.js +3 -5
  376. package/dist/client/conversation/code-agent-transcript.js.map +1 -1
  377. package/dist/client/guided-questions.d.ts.map +1 -1
  378. package/dist/client/guided-questions.js +2 -1
  379. package/dist/client/guided-questions.js.map +1 -1
  380. package/dist/client/index.d.ts +3 -1
  381. package/dist/client/index.d.ts.map +1 -1
  382. package/dist/client/index.js +3 -1
  383. package/dist/client/index.js.map +1 -1
  384. package/dist/client/use-chat-threads.d.ts.map +1 -1
  385. package/dist/client/use-chat-threads.js +9 -1
  386. package/dist/client/use-chat-threads.js.map +1 -1
  387. package/dist/client/voice-provider-status.d.ts +10 -0
  388. package/dist/client/voice-provider-status.d.ts.map +1 -0
  389. package/dist/client/voice-provider-status.js +32 -0
  390. package/dist/client/voice-provider-status.js.map +1 -0
  391. package/dist/code-agents/transcript-normalizer.d.ts +29 -0
  392. package/dist/code-agents/transcript-normalizer.d.ts.map +1 -1
  393. package/dist/code-agents/transcript-normalizer.js +55 -0
  394. package/dist/code-agents/transcript-normalizer.js.map +1 -1
  395. package/dist/collab/routes.d.ts +1 -1
  396. package/dist/deploy/build.d.ts.map +1 -1
  397. package/dist/deploy/build.js +13 -11
  398. package/dist/deploy/build.js.map +1 -1
  399. package/dist/guards/db-tool-scoping.d.ts +38 -0
  400. package/dist/guards/db-tool-scoping.d.ts.map +1 -0
  401. package/dist/guards/db-tool-scoping.js +114 -0
  402. package/dist/guards/db-tool-scoping.js.map +1 -0
  403. package/dist/guards/index.d.ts +23 -0
  404. package/dist/guards/index.d.ts.map +1 -0
  405. package/dist/guards/index.js +19 -0
  406. package/dist/guards/index.js.map +1 -0
  407. package/dist/guards/no-drizzle-push.d.ts +14 -0
  408. package/dist/guards/no-drizzle-push.d.ts.map +1 -0
  409. package/dist/guards/no-drizzle-push.js +97 -0
  410. package/dist/guards/no-drizzle-push.js.map +1 -0
  411. package/dist/guards/no-env-credentials.d.ts +36 -0
  412. package/dist/guards/no-env-credentials.d.ts.map +1 -0
  413. package/dist/guards/no-env-credentials.js +204 -0
  414. package/dist/guards/no-env-credentials.js.map +1 -0
  415. package/dist/guards/no-env-mutation.d.ts +23 -0
  416. package/dist/guards/no-env-mutation.d.ts.map +1 -0
  417. package/dist/guards/no-env-mutation.js +90 -0
  418. package/dist/guards/no-env-mutation.js.map +1 -0
  419. package/dist/guards/no-localhost-fallback.d.ts +30 -0
  420. package/dist/guards/no-localhost-fallback.d.ts.map +1 -0
  421. package/dist/guards/no-localhost-fallback.js +105 -0
  422. package/dist/guards/no-localhost-fallback.js.map +1 -0
  423. package/dist/guards/no-unscoped-credentials.d.ts +28 -0
  424. package/dist/guards/no-unscoped-credentials.d.ts.map +1 -0
  425. package/dist/guards/no-unscoped-credentials.js +199 -0
  426. package/dist/guards/no-unscoped-credentials.js.map +1 -0
  427. package/dist/guards/no-unscoped-queries.d.ts +32 -0
  428. package/dist/guards/no-unscoped-queries.d.ts.map +1 -0
  429. package/dist/guards/no-unscoped-queries.js +720 -0
  430. package/dist/guards/no-unscoped-queries.js.map +1 -0
  431. package/dist/guards/scan-utils.d.ts +28 -0
  432. package/dist/guards/scan-utils.d.ts.map +1 -0
  433. package/dist/guards/scan-utils.js +92 -0
  434. package/dist/guards/scan-utils.js.map +1 -0
  435. package/dist/guards/types.d.ts +28 -0
  436. package/dist/guards/types.d.ts.map +1 -0
  437. package/dist/guards/types.js +11 -0
  438. package/dist/guards/types.js.map +1 -0
  439. package/dist/integrations/a2a-continuation-processor.d.ts.map +1 -1
  440. package/dist/integrations/a2a-continuation-processor.js +25 -5
  441. package/dist/integrations/a2a-continuation-processor.js.map +1 -1
  442. package/dist/integrations/adapters/slack.d.ts.map +1 -1
  443. package/dist/integrations/adapters/slack.js +55 -21
  444. package/dist/integrations/adapters/slack.js.map +1 -1
  445. package/dist/integrations/webhook-handler.d.ts.map +1 -1
  446. package/dist/integrations/webhook-handler.js +11 -0
  447. package/dist/integrations/webhook-handler.js.map +1 -1
  448. package/dist/localization/default-messages.d.ts +5 -0
  449. package/dist/localization/default-messages.d.ts.map +1 -1
  450. package/dist/localization/default-messages.js +5 -0
  451. package/dist/localization/default-messages.js.map +1 -1
  452. package/dist/notifications/routes.d.ts +2 -2
  453. package/dist/observability/hosted-model-experiment.d.ts +39 -0
  454. package/dist/observability/hosted-model-experiment.d.ts.map +1 -0
  455. package/dist/observability/hosted-model-experiment.js +90 -0
  456. package/dist/observability/hosted-model-experiment.js.map +1 -0
  457. package/dist/observability/routes.d.ts +1 -1
  458. package/dist/observability/routes.d.ts.map +1 -1
  459. package/dist/observability/routes.js +16 -0
  460. package/dist/observability/routes.js.map +1 -1
  461. package/dist/observability/store.d.ts.map +1 -1
  462. package/dist/observability/store.js +4 -1
  463. package/dist/observability/store.js.map +1 -1
  464. package/dist/observability/traces.d.ts +6 -0
  465. package/dist/observability/traces.d.ts.map +1 -1
  466. package/dist/observability/traces.js +28 -2
  467. package/dist/observability/traces.js.map +1 -1
  468. package/dist/org/accept-pending.d.ts.map +1 -1
  469. package/dist/org/accept-pending.js +9 -1
  470. package/dist/org/accept-pending.js.map +1 -1
  471. package/dist/org/migrations.d.ts +7 -2
  472. package/dist/org/migrations.d.ts.map +1 -1
  473. package/dist/org/migrations.js +41 -0
  474. package/dist/org/migrations.js.map +1 -1
  475. package/dist/package-lifecycle/index.d.ts +2 -0
  476. package/dist/package-lifecycle/index.d.ts.map +1 -0
  477. package/dist/package-lifecycle/index.js +2 -0
  478. package/dist/package-lifecycle/index.js.map +1 -0
  479. package/dist/package-lifecycle/manifest.d.ts +24 -0
  480. package/dist/package-lifecycle/manifest.d.ts.map +1 -0
  481. package/dist/package-lifecycle/manifest.js +81 -0
  482. package/dist/package-lifecycle/manifest.js.map +1 -0
  483. package/dist/progress/routes.d.ts +1 -1
  484. package/dist/provider-api/custom-registry.d.ts +32 -1
  485. package/dist/provider-api/custom-registry.d.ts.map +1 -1
  486. package/dist/provider-api/custom-registry.js +41 -1
  487. package/dist/provider-api/custom-registry.js.map +1 -1
  488. package/dist/provider-api/index.d.ts +1 -1
  489. package/dist/provider-api/index.d.ts.map +1 -1
  490. package/dist/provider-api/index.js +1 -1
  491. package/dist/provider-api/index.js.map +1 -1
  492. package/dist/resources/handlers.d.ts +1 -1
  493. package/dist/scripts/agent-engines/list-agent-engines.d.ts.map +1 -1
  494. package/dist/scripts/agent-engines/list-agent-engines.js +19 -1
  495. package/dist/scripts/agent-engines/list-agent-engines.js.map +1 -1
  496. package/dist/scripts/call-agent.d.ts.map +1 -1
  497. package/dist/scripts/call-agent.js +43 -3
  498. package/dist/scripts/call-agent.js.map +1 -1
  499. package/dist/scripts/chat/search-chats.d.ts +1 -1
  500. package/dist/scripts/chat/search-chats.d.ts.map +1 -1
  501. package/dist/scripts/chat/search-chats.js +11 -8
  502. package/dist/scripts/chat/search-chats.js.map +1 -1
  503. package/dist/secrets/register-framework-secrets.js +1 -1
  504. package/dist/secrets/register-framework-secrets.js.map +1 -1
  505. package/dist/secrets/storage.d.ts.map +1 -1
  506. package/dist/secrets/storage.js +45 -27
  507. package/dist/secrets/storage.js.map +1 -1
  508. package/dist/server/action-discovery.d.ts.map +1 -1
  509. package/dist/server/action-discovery.js +4 -0
  510. package/dist/server/action-discovery.js.map +1 -1
  511. package/dist/server/agent-chat-plugin.d.ts +11 -0
  512. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  513. package/dist/server/agent-chat-plugin.js +74 -19
  514. package/dist/server/agent-chat-plugin.js.map +1 -1
  515. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  516. package/dist/server/core-routes-plugin.d.ts +3 -0
  517. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  518. package/dist/server/core-routes-plugin.js +36 -12
  519. package/dist/server/core-routes-plugin.js.map +1 -1
  520. package/dist/server/csrf.d.ts.map +1 -1
  521. package/dist/server/csrf.js +18 -5
  522. package/dist/server/csrf.js.map +1 -1
  523. package/dist/server/framework-request-handler.d.ts.map +1 -1
  524. package/dist/server/framework-request-handler.js +34 -0
  525. package/dist/server/framework-request-handler.js.map +1 -1
  526. package/dist/server/google-realtime-session.d.ts.map +1 -1
  527. package/dist/server/google-realtime-session.js +2 -34
  528. package/dist/server/google-realtime-session.js.map +1 -1
  529. package/dist/server/poll.d.ts.map +1 -1
  530. package/dist/server/poll.js +24 -2
  531. package/dist/server/poll.js.map +1 -1
  532. package/dist/server/realtime-voice.d.ts.map +1 -1
  533. package/dist/server/realtime-voice.js +64 -21
  534. package/dist/server/realtime-voice.js.map +1 -1
  535. package/dist/server/request-origin.d.ts +10 -0
  536. package/dist/server/request-origin.d.ts.map +1 -0
  537. package/dist/server/request-origin.js +60 -0
  538. package/dist/server/request-origin.js.map +1 -0
  539. package/dist/server/transcribe-voice.d.ts.map +1 -1
  540. package/dist/server/transcribe-voice.js +2 -48
  541. package/dist/server/transcribe-voice.js.map +1 -1
  542. package/dist/server/voice-providers-status.d.ts.map +1 -1
  543. package/dist/server/voice-providers-status.js +2 -3
  544. package/dist/server/voice-providers-status.js.map +1 -1
  545. package/dist/shared/cache-control.d.ts +4 -6
  546. package/dist/shared/cache-control.d.ts.map +1 -1
  547. package/dist/shared/cache-control.js +2 -7
  548. package/dist/shared/cache-control.js.map +1 -1
  549. package/dist/styles/agent-native.css +1 -0
  550. package/dist/styles/chat-history-list.css +313 -0
  551. package/dist/templates/default/package.json +2 -2
  552. package/dist/templates/headless/package.json +0 -1
  553. package/dist/templates/workspace-core/.agents/skills/audit-log/SKILL.md +3 -0
  554. package/dist/templates/workspace-core/.agents/skills/automations/SKILL.md +1 -1
  555. package/dist/templates/workspace-core/.agents/skills/observability/SKILL.md +20 -0
  556. package/dist/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +8 -4
  557. package/dist/templates/workspace-core/package.json +0 -1
  558. package/dist/templates/workspace-root/package.json +0 -1
  559. package/dist/triggers/actions.d.ts.map +1 -1
  560. package/dist/triggers/actions.js +8 -3
  561. package/dist/triggers/actions.js.map +1 -1
  562. package/dist/triggers/types.d.ts +7 -1
  563. package/dist/triggers/types.d.ts.map +1 -1
  564. package/dist/triggers/types.js.map +1 -1
  565. package/dist/vite/client.d.ts.map +1 -1
  566. package/dist/vite/client.js +1 -0
  567. package/dist/vite/client.js.map +1 -1
  568. package/docs/content/audit-log.mdx +2 -1
  569. package/docs/content/automations.mdx +7 -7
  570. package/docs/content/creating-templates.mdx +1 -1
  571. package/docs/content/internationalization.mdx +2 -1
  572. package/docs/content/locales/ar-SA/audit-log.mdx +2 -1
  573. package/docs/content/locales/ar-SA/automations.mdx +7 -7
  574. package/docs/content/locales/ar-SA/getting-started.mdx +7 -1
  575. package/docs/content/locales/ar-SA/internationalization.mdx +1 -1
  576. package/docs/content/locales/ar-SA/package-lifecycle.mdx +19 -0
  577. package/docs/content/locales/ar-SA/sandbox-adapters.mdx +18 -7
  578. package/docs/content/locales/ar-SA/security.mdx +8 -0
  579. package/docs/content/locales/de-DE/audit-log.mdx +2 -1
  580. package/docs/content/locales/de-DE/automations.mdx +7 -7
  581. package/docs/content/locales/de-DE/getting-started.mdx +7 -1
  582. package/docs/content/locales/de-DE/internationalization.mdx +1 -1
  583. package/docs/content/locales/de-DE/package-lifecycle.mdx +22 -0
  584. package/docs/content/locales/de-DE/sandbox-adapters.mdx +18 -7
  585. package/docs/content/locales/de-DE/security.mdx +8 -0
  586. package/docs/content/locales/es-ES/audit-log.mdx +2 -1
  587. package/docs/content/locales/es-ES/automations.mdx +7 -7
  588. package/docs/content/locales/es-ES/getting-started.mdx +7 -1
  589. package/docs/content/locales/es-ES/internationalization.mdx +1 -1
  590. package/docs/content/locales/es-ES/package-lifecycle.mdx +22 -0
  591. package/docs/content/locales/es-ES/sandbox-adapters.mdx +18 -7
  592. package/docs/content/locales/es-ES/security.mdx +8 -0
  593. package/docs/content/locales/fr-FR/audit-log.mdx +2 -1
  594. package/docs/content/locales/fr-FR/automations.mdx +7 -7
  595. package/docs/content/locales/fr-FR/getting-started.mdx +7 -1
  596. package/docs/content/locales/fr-FR/internationalization.mdx +1 -1
  597. package/docs/content/locales/fr-FR/package-lifecycle.mdx +21 -0
  598. package/docs/content/locales/fr-FR/sandbox-adapters.mdx +19 -8
  599. package/docs/content/locales/fr-FR/security.mdx +8 -0
  600. package/docs/content/locales/hi-IN/audit-log.mdx +2 -1
  601. package/docs/content/locales/hi-IN/automations.mdx +7 -7
  602. package/docs/content/locales/hi-IN/getting-started.mdx +7 -1
  603. package/docs/content/locales/hi-IN/internationalization.mdx +1 -1
  604. package/docs/content/locales/hi-IN/package-lifecycle.mdx +20 -0
  605. package/docs/content/locales/hi-IN/sandbox-adapters.mdx +18 -7
  606. package/docs/content/locales/hi-IN/security.mdx +8 -0
  607. package/docs/content/locales/ja-JP/audit-log.mdx +2 -1
  608. package/docs/content/locales/ja-JP/automations.mdx +7 -7
  609. package/docs/content/locales/ja-JP/getting-started.mdx +7 -1
  610. package/docs/content/locales/ja-JP/internationalization.mdx +1 -1
  611. package/docs/content/locales/ja-JP/package-lifecycle.mdx +21 -0
  612. package/docs/content/locales/ja-JP/sandbox-adapters.mdx +17 -6
  613. package/docs/content/locales/ja-JP/security.mdx +8 -0
  614. package/docs/content/locales/ko-KR/audit-log.mdx +2 -1
  615. package/docs/content/locales/ko-KR/automations.mdx +7 -7
  616. package/docs/content/locales/ko-KR/getting-started.mdx +7 -1
  617. package/docs/content/locales/ko-KR/internationalization.mdx +1 -1
  618. package/docs/content/locales/ko-KR/package-lifecycle.mdx +20 -0
  619. package/docs/content/locales/ko-KR/sandbox-adapters.mdx +17 -6
  620. package/docs/content/locales/ko-KR/security.mdx +8 -0
  621. package/docs/content/locales/pt-BR/audit-log.mdx +2 -1
  622. package/docs/content/locales/pt-BR/automations.mdx +7 -7
  623. package/docs/content/locales/pt-BR/getting-started.mdx +7 -1
  624. package/docs/content/locales/pt-BR/internationalization.mdx +1 -1
  625. package/docs/content/locales/pt-BR/package-lifecycle.mdx +21 -0
  626. package/docs/content/locales/pt-BR/sandbox-adapters.mdx +18 -7
  627. package/docs/content/locales/pt-BR/security.mdx +8 -0
  628. package/docs/content/locales/zh-CN/audit-log.mdx +2 -1
  629. package/docs/content/locales/zh-CN/automations.mdx +7 -7
  630. package/docs/content/locales/zh-CN/getting-started.mdx +6 -4
  631. package/docs/content/locales/zh-CN/internationalization.mdx +1 -1
  632. package/docs/content/locales/zh-CN/package-lifecycle.mdx +19 -0
  633. package/docs/content/locales/zh-CN/sandbox-adapters.mdx +17 -6
  634. package/docs/content/locales/zh-CN/security.mdx +8 -0
  635. package/docs/content/locales/zh-TW/audit-log.mdx +2 -1
  636. package/docs/content/locales/zh-TW/automations.mdx +7 -7
  637. package/docs/content/locales/zh-TW/getting-started.mdx +6 -4
  638. package/docs/content/locales/zh-TW/internationalization.mdx +1 -1
  639. package/docs/content/locales/zh-TW/package-lifecycle.mdx +19 -0
  640. package/docs/content/locales/zh-TW/sandbox-adapters.mdx +18 -7
  641. package/docs/content/locales/zh-TW/security.mdx +8 -0
  642. package/docs/content/package-lifecycle.mdx +51 -0
  643. package/docs/content/sandbox-adapters.mdx +16 -5
  644. package/package.json +12 -3
  645. package/src/templates/default/package.json +2 -2
  646. package/src/templates/headless/package.json +0 -1
  647. package/src/templates/workspace-core/.agents/skills/audit-log/SKILL.md +3 -0
  648. package/src/templates/workspace-core/.agents/skills/automations/SKILL.md +1 -1
  649. package/src/templates/workspace-core/.agents/skills/observability/SKILL.md +20 -0
  650. package/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +8 -4
  651. package/src/templates/workspace-core/package.json +0 -1
  652. package/src/templates/workspace-root/package.json +0 -1
  653. package/corpus/templates/slides/app/components/ui/toast.tsx +0 -1
  654. package/corpus/templates/slides/app/components/ui/toaster.tsx +0 -1
  655. package/corpus/templates/slides/app/hooks/use-toast.ts +0 -1
@@ -76,6 +76,12 @@ import {
76
76
  type ExistingBuilderSourceRowIdentity,
77
77
  } from "./_builder-cms-source-adapter.js";
78
78
  import { mergeBuilderCmsWriteSettingsIntoJson } from "./_builder-cms-write-settings.js";
79
+ import {
80
+ databaseItemsPositionScope,
81
+ documentsPositionScope,
82
+ propertyDefinitionsPositionScope,
83
+ withPositionLock,
84
+ } from "./_position-utils.js";
79
85
  import { listPropertiesForDatabase, nanoid } from "./_property-utils.js";
80
86
  import { isEffectivelyEmptyDocumentContent } from "./update-document.js";
81
87
 
@@ -189,7 +195,12 @@ function normalizeWriteOwner(
189
195
  function normalizeSourceType(
190
196
  value: string | null | undefined,
191
197
  ): ContentDatabaseSourceType {
192
- if (value === "builder-cms" || value === "local-table") return value;
198
+ if (
199
+ value === "builder-cms" ||
200
+ value === "local-table" ||
201
+ value === "notion-database"
202
+ )
203
+ return value;
193
204
  return "mock-local";
194
205
  }
195
206
 
@@ -279,6 +290,7 @@ function sourceMetadataLabel(
279
290
  ) {
280
291
  if (sourceType === "builder-cms") return `builder.cms.${sourceTable}`;
281
292
  if (sourceType === "local-table") return `local.table.${sourceTable}`;
293
+ if (sourceType === "notion-database") return `notion.database.${sourceTable}`;
282
294
  return `mock-local.${sourceTable}`;
283
295
  }
284
296
 
@@ -3102,6 +3114,20 @@ export function serializeSourceMetadataRecord(args: {
3102
3114
  args.builderModelFields ?? existingMetadata?.builderModelFields,
3103
3115
  });
3104
3116
  }
3117
+ if (args.sourceType === "notion-database") {
3118
+ return JSON.stringify({
3119
+ primaryKey: "id",
3120
+ titleField: "title",
3121
+ naturalKeyField: null,
3122
+ pushMode: "none",
3123
+ pushModeLabel: "Read only",
3124
+ pushModeDescription:
3125
+ "Notion database sources are read-only in this pilot.",
3126
+ notes:
3127
+ "Notion values are refreshed through the current user's OAuth connection; write-back is disabled.",
3128
+ label: sourceMetadataLabel(args.sourceType, args.sourceTable),
3129
+ });
3130
+ }
3105
3131
  return JSON.stringify({
3106
3132
  primaryKey: "id",
3107
3133
  titleField: "title",
@@ -3180,6 +3206,21 @@ export function sourceCapabilitiesForType(
3180
3206
  readOnlyRefresh: true,
3181
3207
  });
3182
3208
  }
3209
+ if (sourceType === "notion-database") {
3210
+ return serializeSourceCapabilitiesRecord({
3211
+ canRefresh: true,
3212
+ canCreateChangeSets: false,
3213
+ canWriteFields: false,
3214
+ canWriteBody: false,
3215
+ canPush: false,
3216
+ canPull: true,
3217
+ canPublish: false,
3218
+ canDelete: false,
3219
+ canStageLocalRevision: false,
3220
+ liveWritesEnabled: false,
3221
+ readOnlyRefresh: true,
3222
+ });
3223
+ }
3183
3224
  return serializeSourceCapabilitiesRecord();
3184
3225
  }
3185
3226
 
@@ -4168,82 +4209,106 @@ export async function importBuilderCmsEntriesAsDatabaseItems(args: {
4168
4209
  const existingTitles = new Set(
4169
4210
  currentItems.map((row) => row.document.title.trim().toLowerCase()),
4170
4211
  );
4171
- const [maxDocPos] = await db
4172
- .select({ max: sql<number>`COALESCE(MAX(position), -1)` })
4173
- .from(schema.documents)
4174
- .where(
4175
- and(
4176
- eq(schema.documents.ownerEmail, args.database.ownerEmail),
4177
- eq(schema.documents.parentId, args.database.documentId),
4178
- ),
4179
- );
4180
- const [maxItemPos] = await db
4181
- .select({ max: sql<number>`COALESCE(MAX(position), -1)` })
4182
- .from(schema.contentDatabaseItems)
4183
- .where(eq(schema.contentDatabaseItems.databaseId, args.database.id));
4184
-
4185
- let nextDocPosition = (maxDocPos?.max ?? -1) + 1;
4186
- let nextItemPosition = (maxItemPos?.max ?? -1) + 1;
4187
- const documentRows: (typeof schema.documents.$inferInsert)[] = [];
4188
- const itemRows: (typeof schema.contentDatabaseItems.$inferInsert)[] = [];
4189
-
4190
- for (const entry of args.entries) {
4191
- if (
4192
- builderCmsEntryAlreadyRepresented({
4193
- entry,
4194
- sourceTable: args.sourceTable,
4195
- existingSourceRows: args.existingSourceRows ?? [],
4196
- })
4197
- ) {
4198
- continue;
4199
- }
4200
4212
 
4201
- const title = entry.title.trim() || entry.id;
4202
- const titleKey = title.toLowerCase();
4203
- if (!args.skipTitleDedup && existingTitles.has(titleKey)) continue;
4204
- existingTitles.add(titleKey);
4205
-
4206
- const documentId = nanoid();
4207
- const itemId = nanoid();
4208
- documentRows.push({
4209
- id: documentId,
4210
- ownerEmail: args.database.ownerEmail,
4211
- orgId: args.database.orgId,
4212
- parentId: args.database.documentId,
4213
- title,
4214
- content: "",
4215
- icon: null,
4216
- position: nextDocPosition++,
4217
- isFavorite: 0,
4218
- hideFromSearch: databaseDocument?.hideFromSearch ?? 0,
4219
- visibility: databaseDocument?.visibility ?? "private",
4220
- createdAt: args.now,
4221
- updatedAt: args.now,
4222
- });
4223
- itemRows.push({
4224
- id: itemId,
4225
- ownerEmail: args.database.ownerEmail,
4226
- orgId: args.database.orgId,
4227
- databaseId: args.database.id,
4228
- documentId,
4229
- position: nextItemPosition++,
4230
- bodyHydrationStatus: "pending",
4231
- bodyHydrationError: null,
4232
- createdAt: args.now,
4233
- updatedAt: args.now,
4234
- });
4235
- importedEntriesByDocumentId.set(documentId, entry);
4236
- }
4237
- await db.transaction(async (tx) => {
4238
- for (const chunk of chunks(documentRows, bulkChunkSizeForColumnCount(13))) {
4239
- await tx.insert(schema.documents).values(chunk);
4240
- }
4241
- for (const chunk of chunks(itemRows, bulkChunkSizeForColumnCount(10))) {
4242
- await tx.insert(schema.contentDatabaseItems).values(chunk);
4243
- }
4244
- });
4213
+ // Reads MAX(position) for both `documents` and `content_database_items`
4214
+ // then batch-inserts at MAX+1.. — serialize the whole read-through-write
4215
+ // span per scope so a concurrent import/add/move targeting the same parent
4216
+ // document or the same database can't read the same MAX (see
4217
+ // _position-utils.ts).
4218
+ return withPositionLock(
4219
+ documentsPositionScope(args.database.ownerEmail, args.database.documentId),
4220
+ () =>
4221
+ withPositionLock(
4222
+ databaseItemsPositionScope(args.database.id),
4223
+ async () => {
4224
+ const [maxDocPos] = await db
4225
+ .select({ max: sql<number>`COALESCE(MAX(position), -1)` })
4226
+ .from(schema.documents)
4227
+ .where(
4228
+ and(
4229
+ eq(schema.documents.ownerEmail, args.database.ownerEmail),
4230
+ eq(schema.documents.parentId, args.database.documentId),
4231
+ ),
4232
+ );
4233
+ const [maxItemPos] = await db
4234
+ .select({ max: sql<number>`COALESCE(MAX(position), -1)` })
4235
+ .from(schema.contentDatabaseItems)
4236
+ .where(
4237
+ eq(schema.contentDatabaseItems.databaseId, args.database.id),
4238
+ );
4245
4239
 
4246
- return { imported: itemRows.length, importedEntriesByDocumentId };
4240
+ let nextDocPosition = (maxDocPos?.max ?? -1) + 1;
4241
+ let nextItemPosition = (maxItemPos?.max ?? -1) + 1;
4242
+ const documentRows: (typeof schema.documents.$inferInsert)[] = [];
4243
+ const itemRows: (typeof schema.contentDatabaseItems.$inferInsert)[] =
4244
+ [];
4245
+
4246
+ for (const entry of args.entries) {
4247
+ if (
4248
+ builderCmsEntryAlreadyRepresented({
4249
+ entry,
4250
+ sourceTable: args.sourceTable,
4251
+ existingSourceRows: args.existingSourceRows ?? [],
4252
+ })
4253
+ ) {
4254
+ continue;
4255
+ }
4256
+
4257
+ const title = entry.title.trim() || entry.id;
4258
+ const titleKey = title.toLowerCase();
4259
+ if (!args.skipTitleDedup && existingTitles.has(titleKey)) continue;
4260
+ existingTitles.add(titleKey);
4261
+
4262
+ const documentId = nanoid();
4263
+ const itemId = nanoid();
4264
+ documentRows.push({
4265
+ id: documentId,
4266
+ ownerEmail: args.database.ownerEmail,
4267
+ orgId: args.database.orgId,
4268
+ parentId: args.database.documentId,
4269
+ title,
4270
+ content: "",
4271
+ icon: null,
4272
+ position: nextDocPosition++,
4273
+ isFavorite: 0,
4274
+ hideFromSearch: databaseDocument?.hideFromSearch ?? 0,
4275
+ visibility: databaseDocument?.visibility ?? "private",
4276
+ createdAt: args.now,
4277
+ updatedAt: args.now,
4278
+ });
4279
+ itemRows.push({
4280
+ id: itemId,
4281
+ ownerEmail: args.database.ownerEmail,
4282
+ orgId: args.database.orgId,
4283
+ databaseId: args.database.id,
4284
+ documentId,
4285
+ position: nextItemPosition++,
4286
+ bodyHydrationStatus: "pending",
4287
+ bodyHydrationError: null,
4288
+ createdAt: args.now,
4289
+ updatedAt: args.now,
4290
+ });
4291
+ importedEntriesByDocumentId.set(documentId, entry);
4292
+ }
4293
+ await db.transaction(async (tx) => {
4294
+ for (const chunk of chunks(
4295
+ documentRows,
4296
+ bulkChunkSizeForColumnCount(13),
4297
+ )) {
4298
+ await tx.insert(schema.documents).values(chunk);
4299
+ }
4300
+ for (const chunk of chunks(
4301
+ itemRows,
4302
+ bulkChunkSizeForColumnCount(10),
4303
+ )) {
4304
+ await tx.insert(schema.contentDatabaseItems).values(chunk);
4305
+ }
4306
+ });
4307
+
4308
+ return { imported: itemRows.length, importedEntriesByDocumentId };
4309
+ },
4310
+ ),
4311
+ );
4247
4312
  }
4248
4313
 
4249
4314
  export async function resyncBuilderCmsSourceSnapshot(args: {
@@ -4825,7 +4890,10 @@ export async function storeSecondarySourceRows(args: {
4825
4890
  sourceDisplayKey:
4826
4891
  entry.title?.trim() || `${args.sourceTable}-${index + 1}`,
4827
4892
  sourceValuesJson: JSON.stringify(entry.sourceValues ?? {}),
4828
- provenance: "secondary source row",
4893
+ provenance:
4894
+ args.sourceType === "notion-database"
4895
+ ? "Notion database read adapter"
4896
+ : "secondary source row",
4829
4897
  syncState: "linked" as const,
4830
4898
  freshness: "fresh" as const,
4831
4899
  lastSyncedAt: args.now,
@@ -4844,6 +4912,7 @@ export async function storeSecondarySourceRows(args: {
4844
4912
  export async function seedSecondarySourceFields(args: {
4845
4913
  sourceId: string;
4846
4914
  ownerEmail: string;
4915
+ sourceType?: ContentDatabaseSourceType;
4847
4916
  modelFields: BuilderCmsModelFieldSummary[];
4848
4917
  sampleEntry?: BuilderCmsSourceEntry;
4849
4918
  now: string;
@@ -4909,7 +4978,10 @@ export async function seedSecondarySourceFields(args: {
4909
4978
  mappingType: "property" as const,
4910
4979
  writeOwner: "source" as const,
4911
4980
  readOnly: 1,
4912
- provenance: "secondary source field",
4981
+ provenance:
4982
+ args.sourceType === "notion-database"
4983
+ ? "Notion database read adapter"
4984
+ : "secondary source field",
4913
4985
  freshness: "fresh" as const,
4914
4986
  lastSyncedAt: args.now,
4915
4987
  createdAt: existing?.createdAt ?? args.now,
@@ -5008,6 +5080,39 @@ export async function updateBuilderCmsSourceReadMetadata(args: {
5008
5080
  .where(eq(schema.contentDatabaseSources.id, args.sourceId));
5009
5081
  }
5010
5082
 
5083
+ export async function updateReadOnlySourceMetadata(args: {
5084
+ sourceId: string;
5085
+ sourceType: ContentDatabaseSourceType;
5086
+ sourceTable: string;
5087
+ fetchedAt: string;
5088
+ now: string;
5089
+ message: string | null;
5090
+ metadata?: Record<string, unknown>;
5091
+ syncState?: ContentDatabaseSourceSyncState;
5092
+ }) {
5093
+ const db = getDb();
5094
+ const [current] = await db
5095
+ .select({ metadataJson: schema.contentDatabaseSources.metadataJson })
5096
+ .from(schema.contentDatabaseSources)
5097
+ .where(eq(schema.contentDatabaseSources.id, args.sourceId))
5098
+ .limit(1);
5099
+ const existing =
5100
+ parseObject<Record<string, unknown>>(current?.metadataJson) ?? {};
5101
+ await db
5102
+ .update(schema.contentDatabaseSources)
5103
+ .set({
5104
+ syncState: args.syncState ?? (args.message ? "refreshing" : "linked"),
5105
+ freshness: args.message ? "stale" : "fresh",
5106
+ capabilitiesJson: sourceCapabilitiesForType(args.sourceType),
5107
+ metadataJson: JSON.stringify({ ...existing, ...(args.metadata ?? {}) }),
5108
+ lastRefreshedAt: args.now,
5109
+ lastSourceUpdatedAt: args.fetchedAt,
5110
+ lastError: null,
5111
+ updatedAt: args.now,
5112
+ })
5113
+ .where(eq(schema.contentDatabaseSources.id, args.sourceId));
5114
+ }
5115
+
5011
5116
  export async function getExistingSource(databaseId: string) {
5012
5117
  const db = getDb();
5013
5118
  const [source] = await db
@@ -5154,26 +5259,31 @@ export async function ensureDatabaseSourceProperty(args: {
5154
5259
  .set({ optionsJson, updatedAt: args.now })
5155
5260
  .where(eq(schema.documentPropertyDefinitions.id, existing.id));
5156
5261
  } else {
5157
- const [maxPos] = await db
5158
- .select({ max: sql<number>`COALESCE(MAX(position), -1)` })
5159
- .from(schema.documentPropertyDefinitions)
5160
- .where(
5161
- eq(schema.documentPropertyDefinitions.databaseId, args.database.id),
5162
- );
5163
5262
  propertyId = crypto.randomUUID();
5164
- await db.insert(schema.documentPropertyDefinitions).values({
5165
- id: propertyId,
5166
- ownerEmail: args.database.ownerEmail,
5167
- orgId: args.database.orgId,
5168
- databaseId: args.database.id,
5169
- name: SOURCE_PROPERTY_NAME,
5170
- type: "select",
5171
- visibility: "always_show",
5172
- optionsJson,
5173
- position: (maxPos?.max ?? -1) + 1,
5174
- createdAt: args.now,
5175
- updatedAt: args.now,
5176
- });
5263
+ await withPositionLock(
5264
+ propertyDefinitionsPositionScope(args.database.id),
5265
+ async () => {
5266
+ const [maxPos] = await db
5267
+ .select({ max: sql<number>`COALESCE(MAX(position), -1)` })
5268
+ .from(schema.documentPropertyDefinitions)
5269
+ .where(
5270
+ eq(schema.documentPropertyDefinitions.databaseId, args.database.id),
5271
+ );
5272
+ await db.insert(schema.documentPropertyDefinitions).values({
5273
+ id: propertyId,
5274
+ ownerEmail: args.database.ownerEmail,
5275
+ orgId: args.database.orgId,
5276
+ databaseId: args.database.id,
5277
+ name: SOURCE_PROPERTY_NAME,
5278
+ type: "select",
5279
+ visibility: "always_show",
5280
+ optionsJson,
5281
+ position: (maxPos?.max ?? -1) + 1,
5282
+ createdAt: args.now,
5283
+ updatedAt: args.now,
5284
+ });
5285
+ },
5286
+ );
5177
5287
  }
5178
5288
 
5179
5289
  // A row's Source value IS its owning source id (= the option id); unsourced
@@ -0,0 +1,216 @@
1
+ import { getRequestUserEmail } from "@agent-native/core/server";
2
+
3
+ import {
4
+ getNotionConnectionForOwner,
5
+ notionFetch,
6
+ } from "../server/lib/notion.js";
7
+ import type {
8
+ BuilderCmsModelFieldSummary,
9
+ DocumentPropertyValue,
10
+ } from "../shared/api.js";
11
+ import type { BuilderCmsSourceEntry } from "./_builder-cms-source-adapter.js";
12
+ import type { ContentDatabaseSourceReadResult } from "./_content-database-source-adapters.js";
13
+
14
+ const NOTION_SOURCE_PAGE_SIZE = 100;
15
+ const NOTION_SOURCE_MAX_PAGES = 5;
16
+
17
+ type NotionProperty = {
18
+ id?: string;
19
+ name?: string;
20
+ type?: string;
21
+ [key: string]: unknown;
22
+ };
23
+
24
+ type NotionPage = {
25
+ object?: string;
26
+ id: string;
27
+ url?: string;
28
+ last_edited_time?: string;
29
+ properties?: Record<string, NotionProperty>;
30
+ };
31
+
32
+ function plainText(value: unknown): string {
33
+ return Array.isArray(value)
34
+ ? value
35
+ .map((part) =>
36
+ typeof part === "object" && part && "plain_text" in part
37
+ ? String((part as { plain_text?: unknown }).plain_text ?? "")
38
+ : "",
39
+ )
40
+ .join("")
41
+ : "";
42
+ }
43
+
44
+ const supportedTypes = new Set([
45
+ "title",
46
+ "rich_text",
47
+ "number",
48
+ "checkbox",
49
+ "url",
50
+ "email",
51
+ "phone_number",
52
+ "date",
53
+ "select",
54
+ "status",
55
+ "multi_select",
56
+ ]);
57
+
58
+ function sourceFieldType(type: string) {
59
+ if (!supportedTypes.has(type)) return `unsupported:${type || "unknown"}`;
60
+ if (type === "rich_text" || type === "title") return "text";
61
+ if (type === "phone_number") return "phone";
62
+ return type;
63
+ }
64
+
65
+ function propertyValue(property: NotionProperty): DocumentPropertyValue {
66
+ const type = property.type ?? "unknown";
67
+ if (!supportedTypes.has(type)) {
68
+ return `[Unsupported Notion property: ${type}]`;
69
+ }
70
+ if (type === "title" || type === "rich_text")
71
+ return plainText(property[type]);
72
+ if (type === "number")
73
+ return typeof property.number === "number" ? property.number : null;
74
+ if (type === "checkbox") return property.checkbox === true;
75
+ if (type === "url" || type === "email" || type === "phone_number") {
76
+ const value = property[type];
77
+ return typeof value === "string" ? value : null;
78
+ }
79
+ if (type === "date") {
80
+ const date = property.date as
81
+ | { start?: unknown; end?: unknown }
82
+ | null
83
+ | undefined;
84
+ return typeof date?.start === "string"
85
+ ? {
86
+ start: date.start,
87
+ end: typeof date.end === "string" ? date.end : null,
88
+ includeTime: date.start.includes("T"),
89
+ }
90
+ : null;
91
+ }
92
+ if (type === "select" || type === "status") {
93
+ const option = property[type] as { name?: unknown } | null | undefined;
94
+ return typeof option?.name === "string" ? option.name : null;
95
+ }
96
+ if (type === "multi_select") {
97
+ return Array.isArray(property.multi_select)
98
+ ? property.multi_select.flatMap((option) =>
99
+ typeof option === "object" &&
100
+ option &&
101
+ "name" in option &&
102
+ typeof (option as { name?: unknown }).name === "string"
103
+ ? [(option as { name: string }).name]
104
+ : [],
105
+ )
106
+ : [];
107
+ }
108
+ return null;
109
+ }
110
+
111
+ function titleFromProperties(properties: Record<string, NotionProperty>) {
112
+ const title = Object.values(properties).find(
113
+ (property) => property.type === "title",
114
+ );
115
+ return title ? plainText(title.title) || "Untitled" : "Untitled";
116
+ }
117
+
118
+ export async function readNotionDatabaseSource(args: {
119
+ sourceTable: string;
120
+ limit: number;
121
+ offset: number;
122
+ fullRefresh?: boolean;
123
+ }): Promise<ContentDatabaseSourceReadResult> {
124
+ if (args.offset !== 0) {
125
+ throw new Error(
126
+ "Notion database sources use cursor pagination; numeric offsets are not supported.",
127
+ );
128
+ }
129
+ const userEmail = getRequestUserEmail();
130
+ if (!userEmail)
131
+ throw new Error("Notion database access requires a signed-in user.");
132
+ const connection = await getNotionConnectionForOwner(userEmail);
133
+ if (!connection)
134
+ throw new Error("Connect Notion before attaching a Notion database.");
135
+
136
+ const dataSource = await notionFetch<{
137
+ id: string;
138
+ title?: Array<{ plain_text?: string }>;
139
+ properties?: Record<string, NotionProperty>;
140
+ }>(
141
+ `/data_sources/${encodeURIComponent(args.sourceTable)}`,
142
+ connection.accessToken,
143
+ );
144
+ const fields: BuilderCmsModelFieldSummary[] = Object.entries(
145
+ dataSource.properties ?? {},
146
+ ).map(([name, property]) => ({
147
+ name: property.id || name,
148
+ label: property.name || name,
149
+ type: sourceFieldType(property.type ?? "unknown"),
150
+ required: false,
151
+ }));
152
+
153
+ const entries: BuilderCmsSourceEntry[] = [];
154
+ let cursor: string | undefined;
155
+ let hasMore = false;
156
+ const maxPages = args.fullRefresh ? NOTION_SOURCE_MAX_PAGES : 1;
157
+ for (let page = 0; page < maxPages; page++) {
158
+ const response = await notionFetch<{
159
+ results?: NotionPage[];
160
+ has_more?: boolean;
161
+ next_cursor?: string | null;
162
+ }>(
163
+ `/data_sources/${encodeURIComponent(args.sourceTable)}/query`,
164
+ connection.accessToken,
165
+ {
166
+ method: "POST",
167
+ body: JSON.stringify({
168
+ page_size: Math.min(NOTION_SOURCE_PAGE_SIZE, args.limit),
169
+ ...(cursor ? { start_cursor: cursor } : {}),
170
+ }),
171
+ },
172
+ );
173
+ for (const result of response.results ?? []) {
174
+ if (result.object && result.object !== "page") continue;
175
+ const properties = result.properties ?? {};
176
+ entries.push({
177
+ id: result.id,
178
+ model: args.sourceTable,
179
+ title: titleFromProperties(properties),
180
+ urlPath: result.url ?? "",
181
+ updatedAt: result.last_edited_time ?? "",
182
+ sourceValues: Object.fromEntries(
183
+ Object.entries(properties).map(([name, property]) => [
184
+ property.id || name,
185
+ propertyValue(property),
186
+ ]),
187
+ ),
188
+ });
189
+ }
190
+ hasMore = response.has_more === true && !!response.next_cursor;
191
+ cursor = response.next_cursor ?? undefined;
192
+ if (!hasMore || entries.length >= args.limit) break;
193
+ }
194
+
195
+ const fetchedAt = new Date().toISOString();
196
+ return {
197
+ state: "live",
198
+ entries: entries.slice(0, args.limit),
199
+ fields,
200
+ fetchedAt,
201
+ message: hasMore
202
+ ? `Loaded a bounded Notion snapshot; more rows remain after cursor ${cursor ? "present" : "missing"}.`
203
+ : null,
204
+ metadata: {
205
+ provider: "notion",
206
+ dataSourceId: dataSource.id,
207
+ dataSourceName: plainText(dataSource.title) || "Notion database",
208
+ hasMore,
209
+ nextCursor: cursor ?? null,
210
+ fetchedRowCount: Math.min(entries.length, args.limit),
211
+ unsupportedPropertyCount: fields.filter((field) =>
212
+ field.type.startsWith("unsupported:"),
213
+ ).length,
214
+ },
215
+ };
216
+ }
@@ -0,0 +1,76 @@
1
+ // Serializes "read MAX(position) among siblings, then insert/update at
2
+ // MAX+1" sequences so two concurrent writers (agent + human, two browser
3
+ // tabs, an import job racing a manual add) never read the same MAX and
4
+ // persist duplicate `position` values. `position` columns in this app have
5
+ // no unique/sequence constraint (see templates/content/server/db/schema.ts),
6
+ // so ordering is only as stable as the read-then-insert stays atomic.
7
+ //
8
+ // Mirrors the per-deck lock in templates/slides/actions/patch-deck.ts: an
9
+ // in-process, globalThis-keyed promise chain per scope key. This only
10
+ // serializes writers within THIS process, but that is sufficient here
11
+ // because every write path already goes through the app's own action/route
12
+ // layer in the same server process — it is not a substitute for a DB-level
13
+ // unique constraint, but it closes the read-then-write race that produces
14
+ // duplicate positions today.
15
+ //
16
+ // Sites that already compute MAX(position) and insert inside the SAME
17
+ // `db.transaction()` (e.g. submit-content-database-form.ts,
18
+ // add-content-database-source-field-property.ts) do not need this helper —
19
+ // wrap new sites in the lock instead of introducing a second mechanism.
20
+
21
+ const LOCK_KEY = "__contentPositionLocks" as const;
22
+ type GlobalWithLocks = typeof globalThis & {
23
+ [LOCK_KEY]?: Map<string, Promise<unknown>>;
24
+ };
25
+ const globalRef = globalThis as GlobalWithLocks;
26
+ if (!globalRef[LOCK_KEY]) {
27
+ globalRef[LOCK_KEY] = new Map<string, Promise<unknown>>();
28
+ }
29
+ const positionLocks: Map<string, Promise<unknown>> = globalRef[LOCK_KEY]!;
30
+
31
+ /** Run `fn` serialized against any other call using the same `scopeKey`. */
32
+ export function withPositionLock<T>(
33
+ scopeKey: string,
34
+ fn: () => Promise<T>,
35
+ ): Promise<T> {
36
+ const prev = positionLocks.get(scopeKey) ?? Promise.resolve();
37
+ const next = prev.then(fn, fn);
38
+ positionLocks.set(scopeKey, next);
39
+ next
40
+ .finally(() => {
41
+ if (positionLocks.get(scopeKey) === next) positionLocks.delete(scopeKey);
42
+ })
43
+ .catch(() => {});
44
+ return next;
45
+ }
46
+
47
+ /**
48
+ * Lock scope for `documents.position` siblings under one owner + parent
49
+ * (or one owner's root level when `parentId` is null/undefined). Every call
50
+ * site that computes MAX(position) over `documents` scoped by
51
+ * (ownerEmail, parentId) should share this key so concurrent inserts under
52
+ * the same parent — regardless of which action/route triggers them — are
53
+ * serialized against each other.
54
+ */
55
+ export function documentsPositionScope(
56
+ ownerEmail: string,
57
+ parentId: string | null | undefined,
58
+ ): string {
59
+ return `documents:${ownerEmail}:${parentId ?? "root"}`;
60
+ }
61
+
62
+ /**
63
+ * Lock scope for `content_database_items.position` rows within one
64
+ * database.
65
+ */
66
+ export function databaseItemsPositionScope(databaseId: string): string {
67
+ return `contentDatabaseItems:${databaseId}`;
68
+ }
69
+
70
+ /**
71
+ * Lock scope for `document_property_definitions.position` rows within one
72
+ * database.
73
+ */
74
+ export function propertyDefinitionsPositionScope(databaseId: string): string {
75
+ return `documentPropertyDefinitions:${databaseId}`;
76
+ }