@agent-native/core 0.117.2 → 0.118.1

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 (776) hide show
  1. package/corpus/README.md +3 -3
  2. package/corpus/core/CHANGELOG.md +51 -0
  3. package/corpus/core/docs/content/custom-design-system.mdx +445 -0
  4. package/corpus/core/docs/content/toolkit-ui.mdx +16 -0
  5. package/corpus/core/package.json +1 -1
  6. package/corpus/core/src/a2a/client.ts +32 -10
  7. package/corpus/core/src/a2a/types.ts +1 -0
  8. package/corpus/core/src/agent/types.ts +1 -1
  9. package/corpus/core/src/cli/index.ts +18 -10
  10. package/corpus/core/src/cli/multi-frontier-runs.ts +5 -3
  11. package/corpus/core/src/cli/templates-meta.ts +0 -12
  12. package/corpus/core/src/cli/workspace-dev.ts +1 -1
  13. package/corpus/core/src/client/AgentPanel.tsx +99 -108
  14. package/corpus/core/src/client/AssistantChat.tsx +30 -13
  15. package/corpus/core/src/client/CommandMenu.tsx +68 -178
  16. package/corpus/core/src/client/RunStuckBanner.tsx +39 -47
  17. package/corpus/core/src/client/agent-chat-adapter.ts +3 -1
  18. package/corpus/core/src/client/app-providers.tsx +1 -1
  19. package/corpus/core/src/client/chat/action-chat-ui-surface.tsx +32 -0
  20. package/corpus/core/src/client/chat/message-components.tsx +169 -18
  21. package/corpus/core/src/client/chat/tool-call-display.tsx +50 -14
  22. package/corpus/core/src/client/chat/widgets/InlineExtensionWidget.tsx +13 -40
  23. package/corpus/core/src/client/chat/widgets/builtin-tool-renderers.tsx +7 -3
  24. package/corpus/core/src/client/components/ui/message-scroller.tsx +15 -1
  25. package/corpus/core/src/client/conversation/AgentConversation.tsx +12 -1
  26. package/corpus/core/src/client/error-capture.ts +2 -6
  27. package/corpus/core/src/client/i18n.tsx +22 -0
  28. package/corpus/core/src/client/index.ts +17 -0
  29. package/corpus/core/src/client/org/TeamPage.tsx +257 -153
  30. package/corpus/core/src/client/progress/RunsTray.tsx +89 -88
  31. package/corpus/core/src/client/resources/McpConnectionSuggestion.tsx +25 -16
  32. package/corpus/core/src/client/resources/McpIntegrationDialog.tsx +34 -13
  33. package/corpus/core/src/client/resources/ResourcesPanel.tsx +5 -1
  34. package/corpus/core/src/client/resources/mcp-integration-catalog.ts +20 -17
  35. package/corpus/core/src/client/resources/mcp-integration-logos.ts +2 -2
  36. package/corpus/core/src/client/route-chunk-recovery.ts +5 -3
  37. package/corpus/core/src/client/session-replay.ts +3 -3
  38. package/corpus/core/src/client/settings/DemoModeSection.tsx +6 -18
  39. package/corpus/core/src/client/settings/SecretsSection.tsx +97 -52
  40. package/corpus/core/src/client/settings/SettingsPanel.tsx +217 -252
  41. package/corpus/core/src/client/settings/SettingsTabsPage.tsx +23 -2
  42. package/corpus/core/src/client/settings/VoiceTranscriptionSection.tsx +7 -18
  43. package/corpus/core/src/client/settings/index.ts +7 -0
  44. package/corpus/core/src/client/settings/useSettingsPanelController.ts +155 -0
  45. package/corpus/core/src/client/setup-connections/BuilderConnectCard.tsx +116 -51
  46. package/corpus/core/src/client/setup-connections/index.ts +10 -0
  47. package/corpus/core/src/client/setup-connections/useBuilderConnectCardController.ts +82 -0
  48. package/corpus/core/src/client/sharing/ShareButton.tsx +64 -586
  49. package/corpus/core/src/client/sharing/ShareDialog.tsx +299 -633
  50. package/corpus/core/src/client/sharing/index.ts +42 -0
  51. package/corpus/core/src/client/sharing/share-controller-helpers.ts +318 -0
  52. package/corpus/core/src/client/sharing/useShareButtonController.ts +600 -0
  53. package/corpus/core/src/client/sharing/useShareDialogController.ts +559 -0
  54. package/corpus/core/src/client/sse-event-processor.ts +23 -0
  55. package/corpus/core/src/client/tool-display.ts +71 -0
  56. package/corpus/core/src/client/transcription/use-live-transcription.ts +64 -45
  57. package/corpus/core/src/client/use-agent-engine-configured.ts +12 -19
  58. package/corpus/core/src/client/use-chat-threads.ts +33 -12
  59. package/corpus/core/src/deploy/build.ts +2 -1
  60. package/corpus/core/src/guards/db-tool-scoping.ts +1 -1
  61. package/corpus/core/src/integrations/adapters/slack.ts +84 -5
  62. package/corpus/core/src/integrations/index.ts +5 -1
  63. package/corpus/core/src/localization/default-messages.ts +6 -1
  64. package/corpus/core/src/mcp-client/oauth-routes.ts +61 -14
  65. package/corpus/core/src/scripts/call-agent.ts +5 -1
  66. package/corpus/core/src/server/realtime-voice.ts +5 -2
  67. package/corpus/core/src/styles/agent-native.css +0 -24
  68. package/corpus/core/src/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  69. package/corpus/core/src/templates/default/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  70. package/corpus/core/src/templates/default/.agents/skills/frontend-design/SKILL.md +22 -2
  71. package/corpus/core/src/templates/default/.agents/skills/shadcn-ui/SKILL.md +28 -4
  72. package/corpus/core/src/templates/default/app/components/ui/toolkit-provider.tsx +10 -0
  73. package/corpus/core/src/templates/default/app/design-system.ts +3 -0
  74. package/corpus/core/src/templates/default/app/root.tsx +5 -2
  75. package/corpus/core/src/templates/default/package.json +1 -0
  76. package/corpus/core/src/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  77. package/corpus/core/src/templates/headless/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  78. package/corpus/core/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  79. package/corpus/core/src/templates/workspace-core/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  80. package/corpus/core/src/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +22 -2
  81. package/corpus/core/src/templates/workspace-core/.agents/skills/shadcn-ui/SKILL.md +28 -4
  82. package/corpus/core/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +7 -5
  83. package/corpus/core/src/terminal/cli-registry.ts +1 -1
  84. package/corpus/core/src/vite/client.ts +205 -1
  85. package/corpus/templates/analytics/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  86. package/corpus/templates/analytics/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  87. package/corpus/templates/analytics/.agents/skills/frontend-design/SKILL.md +22 -2
  88. package/corpus/templates/analytics/.agents/skills/shadcn-ui/SKILL.md +28 -4
  89. package/corpus/templates/analytics/app/components/ui/toolkit-provider.tsx +6 -1
  90. package/corpus/templates/analytics/app/design-system.ts +3 -0
  91. package/corpus/templates/analytics/app/hooks/use-dashboard-views.ts +13 -1
  92. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/ViewsMenu.tsx +10 -0
  93. package/corpus/templates/analytics/changelog/2026-07-22-show-a-useful-error-when-saving-a-dashboard-view-fails.md +6 -0
  94. package/corpus/templates/assets/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  95. package/corpus/templates/assets/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  96. package/corpus/templates/assets/.agents/skills/frontend-design/SKILL.md +22 -2
  97. package/corpus/templates/assets/.agents/skills/shadcn-ui/SKILL.md +28 -4
  98. package/corpus/templates/assets/app/components/ui/alert.tsx +1 -0
  99. package/corpus/templates/assets/app/components/ui/toolkit-provider.tsx +6 -1
  100. package/corpus/templates/assets/app/design-system.ts +3 -0
  101. package/corpus/templates/assets/app/routes/brand-kits.$id_.presets.$presetId.tsx +26 -30
  102. package/corpus/templates/brain/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  103. package/corpus/templates/brain/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  104. package/corpus/templates/brain/.agents/skills/frontend-design/SKILL.md +22 -2
  105. package/corpus/templates/brain/.agents/skills/shadcn-ui/SKILL.md +28 -4
  106. package/corpus/templates/brain/app/components/ui/dialog.tsx +1 -0
  107. package/corpus/templates/brain/app/components/ui/toolkit-provider.tsx +6 -1
  108. package/corpus/templates/brain/app/design-system.ts +3 -0
  109. package/corpus/templates/brain/app/routes/sources.tsx +189 -122
  110. package/corpus/templates/calendar/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  111. package/corpus/templates/calendar/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  112. package/corpus/templates/calendar/.agents/skills/frontend-design/SKILL.md +22 -2
  113. package/corpus/templates/calendar/.agents/skills/shadcn-ui/SKILL.md +28 -4
  114. package/corpus/templates/calendar/app/components/ui/toolkit-provider.tsx +6 -1
  115. package/corpus/templates/calendar/app/design-system.ts +3 -0
  116. package/corpus/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  117. package/corpus/templates/chat/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  118. package/corpus/templates/chat/.agents/skills/frontend-design/SKILL.md +22 -2
  119. package/corpus/templates/chat/.agents/skills/shadcn-ui/SKILL.md +28 -4
  120. package/corpus/templates/chat/app/components/ui/toolkit-provider.tsx +6 -1
  121. package/corpus/templates/chat/app/design-system.ts +3 -0
  122. package/corpus/templates/clips/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  123. package/corpus/templates/clips/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  124. package/corpus/templates/clips/.agents/skills/frontend-design/SKILL.md +22 -2
  125. package/corpus/templates/clips/.agents/skills/shadcn-ui/SKILL.md +28 -4
  126. package/corpus/templates/clips/AGENTS.md +8 -6
  127. package/corpus/templates/clips/actions/request-transcript.ts +90 -420
  128. package/corpus/templates/clips/actions/save-browser-transcript.ts +11 -28
  129. package/corpus/templates/clips/app/components/capture-install-options.tsx +5 -3
  130. package/corpus/templates/clips/app/components/player/transcript-panel.tsx +3 -136
  131. package/corpus/templates/clips/app/components/ui/toolkit-provider.tsx +6 -1
  132. package/corpus/templates/clips/app/design-system.ts +3 -0
  133. package/corpus/templates/clips/app/lib/capture-install-options.ts +15 -4
  134. package/corpus/templates/clips/app/routes/_app.library._index.tsx +1 -1
  135. package/corpus/templates/clips/app/routes/download.tsx +3 -2
  136. package/corpus/templates/clips/changelog/2026-07-22-fixed-an-intermittent-hydration-error-on-the-download-page.md +6 -0
  137. package/corpus/templates/clips/changelog/2026-07-22-fixed-transcript-generation-when-native-speech-capture-retur.md +6 -0
  138. package/corpus/templates/clips/changelog/2026-07-22-fixed-windows-recordings-aborting-during-the-countdown.md +6 -0
  139. package/corpus/templates/clips/changelog/2026-07-22-permissions-and-rewind-controls-are-now-grouped-with-clearer.md +6 -0
  140. package/corpus/templates/clips/changelog/2026-07-22-the-desktop-popover-opens-centered-under-the-menu-bar-icon-o.md +6 -0
  141. package/corpus/templates/clips/changelog/2026-07-22-transcript-generation-now-retries-with-backup-speech-recogni.md +6 -0
  142. package/corpus/templates/clips/desktop/src/app.tsx +86 -82
  143. package/corpus/templates/clips/desktop/src/styles.css +13 -0
  144. package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +10 -1
  145. package/corpus/templates/clips/desktop/src-tauri/src/shortcuts.rs +37 -2
  146. package/corpus/templates/clips/desktop/src-tauri/src/tray.rs +18 -0
  147. package/corpus/templates/clips/server/lib/public-agent-context.ts +2 -2
  148. package/corpus/templates/clips/server/register-secrets.ts +7 -6
  149. package/corpus/templates/content/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  150. package/corpus/templates/content/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  151. package/corpus/templates/content/.agents/skills/frontend-design/SKILL.md +22 -2
  152. package/corpus/templates/content/.agents/skills/shadcn-ui/SKILL.md +28 -4
  153. package/corpus/templates/content/app/components/editor/DocumentToolbar.tsx +41 -59
  154. package/corpus/templates/content/app/components/editor/SlashCommandMenu.tsx +16 -84
  155. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +13 -7
  156. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +41 -41
  157. package/corpus/templates/content/app/components/editor/database/GalleryView.tsx +2 -1
  158. package/corpus/templates/content/app/components/editor/database/ListView.tsx +2 -1
  159. package/corpus/templates/content/app/components/editor/database/TimelineView.tsx +9 -9
  160. package/corpus/templates/content/app/components/editor/database/sidebar.tsx +7 -7
  161. package/corpus/templates/content/app/components/editor/database-sources/BuilderSourceReviewDialog.tsx +11 -11
  162. package/corpus/templates/content/app/components/editor/extensions/ImageBlock.tsx +2 -2
  163. package/corpus/templates/content/app/components/ui/checkbox.tsx +1 -0
  164. package/corpus/templates/content/app/components/ui/collapsible.tsx +1 -0
  165. package/corpus/templates/content/app/components/ui/toolkit-provider.tsx +6 -1
  166. package/corpus/templates/content/app/design-system.ts +3 -0
  167. package/corpus/templates/content/app/global.css +29 -3
  168. package/corpus/templates/content/app/i18n-data.ts +10 -0
  169. package/corpus/templates/content/changelog/2026-07-22-page-actions-are-grouped-into-the-three-dots-menu-for-a-clea.md +6 -0
  170. package/corpus/templates/design/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  171. package/corpus/templates/design/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  172. package/corpus/templates/design/.agents/skills/frontend-design/SKILL.md +22 -2
  173. package/corpus/templates/design/.agents/skills/shadcn-ui/SKILL.md +28 -4
  174. package/corpus/templates/design/app/components/editor/PromptDialog.tsx +8 -8
  175. package/corpus/templates/design/app/components/ui/command.tsx +1 -0
  176. package/corpus/templates/design/app/components/ui/toolkit-provider.tsx +6 -1
  177. package/corpus/templates/design/app/design-system.ts +3 -0
  178. package/corpus/templates/design/server/lib/fig-file-decoder.ts +1 -0
  179. package/corpus/templates/dispatch/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  180. package/corpus/templates/dispatch/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  181. package/corpus/templates/dispatch/.agents/skills/frontend-design/SKILL.md +22 -2
  182. package/corpus/templates/dispatch/.agents/skills/shadcn-ui/SKILL.md +28 -4
  183. package/corpus/templates/dispatch/app/components/ui/button.tsx +1 -0
  184. package/corpus/templates/dispatch/app/components/ui/toolkit-provider.tsx +17 -0
  185. package/corpus/templates/dispatch/app/design-system.ts +3 -0
  186. package/corpus/templates/dispatch/app/root.tsx +10 -7
  187. package/corpus/templates/dispatch/package.json +1 -0
  188. package/corpus/templates/forms/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  189. package/corpus/templates/forms/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  190. package/corpus/templates/forms/.agents/skills/frontend-design/SKILL.md +22 -2
  191. package/corpus/templates/forms/.agents/skills/shadcn-ui/SKILL.md +28 -4
  192. package/corpus/templates/forms/app/components/ui/toolkit-provider.tsx +6 -1
  193. package/corpus/templates/forms/app/design-system.ts +3 -0
  194. package/corpus/templates/macros/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  195. package/corpus/templates/macros/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  196. package/corpus/templates/macros/.agents/skills/frontend-design/SKILL.md +22 -2
  197. package/corpus/templates/macros/.agents/skills/shadcn-ui/SKILL.md +28 -4
  198. package/corpus/templates/macros/app/components/ui/toolkit-provider.tsx +6 -1
  199. package/corpus/templates/macros/app/design-system.ts +3 -0
  200. package/corpus/templates/mail/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  201. package/corpus/templates/mail/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  202. package/corpus/templates/mail/.agents/skills/frontend-design/SKILL.md +22 -2
  203. package/corpus/templates/mail/.agents/skills/shadcn-ui/SKILL.md +28 -4
  204. package/corpus/templates/mail/app/components/email/EmailThread.tsx +134 -93
  205. package/corpus/templates/mail/app/components/email/email-iframe-document.ts +8 -2
  206. package/corpus/templates/mail/app/components/ui/textarea.tsx +1 -21
  207. package/corpus/templates/mail/app/components/ui/toolkit-provider.tsx +6 -1
  208. package/corpus/templates/mail/app/design-system.ts +3 -0
  209. package/corpus/templates/mail/changelog/2026-07-22-email-messages-no-longer-flash-with-an-unstyled-page-while-l.md +6 -0
  210. package/corpus/templates/plan/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  211. package/corpus/templates/plan/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  212. package/corpus/templates/plan/.agents/skills/frontend-design/SKILL.md +22 -2
  213. package/corpus/templates/plan/.agents/skills/shadcn-ui/SKILL.md +28 -4
  214. package/corpus/templates/plan/app/components/plan/PlanContentRenderer.tsx +1 -1
  215. package/corpus/templates/plan/app/components/ui/toolkit-provider.tsx +6 -1
  216. package/corpus/templates/plan/app/design-system.ts +3 -0
  217. package/corpus/templates/plan/app/entry.client.tsx +3 -0
  218. package/corpus/templates/plan/changelog/2026-07-22-recover-stale-pages-after-a-deployment-refresh-automatically.md +6 -0
  219. package/corpus/templates/slides/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  220. package/corpus/templates/slides/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  221. package/corpus/templates/slides/.agents/skills/frontend-design/SKILL.md +22 -2
  222. package/corpus/templates/slides/.agents/skills/shadcn-ui/SKILL.md +28 -4
  223. package/corpus/templates/slides/app/components/ui/toolkit-provider.tsx +6 -1
  224. package/corpus/templates/slides/app/design-system.ts +3 -0
  225. package/corpus/templates/slides/app/hooks/use-agent-generating.ts +10 -3
  226. package/corpus/templates/slides/app/pages/Index.tsx +1 -0
  227. package/corpus/templates/slides/changelog/2026-07-22-new-deck-prompts-now-open-in-their-own-visible-chat-thread.md +6 -0
  228. package/corpus/templates/tasks/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  229. package/corpus/templates/tasks/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  230. package/corpus/templates/tasks/.agents/skills/frontend-design/SKILL.md +22 -2
  231. package/corpus/templates/tasks/.agents/skills/shadcn-ui/SKILL.md +28 -4
  232. package/corpus/templates/tasks/app/components/ui/toolkit-provider.tsx +17 -0
  233. package/corpus/templates/tasks/app/design-system.ts +3 -0
  234. package/corpus/templates/tasks/app/root.tsx +5 -2
  235. package/corpus/toolkit/CHANGELOG.md +23 -0
  236. package/corpus/toolkit/agent-native.eject.json +71 -0
  237. package/corpus/toolkit/package.json +31 -1
  238. package/corpus/toolkit/src/chat-history/ChatHistoryRail.tsx +100 -39
  239. package/corpus/toolkit/src/chat-history/index.ts +9 -0
  240. package/corpus/toolkit/src/chat-history/useChatHistoryRailController.ts +76 -0
  241. package/corpus/toolkit/src/chat-history.css +2 -0
  242. package/corpus/toolkit/src/composer/ComposerPlusMenu.tsx +11 -8
  243. package/corpus/toolkit/src/composer/PromptComposer.tsx +32 -7
  244. package/corpus/toolkit/src/composer/RealtimeVoiceMode.tsx +119 -31
  245. package/corpus/toolkit/src/composer/useRealtimeVoiceMode.tsx +124 -9
  246. package/corpus/toolkit/src/conformance/__fixtures__/css-in-js-adapter.tsx +401 -0
  247. package/corpus/toolkit/src/conformance/index.ts +3 -0
  248. package/corpus/toolkit/src/conformance/runner.tsx +862 -0
  249. package/corpus/toolkit/src/conformance/types.ts +45 -0
  250. package/corpus/toolkit/src/conformance/version.ts +22 -0
  251. package/corpus/toolkit/src/design-system/components.tsx +146 -0
  252. package/corpus/toolkit/src/design-system/context.tsx +31 -0
  253. package/corpus/toolkit/src/design-system/default-adapter.tsx +915 -0
  254. package/corpus/toolkit/src/design-system/definition.ts +14 -0
  255. package/corpus/toolkit/src/design-system/error-boundary.tsx +37 -0
  256. package/corpus/toolkit/src/design-system/index.ts +7 -0
  257. package/corpus/toolkit/src/design-system/theme.ts +145 -0
  258. package/corpus/toolkit/src/design-system/types.ts +363 -0
  259. package/corpus/toolkit/src/editor/SharedRichEditor.tsx +13 -2
  260. package/corpus/toolkit/src/editor/SlashCommandMenu.tsx +20 -0
  261. package/corpus/toolkit/src/editor/index.ts +2 -0
  262. package/corpus/toolkit/src/provider.tsx +98 -5
  263. package/corpus/toolkit/src/styles.css +52 -23
  264. package/corpus/toolkit/src/ui/button.tsx +95 -6
  265. package/corpus/toolkit/src/ui/dialog.tsx +48 -23
  266. package/corpus/toolkit/src/ui/popover.tsx +16 -2
  267. package/corpus/toolkit/src/ui/select.tsx +7 -3
  268. package/corpus/toolkit/src/ui/sidebar.tsx +47 -48
  269. package/corpus/toolkit/src/ui/tooltip.tsx +7 -3
  270. package/dist/a2a/client.d.ts +3 -1
  271. package/dist/a2a/client.d.ts.map +1 -1
  272. package/dist/a2a/client.js +17 -12
  273. package/dist/a2a/client.js.map +1 -1
  274. package/dist/a2a/types.d.ts +1 -1
  275. package/dist/a2a/types.d.ts.map +1 -1
  276. package/dist/a2a/types.js.map +1 -1
  277. package/dist/agent/types.d.ts +1 -1
  278. package/dist/agent/types.d.ts.map +1 -1
  279. package/dist/agent/types.js.map +1 -1
  280. package/dist/cli/index.js +18 -8
  281. package/dist/cli/index.js.map +1 -1
  282. package/dist/cli/multi-frontier-runs.js +3 -3
  283. package/dist/cli/multi-frontier-runs.js.map +1 -1
  284. package/dist/cli/templates-meta.d.ts.map +1 -1
  285. package/dist/cli/templates-meta.js +0 -12
  286. package/dist/cli/templates-meta.js.map +1 -1
  287. package/dist/cli/workspace-dev.js +1 -1
  288. package/dist/cli/workspace-dev.js.map +1 -1
  289. package/dist/client/AgentPanel.d.ts.map +1 -1
  290. package/dist/client/AgentPanel.js +18 -15
  291. package/dist/client/AgentPanel.js.map +1 -1
  292. package/dist/client/AssistantChat.d.ts.map +1 -1
  293. package/dist/client/AssistantChat.js +26 -15
  294. package/dist/client/AssistantChat.js.map +1 -1
  295. package/dist/client/CommandMenu.d.ts.map +1 -1
  296. package/dist/client/CommandMenu.js +18 -106
  297. package/dist/client/CommandMenu.js.map +1 -1
  298. package/dist/client/RunStuckBanner.d.ts +3 -5
  299. package/dist/client/RunStuckBanner.d.ts.map +1 -1
  300. package/dist/client/RunStuckBanner.js +17 -18
  301. package/dist/client/RunStuckBanner.js.map +1 -1
  302. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  303. package/dist/client/agent-chat-adapter.js +2 -1
  304. package/dist/client/agent-chat-adapter.js.map +1 -1
  305. package/dist/client/app-providers.d.ts.map +1 -1
  306. package/dist/client/app-providers.js +1 -1
  307. package/dist/client/app-providers.js.map +1 -1
  308. package/dist/client/chat/action-chat-ui-surface.d.ts +8 -0
  309. package/dist/client/chat/action-chat-ui-surface.d.ts.map +1 -0
  310. package/dist/client/chat/action-chat-ui-surface.js +10 -0
  311. package/dist/client/chat/action-chat-ui-surface.js.map +1 -0
  312. package/dist/client/chat/message-components.d.ts +12 -1
  313. package/dist/client/chat/message-components.d.ts.map +1 -1
  314. package/dist/client/chat/message-components.js +102 -14
  315. package/dist/client/chat/message-components.js.map +1 -1
  316. package/dist/client/chat/tool-call-display.d.ts +3 -1
  317. package/dist/client/chat/tool-call-display.d.ts.map +1 -1
  318. package/dist/client/chat/tool-call-display.js +33 -14
  319. package/dist/client/chat/tool-call-display.js.map +1 -1
  320. package/dist/client/chat/widgets/InlineExtensionWidget.d.ts.map +1 -1
  321. package/dist/client/chat/widgets/InlineExtensionWidget.js +9 -12
  322. package/dist/client/chat/widgets/InlineExtensionWidget.js.map +1 -1
  323. package/dist/client/chat/widgets/builtin-tool-renderers.d.ts.map +1 -1
  324. package/dist/client/chat/widgets/builtin-tool-renderers.js +5 -3
  325. package/dist/client/chat/widgets/builtin-tool-renderers.js.map +1 -1
  326. package/dist/client/components/ui/message-scroller.d.ts +1 -1
  327. package/dist/client/components/ui/message-scroller.d.ts.map +1 -1
  328. package/dist/client/components/ui/message-scroller.js +4 -3
  329. package/dist/client/components/ui/message-scroller.js.map +1 -1
  330. package/dist/client/conversation/AgentConversation.d.ts.map +1 -1
  331. package/dist/client/conversation/AgentConversation.js +3 -2
  332. package/dist/client/conversation/AgentConversation.js.map +1 -1
  333. package/dist/client/error-capture.d.ts.map +1 -1
  334. package/dist/client/error-capture.js +2 -2
  335. package/dist/client/error-capture.js.map +1 -1
  336. package/dist/client/i18n.d.ts.map +1 -1
  337. package/dist/client/i18n.js +9 -0
  338. package/dist/client/i18n.js.map +1 -1
  339. package/dist/client/index.d.ts +2 -2
  340. package/dist/client/index.d.ts.map +1 -1
  341. package/dist/client/index.js +2 -2
  342. package/dist/client/index.js.map +1 -1
  343. package/dist/client/org/TeamPage.d.ts.map +1 -1
  344. package/dist/client/org/TeamPage.js +41 -33
  345. package/dist/client/org/TeamPage.js.map +1 -1
  346. package/dist/client/progress/RunsTray.d.ts.map +1 -1
  347. package/dist/client/progress/RunsTray.js +9 -10
  348. package/dist/client/progress/RunsTray.js.map +1 -1
  349. package/dist/client/resources/McpConnectionSuggestion.d.ts +1 -0
  350. package/dist/client/resources/McpConnectionSuggestion.d.ts.map +1 -1
  351. package/dist/client/resources/McpConnectionSuggestion.js +14 -12
  352. package/dist/client/resources/McpConnectionSuggestion.js.map +1 -1
  353. package/dist/client/resources/McpIntegrationDialog.d.ts.map +1 -1
  354. package/dist/client/resources/McpIntegrationDialog.js +10 -8
  355. package/dist/client/resources/McpIntegrationDialog.js.map +1 -1
  356. package/dist/client/resources/ResourcesPanel.d.ts.map +1 -1
  357. package/dist/client/resources/ResourcesPanel.js +3 -1
  358. package/dist/client/resources/ResourcesPanel.js.map +1 -1
  359. package/dist/client/resources/mcp-integration-catalog.d.ts +2 -0
  360. package/dist/client/resources/mcp-integration-catalog.d.ts.map +1 -1
  361. package/dist/client/resources/mcp-integration-catalog.js +17 -17
  362. package/dist/client/resources/mcp-integration-catalog.js.map +1 -1
  363. package/dist/client/resources/mcp-integration-logos.js +2 -2
  364. package/dist/client/resources/mcp-integration-logos.js.map +1 -1
  365. package/dist/client/route-chunk-recovery.d.ts.map +1 -1
  366. package/dist/client/route-chunk-recovery.js +5 -3
  367. package/dist/client/route-chunk-recovery.js.map +1 -1
  368. package/dist/client/session-replay.js +3 -3
  369. package/dist/client/session-replay.js.map +1 -1
  370. package/dist/client/settings/DemoModeSection.d.ts.map +1 -1
  371. package/dist/client/settings/DemoModeSection.js +2 -5
  372. package/dist/client/settings/DemoModeSection.js.map +1 -1
  373. package/dist/client/settings/SecretsSection.d.ts.map +1 -1
  374. package/dist/client/settings/SecretsSection.js +18 -6
  375. package/dist/client/settings/SecretsSection.js.map +1 -1
  376. package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
  377. package/dist/client/settings/SettingsPanel.js +53 -118
  378. package/dist/client/settings/SettingsPanel.js.map +1 -1
  379. package/dist/client/settings/SettingsTabsPage.d.ts.map +1 -1
  380. package/dist/client/settings/SettingsTabsPage.js +16 -1
  381. package/dist/client/settings/SettingsTabsPage.js.map +1 -1
  382. package/dist/client/settings/VoiceTranscriptionSection.d.ts.map +1 -1
  383. package/dist/client/settings/VoiceTranscriptionSection.js +2 -5
  384. package/dist/client/settings/VoiceTranscriptionSection.js.map +1 -1
  385. package/dist/client/settings/index.d.ts +1 -0
  386. package/dist/client/settings/index.d.ts.map +1 -1
  387. package/dist/client/settings/index.js +1 -0
  388. package/dist/client/settings/index.js.map +1 -1
  389. package/dist/client/settings/useSettingsPanelController.d.ts +22 -0
  390. package/dist/client/settings/useSettingsPanelController.d.ts.map +1 -0
  391. package/dist/client/settings/useSettingsPanelController.js +103 -0
  392. package/dist/client/settings/useSettingsPanelController.js.map +1 -0
  393. package/dist/client/setup-connections/BuilderConnectCard.d.ts +14 -6
  394. package/dist/client/setup-connections/BuilderConnectCard.d.ts.map +1 -1
  395. package/dist/client/setup-connections/BuilderConnectCard.js +24 -16
  396. package/dist/client/setup-connections/BuilderConnectCard.js.map +1 -1
  397. package/dist/client/setup-connections/index.d.ts +2 -1
  398. package/dist/client/setup-connections/index.d.ts.map +1 -1
  399. package/dist/client/setup-connections/index.js +2 -1
  400. package/dist/client/setup-connections/index.js.map +1 -1
  401. package/dist/client/setup-connections/useBuilderConnectCardController.d.ts +34 -0
  402. package/dist/client/setup-connections/useBuilderConnectCardController.d.ts.map +1 -0
  403. package/dist/client/setup-connections/useBuilderConnectCardController.js +39 -0
  404. package/dist/client/setup-connections/useBuilderConnectCardController.js.map +1 -0
  405. package/dist/client/sharing/ShareButton.d.ts +2 -1
  406. package/dist/client/sharing/ShareButton.d.ts.map +1 -1
  407. package/dist/client/sharing/ShareButton.js +17 -463
  408. package/dist/client/sharing/ShareButton.js.map +1 -1
  409. package/dist/client/sharing/ShareDialog.d.ts +6 -6
  410. package/dist/client/sharing/ShareDialog.d.ts.map +1 -1
  411. package/dist/client/sharing/ShareDialog.js +69 -283
  412. package/dist/client/sharing/ShareDialog.js.map +1 -1
  413. package/dist/client/sharing/index.d.ts +3 -0
  414. package/dist/client/sharing/index.d.ts.map +1 -1
  415. package/dist/client/sharing/index.js +3 -0
  416. package/dist/client/sharing/index.js.map +1 -1
  417. package/dist/client/sharing/share-controller-helpers.d.ts +70 -0
  418. package/dist/client/sharing/share-controller-helpers.d.ts.map +1 -0
  419. package/dist/client/sharing/share-controller-helpers.js +224 -0
  420. package/dist/client/sharing/share-controller-helpers.js.map +1 -0
  421. package/dist/client/sharing/useShareButtonController.d.ts +91 -0
  422. package/dist/client/sharing/useShareButtonController.d.ts.map +1 -0
  423. package/dist/client/sharing/useShareButtonController.js +397 -0
  424. package/dist/client/sharing/useShareButtonController.js.map +1 -0
  425. package/dist/client/sharing/useShareDialogController.d.ts +109 -0
  426. package/dist/client/sharing/useShareDialogController.d.ts.map +1 -0
  427. package/dist/client/sharing/useShareDialogController.js +348 -0
  428. package/dist/client/sharing/useShareDialogController.js.map +1 -0
  429. package/dist/client/sse-event-processor.d.ts.map +1 -1
  430. package/dist/client/sse-event-processor.js +21 -0
  431. package/dist/client/sse-event-processor.js.map +1 -1
  432. package/dist/client/tool-display.d.ts +16 -0
  433. package/dist/client/tool-display.d.ts.map +1 -1
  434. package/dist/client/tool-display.js +52 -0
  435. package/dist/client/tool-display.js.map +1 -1
  436. package/dist/client/transcription/use-live-transcription.d.ts +3 -3
  437. package/dist/client/transcription/use-live-transcription.d.ts.map +1 -1
  438. package/dist/client/transcription/use-live-transcription.js +22 -11
  439. package/dist/client/transcription/use-live-transcription.js.map +1 -1
  440. package/dist/client/use-agent-engine-configured.d.ts +3 -2
  441. package/dist/client/use-agent-engine-configured.d.ts.map +1 -1
  442. package/dist/client/use-agent-engine-configured.js +7 -19
  443. package/dist/client/use-agent-engine-configured.js.map +1 -1
  444. package/dist/client/use-chat-threads.d.ts.map +1 -1
  445. package/dist/client/use-chat-threads.js +21 -8
  446. package/dist/client/use-chat-threads.js.map +1 -1
  447. package/dist/collab/routes.d.ts +1 -1
  448. package/dist/collab/struct-routes.d.ts +1 -1
  449. package/dist/deploy/build.d.ts.map +1 -1
  450. package/dist/deploy/build.js +1 -1
  451. package/dist/deploy/build.js.map +1 -1
  452. package/dist/guards/db-tool-scoping.js +1 -1
  453. package/dist/guards/db-tool-scoping.js.map +1 -1
  454. package/dist/integrations/adapters/slack.d.ts +1 -0
  455. package/dist/integrations/adapters/slack.d.ts.map +1 -1
  456. package/dist/integrations/adapters/slack.js +61 -5
  457. package/dist/integrations/adapters/slack.js.map +1 -1
  458. package/dist/integrations/index.d.ts +1 -1
  459. package/dist/integrations/index.d.ts.map +1 -1
  460. package/dist/integrations/index.js +1 -1
  461. package/dist/integrations/index.js.map +1 -1
  462. package/dist/localization/default-messages.d.ts +4 -0
  463. package/dist/localization/default-messages.d.ts.map +1 -1
  464. package/dist/localization/default-messages.js +5 -1
  465. package/dist/localization/default-messages.js.map +1 -1
  466. package/dist/mcp-client/oauth-routes.d.ts +5 -0
  467. package/dist/mcp-client/oauth-routes.d.ts.map +1 -1
  468. package/dist/mcp-client/oauth-routes.js +53 -14
  469. package/dist/mcp-client/oauth-routes.js.map +1 -1
  470. package/dist/notifications/routes.d.ts +2 -2
  471. package/dist/observability/routes.d.ts +1 -1
  472. package/dist/provider-api/actions/custom-provider-registration.d.ts +12 -12
  473. package/dist/provider-api/actions/provider-api.d.ts +11 -11
  474. package/dist/scripts/call-agent.d.ts.map +1 -1
  475. package/dist/scripts/call-agent.js +5 -1
  476. package/dist/scripts/call-agent.js.map +1 -1
  477. package/dist/secrets/routes.d.ts +6 -6
  478. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  479. package/dist/server/realtime-voice.d.ts.map +1 -1
  480. package/dist/server/realtime-voice.js +2 -2
  481. package/dist/server/realtime-voice.js.map +1 -1
  482. package/dist/styles/agent-native.css +0 -24
  483. package/dist/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  484. package/dist/templates/chat/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  485. package/dist/templates/chat/.agents/skills/frontend-design/SKILL.md +22 -2
  486. package/dist/templates/chat/.agents/skills/shadcn-ui/SKILL.md +28 -4
  487. package/dist/templates/chat/app/components/ui/toolkit-provider.tsx +6 -1
  488. package/dist/templates/chat/app/design-system.ts +3 -0
  489. package/dist/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  490. package/dist/templates/default/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  491. package/dist/templates/default/.agents/skills/frontend-design/SKILL.md +22 -2
  492. package/dist/templates/default/.agents/skills/shadcn-ui/SKILL.md +28 -4
  493. package/dist/templates/default/app/components/ui/toolkit-provider.tsx +10 -0
  494. package/dist/templates/default/app/design-system.ts +3 -0
  495. package/dist/templates/default/app/root.tsx +5 -2
  496. package/dist/templates/default/package.json +1 -0
  497. package/dist/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  498. package/dist/templates/headless/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  499. package/dist/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  500. package/dist/templates/workspace-core/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  501. package/dist/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +22 -2
  502. package/dist/templates/workspace-core/.agents/skills/shadcn-ui/SKILL.md +28 -4
  503. package/dist/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +7 -5
  504. package/dist/terminal/cli-registry.js +1 -1
  505. package/dist/terminal/cli-registry.js.map +1 -1
  506. package/dist/vite/client.d.ts +11 -1
  507. package/dist/vite/client.d.ts.map +1 -1
  508. package/dist/vite/client.js +144 -2
  509. package/dist/vite/client.js.map +1 -1
  510. package/docs/content/custom-design-system.mdx +445 -0
  511. package/docs/content/toolkit-ui.mdx +16 -0
  512. package/package.json +2 -2
  513. package/src/a2a/client.ts +32 -10
  514. package/src/a2a/types.ts +1 -0
  515. package/src/agent/types.ts +1 -1
  516. package/src/cli/index.ts +18 -10
  517. package/src/cli/multi-frontier-runs.ts +5 -3
  518. package/src/cli/templates-meta.ts +0 -12
  519. package/src/cli/workspace-dev.ts +1 -1
  520. package/src/client/AgentPanel.tsx +99 -108
  521. package/src/client/AssistantChat.tsx +30 -13
  522. package/src/client/CommandMenu.tsx +68 -178
  523. package/src/client/RunStuckBanner.tsx +39 -47
  524. package/src/client/agent-chat-adapter.ts +3 -1
  525. package/src/client/app-providers.tsx +1 -1
  526. package/src/client/chat/action-chat-ui-surface.tsx +32 -0
  527. package/src/client/chat/message-components.tsx +169 -18
  528. package/src/client/chat/tool-call-display.tsx +50 -14
  529. package/src/client/chat/widgets/InlineExtensionWidget.tsx +13 -40
  530. package/src/client/chat/widgets/builtin-tool-renderers.tsx +7 -3
  531. package/src/client/components/ui/message-scroller.tsx +15 -1
  532. package/src/client/conversation/AgentConversation.tsx +12 -1
  533. package/src/client/error-capture.ts +2 -6
  534. package/src/client/i18n.tsx +22 -0
  535. package/src/client/index.ts +17 -0
  536. package/src/client/org/TeamPage.tsx +257 -153
  537. package/src/client/progress/RunsTray.tsx +89 -88
  538. package/src/client/resources/McpConnectionSuggestion.tsx +25 -16
  539. package/src/client/resources/McpIntegrationDialog.tsx +34 -13
  540. package/src/client/resources/ResourcesPanel.tsx +5 -1
  541. package/src/client/resources/mcp-integration-catalog.ts +20 -17
  542. package/src/client/resources/mcp-integration-logos.ts +2 -2
  543. package/src/client/route-chunk-recovery.ts +5 -3
  544. package/src/client/session-replay.ts +3 -3
  545. package/src/client/settings/DemoModeSection.tsx +6 -18
  546. package/src/client/settings/SecretsSection.tsx +97 -52
  547. package/src/client/settings/SettingsPanel.tsx +217 -252
  548. package/src/client/settings/SettingsTabsPage.tsx +23 -2
  549. package/src/client/settings/VoiceTranscriptionSection.tsx +7 -18
  550. package/src/client/settings/index.ts +7 -0
  551. package/src/client/settings/useSettingsPanelController.ts +155 -0
  552. package/src/client/setup-connections/BuilderConnectCard.tsx +116 -51
  553. package/src/client/setup-connections/index.ts +10 -0
  554. package/src/client/setup-connections/useBuilderConnectCardController.ts +82 -0
  555. package/src/client/sharing/ShareButton.tsx +64 -586
  556. package/src/client/sharing/ShareDialog.tsx +299 -633
  557. package/src/client/sharing/index.ts +42 -0
  558. package/src/client/sharing/share-controller-helpers.ts +318 -0
  559. package/src/client/sharing/useShareButtonController.ts +600 -0
  560. package/src/client/sharing/useShareDialogController.ts +559 -0
  561. package/src/client/sse-event-processor.ts +23 -0
  562. package/src/client/tool-display.ts +71 -0
  563. package/src/client/transcription/use-live-transcription.ts +64 -45
  564. package/src/client/use-agent-engine-configured.ts +12 -19
  565. package/src/client/use-chat-threads.ts +33 -12
  566. package/src/deploy/build.ts +2 -1
  567. package/src/guards/db-tool-scoping.ts +1 -1
  568. package/src/integrations/adapters/slack.ts +84 -5
  569. package/src/integrations/index.ts +5 -1
  570. package/src/localization/default-messages.ts +6 -1
  571. package/src/mcp-client/oauth-routes.ts +61 -14
  572. package/src/scripts/call-agent.ts +5 -1
  573. package/src/server/realtime-voice.ts +5 -2
  574. package/src/styles/agent-native.css +0 -24
  575. package/src/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  576. package/src/templates/chat/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  577. package/src/templates/chat/.agents/skills/frontend-design/SKILL.md +22 -2
  578. package/src/templates/chat/.agents/skills/shadcn-ui/SKILL.md +28 -4
  579. package/src/templates/chat/app/components/ui/toolkit-provider.tsx +6 -1
  580. package/src/templates/chat/app/design-system.ts +3 -0
  581. package/src/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  582. package/src/templates/default/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  583. package/src/templates/default/.agents/skills/frontend-design/SKILL.md +22 -2
  584. package/src/templates/default/.agents/skills/shadcn-ui/SKILL.md +28 -4
  585. package/src/templates/default/app/components/ui/toolkit-provider.tsx +10 -0
  586. package/src/templates/default/app/design-system.ts +3 -0
  587. package/src/templates/default/app/root.tsx +5 -2
  588. package/src/templates/default/package.json +1 -0
  589. package/src/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  590. package/src/templates/headless/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  591. package/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +37 -2
  592. package/src/templates/workspace-core/.agents/skills/customizing-agent-native/SKILL.md +86 -11
  593. package/src/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +22 -2
  594. package/src/templates/workspace-core/.agents/skills/shadcn-ui/SKILL.md +28 -4
  595. package/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +7 -5
  596. package/src/terminal/cli-registry.ts +1 -1
  597. package/src/vite/client.ts +205 -1
  598. package/corpus/templates/analytics/app/components/ui/calendar.tsx +0 -70
  599. package/corpus/templates/analytics/app/components/ui/chart.tsx +0 -386
  600. package/corpus/templates/calendar/app/components/ui/sonner.tsx +0 -58
  601. package/corpus/templates/crm/.agents/skills/agent-native-docs/SKILL.md +0 -115
  602. package/corpus/templates/crm/.agents/skills/agent-native-toolkit/SKILL.md +0 -142
  603. package/corpus/templates/crm/.agents/skills/crm/SKILL.md +0 -128
  604. package/corpus/templates/crm/.agents/skills/customizing-agent-native/SKILL.md +0 -145
  605. package/corpus/templates/crm/.agents/skills/feature-flags/SKILL.md +0 -169
  606. package/corpus/templates/crm/.agents/skills/upgrade-agent-native/SKILL.md +0 -100
  607. package/corpus/templates/crm/.env.example +0 -5
  608. package/corpus/templates/crm/AGENTS.md +0 -120
  609. package/corpus/templates/crm/CHANGELOG.md +0 -1
  610. package/corpus/templates/crm/DEVELOPING.md +0 -66
  611. package/corpus/templates/crm/README.md +0 -106
  612. package/corpus/templates/crm/_gitignore +0 -12
  613. package/corpus/templates/crm/actions/_crm-action-utils.ts +0 -87
  614. package/corpus/templates/crm/actions/_crm-dashboard.ts +0 -69
  615. package/corpus/templates/crm/actions/_crm-data-program-actions.ts +0 -15
  616. package/corpus/templates/crm/actions/_crm-signal-utils.ts +0 -79
  617. package/corpus/templates/crm/actions/apply-crm-proposals.ts +0 -203
  618. package/corpus/templates/crm/actions/attach-call-evidence.ts +0 -170
  619. package/corpus/templates/crm/actions/configure-crm-connection.ts +0 -126
  620. package/corpus/templates/crm/actions/configure-native-crm.ts +0 -44
  621. package/corpus/templates/crm/actions/create-crm-record.ts +0 -148
  622. package/corpus/templates/crm/actions/create-crm-signal-tracker.ts +0 -58
  623. package/corpus/templates/crm/actions/delete-staged-dataset.ts +0 -14
  624. package/corpus/templates/crm/actions/get-crm-automation-recipe.ts +0 -36
  625. package/corpus/templates/crm/actions/get-crm-dashboard-panel.ts +0 -35
  626. package/corpus/templates/crm/actions/get-crm-dashboard.ts +0 -14
  627. package/corpus/templates/crm/actions/get-crm-overview.ts +0 -13
  628. package/corpus/templates/crm/actions/get-crm-pipeline-data.ts +0 -61
  629. package/corpus/templates/crm/actions/get-crm-record.ts +0 -75
  630. package/corpus/templates/crm/actions/install-crm-pipeline-dashboard.ts +0 -81
  631. package/corpus/templates/crm/actions/list-crm-dashboard-revisions.ts +0 -15
  632. package/corpus/templates/crm/actions/list-crm-dashboards.ts +0 -15
  633. package/corpus/templates/crm/actions/list-crm-proposals.ts +0 -31
  634. package/corpus/templates/crm/actions/list-crm-records.ts +0 -79
  635. package/corpus/templates/crm/actions/list-crm-saved-views.ts +0 -15
  636. package/corpus/templates/crm/actions/list-crm-signal-hits.ts +0 -43
  637. package/corpus/templates/crm/actions/list-crm-signal-trackers.ts +0 -32
  638. package/corpus/templates/crm/actions/list-crm-tasks.ts +0 -22
  639. package/corpus/templates/crm/actions/list-staged-datasets.ts +0 -14
  640. package/corpus/templates/crm/actions/list-workspace-connections.ts +0 -47
  641. package/corpus/templates/crm/actions/manage-crm-signal-tracker.ts +0 -87
  642. package/corpus/templates/crm/actions/manage-crm-task.ts +0 -132
  643. package/corpus/templates/crm/actions/navigate.ts +0 -63
  644. package/corpus/templates/crm/actions/provider-api-catalog.ts +0 -22
  645. package/corpus/templates/crm/actions/provider-api-docs.ts +0 -54
  646. package/corpus/templates/crm/actions/provider-api-request.ts +0 -92
  647. package/corpus/templates/crm/actions/query-staged-dataset.ts +0 -44
  648. package/corpus/templates/crm/actions/record-crm-call-insight.ts +0 -160
  649. package/corpus/templates/crm/actions/record-crm-smart-signal.ts +0 -150
  650. package/corpus/templates/crm/actions/restore-crm-dashboard-revision.ts +0 -23
  651. package/corpus/templates/crm/actions/review-crm-signal.ts +0 -31
  652. package/corpus/templates/crm/actions/run-crm-saved-view-program.ts +0 -103
  653. package/corpus/templates/crm/actions/run-crm-signal-trackers.ts +0 -275
  654. package/corpus/templates/crm/actions/run.ts +0 -7
  655. package/corpus/templates/crm/actions/save-crm-dashboard.ts +0 -51
  656. package/corpus/templates/crm/actions/save-crm-saved-view.ts +0 -119
  657. package/corpus/templates/crm/actions/sync-crm.ts +0 -146
  658. package/corpus/templates/crm/actions/update-crm-record.ts +0 -510
  659. package/corpus/templates/crm/actions/view-screen.ts +0 -218
  660. package/corpus/templates/crm/agent-native.app-skill.json +0 -58
  661. package/corpus/templates/crm/app/components/crm/CreateCrmRecordDialog.tsx +0 -158
  662. package/corpus/templates/crm/app/components/crm/CrmDashboardPanel.tsx +0 -83
  663. package/corpus/templates/crm/app/components/crm/CrmSignalsPanel.tsx +0 -225
  664. package/corpus/templates/crm/app/components/crm/IntelligenceSettings.tsx +0 -413
  665. package/corpus/templates/crm/app/components/crm/RecordActions.tsx +0 -510
  666. package/corpus/templates/crm/app/components/crm/RecordGrid.tsx +0 -134
  667. package/corpus/templates/crm/app/components/crm/RecordWorkspace.tsx +0 -367
  668. package/corpus/templates/crm/app/components/crm/SavedViewDataProgram.tsx +0 -92
  669. package/corpus/templates/crm/app/components/crm/Surface.tsx +0 -96
  670. package/corpus/templates/crm/app/components/crm/WorkOverview.tsx +0 -180
  671. package/corpus/templates/crm/app/components/layout/CrmLayout.tsx +0 -112
  672. package/corpus/templates/crm/app/components/layout/CrmSidebar.tsx +0 -84
  673. package/corpus/templates/crm/app/entry.client.tsx +0 -16
  674. package/corpus/templates/crm/app/entry.server.tsx +0 -10
  675. package/corpus/templates/crm/app/global.css +0 -167
  676. package/corpus/templates/crm/app/hooks/use-navigation-state.ts +0 -60
  677. package/corpus/templates/crm/app/i18n/en-US.ts +0 -89
  678. package/corpus/templates/crm/app/i18n/index.ts +0 -34
  679. package/corpus/templates/crm/app/lib/dashboard.ts +0 -13
  680. package/corpus/templates/crm/app/lib/navigation.ts +0 -54
  681. package/corpus/templates/crm/app/lib/tab-id.ts +0 -1
  682. package/corpus/templates/crm/app/lib/types.ts +0 -172
  683. package/corpus/templates/crm/app/lib/utils.ts +0 -1
  684. package/corpus/templates/crm/app/root.tsx +0 -88
  685. package/corpus/templates/crm/app/routes/_index.tsx +0 -26
  686. package/corpus/templates/crm/app/routes/accounts.tsx +0 -41
  687. package/corpus/templates/crm/app/routes/agent.tsx +0 -5
  688. package/corpus/templates/crm/app/routes/ask.tsx +0 -53
  689. package/corpus/templates/crm/app/routes/dashboard.tsx +0 -118
  690. package/corpus/templates/crm/app/routes/opportunities.tsx +0 -41
  691. package/corpus/templates/crm/app/routes/people.tsx +0 -41
  692. package/corpus/templates/crm/app/routes/proposals.tsx +0 -266
  693. package/corpus/templates/crm/app/routes/records.$recordId.tsx +0 -35
  694. package/corpus/templates/crm/app/routes/settings.tsx +0 -63
  695. package/corpus/templates/crm/app/routes/setup.tsx +0 -308
  696. package/corpus/templates/crm/app/routes/tasks.tsx +0 -252
  697. package/corpus/templates/crm/app/routes/views.tsx +0 -333
  698. package/corpus/templates/crm/app/routes.ts +0 -17
  699. package/corpus/templates/crm/app/vite-env.d.ts +0 -6
  700. package/corpus/templates/crm/changelog/2026-07-21-agent-native-crm.md +0 -6
  701. package/corpus/templates/crm/changelog/2026-07-21-clips-call-evidence-review-recipe.md +0 -6
  702. package/corpus/templates/crm/changelog/2026-07-21-crm-can-now-find-and-review-evidence-grounded-call-signals-w.md +0 -6
  703. package/corpus/templates/crm/changelog/2026-07-21-crm-can-now-manage-keyword-and-smart-call-signal-trackers-fr.md +0 -6
  704. package/corpus/templates/crm/changelog/2026-07-21-delegated-local-crm-automation.md +0 -6
  705. package/corpus/templates/crm/changelog/2026-07-21-native-sql.md +0 -6
  706. package/corpus/templates/crm/changelog/2026-07-21-pipeline-dashboard.md +0 -6
  707. package/corpus/templates/crm/changelog/2026-07-21-pipeline-dashboards-now-install-correctly-from-the-crm-actio.md +0 -6
  708. package/corpus/templates/crm/changelog/2026-07-21-salesforce-connection.md +0 -6
  709. package/corpus/templates/crm/changelog/2026-07-22-crm-field-validation-now-explains-which-fields-cannot-be-edi.md +0 -6
  710. package/corpus/templates/crm/changelog/2026-07-22-fixed-hubspot-contact-syncs-missing-records-updated-after-th.md +0 -6
  711. package/corpus/templates/crm/changelog/2026-07-22-fixed-pipeline-dashboard-panels-so-their-opportunity-data-lo.md +0 -6
  712. package/corpus/templates/crm/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +0 -6
  713. package/corpus/templates/crm/components.json +0 -20
  714. package/corpus/templates/crm/docs/architecture/crm-contract.md +0 -180
  715. package/corpus/templates/crm/learnings.defaults.md +0 -5
  716. package/corpus/templates/crm/package.json +0 -59
  717. package/corpus/templates/crm/public/favicon.svg +0 -1
  718. package/corpus/templates/crm/public/icon-180.svg +0 -1
  719. package/corpus/templates/crm/public/manifest.json +0 -11
  720. package/corpus/templates/crm/react-router.config.ts +0 -7
  721. package/corpus/templates/crm/server/crm/adapter.ts +0 -32
  722. package/corpus/templates/crm/server/crm/crm-field-firewall.ts +0 -64
  723. package/corpus/templates/crm/server/crm/crm-mirror.ts +0 -711
  724. package/corpus/templates/crm/server/crm/hubspot-adapter.ts +0 -1146
  725. package/corpus/templates/crm/server/crm/native-adapter.ts +0 -1715
  726. package/corpus/templates/crm/server/crm/read-through.ts +0 -207
  727. package/corpus/templates/crm/server/crm/salesforce-adapter.ts +0 -1255
  728. package/corpus/templates/crm/server/db/crm-store.ts +0 -1170
  729. package/corpus/templates/crm/server/db/index.ts +0 -169
  730. package/corpus/templates/crm/server/db/schema.ts +0 -441
  731. package/corpus/templates/crm/server/lib/intelligence/default-detectors.ts +0 -65
  732. package/corpus/templates/crm/server/lib/intelligence/evidence.ts +0 -105
  733. package/corpus/templates/crm/server/lib/intelligence/keyword-detector.ts +0 -100
  734. package/corpus/templates/crm/server/lib/intelligence/smart-detector.ts +0 -144
  735. package/corpus/templates/crm/server/lib/intelligence/summary.ts +0 -141
  736. package/corpus/templates/crm/server/lib/provider-api.ts +0 -64
  737. package/corpus/templates/crm/server/middleware/auth.ts +0 -4
  738. package/corpus/templates/crm/server/plugins/agent-chat.ts +0 -68
  739. package/corpus/templates/crm/server/plugins/auth.ts +0 -14
  740. package/corpus/templates/crm/server/plugins/core-routes.ts +0 -17
  741. package/corpus/templates/crm/server/plugins/db.ts +0 -454
  742. package/corpus/templates/crm/server/routes/[...page].get.ts +0 -5
  743. package/corpus/templates/crm/shared/crm-automation-recipes.ts +0 -132
  744. package/corpus/templates/crm/shared/crm-contract.ts +0 -254
  745. package/corpus/templates/crm/shared/crm-sales-config.ts +0 -63
  746. package/corpus/templates/crm/ssr-entry.ts +0 -11
  747. package/corpus/templates/crm/tsconfig.json +0 -24
  748. package/corpus/templates/crm/vite.config.ts +0 -15
  749. package/corpus/templates/crm/vitest.config.ts +0 -5
  750. package/corpus/templates/forms/app/components/ui/calendar.tsx +0 -214
  751. package/corpus/templates/forms/app/components/ui/chart.tsx +0 -379
  752. package/corpus/templates/forms/app/components/ui/command.tsx +0 -153
  753. package/corpus/templates/forms/app/components/ui/context-menu.tsx +0 -198
  754. package/corpus/templates/forms/app/components/ui/menubar.tsx +0 -254
  755. package/corpus/templates/macros/app/components/ui/menubar.tsx +0 -238
  756. package/corpus/templates/macros/app/components/ui/progress.tsx +0 -26
  757. package/corpus/templates/mail/app/components/ui/calendar.tsx +0 -211
  758. package/corpus/templates/mail/app/components/ui/chart.tsx +0 -379
  759. package/corpus/templates/mail/app/components/ui/command.tsx +0 -153
  760. package/corpus/templates/mail/app/components/ui/context-menu.tsx +0 -198
  761. package/corpus/templates/mail/app/components/ui/menubar.tsx +0 -254
  762. package/corpus/templates/tasks/app/components/ui/accordion.tsx +0 -56
  763. package/corpus/templates/tasks/app/components/ui/aspect-ratio.tsx +0 -5
  764. package/corpus/templates/tasks/app/components/ui/breadcrumb.tsx +0 -115
  765. package/corpus/templates/tasks/app/components/ui/calendar.tsx +0 -70
  766. package/corpus/templates/tasks/app/components/ui/carousel.tsx +0 -260
  767. package/corpus/templates/tasks/app/components/ui/chart.tsx +0 -379
  768. package/corpus/templates/tasks/app/components/ui/form.tsx +0 -177
  769. package/corpus/templates/tasks/app/components/ui/hover-card.tsx +0 -27
  770. package/corpus/templates/tasks/app/components/ui/input-otp.tsx +0 -69
  771. package/corpus/templates/tasks/app/components/ui/menubar.tsx +0 -254
  772. package/corpus/templates/tasks/app/components/ui/navigation-menu.tsx +0 -128
  773. package/corpus/templates/tasks/app/components/ui/pagination.tsx +0 -121
  774. package/corpus/templates/tasks/app/components/ui/resizable.tsx +0 -43
  775. package/corpus/templates/tasks/app/components/ui/sidebar.tsx +0 -782
  776. package/corpus/templates/tasks/app/components/ui/toggle.tsx +0 -43
