@agent-native/core 0.118.0 → 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 (375) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +21 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/a2a/client.ts +32 -10
  5. package/corpus/core/src/cli/index.ts +18 -10
  6. package/corpus/core/src/cli/multi-frontier-runs.ts +5 -3
  7. package/corpus/core/src/cli/templates-meta.ts +0 -12
  8. package/corpus/core/src/cli/workspace-dev.ts +1 -1
  9. package/corpus/core/src/client/AgentPanel.tsx +9 -1
  10. package/corpus/core/src/client/RunStuckBanner.tsx +39 -47
  11. package/corpus/core/src/client/chat/action-chat-ui-surface.tsx +32 -0
  12. package/corpus/core/src/client/chat/message-components.tsx +53 -8
  13. package/corpus/core/src/client/chat/tool-call-display.tsx +24 -9
  14. package/corpus/core/src/client/chat/widgets/InlineExtensionWidget.tsx +0 -1
  15. package/corpus/core/src/client/chat/widgets/builtin-tool-renderers.tsx +7 -3
  16. package/corpus/core/src/client/conversation/AgentConversation.tsx +12 -1
  17. package/corpus/core/src/client/error-capture.ts +2 -6
  18. package/corpus/core/src/client/resources/McpConnectionSuggestion.tsx +20 -14
  19. package/corpus/core/src/client/resources/ResourcesPanel.tsx +5 -1
  20. package/corpus/core/src/client/resources/mcp-integration-catalog.ts +7 -17
  21. package/corpus/core/src/client/resources/mcp-integration-logos.ts +2 -2
  22. package/corpus/core/src/client/route-chunk-recovery.ts +5 -3
  23. package/corpus/core/src/client/session-replay.ts +3 -3
  24. package/corpus/core/src/client/settings/SettingsPanel.tsx +15 -6
  25. package/corpus/core/src/client/transcription/use-live-transcription.ts +64 -45
  26. package/corpus/core/src/deploy/build.ts +2 -1
  27. package/corpus/core/src/guards/db-tool-scoping.ts +1 -1
  28. package/corpus/core/src/integrations/adapters/slack.ts +84 -5
  29. package/corpus/core/src/integrations/index.ts +5 -1
  30. package/corpus/core/src/localization/default-messages.ts +1 -1
  31. package/corpus/core/src/mcp-client/oauth-routes.ts +45 -12
  32. package/corpus/core/src/server/realtime-voice.ts +4 -1
  33. package/corpus/core/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +7 -5
  34. package/corpus/core/src/terminal/cli-registry.ts +1 -1
  35. package/corpus/core/src/vite/client.ts +166 -1
  36. package/corpus/templates/analytics/app/hooks/use-dashboard-views.ts +13 -1
  37. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/ViewsMenu.tsx +10 -0
  38. package/corpus/templates/analytics/changelog/2026-07-22-show-a-useful-error-when-saving-a-dashboard-view-fails.md +6 -0
  39. package/corpus/templates/brain/app/routes/sources.tsx +181 -114
  40. package/corpus/templates/clips/AGENTS.md +8 -6
  41. package/corpus/templates/clips/actions/request-transcript.ts +90 -420
  42. package/corpus/templates/clips/actions/save-browser-transcript.ts +11 -28
  43. package/corpus/templates/clips/app/components/capture-install-options.tsx +5 -3
  44. package/corpus/templates/clips/app/components/player/transcript-panel.tsx +3 -136
  45. package/corpus/templates/clips/app/lib/capture-install-options.ts +15 -4
  46. package/corpus/templates/clips/app/routes/download.tsx +3 -2
  47. package/corpus/templates/clips/changelog/2026-07-22-fixed-an-intermittent-hydration-error-on-the-download-page.md +6 -0
  48. package/corpus/templates/clips/changelog/2026-07-22-fixed-transcript-generation-when-native-speech-capture-retur.md +6 -0
  49. package/corpus/templates/clips/changelog/2026-07-22-transcript-generation-now-retries-with-backup-speech-recogni.md +6 -0
  50. package/corpus/templates/clips/server/lib/public-agent-context.ts +2 -2
  51. package/corpus/templates/clips/server/register-secrets.ts +7 -6
  52. package/corpus/templates/content/app/components/editor/SlashCommandMenu.tsx +16 -84
  53. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +13 -7
  54. package/corpus/templates/content/app/components/editor/extensions/ImageBlock.tsx +2 -2
  55. package/corpus/templates/content/app/i18n-data.ts +10 -0
  56. package/corpus/templates/design/server/lib/fig-file-decoder.ts +1 -0
  57. package/corpus/templates/plan/app/entry.client.tsx +3 -0
  58. package/corpus/templates/plan/changelog/2026-07-22-recover-stale-pages-after-a-deployment-refresh-automatically.md +6 -0
  59. package/corpus/templates/slides/app/hooks/use-agent-generating.ts +10 -3
  60. package/corpus/templates/slides/app/pages/Index.tsx +1 -0
  61. package/corpus/templates/slides/changelog/2026-07-22-new-deck-prompts-now-open-in-their-own-visible-chat-thread.md +6 -0
  62. package/corpus/toolkit/CHANGELOG.md +9 -0
  63. package/corpus/toolkit/package.json +1 -1
  64. package/corpus/toolkit/src/composer/PromptComposer.tsx +5 -1
  65. package/corpus/toolkit/src/composer/RealtimeVoiceMode.tsx +119 -31
  66. package/corpus/toolkit/src/composer/useRealtimeVoiceMode.tsx +30 -0
  67. package/corpus/toolkit/src/conformance/runner.tsx +153 -1
  68. package/corpus/toolkit/src/editor/SharedRichEditor.tsx +13 -2
  69. package/corpus/toolkit/src/editor/SlashCommandMenu.tsx +20 -0
  70. package/corpus/toolkit/src/editor/index.ts +2 -0
  71. package/corpus/toolkit/src/styles.css +52 -23
  72. package/dist/a2a/client.d.ts +3 -1
  73. package/dist/a2a/client.d.ts.map +1 -1
  74. package/dist/a2a/client.js +17 -12
  75. package/dist/a2a/client.js.map +1 -1
  76. package/dist/cli/index.js +18 -8
  77. package/dist/cli/index.js.map +1 -1
  78. package/dist/cli/multi-frontier-runs.js +3 -3
  79. package/dist/cli/multi-frontier-runs.js.map +1 -1
  80. package/dist/cli/templates-meta.d.ts.map +1 -1
  81. package/dist/cli/templates-meta.js +0 -12
  82. package/dist/cli/templates-meta.js.map +1 -1
  83. package/dist/cli/workspace-dev.js +1 -1
  84. package/dist/cli/workspace-dev.js.map +1 -1
  85. package/dist/client/AgentPanel.d.ts.map +1 -1
  86. package/dist/client/AgentPanel.js +4 -1
  87. package/dist/client/AgentPanel.js.map +1 -1
  88. package/dist/client/RunStuckBanner.d.ts +3 -5
  89. package/dist/client/RunStuckBanner.d.ts.map +1 -1
  90. package/dist/client/RunStuckBanner.js +17 -18
  91. package/dist/client/RunStuckBanner.js.map +1 -1
  92. package/dist/client/chat/action-chat-ui-surface.d.ts +8 -0
  93. package/dist/client/chat/action-chat-ui-surface.d.ts.map +1 -0
  94. package/dist/client/chat/action-chat-ui-surface.js +10 -0
  95. package/dist/client/chat/action-chat-ui-surface.js.map +1 -0
  96. package/dist/client/chat/message-components.d.ts +3 -0
  97. package/dist/client/chat/message-components.d.ts.map +1 -1
  98. package/dist/client/chat/message-components.js +40 -7
  99. package/dist/client/chat/message-components.js.map +1 -1
  100. package/dist/client/chat/tool-call-display.d.ts +3 -1
  101. package/dist/client/chat/tool-call-display.d.ts.map +1 -1
  102. package/dist/client/chat/tool-call-display.js +13 -9
  103. package/dist/client/chat/tool-call-display.js.map +1 -1
  104. package/dist/client/chat/widgets/InlineExtensionWidget.d.ts.map +1 -1
  105. package/dist/client/chat/widgets/InlineExtensionWidget.js +1 -1
  106. package/dist/client/chat/widgets/InlineExtensionWidget.js.map +1 -1
  107. package/dist/client/chat/widgets/builtin-tool-renderers.d.ts.map +1 -1
  108. package/dist/client/chat/widgets/builtin-tool-renderers.js +5 -3
  109. package/dist/client/chat/widgets/builtin-tool-renderers.js.map +1 -1
  110. package/dist/client/conversation/AgentConversation.d.ts.map +1 -1
  111. package/dist/client/conversation/AgentConversation.js +3 -2
  112. package/dist/client/conversation/AgentConversation.js.map +1 -1
  113. package/dist/client/error-capture.d.ts.map +1 -1
  114. package/dist/client/error-capture.js +2 -2
  115. package/dist/client/error-capture.js.map +1 -1
  116. package/dist/client/resources/McpConnectionSuggestion.d.ts +1 -0
  117. package/dist/client/resources/McpConnectionSuggestion.d.ts.map +1 -1
  118. package/dist/client/resources/McpConnectionSuggestion.js +9 -8
  119. package/dist/client/resources/McpConnectionSuggestion.js.map +1 -1
  120. package/dist/client/resources/ResourcesPanel.d.ts.map +1 -1
  121. package/dist/client/resources/ResourcesPanel.js +3 -1
  122. package/dist/client/resources/ResourcesPanel.js.map +1 -1
  123. package/dist/client/resources/mcp-integration-catalog.d.ts +1 -0
  124. package/dist/client/resources/mcp-integration-catalog.d.ts.map +1 -1
  125. package/dist/client/resources/mcp-integration-catalog.js +4 -17
  126. package/dist/client/resources/mcp-integration-catalog.js.map +1 -1
  127. package/dist/client/resources/mcp-integration-logos.js +2 -2
  128. package/dist/client/resources/mcp-integration-logos.js.map +1 -1
  129. package/dist/client/route-chunk-recovery.d.ts.map +1 -1
  130. package/dist/client/route-chunk-recovery.js +5 -3
  131. package/dist/client/route-chunk-recovery.js.map +1 -1
  132. package/dist/client/session-replay.js +3 -3
  133. package/dist/client/session-replay.js.map +1 -1
  134. package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
  135. package/dist/client/settings/SettingsPanel.js +2 -2
  136. package/dist/client/settings/SettingsPanel.js.map +1 -1
  137. package/dist/client/transcription/use-live-transcription.d.ts +3 -3
  138. package/dist/client/transcription/use-live-transcription.d.ts.map +1 -1
  139. package/dist/client/transcription/use-live-transcription.js +22 -11
  140. package/dist/client/transcription/use-live-transcription.js.map +1 -1
  141. package/dist/collab/routes.d.ts +1 -1
  142. package/dist/collab/struct-routes.d.ts +1 -1
  143. package/dist/deploy/build.d.ts.map +1 -1
  144. package/dist/deploy/build.js +1 -1
  145. package/dist/deploy/build.js.map +1 -1
  146. package/dist/guards/db-tool-scoping.js +1 -1
  147. package/dist/guards/db-tool-scoping.js.map +1 -1
  148. package/dist/integrations/adapters/slack.d.ts +1 -0
  149. package/dist/integrations/adapters/slack.d.ts.map +1 -1
  150. package/dist/integrations/adapters/slack.js +61 -5
  151. package/dist/integrations/adapters/slack.js.map +1 -1
  152. package/dist/integrations/index.d.ts +1 -1
  153. package/dist/integrations/index.d.ts.map +1 -1
  154. package/dist/integrations/index.js +1 -1
  155. package/dist/integrations/index.js.map +1 -1
  156. package/dist/localization/default-messages.js +1 -1
  157. package/dist/localization/default-messages.js.map +1 -1
  158. package/dist/mcp-client/oauth-routes.d.ts +3 -0
  159. package/dist/mcp-client/oauth-routes.d.ts.map +1 -1
  160. package/dist/mcp-client/oauth-routes.js +41 -12
  161. package/dist/mcp-client/oauth-routes.js.map +1 -1
  162. package/dist/notifications/routes.d.ts +2 -2
  163. package/dist/observability/routes.d.ts +3 -3
  164. package/dist/provider-api/actions/custom-provider-registration.d.ts +8 -8
  165. package/dist/provider-api/actions/provider-api.d.ts +11 -11
  166. package/dist/secrets/routes.d.ts +6 -6
  167. package/dist/server/realtime-voice.d.ts.map +1 -1
  168. package/dist/server/realtime-voice.js +1 -1
  169. package/dist/server/realtime-voice.js.map +1 -1
  170. package/dist/server/transcribe-voice.d.ts +1 -1
  171. package/dist/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +7 -5
  172. package/dist/terminal/cli-registry.js +1 -1
  173. package/dist/terminal/cli-registry.js.map +1 -1
  174. package/dist/vite/client.d.ts +8 -1
  175. package/dist/vite/client.d.ts.map +1 -1
  176. package/dist/vite/client.js +113 -2
  177. package/dist/vite/client.js.map +1 -1
  178. package/package.json +2 -2
  179. package/src/a2a/client.ts +32 -10
  180. package/src/cli/index.ts +18 -10
  181. package/src/cli/multi-frontier-runs.ts +5 -3
  182. package/src/cli/templates-meta.ts +0 -12
  183. package/src/cli/workspace-dev.ts +1 -1
  184. package/src/client/AgentPanel.tsx +9 -1
  185. package/src/client/RunStuckBanner.tsx +39 -47
  186. package/src/client/chat/action-chat-ui-surface.tsx +32 -0
  187. package/src/client/chat/message-components.tsx +53 -8
  188. package/src/client/chat/tool-call-display.tsx +24 -9
  189. package/src/client/chat/widgets/InlineExtensionWidget.tsx +0 -1
  190. package/src/client/chat/widgets/builtin-tool-renderers.tsx +7 -3
  191. package/src/client/conversation/AgentConversation.tsx +12 -1
  192. package/src/client/error-capture.ts +2 -6
  193. package/src/client/resources/McpConnectionSuggestion.tsx +20 -14
  194. package/src/client/resources/ResourcesPanel.tsx +5 -1
  195. package/src/client/resources/mcp-integration-catalog.ts +7 -17
  196. package/src/client/resources/mcp-integration-logos.ts +2 -2
  197. package/src/client/route-chunk-recovery.ts +5 -3
  198. package/src/client/session-replay.ts +3 -3
  199. package/src/client/settings/SettingsPanel.tsx +15 -6
  200. package/src/client/transcription/use-live-transcription.ts +64 -45
  201. package/src/deploy/build.ts +2 -1
  202. package/src/guards/db-tool-scoping.ts +1 -1
  203. package/src/integrations/adapters/slack.ts +84 -5
  204. package/src/integrations/index.ts +5 -1
  205. package/src/localization/default-messages.ts +1 -1
  206. package/src/mcp-client/oauth-routes.ts +45 -12
  207. package/src/server/realtime-voice.ts +4 -1
  208. package/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +7 -5
  209. package/src/terminal/cli-registry.ts +1 -1
  210. package/src/vite/client.ts +166 -1
  211. package/corpus/templates/crm/.agents/skills/agent-native-docs/SKILL.md +0 -115
  212. package/corpus/templates/crm/.agents/skills/agent-native-toolkit/SKILL.md +0 -177
  213. package/corpus/templates/crm/.agents/skills/crm/SKILL.md +0 -128
  214. package/corpus/templates/crm/.agents/skills/customizing-agent-native/SKILL.md +0 -220
  215. package/corpus/templates/crm/.agents/skills/feature-flags/SKILL.md +0 -169
  216. package/corpus/templates/crm/.agents/skills/upgrade-agent-native/SKILL.md +0 -100
  217. package/corpus/templates/crm/.env.example +0 -5
  218. package/corpus/templates/crm/AGENTS.md +0 -120
  219. package/corpus/templates/crm/CHANGELOG.md +0 -1
  220. package/corpus/templates/crm/DEVELOPING.md +0 -66
  221. package/corpus/templates/crm/README.md +0 -106
  222. package/corpus/templates/crm/_gitignore +0 -12
  223. package/corpus/templates/crm/actions/_crm-action-utils.ts +0 -87
  224. package/corpus/templates/crm/actions/_crm-dashboard.ts +0 -69
  225. package/corpus/templates/crm/actions/_crm-data-program-actions.ts +0 -15
  226. package/corpus/templates/crm/actions/_crm-signal-utils.ts +0 -79
  227. package/corpus/templates/crm/actions/apply-crm-proposals.ts +0 -203
  228. package/corpus/templates/crm/actions/attach-call-evidence.ts +0 -170
  229. package/corpus/templates/crm/actions/configure-crm-connection.ts +0 -126
  230. package/corpus/templates/crm/actions/configure-native-crm.ts +0 -44
  231. package/corpus/templates/crm/actions/create-crm-record.ts +0 -148
  232. package/corpus/templates/crm/actions/create-crm-signal-tracker.ts +0 -58
  233. package/corpus/templates/crm/actions/delete-staged-dataset.ts +0 -14
  234. package/corpus/templates/crm/actions/get-crm-automation-recipe.ts +0 -36
  235. package/corpus/templates/crm/actions/get-crm-dashboard-panel.ts +0 -35
  236. package/corpus/templates/crm/actions/get-crm-dashboard.ts +0 -14
  237. package/corpus/templates/crm/actions/get-crm-overview.ts +0 -13
  238. package/corpus/templates/crm/actions/get-crm-pipeline-data.ts +0 -61
  239. package/corpus/templates/crm/actions/get-crm-record.ts +0 -75
  240. package/corpus/templates/crm/actions/install-crm-pipeline-dashboard.ts +0 -81
  241. package/corpus/templates/crm/actions/list-crm-dashboard-revisions.ts +0 -15
  242. package/corpus/templates/crm/actions/list-crm-dashboards.ts +0 -15
  243. package/corpus/templates/crm/actions/list-crm-proposals.ts +0 -31
  244. package/corpus/templates/crm/actions/list-crm-records.ts +0 -79
  245. package/corpus/templates/crm/actions/list-crm-saved-views.ts +0 -15
  246. package/corpus/templates/crm/actions/list-crm-signal-hits.ts +0 -43
  247. package/corpus/templates/crm/actions/list-crm-signal-trackers.ts +0 -32
  248. package/corpus/templates/crm/actions/list-crm-tasks.ts +0 -22
  249. package/corpus/templates/crm/actions/list-staged-datasets.ts +0 -14
  250. package/corpus/templates/crm/actions/list-workspace-connections.ts +0 -47
  251. package/corpus/templates/crm/actions/manage-crm-signal-tracker.ts +0 -87
  252. package/corpus/templates/crm/actions/manage-crm-task.ts +0 -132
  253. package/corpus/templates/crm/actions/navigate.ts +0 -63
  254. package/corpus/templates/crm/actions/provider-api-catalog.ts +0 -22
  255. package/corpus/templates/crm/actions/provider-api-docs.ts +0 -54
  256. package/corpus/templates/crm/actions/provider-api-request.ts +0 -92
  257. package/corpus/templates/crm/actions/query-staged-dataset.ts +0 -44
  258. package/corpus/templates/crm/actions/record-crm-call-insight.ts +0 -160
  259. package/corpus/templates/crm/actions/record-crm-smart-signal.ts +0 -150
  260. package/corpus/templates/crm/actions/restore-crm-dashboard-revision.ts +0 -23
  261. package/corpus/templates/crm/actions/review-crm-signal.ts +0 -31
  262. package/corpus/templates/crm/actions/run-crm-saved-view-program.ts +0 -103
  263. package/corpus/templates/crm/actions/run-crm-signal-trackers.ts +0 -275
  264. package/corpus/templates/crm/actions/run.ts +0 -7
  265. package/corpus/templates/crm/actions/save-crm-dashboard.ts +0 -51
  266. package/corpus/templates/crm/actions/save-crm-saved-view.ts +0 -119
  267. package/corpus/templates/crm/actions/sync-crm.ts +0 -146
  268. package/corpus/templates/crm/actions/update-crm-record.ts +0 -510
  269. package/corpus/templates/crm/actions/view-screen.ts +0 -218
  270. package/corpus/templates/crm/agent-native.app-skill.json +0 -58
  271. package/corpus/templates/crm/app/components/crm/CreateCrmRecordDialog.tsx +0 -158
  272. package/corpus/templates/crm/app/components/crm/CrmDashboardPanel.tsx +0 -78
  273. package/corpus/templates/crm/app/components/crm/CrmSignalsPanel.tsx +0 -226
  274. package/corpus/templates/crm/app/components/crm/IntelligenceSettings.tsx +0 -414
  275. package/corpus/templates/crm/app/components/crm/RecordActions.tsx +0 -510
  276. package/corpus/templates/crm/app/components/crm/RecordGrid.tsx +0 -134
  277. package/corpus/templates/crm/app/components/crm/RecordWorkspace.tsx +0 -362
  278. package/corpus/templates/crm/app/components/crm/SavedViewDataProgram.tsx +0 -93
  279. package/corpus/templates/crm/app/components/crm/Surface.tsx +0 -97
  280. package/corpus/templates/crm/app/components/crm/WorkOverview.tsx +0 -180
  281. package/corpus/templates/crm/app/components/layout/CrmLayout.tsx +0 -112
  282. package/corpus/templates/crm/app/components/layout/CrmSidebar.tsx +0 -84
  283. package/corpus/templates/crm/app/components/ui/alert-dialog.tsx +0 -1
  284. package/corpus/templates/crm/app/components/ui/badge.tsx +0 -1
  285. package/corpus/templates/crm/app/components/ui/button.tsx +0 -1
  286. package/corpus/templates/crm/app/components/ui/card.tsx +0 -1
  287. package/corpus/templates/crm/app/components/ui/dialog.tsx +0 -1
  288. package/corpus/templates/crm/app/components/ui/input.tsx +0 -1
  289. package/corpus/templates/crm/app/components/ui/label.tsx +0 -1
  290. package/corpus/templates/crm/app/components/ui/select.tsx +0 -1
  291. package/corpus/templates/crm/app/components/ui/sheet.tsx +0 -1
  292. package/corpus/templates/crm/app/components/ui/skeleton.tsx +0 -1
  293. package/corpus/templates/crm/app/components/ui/switch.tsx +0 -1
  294. package/corpus/templates/crm/app/components/ui/table.tsx +0 -1
  295. package/corpus/templates/crm/app/components/ui/tabs.tsx +0 -1
  296. package/corpus/templates/crm/app/components/ui/textarea.tsx +0 -1
  297. package/corpus/templates/crm/app/components/ui/toolkit-provider.tsx +0 -17
  298. package/corpus/templates/crm/app/design-system.ts +0 -3
  299. package/corpus/templates/crm/app/entry.client.tsx +0 -16
  300. package/corpus/templates/crm/app/entry.server.tsx +0 -10
  301. package/corpus/templates/crm/app/global.css +0 -167
  302. package/corpus/templates/crm/app/hooks/use-navigation-state.ts +0 -60
  303. package/corpus/templates/crm/app/i18n/en-US.ts +0 -89
  304. package/corpus/templates/crm/app/i18n/index.ts +0 -34
  305. package/corpus/templates/crm/app/lib/dashboard.ts +0 -13
  306. package/corpus/templates/crm/app/lib/navigation.ts +0 -54
  307. package/corpus/templates/crm/app/lib/tab-id.ts +0 -1
  308. package/corpus/templates/crm/app/lib/types.ts +0 -172
  309. package/corpus/templates/crm/app/lib/utils.ts +0 -1
  310. package/corpus/templates/crm/app/root.tsx +0 -91
  311. package/corpus/templates/crm/app/routes/_index.tsx +0 -26
  312. package/corpus/templates/crm/app/routes/accounts.tsx +0 -41
  313. package/corpus/templates/crm/app/routes/agent.tsx +0 -5
  314. package/corpus/templates/crm/app/routes/ask.tsx +0 -53
  315. package/corpus/templates/crm/app/routes/dashboard.tsx +0 -118
  316. package/corpus/templates/crm/app/routes/opportunities.tsx +0 -41
  317. package/corpus/templates/crm/app/routes/people.tsx +0 -41
  318. package/corpus/templates/crm/app/routes/proposals.tsx +0 -266
  319. package/corpus/templates/crm/app/routes/records.$recordId.tsx +0 -35
  320. package/corpus/templates/crm/app/routes/settings.tsx +0 -63
  321. package/corpus/templates/crm/app/routes/setup.tsx +0 -308
  322. package/corpus/templates/crm/app/routes/tasks.tsx +0 -252
  323. package/corpus/templates/crm/app/routes/views.tsx +0 -333
  324. package/corpus/templates/crm/app/routes.ts +0 -17
  325. package/corpus/templates/crm/app/vite-env.d.ts +0 -6
  326. package/corpus/templates/crm/changelog/2026-07-21-agent-native-crm.md +0 -6
  327. package/corpus/templates/crm/changelog/2026-07-21-clips-call-evidence-review-recipe.md +0 -6
  328. package/corpus/templates/crm/changelog/2026-07-21-crm-can-now-find-and-review-evidence-grounded-call-signals-w.md +0 -6
  329. package/corpus/templates/crm/changelog/2026-07-21-crm-can-now-manage-keyword-and-smart-call-signal-trackers-fr.md +0 -6
  330. package/corpus/templates/crm/changelog/2026-07-21-delegated-local-crm-automation.md +0 -6
  331. package/corpus/templates/crm/changelog/2026-07-21-native-sql.md +0 -6
  332. package/corpus/templates/crm/changelog/2026-07-21-pipeline-dashboard.md +0 -6
  333. package/corpus/templates/crm/changelog/2026-07-21-pipeline-dashboards-now-install-correctly-from-the-crm-actio.md +0 -6
  334. package/corpus/templates/crm/changelog/2026-07-21-salesforce-connection.md +0 -6
  335. package/corpus/templates/crm/changelog/2026-07-22-crm-field-validation-now-explains-which-fields-cannot-be-edi.md +0 -6
  336. package/corpus/templates/crm/changelog/2026-07-22-fixed-hubspot-contact-syncs-missing-records-updated-after-th.md +0 -6
  337. package/corpus/templates/crm/changelog/2026-07-22-fixed-pipeline-dashboard-panels-so-their-opportunity-data-lo.md +0 -6
  338. package/corpus/templates/crm/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +0 -6
  339. package/corpus/templates/crm/components.json +0 -20
  340. package/corpus/templates/crm/docs/architecture/crm-contract.md +0 -180
  341. package/corpus/templates/crm/learnings.defaults.md +0 -5
  342. package/corpus/templates/crm/package.json +0 -59
  343. package/corpus/templates/crm/public/favicon.svg +0 -1
  344. package/corpus/templates/crm/public/icon-180.svg +0 -1
  345. package/corpus/templates/crm/public/manifest.json +0 -11
  346. package/corpus/templates/crm/react-router.config.ts +0 -7
  347. package/corpus/templates/crm/server/crm/adapter.ts +0 -32
  348. package/corpus/templates/crm/server/crm/crm-field-firewall.ts +0 -64
  349. package/corpus/templates/crm/server/crm/crm-mirror.ts +0 -711
  350. package/corpus/templates/crm/server/crm/hubspot-adapter.ts +0 -1146
  351. package/corpus/templates/crm/server/crm/native-adapter.ts +0 -1715
  352. package/corpus/templates/crm/server/crm/read-through.ts +0 -207
  353. package/corpus/templates/crm/server/crm/salesforce-adapter.ts +0 -1255
  354. package/corpus/templates/crm/server/db/crm-store.ts +0 -1170
  355. package/corpus/templates/crm/server/db/index.ts +0 -169
  356. package/corpus/templates/crm/server/db/schema.ts +0 -441
  357. package/corpus/templates/crm/server/lib/intelligence/default-detectors.ts +0 -65
  358. package/corpus/templates/crm/server/lib/intelligence/evidence.ts +0 -105
  359. package/corpus/templates/crm/server/lib/intelligence/keyword-detector.ts +0 -100
  360. package/corpus/templates/crm/server/lib/intelligence/smart-detector.ts +0 -144
  361. package/corpus/templates/crm/server/lib/intelligence/summary.ts +0 -141
  362. package/corpus/templates/crm/server/lib/provider-api.ts +0 -64
  363. package/corpus/templates/crm/server/middleware/auth.ts +0 -4
  364. package/corpus/templates/crm/server/plugins/agent-chat.ts +0 -68
  365. package/corpus/templates/crm/server/plugins/auth.ts +0 -14
  366. package/corpus/templates/crm/server/plugins/core-routes.ts +0 -17
  367. package/corpus/templates/crm/server/plugins/db.ts +0 -454
  368. package/corpus/templates/crm/server/routes/[...page].get.ts +0 -5
  369. package/corpus/templates/crm/shared/crm-automation-recipes.ts +0 -132
  370. package/corpus/templates/crm/shared/crm-contract.ts +0 -254
  371. package/corpus/templates/crm/shared/crm-sales-config.ts +0 -63
  372. package/corpus/templates/crm/ssr-entry.ts +0 -11
  373. package/corpus/templates/crm/tsconfig.json +0 -24
  374. package/corpus/templates/crm/vite.config.ts +0 -15
  375. package/corpus/templates/crm/vitest.config.ts +0 -5
@@ -1,711 +0,0 @@
1
- import { assertAccess, accessFilter } from "@agent-native/core/sharing";
2
- import { and, eq, inArray } from "drizzle-orm";
3
-
4
- import type {
5
- CrmAdapter,
6
- CrmFieldDefinition,
7
- CrmFieldStoragePolicy,
8
- CrmObjectDefinition,
9
- CrmRecord,
10
- CrmSyncScope,
11
- CrmValue,
12
- } from "../../shared/crm-contract.js";
13
- import { getDb, schema } from "../db/index.js";
14
-
15
- export const CORE_HUBSPOT_OBJECTS = ["companies", "contacts", "deals"] as const;
16
- export const CORE_SALESFORCE_OBJECTS = [
17
- "Account",
18
- "Contact",
19
- "Opportunity",
20
- ] as const;
21
- export const MAX_SYNC_PAGE_SIZE = 50;
22
- export const MAX_SYNC_PAGES = 5;
23
-
24
- const DEFAULT_FIELDS: Record<string, string[]> = {
25
- companies: ["name", "domain", "industry", "city", "state", "country"],
26
- contacts: ["firstname", "lastname", "email", "jobtitle", "company"],
27
- deals: ["dealname", "amount", "dealstage", "pipeline", "closedate"],
28
- Account: [
29
- "Name",
30
- "Website",
31
- "Industry",
32
- "BillingCity",
33
- "BillingState",
34
- "BillingCountry",
35
- "OwnerId",
36
- ],
37
- Contact: ["FirstName", "LastName", "Email", "Title", "AccountId", "OwnerId"],
38
- Opportunity: [
39
- "Name",
40
- "Amount",
41
- "StageName",
42
- "CloseDate",
43
- "AccountId",
44
- "OwnerId",
45
- "Type",
46
- ],
47
- };
48
-
49
- const BINARY_OR_TRANSCRIPT_FIELD =
50
- /(?:attachment|audio|base64|binary|file|image|media|recording|transcript|video)/i;
51
-
52
- const CADENCE_FIELDS: CrmFieldDefinition[] = [
53
- {
54
- name: "desiredCadenceDays",
55
- label: "Desired cadence days",
56
- valueType: "number",
57
- storagePolicy: "local-authoritative",
58
- sensitive: false,
59
- readable: true,
60
- createable: false,
61
- updateable: true,
62
- required: false,
63
- },
64
- {
65
- name: "lastMeaningfulInteractionAt",
66
- label: "Last meaningful interaction",
67
- valueType: "datetime",
68
- storagePolicy: "derived-local",
69
- sensitive: false,
70
- readable: true,
71
- createable: false,
72
- updateable: false,
73
- required: false,
74
- },
75
- {
76
- name: "nextContactAt",
77
- label: "Next contact",
78
- valueType: "datetime",
79
- storagePolicy: "derived-local",
80
- sensitive: false,
81
- readable: true,
82
- createable: false,
83
- updateable: false,
84
- required: false,
85
- },
86
- ];
87
-
88
- type Ownership = {
89
- ownerEmail: string;
90
- orgId: string | null;
91
- visibility: "private" | "org";
92
- };
93
-
94
- export type MirrorSyncInput = {
95
- connectionId: string;
96
- objectType: string;
97
- scope: CrmSyncScope;
98
- fieldAllowList?: string[];
99
- allowCustomObject?: boolean;
100
- cursor?: string;
101
- pageSize?: number;
102
- maxPages?: number;
103
- ownership: Ownership;
104
- adapter: CrmAdapter;
105
- };
106
-
107
- export function isCustomHubSpotObject(objectType: string): boolean {
108
- return !CORE_HUBSPOT_OBJECTS.includes(
109
- objectType as (typeof CORE_HUBSPOT_OBJECTS)[number],
110
- );
111
- }
112
-
113
- export function resolveMirrorFields(input: {
114
- object: CrmObjectDefinition;
115
- requested: string[] | undefined;
116
- allowCustomObject: boolean | undefined;
117
- }): string[] {
118
- const available = new Map(
119
- input.object.fields
120
- .filter((field) => field.readable)
121
- .map((field) => [field.name, field]),
122
- );
123
- const custom = input.object.custom;
124
- const requested =
125
- input.requested?.map((field) => field.trim()).filter(Boolean) ?? [];
126
- if (custom && (!input.allowCustomObject || requested.length === 0)) {
127
- throw new Error(
128
- "Custom CRM objects require allowCustomObject and an explicit field allow-list.",
129
- );
130
- }
131
- const candidates = requested.length
132
- ? requested
133
- : (DEFAULT_FIELDS[input.object.objectType] ?? []);
134
- return Array.from(new Set(candidates)).filter((fieldName) => {
135
- const field = available.get(fieldName);
136
- return Boolean(
137
- field && !field.sensitive && !BINARY_OR_TRANSCRIPT_FIELD.test(fieldName),
138
- );
139
- });
140
- }
141
-
142
- export function storagePolicyFor(
143
- field: CrmFieldDefinition,
144
- mirrored: Set<string>,
145
- ): CrmFieldStoragePolicy {
146
- if (field.sensitive) return "redacted";
147
- return mirrored.has(field.name) ? "mirrored" : "remote-only";
148
- }
149
-
150
- export function fieldsForPolicyDiscovery(object: CrmObjectDefinition) {
151
- if (object.kind !== "account" && object.kind !== "person") {
152
- return object.fields;
153
- }
154
- return [...object.fields, ...CADENCE_FIELDS];
155
- }
156
-
157
- function hasBase64Shape(value: string): boolean {
158
- return (
159
- value.length > 256 &&
160
- /^[A-Za-z0-9+/=\s]+$/.test(value) &&
161
- value.replace(/\s/g, "").length % 4 === 0
162
- );
163
- }
164
-
165
- export function safeMirroredValue(value: CrmValue, depth = 0): CrmValue | null {
166
- if (value === null || typeof value === "boolean" || typeof value === "number")
167
- return value;
168
- if (typeof value === "string") {
169
- if (
170
- value.length > 2_000 ||
171
- value.trimStart().startsWith("data:") ||
172
- hasBase64Shape(value)
173
- )
174
- return null;
175
- return value;
176
- }
177
- if (depth >= 3 || !Array.isArray(value) || value.length > 40) return null;
178
- const values = value.map((item) => safeMirroredValue(item, depth + 1));
179
- return values.every((item) => item !== null) ? (values as CrmValue) : null;
180
- }
181
-
182
- function fieldColumns(value: CrmValue) {
183
- if (typeof value === "string")
184
- return {
185
- stringValue: value,
186
- numberValue: null,
187
- booleanValue: null,
188
- jsonValue: null,
189
- };
190
- if (typeof value === "number")
191
- return {
192
- stringValue: null,
193
- numberValue: value,
194
- booleanValue: null,
195
- jsonValue: null,
196
- };
197
- if (typeof value === "boolean")
198
- return {
199
- stringValue: null,
200
- numberValue: null,
201
- booleanValue: value,
202
- jsonValue: null,
203
- };
204
- if (value === null)
205
- return {
206
- stringValue: null,
207
- numberValue: null,
208
- booleanValue: null,
209
- jsonValue: null,
210
- };
211
- const jsonValue = JSON.stringify(value);
212
- if (jsonValue.length > 8_000)
213
- throw new Error("Mirrored field value exceeds the 8,000-character limit.");
214
- return {
215
- stringValue: null,
216
- numberValue: null,
217
- booleanValue: null,
218
- jsonValue,
219
- };
220
- }
221
-
222
- export function crmRecordIdentityColumns(record: CrmRecord) {
223
- return {
224
- provider: record.ref.provider,
225
- objectType: record.ref.objectType,
226
- kind: record.ref.kind,
227
- };
228
- }
229
-
230
- export function crmRecordSummaryColumns(record: CrmRecord) {
231
- const fields = record.fields;
232
- const string = (...names: string[]) => {
233
- for (const name of names) {
234
- if (typeof fields[name] === "string") return fields[name];
235
- }
236
- return null;
237
- };
238
- const numeric = (...names: string[]) => {
239
- for (const name of names) {
240
- const value = fields[name];
241
- if (typeof value === "number") return value;
242
- if (typeof value === "string" && /^-?\d+(\.\d+)?$/.test(value))
243
- return Number(value);
244
- }
245
- return null;
246
- };
247
- return {
248
- displayName: record.displayName.slice(0, 500),
249
- primaryEmail: string("email", "Email"),
250
- domain: string("domain", "Website"),
251
- stage: string("dealstage", "StageName"),
252
- pipelineId: string("pipeline", "RecordTypeId"),
253
- ownerRemoteId: string("hubspot_owner_id", "OwnerId"),
254
- amount: numeric("amount", "Amount"),
255
- closeDate: string("closedate", "CloseDate"),
256
- remoteRevision: record.remoteRevision ?? null,
257
- remoteUpdatedAt: record.remoteUpdatedAt ?? null,
258
- lastSyncedAt: new Date().toISOString(),
259
- accessScopeKey: record.accessScope.key.slice(0, 500),
260
- accessScopeJson: JSON.stringify(record.accessScope).slice(0, 8_000),
261
- tombstone: record.deleted,
262
- updatedAt: new Date().toISOString(),
263
- };
264
- }
265
-
266
- async function persistSchema(input: {
267
- connectionId: string;
268
- object: CrmObjectDefinition;
269
- mirrored: Set<string>;
270
- ownership: Ownership;
271
- capabilities: CrmAdapter["capabilities"];
272
- }): Promise<Map<string, string>> {
273
- const db = getDb();
274
- const [existingObject] = await db
275
- .select({ id: schema.crmObjects.id })
276
- .from(schema.crmObjects)
277
- .where(
278
- and(
279
- eq(schema.crmObjects.connectionId, input.connectionId),
280
- eq(schema.crmObjects.objectType, input.object.objectType),
281
- accessFilter(schema.crmObjects, schema.crmObjectShares),
282
- ),
283
- )
284
- .limit(1);
285
- const now = new Date().toISOString();
286
- const objectValues = {
287
- provider: input.object.provider,
288
- objectType: input.object.objectType,
289
- kind: input.object.kind,
290
- label: input.object.label.slice(0, 500),
291
- pluralLabel: input.object.pluralLabel.slice(0, 500),
292
- custom: input.object.custom,
293
- queryable: input.object.queryable,
294
- searchable: input.object.searchable,
295
- createable: input.object.createable,
296
- updateable: input.object.updateable,
297
- deleteable: input.object.deleteable,
298
- capabilitiesJson: JSON.stringify(input.capabilities).slice(0, 4_000),
299
- updatedAt: now,
300
- };
301
- if (existingObject) {
302
- await assertAccess("crm-object", existingObject.id, "editor");
303
- await db
304
- .update(schema.crmObjects)
305
- .set(objectValues)
306
- .where(
307
- and(
308
- eq(schema.crmObjects.id, existingObject.id),
309
- accessFilter(
310
- schema.crmObjects,
311
- schema.crmObjectShares,
312
- undefined,
313
- "editor",
314
- ),
315
- ),
316
- );
317
- } else {
318
- await db.insert(schema.crmObjects).values({
319
- id: crypto.randomUUID(),
320
- connectionId: input.connectionId,
321
- ...objectValues,
322
- ...input.ownership,
323
- createdAt: now,
324
- });
325
- }
326
-
327
- const existingPolicies = await db
328
- .select({
329
- id: schema.crmFieldPolicies.id,
330
- fieldName: schema.crmFieldPolicies.fieldName,
331
- storagePolicy: schema.crmFieldPolicies.storagePolicy,
332
- })
333
- .from(schema.crmFieldPolicies)
334
- .where(
335
- and(
336
- eq(schema.crmFieldPolicies.connectionId, input.connectionId),
337
- eq(schema.crmFieldPolicies.objectType, input.object.objectType),
338
- accessFilter(schema.crmFieldPolicies, schema.crmFieldPolicyShares),
339
- ),
340
- );
341
- const byName = new Map(
342
- existingPolicies.map((policy) => [policy.fieldName, policy]),
343
- );
344
- const policyIds = new Map(
345
- existingPolicies.map((policy) => [policy.fieldName, policy.id]),
346
- );
347
- for (const field of fieldsForPolicyDiscovery(input.object)) {
348
- const existing = byName.get(field.name);
349
- const discoveredPolicy =
350
- field.storagePolicy === "local-authoritative" ||
351
- field.storagePolicy === "derived-local"
352
- ? field.storagePolicy
353
- : storagePolicyFor(field, input.mirrored);
354
- const storagePolicy =
355
- existing?.storagePolicy === "local-authoritative"
356
- ? "local-authoritative"
357
- : discoveredPolicy;
358
- const values = {
359
- label: field.label.slice(0, 500),
360
- valueType: field.valueType,
361
- storagePolicy,
362
- sensitive: field.sensitive,
363
- readable: field.readable,
364
- createable: field.createable,
365
- updateable: field.updateable,
366
- required: field.required,
367
- metadataJson: JSON.stringify({
368
- options: field.options,
369
- referencedObjectType: field.referencedObjectType,
370
- }).slice(0, 8_000),
371
- updatedAt: now,
372
- };
373
- if (existing) {
374
- await assertAccess("crm-field-policy", existing.id, "editor");
375
- await db
376
- .update(schema.crmFieldPolicies)
377
- .set(values)
378
- .where(
379
- and(
380
- eq(schema.crmFieldPolicies.id, existing.id),
381
- accessFilter(
382
- schema.crmFieldPolicies,
383
- schema.crmFieldPolicyShares,
384
- undefined,
385
- "editor",
386
- ),
387
- ),
388
- );
389
- } else {
390
- const id = crypto.randomUUID();
391
- await db.insert(schema.crmFieldPolicies).values({
392
- id,
393
- connectionId: input.connectionId,
394
- objectType: input.object.objectType,
395
- fieldName: field.name,
396
- ...values,
397
- ...input.ownership,
398
- createdAt: now,
399
- });
400
- policyIds.set(field.name, id);
401
- }
402
- }
403
- return policyIds;
404
- }
405
-
406
- async function persistPage(input: {
407
- connectionId: string;
408
- provider: string;
409
- objectType: string;
410
- page: Awaited<ReturnType<CrmAdapter["syncPage"]>>;
411
- fields: Map<string, CrmFieldDefinition>;
412
- fieldPolicyIds: Map<string, string>;
413
- mirrored: Set<string>;
414
- ownership: Ownership;
415
- }) {
416
- const db = getDb();
417
- const remoteIds = input.page.records.map((record) => record.ref.remoteId);
418
- if (remoteIds.length === 0)
419
- return { recordsUpserted: 0, tombstonesApplied: 0, rejectedFields: 0 };
420
- return db.transaction(async (tx) => {
421
- const existingRecords = await tx
422
- .select({
423
- id: schema.crmRecords.id,
424
- remoteId: schema.crmRecords.remoteId,
425
- ownerEmail: schema.crmRecords.ownerEmail,
426
- orgId: schema.crmRecords.orgId,
427
- visibility: schema.crmRecords.visibility,
428
- })
429
- .from(schema.crmRecords)
430
- .where(
431
- and(
432
- eq(schema.crmRecords.connectionId, input.connectionId),
433
- eq(schema.crmRecords.provider, input.provider),
434
- eq(schema.crmRecords.objectType, input.objectType),
435
- inArray(schema.crmRecords.remoteId, remoteIds),
436
- accessFilter(schema.crmRecords, schema.crmRecordShares),
437
- ),
438
- );
439
- const recordsByRemoteId = new Map(
440
- existingRecords.map((record) => [record.remoteId, record]),
441
- );
442
- const recordIds = existingRecords.map((record) => record.id);
443
- const existingFields = recordIds.length
444
- ? await tx
445
- .select({
446
- id: schema.crmRecordFields.id,
447
- recordId: schema.crmRecordFields.recordId,
448
- fieldName: schema.crmRecordFields.fieldName,
449
- storagePolicy: schema.crmRecordFields.storagePolicy,
450
- })
451
- .from(schema.crmRecordFields)
452
- .where(
453
- and(
454
- inArray(schema.crmRecordFields.recordId, recordIds),
455
- accessFilter(schema.crmRecordFields, schema.crmRecordFieldShares),
456
- ),
457
- )
458
- : [];
459
- const fieldsByRecord = new Map(
460
- existingFields.map((field) => [
461
- `${field.recordId}:${field.fieldName}`,
462
- field,
463
- ]),
464
- );
465
- let recordsUpserted = 0;
466
- let tombstonesApplied = 0;
467
- let rejectedFields = 0;
468
- for (const remoteRecord of input.page.records) {
469
- const existing = recordsByRemoteId.get(remoteRecord.ref.remoteId);
470
- const values = crmRecordSummaryColumns(remoteRecord);
471
- const identity = crmRecordIdentityColumns(remoteRecord);
472
- const recordId = existing?.id ?? crypto.randomUUID();
473
- const ownership = existing
474
- ? {
475
- ownerEmail: existing.ownerEmail,
476
- orgId: existing.orgId,
477
- visibility: existing.visibility,
478
- }
479
- : input.ownership;
480
- if (existing) {
481
- await tx
482
- .update(schema.crmRecords)
483
- .set({
484
- ...values,
485
- ...identity,
486
- })
487
- .where(
488
- and(
489
- eq(schema.crmRecords.id, existing.id),
490
- accessFilter(
491
- schema.crmRecords,
492
- schema.crmRecordShares,
493
- undefined,
494
- "editor",
495
- ),
496
- ),
497
- );
498
- } else {
499
- await tx.insert(schema.crmRecords).values({
500
- id: recordId,
501
- connectionId: input.connectionId,
502
- ...identity,
503
- remoteId: remoteRecord.ref.remoteId,
504
- ...values,
505
- ...ownership,
506
- createdAt: new Date().toISOString(),
507
- });
508
- }
509
- recordsUpserted += 1;
510
- if (remoteRecord.deleted) tombstonesApplied += 1;
511
- for (const [fieldName, rawValue] of Object.entries(remoteRecord.fields)) {
512
- if (
513
- !input.mirrored.has(fieldName) ||
514
- BINARY_OR_TRANSCRIPT_FIELD.test(fieldName)
515
- ) {
516
- rejectedFields += 1;
517
- continue;
518
- }
519
- const definition = input.fields.get(fieldName);
520
- if (!definition || definition.sensitive) {
521
- rejectedFields += 1;
522
- continue;
523
- }
524
- const value = safeMirroredValue(rawValue);
525
- if (value === null && rawValue !== null) {
526
- rejectedFields += 1;
527
- continue;
528
- }
529
- const provenance = remoteRecord.provenance.filter(
530
- (item) => item.fieldName === fieldName,
531
- );
532
- const fieldValues = {
533
- fieldPolicyId: input.fieldPolicyIds.get(fieldName) ?? null,
534
- valueType: definition.valueType,
535
- storagePolicy: "mirrored" as const,
536
- ...fieldColumns(value),
537
- provenanceJson: JSON.stringify(provenance).slice(0, 4_000),
538
- accessScopeKey: remoteRecord.accessScope.key.slice(0, 500),
539
- accessScopeJson: JSON.stringify(remoteRecord.accessScope).slice(
540
- 0,
541
- 8_000,
542
- ),
543
- remoteRevision: remoteRecord.remoteRevision ?? null,
544
- updatedAt: new Date().toISOString(),
545
- };
546
- const existingField = fieldsByRecord.get(`${recordId}:${fieldName}`);
547
- if (existingField?.storagePolicy === "local-authoritative") continue;
548
- if (existingField) {
549
- await tx
550
- .update(schema.crmRecordFields)
551
- .set(fieldValues)
552
- .where(
553
- and(
554
- eq(schema.crmRecordFields.id, existingField.id),
555
- accessFilter(
556
- schema.crmRecordFields,
557
- schema.crmRecordFieldShares,
558
- undefined,
559
- "editor",
560
- ),
561
- ),
562
- );
563
- } else {
564
- await tx.insert(schema.crmRecordFields).values({
565
- id: crypto.randomUUID(),
566
- recordId,
567
- fieldName,
568
- ...fieldValues,
569
- ...ownership,
570
- createdAt: new Date().toISOString(),
571
- });
572
- }
573
- }
574
- }
575
- return { recordsUpserted, tombstonesApplied, rejectedFields };
576
- });
577
- }
578
-
579
- export async function syncCrmMirror(input: MirrorSyncInput) {
580
- const object = await input.adapter.describeObject(input.objectType);
581
- const fields = resolveMirrorFields({
582
- object,
583
- requested: input.fieldAllowList,
584
- allowCustomObject: input.allowCustomObject,
585
- });
586
- if (fields.length === 0)
587
- throw new Error(
588
- "No readable, safe fields are eligible for thin mirroring.",
589
- );
590
- const mirrored = new Set(fields);
591
- const fieldPolicyIds = await persistSchema({
592
- connectionId: input.connectionId,
593
- object,
594
- mirrored,
595
- ownership: input.ownership,
596
- capabilities: input.adapter.capabilities,
597
- });
598
- const db = getDb();
599
- const startedAt = new Date().toISOString();
600
- const syncRunId = crypto.randomUUID();
601
- await db.insert(schema.crmSyncRuns).values({
602
- id: syncRunId,
603
- connectionId: input.connectionId,
604
- status: "running",
605
- scopeJson: JSON.stringify(input.scope).slice(0, 8_000),
606
- cursor: input.cursor ?? null,
607
- startedAt,
608
- ...input.ownership,
609
- createdAt: startedAt,
610
- updatedAt: startedAt,
611
- });
612
- let cursor = input.cursor;
613
- let complete = false;
614
- let recordsUpserted = 0;
615
- let tombstonesApplied = 0;
616
- let rejectedFields = 0;
617
- const pages = Math.min(input.maxPages ?? MAX_SYNC_PAGES, MAX_SYNC_PAGES);
618
- const pageSize = Math.min(
619
- input.pageSize ?? MAX_SYNC_PAGE_SIZE,
620
- MAX_SYNC_PAGE_SIZE,
621
- );
622
- try {
623
- for (let pageIndex = 0; pageIndex < pages; pageIndex += 1) {
624
- const page = await input.adapter.syncPage({
625
- scope: input.scope,
626
- fieldAllowList: fields,
627
- cursor,
628
- limit: pageSize,
629
- });
630
- const result = await persistPage({
631
- connectionId: input.connectionId,
632
- provider: object.provider,
633
- objectType: object.objectType,
634
- page,
635
- fields: new Map(object.fields.map((field) => [field.name, field])),
636
- fieldPolicyIds,
637
- mirrored,
638
- ownership: input.ownership,
639
- });
640
- recordsUpserted += result.recordsUpserted;
641
- tombstonesApplied += result.tombstonesApplied;
642
- rejectedFields += result.rejectedFields;
643
- cursor = page.nextCursor;
644
- complete = page.complete;
645
- if (complete || !cursor) break;
646
- }
647
- const status = complete ? "success" : "partial";
648
- const completedAt = new Date().toISOString();
649
- await db
650
- .update(schema.crmSyncRuns)
651
- .set({
652
- status,
653
- cursor: cursor ?? null,
654
- recordsUpserted,
655
- tombstonesApplied,
656
- relationshipsUpserted: 0,
657
- completedAt,
658
- updatedAt: completedAt,
659
- })
660
- .where(
661
- and(
662
- eq(schema.crmSyncRuns.id, syncRunId),
663
- accessFilter(
664
- schema.crmSyncRuns,
665
- schema.crmSyncRunShares,
666
- undefined,
667
- "editor",
668
- ),
669
- ),
670
- );
671
- return {
672
- syncRunId,
673
- status,
674
- cursor,
675
- complete,
676
- recordsUpserted,
677
- tombstonesApplied,
678
- relationshipsUpserted: 0,
679
- rejectedFields,
680
- fieldAllowList: fields,
681
- };
682
- } catch (error) {
683
- const message =
684
- error instanceof Error
685
- ? error.message.slice(0, 1_000)
686
- : "CRM sync failed.";
687
- await db
688
- .update(schema.crmSyncRuns)
689
- .set({
690
- status: "failed",
691
- cursor: cursor ?? null,
692
- recordsUpserted,
693
- tombstonesApplied,
694
- error: message,
695
- completedAt: new Date().toISOString(),
696
- updatedAt: new Date().toISOString(),
697
- })
698
- .where(
699
- and(
700
- eq(schema.crmSyncRuns.id, syncRunId),
701
- accessFilter(
702
- schema.crmSyncRuns,
703
- schema.crmSyncRunShares,
704
- undefined,
705
- "editor",
706
- ),
707
- ),
708
- );
709
- throw error;
710
- }
711
- }