@@ -1,1715 +0,0 @@
1
- import { accessFilter, assertAccess } from "@agent-native/core/sharing";
2
- import { and, asc, eq, gt, inArray, isNull, like, or } from "drizzle-orm";
3
-
4
- import type {
5
- CrmAccessScope,
6
- CrmAdapter,
7
- CrmAdapterCapabilities,
8
- CrmConnectionRef,
9
- CrmFieldDefinition,
10
- CrmMutation,
11
- CrmMutationResult,
12
- CrmObjectDefinition,
13
- CrmObjectKind,
14
- CrmRecord,
15
- CrmRecordRef,
16
- CrmRelationship,
17
- CrmSyncScope,
18
- CrmSyncPage,
19
- CrmValue,
20
- } from "../../shared/crm-contract.js";
21
- import { getDb, schema } from "../db/index.js";
22
- import {
23
- isBoundedCrmValue,
24
- isSafeCrmMutationFieldName,
25
- } from "./crm-field-firewall.js";
26
-
27
- const MAX_PAGE_SIZE = 100;
28
- const MAX_FIELDS = 80;
29
- const MAX_MUTATION_FIELDS = 20;
30
- const STANDARD_OBJECTS = ["accounts", "people", "opportunities"] as const;
31
-
32
- type Ownership = {
33
- ownerEmail: string;
34
- orgId: string | null;
35
- visibility: "private" | "org" | "public";
36
- };
37
-
38
- type NativeConnection = Ownership & {
39
- id: string;
40
- accountId: string | null;
41
- accessScopeKey: string;
42
- accessScopeJson: string;
43
- };
44
-
45
- type StoredField = {
46
- fieldName: string;
47
- valueType: string;
48
- stringValue: string | null;
49
- numberValue: number | null;
50
- booleanValue: boolean | null;
51
- jsonValue: string | null;
52
- };
53
-
54
- function kindForObject(objectType: string): CrmObjectKind {
55
- if (objectType === "accounts") return "account";
56
- if (objectType === "people") return "person";
57
- if (objectType === "opportunities") return "opportunity";
58
- return "custom";
59
- }
60
-
61
- function labelForObject(objectType: string): string {
62
- if (objectType === "accounts") return "Account";
63
- if (objectType === "people") return "Person";
64
- if (objectType === "opportunities") return "Opportunity";
65
- return objectType
66
- .replace(/[_-]+/g, " ")
67
- .replace(/\b\w/g, (character) => character.toUpperCase());
68
- }
69
-
70
- function nativeField(
71
- name: string,
72
- valueType: CrmFieldDefinition["valueType"],
73
- required = false,
74
- ): CrmFieldDefinition {
75
- return {
76
- name,
77
- label: name
78
- .replace(/([A-Z])/g, " $1")
79
- .replace(/^./, (character) => character.toUpperCase()),
80
- valueType,
81
- storagePolicy: "local-authoritative",
82
- sensitive: false,
83
- readable: true,
84
- createable: true,
85
- updateable: true,
86
- required,
87
- };
88
- }
89
-
90
- export function nativeObjectTemplate(objectType: string): CrmObjectDefinition {
91
- const objectFields =
92
- objectType === "accounts"
93
- ? [
94
- nativeField("name", "string", true),
95
- nativeField("domain", "string"),
96
- nativeField("industry", "string"),
97
- nativeField("ownerName", "string"),
98
- ]
99
- : objectType === "people"
100
- ? [
101
- nativeField("firstName", "string"),
102
- nativeField("lastName", "string"),
103
- nativeField("email", "string"),
104
- nativeField("title", "string"),
105
- nativeField("accountId", "reference"),
106
- nativeField("ownerName", "string"),
107
- ]
108
- : objectType === "opportunities"
109
- ? [
110
- nativeField("name", "string", true),
111
- nativeField("amount", "currency"),
112
- nativeField("stage", "string"),
113
- nativeField("closeDate", "date"),
114
- nativeField("accountId", "reference"),
115
- nativeField("ownerName", "string"),
116
- ]
117
- : [];
118
- const fields = objectFields.length
119
- ? [
120
- ...objectFields,
121
- nativeField("desiredCadenceDays", "number"),
122
- nativeField("lastMeaningfulInteractionAt", "datetime"),
123
- nativeField("nextContactAt", "datetime"),
124
- ]
125
- : [];
126
- const label = labelForObject(objectType);
127
- return {
128
- connectionId: "",
129
- provider: "native",
130
- objectType,
131
- kind: kindForObject(objectType),
132
- label,
133
- pluralLabel: `${label}s`,
134
- custom: !STANDARD_OBJECTS.includes(
135
- objectType as (typeof STANDARD_OBJECTS)[number],
136
- ),
137
- queryable: true,
138
- searchable: true,
139
- createable: true,
140
- updateable: true,
141
- deleteable: true,
142
- fields,
143
- };
144
- }
145
-
146
- function valueType(value: CrmValue): CrmFieldDefinition["valueType"] {
147
- if (typeof value === "boolean") return "boolean";
148
- if (typeof value === "number") return "number";
149
- if (Array.isArray(value) || typeof value === "object") return "json";
150
- return "string";
151
- }
152
-
153
- function fieldColumns(value: CrmValue) {
154
- if (typeof value === "string") {
155
- return {
156
- stringValue: value,
157
- numberValue: null,
158
- booleanValue: null,
159
- jsonValue: null,
160
- };
161
- }
162
- if (typeof value === "number") {
163
- return {
164
- stringValue: null,
165
- numberValue: value,
166
- booleanValue: null,
167
- jsonValue: null,
168
- };
169
- }
170
- if (typeof value === "boolean") {
171
- return {
172
- stringValue: null,
173
- numberValue: null,
174
- booleanValue: value,
175
- jsonValue: null,
176
- };
177
- }
178
- if (value === null) {
179
- return {
180
- stringValue: null,
181
- numberValue: null,
182
- booleanValue: null,
183
- jsonValue: null,
184
- };
185
- }
186
- return {
187
- stringValue: null,
188
- numberValue: null,
189
- booleanValue: null,
190
- jsonValue: JSON.stringify(value),
191
- };
192
- }
193
-
194
- function fieldValue(field: StoredField): CrmValue {
195
- if (field.stringValue !== null) return field.stringValue;
196
- if (field.numberValue !== null) return field.numberValue;
197
- if (field.booleanValue !== null) return field.booleanValue;
198
- if (!field.jsonValue) return null;
199
- try {
200
- const value = JSON.parse(field.jsonValue) as unknown;
201
- return isBoundedCrmValue(value) ? (value as CrmValue) : null;
202
- } catch {
203
- return null;
204
- }
205
- }
206
-
207
- function encodeCursor(offset: number): string {
208
- return btoa(String(offset)).replace(/=+$/, "");
209
- }
210
-
211
- function decodeCursor(cursor: string | undefined): number {
212
- if (!cursor) return 0;
213
- try {
214
- const value = Number.parseInt(atob(cursor), 10);
215
- return Number.isSafeInteger(value) && value >= 0 ? value : 0;
216
- } catch {
217
- return 0;
218
- }
219
- }
220
-
221
- export function nextNativeRevision(value: string | null | undefined): string {
222
- try {
223
- const parsed = BigInt(value ?? "0");
224
- return (parsed >= 0n ? parsed + 1n : 1n).toString();
225
- } catch {
226
- return "1";
227
- }
228
- }
229
-
230
- function scopeFromConnection(connection: NativeConnection): CrmAccessScope {
231
- try {
232
- const stored = JSON.parse(
233
- connection.accessScopeJson,
234
- ) as Partial<CrmAccessScope>;
235
- if (stored.mode === "native" && stored.key === connection.accessScopeKey) {
236
- return {
237
- key: stored.key,
238
- actorId: stored.actorId ?? connection.ownerEmail,
239
- mode: "native",
240
- objectReadable: true,
241
- objectCreateable: true,
242
- objectUpdateable: true,
243
- objectDeleteable: true,
244
- recordVisibility:
245
- stored.recordVisibility ??
246
- (connection.visibility === "org" ? "workspace" : "actor"),
247
- ...(stored.fieldPermissionsHash
248
- ? { fieldPermissionsHash: stored.fieldPermissionsHash }
249
- : {}),
250
- ...(stored.sharingFingerprint
251
- ? { sharingFingerprint: stored.sharingFingerprint }
252
- : {}),
253
- };
254
- }
255
- } catch {}
256
- return {
257
- key: connection.accessScopeKey,
258
- actorId: connection.ownerEmail,
259
- mode: "native",
260
- objectReadable: true,
261
- objectCreateable: true,
262
- objectUpdateable: true,
263
- objectDeleteable: true,
264
- recordVisibility: connection.visibility === "org" ? "workspace" : "actor",
265
- };
266
- }
267
-
268
- function json(value: unknown, maximum = 12_000): string {
269
- const encoded = JSON.stringify(value);
270
- if (encoded.length > maximum)
271
- throw new Error("CRM mutation payload exceeds the allowed size.");
272
- return encoded;
273
- }
274
-
275
- function mutationFields(mutation: CrmMutation): Record<string, CrmValue> {
276
- const fields = mutation.fields ?? {};
277
- const entries = Object.entries(fields);
278
- if (
279
- entries.length > MAX_MUTATION_FIELDS ||
280
- entries.some(
281
- ([name, value]) =>
282
- !name ||
283
- name.length > 120 ||
284
- !isSafeCrmMutationFieldName(name) ||
285
- !isBoundedCrmValue(value),
286
- )
287
- ) {
288
- throw new Error(
289
- "Native CRM mutations require bounded, non-media field values.",
290
- );
291
- }
292
- return fields;
293
- }
294
-
295
- function summaryColumns(fields: Record<string, CrmValue>, fallback: string) {
296
- const string = (...names: string[]) => {
297
- for (const name of names) {
298
- const value = fields[name];
299
- if (typeof value === "string") return value.slice(0, 500);
300
- }
301
- return null;
302
- };
303
- const number = (...names: string[]) => {
304
- for (const name of names) {
305
- const value = fields[name];
306
- if (typeof value === "number") return value;
307
- }
308
- return null;
309
- };
310
- const desiredCadenceDays = fields.desiredCadenceDays;
311
- if (
312
- desiredCadenceDays !== undefined &&
313
- desiredCadenceDays !== null &&
314
- (typeof desiredCadenceDays !== "number" ||
315
- !Number.isInteger(desiredCadenceDays) ||
316
- desiredCadenceDays < 1 ||
317
- desiredCadenceDays > 365)
318
- ) {
319
- throw new Error(
320
- "desiredCadenceDays must be null or an integer from 1 to 365.",
321
- );
322
- }
323
- return {
324
- displayName: string("displayName", "name", "Name") ?? fallback,
325
- primaryEmail: string("email", "Email"),
326
- domain: string("domain", "website", "Website"),
327
- stage: string("stage", "StageName"),
328
- ownerName: string("ownerName"),
329
- amount: number("amount", "Amount"),
330
- closeDate: string("closeDate", "CloseDate"),
331
- desiredCadenceDays: desiredCadenceDays ?? null,
332
- lastMeaningfulInteractionAt: string("lastMeaningfulInteractionAt"),
333
- nextContactAt: string("nextContactAt"),
334
- };
335
- }
336
-
337
- async function loadNativeConnection(
338
- connectionId: string,
339
- accessTier: "viewer" | "editor" = "viewer",
340
- ): Promise<NativeConnection> {
341
- await assertAccess("crm-connection", connectionId, accessTier);
342
- const db = getDb();
343
- const [connection] = await db
344
- .select({
345
- id: schema.crmConnections.id,
346
- accountId: schema.crmConnections.accountId,
347
- accessScopeKey: schema.crmConnections.accessScopeKey,
348
- accessScopeJson: schema.crmConnections.accessScopeJson,
349
- ownerEmail: schema.crmConnections.ownerEmail,
350
- orgId: schema.crmConnections.orgId,
351
- visibility: schema.crmConnections.visibility,
352
- })
353
- .from(schema.crmConnections)
354
- .where(
355
- and(
356
- eq(schema.crmConnections.id, connectionId),
357
- eq(schema.crmConnections.provider, "native"),
358
- accessFilter(
359
- schema.crmConnections,
360
- schema.crmConnectionShares,
361
- undefined,
362
- accessTier,
363
- ),
364
- ),
365
- )
366
- .limit(1);
367
- if (!connection) throw new Error("Native CRM connection is unavailable.");
368
- return connection as NativeConnection;
369
- }
370
-
371
- async function ensureNativeObject(input: {
372
- connection: NativeConnection;
373
- objectType: string;
374
- fields: CrmFieldDefinition[];
375
- }) {
376
- const db = getDb();
377
- const template = nativeObjectTemplate(input.objectType);
378
- const object = { ...template, connectionId: input.connection.id };
379
- const [existing] = await db
380
- .select({ id: schema.crmObjects.id })
381
- .from(schema.crmObjects)
382
- .where(
383
- and(
384
- eq(schema.crmObjects.connectionId, input.connection.id),
385
- eq(schema.crmObjects.objectType, input.objectType),
386
- accessFilter(schema.crmObjects, schema.crmObjectShares),
387
- ),
388
- )
389
- .limit(1);
390
- const now = new Date().toISOString();
391
- const objectValues = {
392
- provider: "native" as const,
393
- objectType: input.objectType,
394
- kind: object.kind,
395
- label: object.label,
396
- pluralLabel: object.pluralLabel,
397
- custom: object.custom,
398
- queryable: true,
399
- searchable: true,
400
- createable: true,
401
- updateable: true,
402
- deleteable: true,
403
- capabilitiesJson: json(NATIVE_CAPABILITIES, 4_000),
404
- updatedAt: now,
405
- };
406
- if (existing) {
407
- await assertAccess("crm-object", existing.id, "editor");
408
- await db
409
- .update(schema.crmObjects)
410
- .set(objectValues)
411
- .where(
412
- and(
413
- eq(schema.crmObjects.id, existing.id),
414
- accessFilter(
415
- schema.crmObjects,
416
- schema.crmObjectShares,
417
- undefined,
418
- "editor",
419
- ),
420
- ),
421
- );
422
- } else {
423
- await db.insert(schema.crmObjects).values({
424
- id: crypto.randomUUID(),
425
- connectionId: input.connection.id,
426
- ...objectValues,
427
- ownerEmail: input.connection.ownerEmail,
428
- orgId: input.connection.orgId,
429
- visibility: input.connection.visibility,
430
- createdAt: now,
431
- });
432
- }
433
- const known = new Map<string, CrmFieldDefinition>();
434
- for (const field of [...template.fields, ...input.fields])
435
- known.set(field.name, field);
436
- const existingPolicies = await db
437
- .select({
438
- id: schema.crmFieldPolicies.id,
439
- fieldName: schema.crmFieldPolicies.fieldName,
440
- })
441
- .from(schema.crmFieldPolicies)
442
- .where(
443
- and(
444
- eq(schema.crmFieldPolicies.connectionId, input.connection.id),
445
- eq(schema.crmFieldPolicies.objectType, input.objectType),
446
- accessFilter(schema.crmFieldPolicies, schema.crmFieldPolicyShares),
447
- ),
448
- );
449
- const policyByName = new Map(
450
- existingPolicies.map((policy) => [policy.fieldName, policy]),
451
- );
452
- for (const field of known.values()) {
453
- const existingPolicy = policyByName.get(field.name);
454
- const values = {
455
- label: field.label,
456
- valueType: field.valueType,
457
- storagePolicy: "local-authoritative" as const,
458
- sensitive: false,
459
- readable: true,
460
- createable: true,
461
- updateable: true,
462
- required: field.required,
463
- metadataJson: "{}",
464
- updatedAt: now,
465
- };
466
- if (existingPolicy) {
467
- await assertAccess("crm-field-policy", existingPolicy.id, "editor");
468
- await db
469
- .update(schema.crmFieldPolicies)
470
- .set(values)
471
- .where(
472
- and(
473
- eq(schema.crmFieldPolicies.id, existingPolicy.id),
474
- accessFilter(
475
- schema.crmFieldPolicies,
476
- schema.crmFieldPolicyShares,
477
- undefined,
478
- "editor",
479
- ),
480
- ),
481
- );
482
- } else {
483
- await db.insert(schema.crmFieldPolicies).values({
484
- id: crypto.randomUUID(),
485
- connectionId: input.connection.id,
486
- objectType: input.objectType,
487
- fieldName: field.name,
488
- ...values,
489
- ownerEmail: input.connection.ownerEmail,
490
- orgId: input.connection.orgId,
491
- visibility: input.connection.visibility,
492
- createdAt: now,
493
- });
494
- }
495
- }
496
- }
497
-
498
- export async function initializeNativeCrmDomain(connectionId: string) {
499
- const connection = await loadNativeConnection(connectionId, "editor");
500
- await Promise.all(
501
- STANDARD_OBJECTS.map((objectType) =>
502
- ensureNativeObject({ connection, objectType, fields: [] }),
503
- ),
504
- );
505
- }
506
-
507
- export async function configureNativeCrmConnection(input: {
508
- label?: string;
509
- ownership: Ownership;
510
- }) {
511
- const db = getDb();
512
- const label = input.label?.trim() || "Native SQL";
513
- const [existing] = await db
514
- .select({ id: schema.crmConnections.id })
515
- .from(schema.crmConnections)
516
- .where(
517
- and(
518
- eq(schema.crmConnections.provider, "native"),
519
- eq(schema.crmConnections.label, label),
520
- accessFilter(schema.crmConnections, schema.crmConnectionShares),
521
- ),
522
- )
523
- .limit(1);
524
- const id = existing?.id ?? crypto.randomUUID();
525
- const now = new Date().toISOString();
526
- const accessScope: CrmAccessScope = {
527
- key: `native:${id}`,
528
- actorId: input.ownership.ownerEmail,
529
- mode: "native",
530
- objectReadable: true,
531
- objectCreateable: true,
532
- objectUpdateable: true,
533
- objectDeleteable: true,
534
- recordVisibility:
535
- input.ownership.visibility === "org" ? "workspace" : "actor",
536
- };
537
- const values = {
538
- provider: "native" as const,
539
- workspaceConnectionId: null,
540
- label,
541
- accountId: null,
542
- mode: "native" as const,
543
- status: "connected" as const,
544
- selectedPipelinesJson: "[]",
545
- selectedObjectTypesJson: json(STANDARD_OBJECTS, 1_000),
546
- accessScopeKey: accessScope.key,
547
- accessScopeJson: json(accessScope, 4_000),
548
- lastError: null,
549
- updatedAt: now,
550
- };
551
- if (existing) {
552
- await assertAccess("crm-connection", existing.id, "editor");
553
- await db
554
- .update(schema.crmConnections)
555
- .set(values)
556
- .where(
557
- and(
558
- eq(schema.crmConnections.id, existing.id),
559
- accessFilter(
560
- schema.crmConnections,
561
- schema.crmConnectionShares,
562
- undefined,
563
- "editor",
564
- ),
565
- ),
566
- );
567
- } else {
568
- await db.insert(schema.crmConnections).values({
569
- id,
570
- ...values,
571
- ownerEmail: input.ownership.ownerEmail,
572
- orgId: input.ownership.orgId,
573
- visibility: input.ownership.visibility,
574
- createdAt: now,
575
- });
576
- }
577
- await initializeNativeCrmDomain(id);
578
- return { id, label, accessScope, ...input.ownership };
579
- }
580
-
581
- export async function resolveNativeCrmAccessScope(input: {
582
- connectionId: string;
583
- objectType: string;
584
- }): Promise<CrmAccessScope | null> {
585
- const connection = await loadNativeConnection(input.connectionId);
586
- const db = getDb();
587
- const [object] = await db
588
- .select({ id: schema.crmObjects.id })
589
- .from(schema.crmObjects)
590
- .where(
591
- and(
592
- eq(schema.crmObjects.connectionId, connection.id),
593
- eq(schema.crmObjects.objectType, input.objectType),
594
- accessFilter(schema.crmObjects, schema.crmObjectShares),
595
- ),
596
- )
597
- .limit(1);
598
- return object ? scopeFromConnection(connection) : null;
599
- }
600
-
601
- async function fieldsForRecord(recordId: string, requested?: string[]) {
602
- const db = getDb();
603
- const rows = await db
604
- .select({
605
- fieldName: schema.crmRecordFields.fieldName,
606
- valueType: schema.crmRecordFields.valueType,
607
- stringValue: schema.crmRecordFields.stringValue,
608
- numberValue: schema.crmRecordFields.numberValue,
609
- booleanValue: schema.crmRecordFields.booleanValue,
610
- jsonValue: schema.crmRecordFields.jsonValue,
611
- })
612
- .from(schema.crmRecordFields)
613
- .where(
614
- and(
615
- eq(schema.crmRecordFields.recordId, recordId),
616
- eq(schema.crmRecordFields.storagePolicy, "local-authoritative"),
617
- accessFilter(schema.crmRecordFields, schema.crmRecordFieldShares),
618
- ),
619
- );
620
- const allowed = requested?.length ? new Set(requested) : null;
621
- return Object.fromEntries(
622
- rows
623
- .filter((field) => !allowed || allowed.has(field.fieldName))
624
- .map((field) => [field.fieldName, fieldValue(field as StoredField)]),
625
- ) as Record<string, CrmValue>;
626
- }
627
-
628
- async function materializeRecord(input: {
629
- row: typeof schema.crmRecords.$inferSelect;
630
- connection: NativeConnection;
631
- fields?: string[];
632
- }): Promise<CrmRecord> {
633
- const fields = await fieldsForRecord(input.row.id, input.fields);
634
- const scope = scopeFromConnection(input.connection);
635
- const observedAt = input.row.updatedAt;
636
- return {
637
- ref: {
638
- connectionId: input.connection.id,
639
- provider: "native",
640
- accountId: input.connection.accountId ?? undefined,
641
- actorId: scope.actorId,
642
- objectType: input.row.objectType,
643
- kind: input.row.kind as CrmObjectKind,
644
- remoteId: input.row.remoteId,
645
- localId: input.row.id,
646
- },
647
- displayName: input.row.displayName,
648
- fields,
649
- remoteRevision: input.row.remoteRevision ?? undefined,
650
- remoteUpdatedAt: input.row.remoteUpdatedAt ?? undefined,
651
- deleted: input.row.tombstone,
652
- accessScope: scope,
653
- provenance: Object.keys(fields).map((fieldName) => ({
654
- provider: "native",
655
- connectionId: input.connection.id,
656
- objectType: input.row.objectType,
657
- remoteId: input.row.remoteId,
658
- fieldName,
659
- remoteRevision: input.row.remoteRevision ?? undefined,
660
- observedAt,
661
- })),
662
- };
663
- }
664
-
665
- const NATIVE_CAPABILITIES: CrmAdapterCapabilities = {
666
- schemaDiscovery: true,
667
- customObjects: true,
668
- search: true,
669
- incrementalSync: true,
670
- deletedRecordSync: true,
671
- conditionalMutations: true,
672
- labeledRelationships: true,
673
- perFieldPermissions: true,
674
- perRecordPermissions: true,
675
- };
676
-
677
- export class NativeCrmAdapter implements CrmAdapter {
678
- readonly capabilities = NATIVE_CAPABILITIES;
679
- readonly connection: CrmConnectionRef;
680
- readonly #nativeConnection: NativeConnection;
681
- readonly #initiatedBy: "human" | "agent" | "automation";
682
-
683
- constructor(
684
- connection: NativeConnection,
685
- initiatedBy: "human" | "agent" | "automation" = "agent",
686
- ) {
687
- this.#nativeConnection = connection;
688
- this.#initiatedBy = initiatedBy;
689
- const scope = scopeFromConnection(connection);
690
- this.connection = {
691
- connectionId: connection.id,
692
- provider: "native",
693
- ...(connection.accountId ? { accountId: connection.accountId } : {}),
694
- ...(scope.actorId ? { actorId: scope.actorId } : {}),
695
- };
696
- }
697
-
698
- getAccessScope(): CrmAccessScope {
699
- return scopeFromConnection(this.#nativeConnection);
700
- }
701
-
702
- async discoverObjects(): Promise<CrmObjectDefinition[]> {
703
- const db = getDb();
704
- const objects = await db
705
- .select()
706
- .from(schema.crmObjects)
707
- .where(
708
- and(
709
- eq(schema.crmObjects.connectionId, this.connection.connectionId),
710
- eq(schema.crmObjects.provider, "native"),
711
- accessFilter(schema.crmObjects, schema.crmObjectShares),
712
- ),
713
- )
714
- .orderBy(asc(schema.crmObjects.objectType));
715
- return Promise.all(
716
- objects.map((object) => this.describeObject(object.objectType)),
717
- );
718
- }
719
-
720
- async describeObject(objectType: string): Promise<CrmObjectDefinition> {
721
- const db = getDb();
722
- const [object] = await db
723
- .select()
724
- .from(schema.crmObjects)
725
- .where(
726
- and(
727
- eq(schema.crmObjects.connectionId, this.connection.connectionId),
728
- eq(schema.crmObjects.objectType, objectType),
729
- accessFilter(schema.crmObjects, schema.crmObjectShares),
730
- ),
731
- )
732
- .limit(1);
733
- if (!object)
734
- throw new Error(`Native CRM object ${objectType} is not registered.`);
735
- const policies = await db
736
- .select()
737
- .from(schema.crmFieldPolicies)
738
- .where(
739
- and(
740
- eq(
741
- schema.crmFieldPolicies.connectionId,
742
- this.connection.connectionId,
743
- ),
744
- eq(schema.crmFieldPolicies.objectType, objectType),
745
- accessFilter(schema.crmFieldPolicies, schema.crmFieldPolicyShares),
746
- ),
747
- )
748
- .orderBy(asc(schema.crmFieldPolicies.fieldName));
749
- return {
750
- ...this.connection,
751
- objectType,
752
- kind: object.kind as CrmObjectKind,
753
- label: object.label,
754
- pluralLabel: object.pluralLabel,
755
- custom: object.custom,
756
- queryable: object.queryable,
757
- searchable: object.searchable,
758
- createable: object.createable,
759
- updateable: object.updateable,
760
- deleteable: object.deleteable,
761
- fields: policies.map((policy) => ({
762
- name: policy.fieldName,
763
- label: policy.label,
764
- valueType: policy.valueType as CrmFieldDefinition["valueType"],
765
- storagePolicy: "local-authoritative",
766
- sensitive: policy.sensitive,
767
- readable: policy.readable,
768
- createable: policy.createable,
769
- updateable: policy.updateable,
770
- required: policy.required,
771
- })),
772
- };
773
- }
774
-
775
- async syncPage(input: {
776
- scope: CrmSyncScope;
777
- fieldAllowList: string[];
778
- cursor?: string;
779
- limit: number;
780
- }): Promise<CrmSyncPage> {
781
- const db = getDb();
782
- const limit = Math.min(Math.max(input.limit, 1), MAX_PAGE_SIZE);
783
- const offset = decodeCursor(input.cursor);
784
- const conditions = [
785
- eq(schema.crmRecords.connectionId, this.connection.connectionId),
786
- eq(schema.crmRecords.provider, "native"),
787
- eq(schema.crmRecords.objectType, input.scope.objectType),
788
- accessFilter(schema.crmRecords, schema.crmRecordShares),
789
- ];
790
- if (!input.scope.includeDeleted)
791
- conditions.push(eq(schema.crmRecords.tombstone, false));
792
- if (input.scope.recordIds?.length)
793
- conditions.push(
794
- inArray(schema.crmRecords.remoteId, input.scope.recordIds),
795
- );
796
- if (input.scope.ownerIds?.length)
797
- conditions.push(
798
- inArray(schema.crmRecords.ownerRemoteId, input.scope.ownerIds),
799
- );
800
- if (input.scope.pipelineIds?.length)
801
- conditions.push(
802
- inArray(schema.crmRecords.pipelineId, input.scope.pipelineIds),
803
- );
804
- if (input.scope.updatedAfter)
805
- conditions.push(
806
- gt(schema.crmRecords.updatedAt, input.scope.updatedAfter),
807
- );
808
- if (input.scope.associatedRecordIds?.length) {
809
- throw new Error(
810
- "Native CRM sync does not infer associated-record cohorts; use explicit recordIds.",
811
- );
812
- }
813
- const rows = await db
814
- .select()
815
- .from(schema.crmRecords)
816
- .where(and(...conditions))
817
- .orderBy(asc(schema.crmRecords.updatedAt), asc(schema.crmRecords.id))
818
- .limit(limit + 1)
819
- .offset(offset);
820
- const page = rows.slice(0, limit);
821
- return {
822
- records: await Promise.all(
823
- page.map((row) =>
824
- materializeRecord({
825
- row,
826
- connection: this.#nativeConnection,
827
- fields: input.fieldAllowList.slice(0, MAX_FIELDS),
828
- }),
829
- ),
830
- ),
831
- relationships: [],
832
- ...(rows.length > limit
833
- ? { nextCursor: encodeCursor(offset + limit) }
834
- : {}),
835
- complete: rows.length <= limit,
836
- };
837
- }
838
-
839
- async getRecord(input: {
840
- record: CrmRecordRef;
841
- fields: string[];
842
- }): Promise<CrmRecord | null> {
843
- const db = getDb();
844
- const conditions = [
845
- eq(schema.crmRecords.connectionId, this.connection.connectionId),
846
- eq(schema.crmRecords.provider, "native"),
847
- eq(schema.crmRecords.objectType, input.record.objectType),
848
- eq(schema.crmRecords.remoteId, input.record.remoteId),
849
- accessFilter(schema.crmRecords, schema.crmRecordShares),
850
- ];
851
- if (input.record.localId)
852
- conditions.push(eq(schema.crmRecords.id, input.record.localId));
853
- const [row] = await db
854
- .select()
855
- .from(schema.crmRecords)
856
- .where(and(...conditions))
857
- .limit(1);
858
- return row
859
- ? materializeRecord({
860
- row,
861
- connection: this.#nativeConnection,
862
- fields: input.fields.slice(0, MAX_FIELDS),
863
- })
864
- : null;
865
- }
866
-
867
- async search(input: {
868
- objectTypes: string[];
869
- query: string;
870
- fields: string[];
871
- limit: number;
872
- cursor?: string;
873
- }): Promise<CrmSyncPage> {
874
- if (!input.objectTypes.length || input.objectTypes.length > 50)
875
- throw new Error("Native CRM search requires 1 to 50 object types.");
876
- const db = getDb();
877
- const limit = Math.min(Math.max(input.limit, 1), MAX_PAGE_SIZE);
878
- const offset = decodeCursor(input.cursor);
879
- const query = input.query.trim().slice(0, 120);
880
- const rows = await db
881
- .select()
882
- .from(schema.crmRecords)
883
- .where(
884
- and(
885
- eq(schema.crmRecords.connectionId, this.connection.connectionId),
886
- eq(schema.crmRecords.provider, "native"),
887
- eq(schema.crmRecords.tombstone, false),
888
- inArray(schema.crmRecords.objectType, input.objectTypes),
889
- or(
890
- like(schema.crmRecords.displayName, `%${query}%`),
891
- like(schema.crmRecords.primaryEmail, `%${query}%`),
892
- like(schema.crmRecords.domain, `%${query}%`),
893
- ),
894
- accessFilter(schema.crmRecords, schema.crmRecordShares),
895
- ),
896
- )
897
- .orderBy(asc(schema.crmRecords.displayName), asc(schema.crmRecords.id))
898
- .limit(limit + 1)
899
- .offset(offset);
900
- const page = rows.slice(0, limit);
901
- return {
902
- records: await Promise.all(
903
- page.map((row) =>
904
- materializeRecord({
905
- row,
906
- connection: this.#nativeConnection,
907
- fields: input.fields.slice(0, MAX_FIELDS),
908
- }),
909
- ),
910
- ),
911
- relationships: [],
912
- ...(rows.length > limit
913
- ? { nextCursor: encodeCursor(offset + limit) }
914
- : {}),
915
- complete: rows.length <= limit,
916
- };
917
- }
918
-
919
- async listRelationships(input: {
920
- record: CrmRecordRef;
921
- targetObjectTypes?: string[];
922
- limit: number;
923
- cursor?: string;
924
- }): Promise<{
925
- relationships: CrmRelationship[];
926
- nextCursor?: string;
927
- complete: boolean;
928
- }> {
929
- const source = await this.getRecord({ record: input.record, fields: [] });
930
- if (!source?.ref.localId) return { relationships: [], complete: true };
931
- const db = getDb();
932
- const limit = Math.min(Math.max(input.limit, 1), MAX_PAGE_SIZE);
933
- const rows = await db
934
- .select()
935
- .from(schema.crmRelationships)
936
- .where(
937
- and(
938
- eq(
939
- schema.crmRelationships.connectionId,
940
- this.connection.connectionId,
941
- ),
942
- eq(schema.crmRelationships.fromRecordId, source.ref.localId),
943
- eq(schema.crmRelationships.tombstone, false),
944
- accessFilter(schema.crmRelationships, schema.crmRelationshipShares),
945
- ),
946
- )
947
- .orderBy(asc(schema.crmRelationships.id))
948
- .limit(limit + 1)
949
- .offset(decodeCursor(input.cursor));
950
- const page = rows.slice(0, limit);
951
- const targetIds = page.map((row) => row.toRecordId);
952
- if (!targetIds.length)
953
- return { relationships: [], complete: rows.length <= limit };
954
- const targets = await db
955
- .select()
956
- .from(schema.crmRecords)
957
- .where(
958
- and(
959
- inArray(schema.crmRecords.id, targetIds),
960
- eq(schema.crmRecords.connectionId, this.connection.connectionId),
961
- eq(schema.crmRecords.tombstone, false),
962
- accessFilter(schema.crmRecords, schema.crmRecordShares),
963
- ),
964
- );
965
- const byId = new Map(targets.map((target) => [target.id, target]));
966
- const relationships = page.flatMap((relationship) => {
967
- const target = byId.get(relationship.toRecordId);
968
- if (
969
- !target ||
970
- (input.targetObjectTypes?.length &&
971
- !input.targetObjectTypes.includes(target.objectType))
972
- )
973
- return [];
974
- return [
975
- {
976
- from: source.ref,
977
- to: {
978
- ...this.connection,
979
- objectType: target.objectType,
980
- kind: target.kind as CrmObjectKind,
981
- remoteId: target.remoteId,
982
- localId: target.id,
983
- },
984
- relationshipType: relationship.relationshipType,
985
- ...(relationship.label ? { label: relationship.label } : {}),
986
- ...(relationship.inverseLabel
987
- ? { inverseLabel: relationship.inverseLabel }
988
- : {}),
989
- ...(relationship.sourceField
990
- ? { sourceField: relationship.sourceField }
991
- : {}),
992
- },
993
- ];
994
- });
995
- const offset = decodeCursor(input.cursor);
996
- return {
997
- relationships,
998
- ...(rows.length > limit
999
- ? { nextCursor: encodeCursor(offset + limit) }
1000
- : {}),
1001
- complete: rows.length <= limit,
1002
- };
1003
- }
1004
-
1005
- async applyMutation(mutation: CrmMutation): Promise<CrmMutationResult> {
1006
- if (
1007
- mutation.record.connectionId !== this.connection.connectionId ||
1008
- mutation.record.provider !== "native"
1009
- ) {
1010
- return {
1011
- status: "rejected",
1012
- message: "Native CRM mutations must target this native connection.",
1013
- };
1014
- }
1015
- if (!mutation.idempotencyKey || mutation.idempotencyKey.length > 256) {
1016
- return {
1017
- status: "rejected",
1018
- message: "Native CRM mutations require a bounded idempotency key.",
1019
- };
1020
- }
1021
- const fields = mutationFields(mutation);
1022
- if (mutation.operation === "create")
1023
- return this.createRecord(mutation, fields);
1024
- if (mutation.operation === "update")
1025
- return this.updateRecord(mutation, fields);
1026
- if (mutation.operation === "delete") return this.deleteRecord(mutation);
1027
- if (
1028
- mutation.operation === "associate" ||
1029
- mutation.operation === "disassociate"
1030
- ) {
1031
- return this.mutateRelationship(mutation);
1032
- }
1033
- return { status: "rejected", message: "Unsupported native CRM mutation." };
1034
- }
1035
-
1036
- private async replayOrStart(mutation: CrmMutation, patchJson: string) {
1037
- const db = getDb();
1038
- const [existing] = await db
1039
- .select()
1040
- .from(schema.crmMutations)
1041
- .where(
1042
- and(
1043
- eq(schema.crmMutations.idempotencyKey, mutation.idempotencyKey),
1044
- accessFilter(schema.crmMutations, schema.crmMutationShares),
1045
- ),
1046
- )
1047
- .limit(1);
1048
- if (!existing) return null;
1049
- if (
1050
- existing.operation !== mutation.operation ||
1051
- existing.patchJson !== patchJson
1052
- ) {
1053
- throw new Error(
1054
- "Native CRM idempotency key was already used for a different mutation.",
1055
- );
1056
- }
1057
- if (existing.status === "conflict")
1058
- return {
1059
- status: "conflict" as const,
1060
- message: existing.error ?? undefined,
1061
- };
1062
- const record = existing.recordId
1063
- ? await this.getRecord({
1064
- record: { ...mutation.record, localId: existing.recordId },
1065
- fields: [],
1066
- })
1067
- : null;
1068
- return {
1069
- status: "applied" as const,
1070
- ...(record ? { record, remoteRevision: record.remoteRevision } : {}),
1071
- };
1072
- }
1073
-
1074
- private async createRecord(
1075
- mutation: CrmMutation,
1076
- fields: Record<string, CrmValue>,
1077
- ): Promise<CrmMutationResult> {
1078
- const objectType = mutation.record.objectType;
1079
- await ensureNativeObject({
1080
- connection: this.#nativeConnection,
1081
- objectType,
1082
- fields: Object.entries(fields).map(([name, value]) =>
1083
- nativeField(name, valueType(value)),
1084
- ),
1085
- });
1086
- const patchJson = json({ fields });
1087
- const replay = await this.replayOrStart(mutation, patchJson);
1088
- if (replay) return replay;
1089
- const db = getDb();
1090
- const remoteId = mutation.record.remoteId || crypto.randomUUID();
1091
- const recordId = crypto.randomUUID();
1092
- const now = new Date().toISOString();
1093
- const revision = "1";
1094
- try {
1095
- await db.transaction(async (tx) => {
1096
- const [duplicate] = await tx
1097
- .select({ id: schema.crmRecords.id })
1098
- .from(schema.crmRecords)
1099
- .where(
1100
- and(
1101
- eq(schema.crmRecords.connectionId, this.connection.connectionId),
1102
- eq(schema.crmRecords.objectType, objectType),
1103
- eq(schema.crmRecords.remoteId, remoteId),
1104
- accessFilter(schema.crmRecords, schema.crmRecordShares),
1105
- ),
1106
- )
1107
- .limit(1);
1108
- if (duplicate) throw new Error("NATIVE_CRM_DUPLICATE_RECORD");
1109
- await tx.insert(schema.crmRecords).values({
1110
- id: recordId,
1111
- connectionId: this.connection.connectionId,
1112
- provider: "native",
1113
- objectType,
1114
- kind: kindForObject(objectType),
1115
- remoteId,
1116
- ...summaryColumns(fields, remoteId),
1117
- remoteRevision: revision,
1118
- remoteUpdatedAt: now,
1119
- accessScopeKey: this.getAccessScope().key,
1120
- accessScopeJson: json(this.getAccessScope(), 4_000),
1121
- tombstone: false,
1122
- ownerEmail: this.#nativeConnection.ownerEmail,
1123
- orgId: this.#nativeConnection.orgId,
1124
- visibility: this.#nativeConnection.visibility,
1125
- createdAt: now,
1126
- updatedAt: now,
1127
- });
1128
- for (const [fieldName, value] of Object.entries(fields)) {
1129
- await tx.insert(schema.crmRecordFields).values({
1130
- id: crypto.randomUUID(),
1131
- recordId,
1132
- fieldName,
1133
- valueType: valueType(value),
1134
- storagePolicy: "local-authoritative",
1135
- ...fieldColumns(value),
1136
- provenanceJson: json(
1137
- [
1138
- {
1139
- provider: "native",
1140
- connectionId: this.connection.connectionId,
1141
- objectType,
1142
- remoteId,
1143
- fieldName,
1144
- remoteRevision: revision,
1145
- observedAt: now,
1146
- },
1147
- ],
1148
- 4_000,
1149
- ),
1150
- accessScopeKey: this.getAccessScope().key,
1151
- accessScopeJson: json(this.getAccessScope(), 4_000),
1152
- remoteRevision: revision,
1153
- ownerEmail: this.#nativeConnection.ownerEmail,
1154
- orgId: this.#nativeConnection.orgId,
1155
- visibility: this.#nativeConnection.visibility,
1156
- createdAt: now,
1157
- updatedAt: now,
1158
- });
1159
- }
1160
- await tx.insert(schema.crmMutations).values({
1161
- id: crypto.randomUUID(),
1162
- recordId,
1163
- connectionId: this.connection.connectionId,
1164
- operation: "create",
1165
- initiatedBy: this.#initiatedBy,
1166
- target: "local",
1167
- policyDecision: "execute",
1168
- risk: "routine",
1169
- status: "applied",
1170
- patchJson,
1171
- beforeJson: "{}",
1172
- afterJson: json({ remoteId, revision }),
1173
- idempotencyKey: mutation.idempotencyKey,
1174
- providerRemoteRevision: revision,
1175
- appliedAt: now,
1176
- ownerEmail: this.#nativeConnection.ownerEmail,
1177
- orgId: this.#nativeConnection.orgId,
1178
- visibility: this.#nativeConnection.visibility,
1179
- createdAt: now,
1180
- updatedAt: now,
1181
- });
1182
- });
1183
- } catch (error) {
1184
- if (
1185
- error instanceof Error &&
1186
- error.message === "NATIVE_CRM_DUPLICATE_RECORD"
1187
- )
1188
- return {
1189
- status: "conflict",
1190
- message: "A native CRM record with this id already exists.",
1191
- };
1192
- throw error;
1193
- }
1194
- const record = await this.getRecord({
1195
- record: { ...mutation.record, remoteId, localId: recordId },
1196
- fields: Object.keys(fields),
1197
- });
1198
- return record
1199
- ? { status: "applied", record, remoteRevision: revision }
1200
- : { status: "rejected", message: "Native CRM record was not persisted." };
1201
- }
1202
-
1203
- private async updateRecord(
1204
- mutation: CrmMutation,
1205
- fields: Record<string, CrmValue>,
1206
- ): Promise<CrmMutationResult> {
1207
- await ensureNativeObject({
1208
- connection: this.#nativeConnection,
1209
- objectType: mutation.record.objectType,
1210
- fields: Object.entries(fields).map(([name, value]) =>
1211
- nativeField(name, valueType(value)),
1212
- ),
1213
- });
1214
- const patchJson = json({ fields });
1215
- const replay = await this.replayOrStart(mutation, patchJson);
1216
- if (replay) return replay;
1217
- const db = getDb();
1218
- const [record] = await db
1219
- .select()
1220
- .from(schema.crmRecords)
1221
- .where(
1222
- and(
1223
- eq(schema.crmRecords.connectionId, this.connection.connectionId),
1224
- eq(schema.crmRecords.objectType, mutation.record.objectType),
1225
- eq(schema.crmRecords.remoteId, mutation.record.remoteId),
1226
- accessFilter(
1227
- schema.crmRecords,
1228
- schema.crmRecordShares,
1229
- undefined,
1230
- "editor",
1231
- ),
1232
- ),
1233
- )
1234
- .limit(1);
1235
- if (!record || record.tombstone)
1236
- return {
1237
- status: "rejected",
1238
- message: "Native CRM record is unavailable.",
1239
- };
1240
- await assertAccess("crm-record", record.id, "editor");
1241
- if (
1242
- mutation.expectedRemoteRevision &&
1243
- mutation.expectedRemoteRevision !== record.remoteRevision
1244
- )
1245
- return {
1246
- status: "conflict",
1247
- message: "Native CRM record revision changed.",
1248
- };
1249
- const current = await fieldsForRecord(record.id);
1250
- const merged = { ...current, ...fields };
1251
- const revision = nextNativeRevision(record.remoteRevision);
1252
- const now = new Date().toISOString();
1253
- const revisionGuard =
1254
- record.remoteRevision === null
1255
- ? isNull(schema.crmRecords.remoteRevision)
1256
- : eq(schema.crmRecords.remoteRevision, record.remoteRevision);
1257
- try {
1258
- await db.transaction(async (tx) => {
1259
- const guarded = await tx
1260
- .update(schema.crmRecords)
1261
- .set({
1262
- ...summaryColumns(merged, record.remoteId),
1263
- remoteRevision: revision,
1264
- remoteUpdatedAt: now,
1265
- updatedAt: now,
1266
- })
1267
- .where(
1268
- and(
1269
- eq(schema.crmRecords.id, record.id),
1270
- revisionGuard,
1271
- accessFilter(
1272
- schema.crmRecords,
1273
- schema.crmRecordShares,
1274
- undefined,
1275
- "editor",
1276
- ),
1277
- ),
1278
- )
1279
- .returning({ id: schema.crmRecords.id });
1280
- if (guarded.length !== 1)
1281
- throw new Error("NATIVE_CRM_REVISION_CONFLICT");
1282
- for (const [fieldName, value] of Object.entries(fields)) {
1283
- const [existing] = await tx
1284
- .select({ id: schema.crmRecordFields.id })
1285
- .from(schema.crmRecordFields)
1286
- .where(
1287
- and(
1288
- eq(schema.crmRecordFields.recordId, record.id),
1289
- eq(schema.crmRecordFields.fieldName, fieldName),
1290
- accessFilter(
1291
- schema.crmRecordFields,
1292
- schema.crmRecordFieldShares,
1293
- undefined,
1294
- "editor",
1295
- ),
1296
- ),
1297
- )
1298
- .limit(1);
1299
- const values = {
1300
- valueType: valueType(value),
1301
- storagePolicy: "local-authoritative" as const,
1302
- ...fieldColumns(value),
1303
- provenanceJson: json(
1304
- [
1305
- {
1306
- provider: "native",
1307
- connectionId: this.connection.connectionId,
1308
- objectType: record.objectType,
1309
- remoteId: record.remoteId,
1310
- fieldName,
1311
- remoteRevision: revision,
1312
- observedAt: now,
1313
- },
1314
- ],
1315
- 4_000,
1316
- ),
1317
- accessScopeKey: this.getAccessScope().key,
1318
- accessScopeJson: json(this.getAccessScope(), 4_000),
1319
- remoteRevision: revision,
1320
- updatedAt: now,
1321
- };
1322
- if (existing) {
1323
- await tx
1324
- .update(schema.crmRecordFields)
1325
- .set(values)
1326
- .where(
1327
- and(
1328
- eq(schema.crmRecordFields.id, existing.id),
1329
- accessFilter(
1330
- schema.crmRecordFields,
1331
- schema.crmRecordFieldShares,
1332
- undefined,
1333
- "editor",
1334
- ),
1335
- ),
1336
- );
1337
- } else {
1338
- await tx.insert(schema.crmRecordFields).values({
1339
- id: crypto.randomUUID(),
1340
- recordId: record.id,
1341
- fieldName,
1342
- ...values,
1343
- ownerEmail: record.ownerEmail,
1344
- orgId: record.orgId,
1345
- visibility: record.visibility,
1346
- createdAt: now,
1347
- });
1348
- }
1349
- }
1350
- await tx.insert(schema.crmMutations).values({
1351
- id: crypto.randomUUID(),
1352
- recordId: record.id,
1353
- connectionId: this.connection.connectionId,
1354
- operation: "update",
1355
- initiatedBy: this.#initiatedBy,
1356
- target: "local",
1357
- policyDecision: "execute",
1358
- risk: "routine",
1359
- status: "applied",
1360
- patchJson,
1361
- beforeJson: json({ remoteRevision: record.remoteRevision }),
1362
- afterJson: json({ remoteRevision: revision }),
1363
- idempotencyKey: mutation.idempotencyKey,
1364
- expectedRemoteRevision: mutation.expectedRemoteRevision ?? null,
1365
- providerRemoteRevision: revision,
1366
- appliedAt: now,
1367
- ownerEmail: record.ownerEmail,
1368
- orgId: record.orgId,
1369
- visibility: record.visibility,
1370
- createdAt: now,
1371
- updatedAt: now,
1372
- });
1373
- });
1374
- } catch (error) {
1375
- if (
1376
- error instanceof Error &&
1377
- error.message === "NATIVE_CRM_REVISION_CONFLICT"
1378
- )
1379
- return {
1380
- status: "conflict",
1381
- message: "Native CRM record revision changed.",
1382
- };
1383
- throw error;
1384
- }
1385
- const updated = await this.getRecord({
1386
- record: mutation.record,
1387
- fields: Object.keys(merged),
1388
- });
1389
- return updated
1390
- ? { status: "applied", record: updated, remoteRevision: revision }
1391
- : { status: "rejected", message: "Native CRM record was not updated." };
1392
- }
1393
-
1394
- private async deleteRecord(
1395
- mutation: CrmMutation,
1396
- ): Promise<CrmMutationResult> {
1397
- const patchJson = "{}";
1398
- const replay = await this.replayOrStart(mutation, patchJson);
1399
- if (replay) return replay;
1400
- const db = getDb();
1401
- const [record] = await db
1402
- .select()
1403
- .from(schema.crmRecords)
1404
- .where(
1405
- and(
1406
- eq(schema.crmRecords.connectionId, this.connection.connectionId),
1407
- eq(schema.crmRecords.objectType, mutation.record.objectType),
1408
- eq(schema.crmRecords.remoteId, mutation.record.remoteId),
1409
- accessFilter(
1410
- schema.crmRecords,
1411
- schema.crmRecordShares,
1412
- undefined,
1413
- "editor",
1414
- ),
1415
- ),
1416
- )
1417
- .limit(1);
1418
- if (!record)
1419
- return {
1420
- status: "rejected",
1421
- message: "Native CRM record is unavailable.",
1422
- };
1423
- await assertAccess("crm-record", record.id, "editor");
1424
- if (
1425
- mutation.expectedRemoteRevision &&
1426
- mutation.expectedRemoteRevision !== record.remoteRevision
1427
- )
1428
- return {
1429
- status: "conflict",
1430
- message: "Native CRM record revision changed.",
1431
- };
1432
- const revision = nextNativeRevision(record.remoteRevision);
1433
- const now = new Date().toISOString();
1434
- const revisionGuard =
1435
- record.remoteRevision === null
1436
- ? isNull(schema.crmRecords.remoteRevision)
1437
- : eq(schema.crmRecords.remoteRevision, record.remoteRevision);
1438
- try {
1439
- await db.transaction(async (tx) => {
1440
- const guarded = await tx
1441
- .update(schema.crmRecords)
1442
- .set({
1443
- tombstone: true,
1444
- remoteRevision: revision,
1445
- remoteUpdatedAt: now,
1446
- updatedAt: now,
1447
- })
1448
- .where(
1449
- and(
1450
- eq(schema.crmRecords.id, record.id),
1451
- revisionGuard,
1452
- accessFilter(
1453
- schema.crmRecords,
1454
- schema.crmRecordShares,
1455
- undefined,
1456
- "editor",
1457
- ),
1458
- ),
1459
- )
1460
- .returning({ id: schema.crmRecords.id });
1461
- if (guarded.length !== 1)
1462
- throw new Error("NATIVE_CRM_REVISION_CONFLICT");
1463
- const relationships = await tx
1464
- .select({ id: schema.crmRelationships.id })
1465
- .from(schema.crmRelationships)
1466
- .where(
1467
- and(
1468
- eq(
1469
- schema.crmRelationships.connectionId,
1470
- this.connection.connectionId,
1471
- ),
1472
- or(
1473
- eq(schema.crmRelationships.fromRecordId, record.id),
1474
- eq(schema.crmRelationships.toRecordId, record.id),
1475
- ),
1476
- accessFilter(
1477
- schema.crmRelationships,
1478
- schema.crmRelationshipShares,
1479
- undefined,
1480
- "editor",
1481
- ),
1482
- ),
1483
- );
1484
- for (const relationship of relationships) {
1485
- await assertAccess("crm-relationship", relationship.id, "editor");
1486
- await tx
1487
- .update(schema.crmRelationships)
1488
- .set({ tombstone: true, updatedAt: now })
1489
- .where(
1490
- and(
1491
- eq(schema.crmRelationships.id, relationship.id),
1492
- accessFilter(
1493
- schema.crmRelationships,
1494
- schema.crmRelationshipShares,
1495
- undefined,
1496
- "editor",
1497
- ),
1498
- ),
1499
- );
1500
- }
1501
- await tx.insert(schema.crmMutations).values({
1502
- id: crypto.randomUUID(),
1503
- recordId: record.id,
1504
- connectionId: this.connection.connectionId,
1505
- operation: "delete",
1506
- initiatedBy: this.#initiatedBy,
1507
- target: "local",
1508
- policyDecision: "execute",
1509
- risk: "routine",
1510
- status: "applied",
1511
- patchJson,
1512
- beforeJson: json({ remoteRevision: record.remoteRevision }),
1513
- afterJson: json({ remoteRevision: revision, tombstone: true }),
1514
- idempotencyKey: mutation.idempotencyKey,
1515
- expectedRemoteRevision: mutation.expectedRemoteRevision ?? null,
1516
- providerRemoteRevision: revision,
1517
- appliedAt: now,
1518
- ownerEmail: record.ownerEmail,
1519
- orgId: record.orgId,
1520
- visibility: record.visibility,
1521
- createdAt: now,
1522
- updatedAt: now,
1523
- });
1524
- });
1525
- } catch (error) {
1526
- if (
1527
- error instanceof Error &&
1528
- error.message === "NATIVE_CRM_REVISION_CONFLICT"
1529
- )
1530
- return {
1531
- status: "conflict",
1532
- message: "Native CRM record revision changed.",
1533
- };
1534
- throw error;
1535
- }
1536
- const deleted = await this.getRecord({
1537
- record: mutation.record,
1538
- fields: [],
1539
- });
1540
- return {
1541
- status: "applied",
1542
- ...(deleted ? { record: deleted } : {}),
1543
- remoteRevision: revision,
1544
- };
1545
- }
1546
-
1547
- private async mutateRelationship(
1548
- mutation: CrmMutation,
1549
- ): Promise<CrmMutationResult> {
1550
- if (!mutation.relationship)
1551
- return {
1552
- status: "rejected",
1553
- message: "Native relationship mutations require an edge.",
1554
- };
1555
- const patchJson = json({ relationship: mutation.relationship });
1556
- const replay = await this.replayOrStart(mutation, patchJson);
1557
- if (replay) return replay;
1558
- const from = await this.getRecord({
1559
- record: mutation.relationship.from,
1560
- fields: [],
1561
- });
1562
- const to = await this.getRecord({
1563
- record: mutation.relationship.to,
1564
- fields: [],
1565
- });
1566
- if (!from?.ref.localId || !to?.ref.localId)
1567
- return {
1568
- status: "rejected",
1569
- message: "Native relationship records are unavailable.",
1570
- };
1571
- await assertAccess("crm-record", from.ref.localId, "editor");
1572
- await assertAccess("crm-record", to.ref.localId, "editor");
1573
- const db = getDb();
1574
- const now = new Date().toISOString();
1575
- await db.transaction(async (tx) => {
1576
- const [existing] = await tx
1577
- .select({ id: schema.crmRelationships.id })
1578
- .from(schema.crmRelationships)
1579
- .where(
1580
- and(
1581
- eq(
1582
- schema.crmRelationships.connectionId,
1583
- this.connection.connectionId,
1584
- ),
1585
- eq(schema.crmRelationships.fromRecordId, from.ref.localId!),
1586
- eq(schema.crmRelationships.toRecordId, to.ref.localId!),
1587
- eq(
1588
- schema.crmRelationships.relationshipType,
1589
- mutation.relationship!.relationshipType,
1590
- ),
1591
- accessFilter(
1592
- schema.crmRelationships,
1593
- schema.crmRelationshipShares,
1594
- undefined,
1595
- "editor",
1596
- ),
1597
- ),
1598
- )
1599
- .limit(1);
1600
- if (existing) {
1601
- await assertAccess("crm-relationship", existing.id, "editor");
1602
- await tx
1603
- .update(schema.crmRelationships)
1604
- .set({
1605
- tombstone: mutation.operation === "disassociate",
1606
- label: mutation.relationship!.label ?? null,
1607
- inverseLabel: mutation.relationship!.inverseLabel ?? null,
1608
- sourceField: mutation.relationship!.sourceField ?? null,
1609
- updatedAt: now,
1610
- })
1611
- .where(
1612
- and(
1613
- eq(schema.crmRelationships.id, existing.id),
1614
- accessFilter(
1615
- schema.crmRelationships,
1616
- schema.crmRelationshipShares,
1617
- undefined,
1618
- "editor",
1619
- ),
1620
- ),
1621
- );
1622
- } else if (mutation.operation === "associate") {
1623
- await tx.insert(schema.crmRelationships).values({
1624
- id: crypto.randomUUID(),
1625
- connectionId: this.connection.connectionId,
1626
- fromRecordId: from.ref.localId!,
1627
- toRecordId: to.ref.localId!,
1628
- relationshipType: mutation.relationship!.relationshipType,
1629
- label: mutation.relationship!.label ?? null,
1630
- inverseLabel: mutation.relationship!.inverseLabel ?? null,
1631
- sourceField: mutation.relationship!.sourceField ?? null,
1632
- tombstone: false,
1633
- ownerEmail: this.#nativeConnection.ownerEmail,
1634
- orgId: this.#nativeConnection.orgId,
1635
- visibility: this.#nativeConnection.visibility,
1636
- createdAt: now,
1637
- updatedAt: now,
1638
- });
1639
- }
1640
- await tx.insert(schema.crmMutations).values({
1641
- id: crypto.randomUUID(),
1642
- recordId: from.ref.localId,
1643
- connectionId: this.connection.connectionId,
1644
- operation: mutation.operation,
1645
- initiatedBy: this.#initiatedBy,
1646
- target: "local",
1647
- policyDecision: "execute",
1648
- risk: "routine",
1649
- status: "applied",
1650
- patchJson,
1651
- beforeJson: "{}",
1652
- afterJson: json({ relationship: mutation.operation }),
1653
- idempotencyKey: mutation.idempotencyKey,
1654
- appliedAt: now,
1655
- ownerEmail: this.#nativeConnection.ownerEmail,
1656
- orgId: this.#nativeConnection.orgId,
1657
- visibility: this.#nativeConnection.visibility,
1658
- createdAt: now,
1659
- updatedAt: now,
1660
- });
1661
- });
1662
- return {
1663
- status: "applied",
1664
- record: from,
1665
- remoteRevision: from.remoteRevision,
1666
- };
1667
- }
1668
- }
1669
-
1670
- export async function createNativeCrmAdapter(options: {
1671
- connectionId: string;
1672
- initiatedBy?: "human" | "agent" | "automation";
1673
- accessTier?: "viewer" | "editor";
1674
- }): Promise<NativeCrmAdapter> {
1675
- const accessTier = options.accessTier ?? "editor";
1676
- const connection = await loadNativeConnection(
1677
- options.connectionId,
1678
- accessTier,
1679
- );
1680
- if (accessTier === "editor") {
1681
- await initializeNativeCrmDomain(options.connectionId);
1682
- }
1683
- return new NativeCrmAdapter(connection, options.initiatedBy);
1684
- }
1685
-
1686
- export async function createNativeCrmRecord(input: {
1687
- connectionId: string;
1688
- kind: "account" | "person" | "opportunity";
1689
- displayName: string;
1690
- fields: Record<string, CrmValue>;
1691
- idempotencyKey?: string;
1692
- initiatedBy?: "human" | "agent" | "automation";
1693
- }) {
1694
- const objectType =
1695
- input.kind === "account"
1696
- ? "accounts"
1697
- : input.kind === "person"
1698
- ? "people"
1699
- : "opportunities";
1700
- const adapter = await createNativeCrmAdapter({
1701
- connectionId: input.connectionId,
1702
- initiatedBy: input.initiatedBy,
1703
- });
1704
- return adapter.applyMutation({
1705
- operation: "create",
1706
- record: {
1707
- ...adapter.connection,
1708
- objectType,
1709
- kind: input.kind,
1710
- remoteId: `native-${crypto.randomUUID()}`,
1711
- },
1712
- fields: { ...input.fields, displayName: input.displayName },
1713
- idempotencyKey: input.idempotencyKey ?? crypto.randomUUID(),
1714
- });
1715
- }