@agent-native/core 0.123.0 → 0.123.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 (263) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +16 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/cli/templates-meta.ts +12 -0
  5. package/corpus/templates/crm/.agents/skills/agent-native-docs/SKILL.md +115 -0
  6. package/corpus/templates/crm/.agents/skills/agent-native-toolkit/SKILL.md +177 -0
  7. package/corpus/templates/crm/.agents/skills/crm/SKILL.md +205 -0
  8. package/corpus/templates/crm/.agents/skills/customizing-agent-native/SKILL.md +220 -0
  9. package/corpus/templates/crm/.agents/skills/feature-flags/SKILL.md +169 -0
  10. package/corpus/templates/crm/.agents/skills/sharing/SKILL.md +228 -0
  11. package/corpus/templates/crm/.agents/skills/storing-data/SKILL.md +178 -0
  12. package/corpus/templates/crm/.agents/skills/upgrade-agent-native/SKILL.md +122 -0
  13. package/corpus/templates/crm/.env.example +5 -0
  14. package/corpus/templates/crm/AGENTS.md +82 -0
  15. package/corpus/templates/crm/CHANGELOG.md +25 -0
  16. package/corpus/templates/crm/DEVELOPING.md +66 -0
  17. package/corpus/templates/crm/README.md +122 -0
  18. package/corpus/templates/crm/_gitignore +12 -0
  19. package/corpus/templates/crm/actions/_crm-action-utils.ts +98 -0
  20. package/corpus/templates/crm/actions/_crm-attribute-utils.ts +219 -0
  21. package/corpus/templates/crm/actions/_crm-dashboard.ts +69 -0
  22. package/corpus/templates/crm/actions/_crm-data-program-actions.ts +15 -0
  23. package/corpus/templates/crm/actions/_crm-list-utils.ts +775 -0
  24. package/corpus/templates/crm/actions/_crm-signal-utils.ts +79 -0
  25. package/corpus/templates/crm/actions/add-crm-record-to-list.ts +164 -0
  26. package/corpus/templates/crm/actions/apply-crm-proposals.ts +291 -0
  27. package/corpus/templates/crm/actions/archive-crm-attribute.ts +69 -0
  28. package/corpus/templates/crm/actions/attach-call-evidence.ts +170 -0
  29. package/corpus/templates/crm/actions/configure-crm-connection.ts +126 -0
  30. package/corpus/templates/crm/actions/configure-native-crm.ts +44 -0
  31. package/corpus/templates/crm/actions/create-crm-attribute.ts +350 -0
  32. package/corpus/templates/crm/actions/create-crm-list.ts +194 -0
  33. package/corpus/templates/crm/actions/create-crm-record.ts +148 -0
  34. package/corpus/templates/crm/actions/create-crm-signal-tracker.ts +58 -0
  35. package/corpus/templates/crm/actions/delete-crm-saved-view.ts +68 -0
  36. package/corpus/templates/crm/actions/delete-staged-dataset.ts +14 -0
  37. package/corpus/templates/crm/actions/estimate-crm-enrichment.ts +229 -0
  38. package/corpus/templates/crm/actions/find-crm-duplicates.ts +87 -0
  39. package/corpus/templates/crm/actions/get-crm-automation-recipe.ts +36 -0
  40. package/corpus/templates/crm/actions/get-crm-dashboard-panel.ts +35 -0
  41. package/corpus/templates/crm/actions/get-crm-dashboard.ts +14 -0
  42. package/corpus/templates/crm/actions/get-crm-overview.ts +13 -0
  43. package/corpus/templates/crm/actions/get-crm-pipeline-data.ts +61 -0
  44. package/corpus/templates/crm/actions/get-crm-record-page.ts +287 -0
  45. package/corpus/templates/crm/actions/get-crm-record.ts +75 -0
  46. package/corpus/templates/crm/actions/get-crm-workspace.ts +364 -0
  47. package/corpus/templates/crm/actions/install-crm-pipeline-dashboard.ts +81 -0
  48. package/corpus/templates/crm/actions/list-crm-attributes.ts +71 -0
  49. package/corpus/templates/crm/actions/list-crm-connections.ts +68 -0
  50. package/corpus/templates/crm/actions/list-crm-dashboard-revisions.ts +15 -0
  51. package/corpus/templates/crm/actions/list-crm-dashboards.ts +15 -0
  52. package/corpus/templates/crm/actions/list-crm-enrichment-slots.ts +28 -0
  53. package/corpus/templates/crm/actions/list-crm-list-entries.ts +192 -0
  54. package/corpus/templates/crm/actions/list-crm-lists.ts +80 -0
  55. package/corpus/templates/crm/actions/list-crm-proposals.ts +55 -0
  56. package/corpus/templates/crm/actions/list-crm-record-field-history.ts +201 -0
  57. package/corpus/templates/crm/actions/list-crm-record-values.ts +200 -0
  58. package/corpus/templates/crm/actions/list-crm-records.ts +103 -0
  59. package/corpus/templates/crm/actions/list-crm-saved-views.ts +66 -0
  60. package/corpus/templates/crm/actions/list-crm-signal-hits.ts +43 -0
  61. package/corpus/templates/crm/actions/list-crm-signal-trackers.ts +32 -0
  62. package/corpus/templates/crm/actions/list-crm-tasks.ts +22 -0
  63. package/corpus/templates/crm/actions/list-staged-datasets.ts +14 -0
  64. package/corpus/templates/crm/actions/list-workspace-connections.ts +47 -0
  65. package/corpus/templates/crm/actions/manage-crm-attribute-option.ts +272 -0
  66. package/corpus/templates/crm/actions/manage-crm-signal-tracker.ts +87 -0
  67. package/corpus/templates/crm/actions/manage-crm-task.ts +132 -0
  68. package/corpus/templates/crm/actions/merge-crm-records.ts +691 -0
  69. package/corpus/templates/crm/actions/navigate.ts +54 -0
  70. package/corpus/templates/crm/actions/provider-api-catalog.ts +22 -0
  71. package/corpus/templates/crm/actions/provider-api-docs.ts +54 -0
  72. package/corpus/templates/crm/actions/provider-api-request.ts +92 -0
  73. package/corpus/templates/crm/actions/query-staged-dataset.ts +44 -0
  74. package/corpus/templates/crm/actions/record-crm-call-insight.ts +160 -0
  75. package/corpus/templates/crm/actions/record-crm-smart-signal.ts +150 -0
  76. package/corpus/templates/crm/actions/remove-crm-list-entry.ts +79 -0
  77. package/corpus/templates/crm/actions/restore-crm-dashboard-revision.ts +23 -0
  78. package/corpus/templates/crm/actions/review-crm-signal.ts +31 -0
  79. package/corpus/templates/crm/actions/run-crm-attribute-fill.ts +443 -0
  80. package/corpus/templates/crm/actions/run-crm-enrichment.ts +647 -0
  81. package/corpus/templates/crm/actions/run-crm-saved-view-program.ts +103 -0
  82. package/corpus/templates/crm/actions/run-crm-signal-trackers.ts +265 -0
  83. package/corpus/templates/crm/actions/run.ts +7 -0
  84. package/corpus/templates/crm/actions/save-crm-dashboard.ts +51 -0
  85. package/corpus/templates/crm/actions/save-crm-saved-view.ts +296 -0
  86. package/corpus/templates/crm/actions/sync-crm.ts +146 -0
  87. package/corpus/templates/crm/actions/update-crm-attribute.ts +111 -0
  88. package/corpus/templates/crm/actions/update-crm-list-entry.ts +102 -0
  89. package/corpus/templates/crm/actions/update-crm-list.ts +97 -0
  90. package/corpus/templates/crm/actions/update-crm-record.ts +543 -0
  91. package/corpus/templates/crm/actions/view-screen.ts +324 -0
  92. package/corpus/templates/crm/agent-native.app-skill.json +69 -0
  93. package/corpus/templates/crm/app/components/crm/CreateCrmRecordDialog.tsx +158 -0
  94. package/corpus/templates/crm/app/components/crm/CrmDashboardPanel.tsx +78 -0
  95. package/corpus/templates/crm/app/components/crm/CrmSignalsPanel.tsx +220 -0
  96. package/corpus/templates/crm/app/components/crm/IntelligenceSettings.tsx +414 -0
  97. package/corpus/templates/crm/app/components/crm/RecordActions.tsx +648 -0
  98. package/corpus/templates/crm/app/components/crm/RecordGrid.tsx +488 -0
  99. package/corpus/templates/crm/app/components/crm/RecordWorkspace.tsx +232 -0
  100. package/corpus/templates/crm/app/components/crm/SavedViewDataProgram.tsx +93 -0
  101. package/corpus/templates/crm/app/components/crm/Surface.tsx +97 -0
  102. package/corpus/templates/crm/app/components/crm/WorkOverview.tsx +180 -0
  103. package/corpus/templates/crm/app/components/crm/board/CrmBoard.tsx +988 -0
  104. package/corpus/templates/crm/app/components/crm/board/board-model.ts +399 -0
  105. package/corpus/templates/crm/app/components/crm/board/entry-filter.ts +150 -0
  106. package/corpus/templates/crm/app/components/crm/board/view-draft.ts +227 -0
  107. package/corpus/templates/crm/app/components/crm/grid/CrmGrid.tsx +1033 -0
  108. package/corpus/templates/crm/app/components/crm/grid/GridCell.tsx +620 -0
  109. package/corpus/templates/crm/app/components/crm/grid/clipboard.ts +84 -0
  110. package/corpus/templates/crm/app/components/crm/grid/model.ts +304 -0
  111. package/corpus/templates/crm/app/components/crm/grid/navigation.ts +220 -0
  112. package/corpus/templates/crm/app/components/crm/grid/query.ts +214 -0
  113. package/corpus/templates/crm/app/components/crm/record/AttributePanel.tsx +192 -0
  114. package/corpus/templates/crm/app/components/crm/record/FieldHistory.tsx +179 -0
  115. package/corpus/templates/crm/app/components/crm/record/RecordHeader.tsx +103 -0
  116. package/corpus/templates/crm/app/components/crm/record/RecordLists.tsx +235 -0
  117. package/corpus/templates/crm/app/components/crm/record/RecordTabs.tsx +206 -0
  118. package/corpus/templates/crm/app/components/crm/record/field-editors.tsx +245 -0
  119. package/corpus/templates/crm/app/components/crm/record/record-data.ts +603 -0
  120. package/corpus/templates/crm/app/components/crm/settings/AdvancedSettings.tsx +49 -0
  121. package/corpus/templates/crm/app/components/crm/settings/AttributeOptionsEditor.tsx +441 -0
  122. package/corpus/templates/crm/app/components/crm/settings/ConnectionSettings.tsx +169 -0
  123. package/corpus/templates/crm/app/components/crm/settings/FieldsSettings.tsx +935 -0
  124. package/corpus/templates/crm/app/components/crm/settings/ListsSettings.tsx +339 -0
  125. package/corpus/templates/crm/app/components/crm/settings/settings-admin.ts +377 -0
  126. package/corpus/templates/crm/app/components/crm/shared/AttributeValueParts.tsx +69 -0
  127. package/corpus/templates/crm/app/components/crm/shared/attribute-value.ts +591 -0
  128. package/corpus/templates/crm/app/components/layout/CrmCommandMenu.tsx +556 -0
  129. package/corpus/templates/crm/app/components/layout/CrmLayout.tsx +130 -0
  130. package/corpus/templates/crm/app/components/layout/CrmSidebar.tsx +407 -0
  131. package/corpus/templates/crm/app/components/layout/command-palette-search.ts +126 -0
  132. package/corpus/templates/crm/app/components/layout/crm-ui-intents.ts +41 -0
  133. package/corpus/templates/crm/app/components/layout/sidebar-collapse.ts +40 -0
  134. package/corpus/templates/crm/app/components/layout/sidebar-lists.ts +109 -0
  135. package/corpus/templates/crm/app/components/ui/alert-dialog.tsx +1 -0
  136. package/corpus/templates/crm/app/components/ui/badge.tsx +1 -0
  137. package/corpus/templates/crm/app/components/ui/button.tsx +1 -0
  138. package/corpus/templates/crm/app/components/ui/card.tsx +1 -0
  139. package/corpus/templates/crm/app/components/ui/checkbox.tsx +1 -0
  140. package/corpus/templates/crm/app/components/ui/dialog.tsx +1 -0
  141. package/corpus/templates/crm/app/components/ui/dropdown-menu.tsx +1 -0
  142. package/corpus/templates/crm/app/components/ui/hover-card.tsx +1 -0
  143. package/corpus/templates/crm/app/components/ui/input.tsx +1 -0
  144. package/corpus/templates/crm/app/components/ui/label.tsx +1 -0
  145. package/corpus/templates/crm/app/components/ui/popover.tsx +1 -0
  146. package/corpus/templates/crm/app/components/ui/select.tsx +1 -0
  147. package/corpus/templates/crm/app/components/ui/sheet.tsx +1 -0
  148. package/corpus/templates/crm/app/components/ui/skeleton.tsx +1 -0
  149. package/corpus/templates/crm/app/components/ui/switch.tsx +1 -0
  150. package/corpus/templates/crm/app/components/ui/table.tsx +1 -0
  151. package/corpus/templates/crm/app/components/ui/tabs.tsx +1 -0
  152. package/corpus/templates/crm/app/components/ui/textarea.tsx +1 -0
  153. package/corpus/templates/crm/app/components/ui/toolkit-provider.tsx +17 -0
  154. package/corpus/templates/crm/app/components/ui/tooltip.tsx +1 -0
  155. package/corpus/templates/crm/app/design-system.ts +3 -0
  156. package/corpus/templates/crm/app/entry.client.tsx +19 -0
  157. package/corpus/templates/crm/app/entry.server.tsx +10 -0
  158. package/corpus/templates/crm/app/global.css +167 -0
  159. package/corpus/templates/crm/app/hooks/use-keyboard-shortcuts.ts +206 -0
  160. package/corpus/templates/crm/app/hooks/use-navigation-state.ts +48 -0
  161. package/corpus/templates/crm/app/i18n/en-US.ts +684 -0
  162. package/corpus/templates/crm/app/i18n/index.ts +34 -0
  163. package/corpus/templates/crm/app/lib/dashboard.ts +13 -0
  164. package/corpus/templates/crm/app/lib/navigation.ts +17 -0
  165. package/corpus/templates/crm/app/lib/tab-id.ts +1 -0
  166. package/corpus/templates/crm/app/lib/types.ts +172 -0
  167. package/corpus/templates/crm/app/lib/utils.ts +1 -0
  168. package/corpus/templates/crm/app/root.tsx +93 -0
  169. package/corpus/templates/crm/app/routes/_index.tsx +26 -0
  170. package/corpus/templates/crm/app/routes/accounts.tsx +41 -0
  171. package/corpus/templates/crm/app/routes/agent.tsx +5 -0
  172. package/corpus/templates/crm/app/routes/ask.tsx +53 -0
  173. package/corpus/templates/crm/app/routes/dashboard.tsx +118 -0
  174. package/corpus/templates/crm/app/routes/lists.tsx +228 -0
  175. package/corpus/templates/crm/app/routes/opportunities.tsx +41 -0
  176. package/corpus/templates/crm/app/routes/people.tsx +41 -0
  177. package/corpus/templates/crm/app/routes/proposals.tsx +388 -0
  178. package/corpus/templates/crm/app/routes/records.$recordId.tsx +53 -0
  179. package/corpus/templates/crm/app/routes/records.tsx +64 -0
  180. package/corpus/templates/crm/app/routes/settings.tsx +118 -0
  181. package/corpus/templates/crm/app/routes/setup.tsx +308 -0
  182. package/corpus/templates/crm/app/routes/tasks.tsx +252 -0
  183. package/corpus/templates/crm/app/routes/views.tsx +683 -0
  184. package/corpus/templates/crm/app/routes.ts +19 -0
  185. package/corpus/templates/crm/app/vite-env.d.ts +6 -0
  186. package/corpus/templates/crm/changelog/2026-07-26-any-list-or-view-can-be-shown-as-a-board-grouped-by-stage-so.md +6 -0
  187. package/corpus/templates/crm/changelog/2026-07-26-crm-now-finds-likely-duplicate-records-and-tells-you-exactly.md +6 -0
  188. package/corpus/templates/crm/changelog/2026-07-26-enrichment-now-shows-what-a-run-will-cost-before-it-starts-g.md +6 -0
  189. package/corpus/templates/crm/changelog/2026-07-26-lists-turn-any-set-of-records-into-a-pipeline-with-its-own-s.md +6 -0
  190. package/corpus/templates/crm/changelog/2026-07-26-moving-a-record-or-list-entry-into-a-stage-that-was-retired-.md +6 -0
  191. package/corpus/templates/crm/changelog/2026-07-26-opening-a-record-now-shows-one-page-with-its-fields-the-list.md +6 -0
  192. package/corpus/templates/crm/changelog/2026-07-26-press-cmd-k-to-jump-to-any-record-list-view-or-setting-and-u.md +6 -0
  193. package/corpus/templates/crm/changelog/2026-07-26-provider-record-edits-now-show-an-exact-before-after-diff-an.md +6 -0
  194. package/corpus/templates/crm/changelog/2026-07-26-record-page-attribute-labels-now-read-in-full-instead-of-bei.md +6 -0
  195. package/corpus/templates/crm/changelog/2026-07-26-records-now-use-typed-fields-currency-dates-ratings-status-s.md +6 -0
  196. package/corpus/templates/crm/changelog/2026-07-26-retyping-a-value-on-the-record-page-now-replaces-it-instead-.md +6 -0
  197. package/corpus/templates/crm/changelog/2026-07-26-settings-now-has-fields-and-lists-tabs-for-adding-fields-edi.md +6 -0
  198. package/corpus/templates/crm/changelog/2026-07-26-the-proposal-review-now-shows-the-field-that-actually-change.md +6 -0
  199. package/corpus/templates/crm/changelog/2026-07-26-the-record-grid-is-now-a-real-spreadsheet-filter-sort-and-pa.md +6 -0
  200. package/corpus/templates/crm/components.json +20 -0
  201. package/corpus/templates/crm/docs/architecture/crm-contract.md +214 -0
  202. package/corpus/templates/crm/docs/features/README.md +55 -0
  203. package/corpus/templates/crm/docs/features/f1-records-and-fields.mdx +52 -0
  204. package/corpus/templates/crm/docs/features/f2-lists-pipelines-and-views.mdx +48 -0
  205. package/corpus/templates/crm/docs/features/f3-work-and-record-quality.mdx +40 -0
  206. package/corpus/templates/crm/docs/features/f4-evidence-and-chat.mdx +47 -0
  207. package/corpus/templates/crm/learnings.defaults.md +5 -0
  208. package/corpus/templates/crm/netlify.toml +11 -0
  209. package/corpus/templates/crm/package.json +61 -0
  210. package/corpus/templates/crm/public/favicon.svg +1 -0
  211. package/corpus/templates/crm/public/icon-180.svg +1 -0
  212. package/corpus/templates/crm/public/manifest.json +11 -0
  213. package/corpus/templates/crm/react-router.config.ts +7 -0
  214. package/corpus/templates/crm/server/crm/adapter.ts +32 -0
  215. package/corpus/templates/crm/server/crm/crm-field-firewall.ts +64 -0
  216. package/corpus/templates/crm/server/crm/crm-mirror.ts +721 -0
  217. package/corpus/templates/crm/server/crm/field-policy-attributes.ts +48 -0
  218. package/corpus/templates/crm/server/crm/hubspot-adapter.ts +1146 -0
  219. package/corpus/templates/crm/server/crm/native-adapter.ts +1810 -0
  220. package/corpus/templates/crm/server/crm/provider-record-link.ts +264 -0
  221. package/corpus/templates/crm/server/crm/read-through.ts +207 -0
  222. package/corpus/templates/crm/server/crm/salesforce-adapter.ts +1255 -0
  223. package/corpus/templates/crm/server/db/crm-store.ts +1112 -0
  224. package/corpus/templates/crm/server/db/index.ts +191 -0
  225. package/corpus/templates/crm/server/db/schema.ts +647 -0
  226. package/corpus/templates/crm/server/lib/crm-query.ts +1558 -0
  227. package/corpus/templates/crm/server/lib/dedupe.ts +714 -0
  228. package/corpus/templates/crm/server/lib/enrichment-cost.ts +381 -0
  229. package/corpus/templates/crm/server/lib/enrichment-slots.ts +519 -0
  230. package/corpus/templates/crm/server/lib/intelligence/default-detectors.ts +65 -0
  231. package/corpus/templates/crm/server/lib/intelligence/evidence.ts +105 -0
  232. package/corpus/templates/crm/server/lib/intelligence/keyword-detector.ts +94 -0
  233. package/corpus/templates/crm/server/lib/intelligence/smart-detector.ts +39 -0
  234. package/corpus/templates/crm/server/lib/intelligence/summary.ts +19 -0
  235. package/corpus/templates/crm/server/lib/lifecycle.ts +634 -0
  236. package/corpus/templates/crm/server/lib/provider-api.ts +64 -0
  237. package/corpus/templates/crm/server/middleware/auth.ts +4 -0
  238. package/corpus/templates/crm/server/plugins/agent-chat.ts +65 -0
  239. package/corpus/templates/crm/server/plugins/auth.ts +14 -0
  240. package/corpus/templates/crm/server/plugins/core-routes.ts +17 -0
  241. package/corpus/templates/crm/server/plugins/db.ts +738 -0
  242. package/corpus/templates/crm/server/routes/[...page].get.ts +5 -0
  243. package/corpus/templates/crm/shared/crm-attributes.ts +469 -0
  244. package/corpus/templates/crm/shared/crm-automation-recipes.ts +132 -0
  245. package/corpus/templates/crm/shared/crm-contract.ts +417 -0
  246. package/corpus/templates/crm/shared/crm-navigation.ts +190 -0
  247. package/corpus/templates/crm/shared/crm-sales-config.ts +63 -0
  248. package/corpus/templates/crm/ssr-entry.ts +11 -0
  249. package/corpus/templates/crm/tsconfig.json +24 -0
  250. package/corpus/templates/crm/vite.config.ts +15 -0
  251. package/corpus/templates/crm/vitest.config.ts +5 -0
  252. package/corpus/templates/design/app/i18n-data.ts +12 -12
  253. package/corpus/templates/design/changelog/2026-07-26-apply-design-updates-from-a-centered-top-toolbar-without-cov.md +6 -0
  254. package/dist/cli/templates-meta.d.ts.map +1 -1
  255. package/dist/cli/templates-meta.js +12 -0
  256. package/dist/cli/templates-meta.js.map +1 -1
  257. package/dist/collab/routes.d.ts +1 -1
  258. package/dist/observability/routes.d.ts +3 -3
  259. package/dist/resources/handlers.d.ts +1 -1
  260. package/dist/server/realtime-token.d.ts +1 -1
  261. package/dist/server/transcribe-voice.d.ts +1 -1
  262. package/package.json +1 -1
  263. package/src/cli/templates-meta.ts +12 -0
@@ -0,0 +1,53 @@
1
+ import {
2
+ useActionMutation,
3
+ useActionQuery,
4
+ } from "@agent-native/core/client/hooks";
5
+ import { useT } from "@agent-native/core/client/i18n";
6
+ import { useParams } from "react-router";
7
+
8
+ import { RecordActions } from "@/components/crm/RecordActions";
9
+ import { RecordWorkspace } from "@/components/crm/RecordWorkspace";
10
+ import { normalizeRecord, type CrmRecordDetail } from "@/lib/types";
11
+
12
+ export default function RecordRoute() {
13
+ const t = useT();
14
+ const { recordId } = useParams();
15
+ const query = useActionQuery<unknown>(
16
+ "get-crm-record" as never,
17
+ { recordId } as never,
18
+ { enabled: Boolean(recordId) },
19
+ );
20
+ const manageTask = useActionMutation<
21
+ unknown,
22
+ { taskId: string; status: "done" }
23
+ >("manage-crm-task" as never);
24
+ const summary = normalizeRecord(query.data, "account");
25
+ const detail = summary
26
+ ? ({ ...summary, ...(query.data as object) } as CrmRecordDetail)
27
+ : undefined;
28
+
29
+ // A read that failed is not a record that is gone. Rendering the "archived or
30
+ // no longer shared" empty state for a transport or permission error would
31
+ // tell the user something we do not know.
32
+ if (query.error)
33
+ return (
34
+ <div className="p-6">
35
+ <p className="text-sm font-medium">{t("record.loadFailedTitle")}</p>
36
+ <p className="mt-1 text-sm text-muted-foreground">
37
+ {query.error instanceof Error
38
+ ? query.error.message
39
+ : t("record.loadFailedDescription")}
40
+ </p>
41
+ </div>
42
+ );
43
+
44
+ return (
45
+ <RecordWorkspace
46
+ record={detail}
47
+ isLoading={query.isLoading}
48
+ isCompletingTask={manageTask.isPending}
49
+ onCompleteTask={(taskId) => manageTask.mutate({ taskId, status: "done" })}
50
+ actions={detail ? <RecordActions record={detail} /> : undefined}
51
+ />
52
+ );
53
+ }
@@ -0,0 +1,64 @@
1
+ import { useT } from "@agent-native/core/client/i18n";
2
+ import { useSearchParams } from "react-router";
3
+
4
+ import { CreateCrmRecordDialog } from "@/components/crm/CreateCrmRecordDialog";
5
+ import { RecordGrid } from "@/components/crm/RecordGrid";
6
+ import { PageHeader } from "@/components/crm/Surface";
7
+ import type { CrmKind } from "@/lib/types";
8
+ import { cn } from "@/lib/utils";
9
+
10
+ const KINDS: CrmKind[] = ["account", "person", "opportunity"];
11
+
12
+ export function meta() {
13
+ return [{ title: "Records · CRM" }]; // i18n-ignore
14
+ }
15
+
16
+ export default function RecordsRoute() {
17
+ const t = useT();
18
+ const [params, setParams] = useSearchParams();
19
+ const raw = params.get("kind");
20
+ const kind: CrmKind = KINDS.includes(raw as CrmKind)
21
+ ? (raw as CrmKind)
22
+ : "account";
23
+
24
+ function selectKind(next: CrmKind) {
25
+ const updated = new URLSearchParams(params);
26
+ updated.set("kind", next);
27
+ // A cursor and a search term belong to the kind that produced them.
28
+ updated.delete("q");
29
+ setParams(updated, { replace: true });
30
+ }
31
+
32
+ return (
33
+ <>
34
+ <PageHeader
35
+ eyebrow={t("records.eyebrow")}
36
+ title={t("records.title")}
37
+ description={t("records.description")}
38
+ actions={<CreateCrmRecordDialog kind={kind} />}
39
+ />
40
+ <div className="flex items-center gap-1 border-b border-border/70 px-5 py-2">
41
+ {KINDS.map((candidate) => (
42
+ <button
43
+ key={candidate}
44
+ type="button"
45
+ onClick={() => selectKind(candidate)}
46
+ className={cn(
47
+ "cursor-pointer rounded-full px-3 py-1 text-sm transition-colors",
48
+ candidate === kind
49
+ ? "bg-primary text-primary-foreground"
50
+ : "text-muted-foreground hover:bg-muted hover:text-foreground",
51
+ )}
52
+ >
53
+ {t(`records.kind.${candidate}`)}
54
+ </button>
55
+ ))}
56
+ </div>
57
+ <RecordGrid
58
+ key={kind}
59
+ kind={kind}
60
+ emptyTitle={t(`records.empty.${kind}`)}
61
+ />
62
+ </>
63
+ );
64
+ }
@@ -0,0 +1,118 @@
1
+ import { ChangelogSettingsCard } from "@agent-native/core/client/changelog";
2
+ import { useT } from "@agent-native/core/client/i18n";
3
+ import {
4
+ SettingsTabsPage,
5
+ useAgentSettingsTabs,
6
+ type SettingsTabItem,
7
+ } from "@agent-native/core/client/settings";
8
+ import {
9
+ IconAdjustments,
10
+ IconColumns3,
11
+ IconListDetails,
12
+ IconPlugConnected,
13
+ IconWaveSine,
14
+ } from "@tabler/icons-react";
15
+ import { useMemo } from "react";
16
+ import { useLocation } from "react-router";
17
+
18
+ import { IntelligenceSettings } from "@/components/crm/IntelligenceSettings";
19
+ import { AdvancedSettings } from "@/components/crm/settings/AdvancedSettings";
20
+ import { ConnectionSettings } from "@/components/crm/settings/ConnectionSettings";
21
+ import { FieldsSettings } from "@/components/crm/settings/FieldsSettings";
22
+ import { ListsSettings } from "@/components/crm/settings/ListsSettings";
23
+
24
+ import changelog from "../../CHANGELOG.md?raw";
25
+
26
+ export function meta() {
27
+ return [{ title: "CRM settings" }];
28
+ }
29
+
30
+ /**
31
+ * `/settings/<section>` deep links. `connections` is the shared workspace tab
32
+ * and `connection` is the CRM one, so the segment is matched exactly rather
33
+ * than by substring.
34
+ */
35
+ const SETTINGS_SECTIONS: readonly string[] = [
36
+ "fields",
37
+ "lists",
38
+ "intelligence",
39
+ "connection",
40
+ "connections",
41
+ "advanced",
42
+ ];
43
+
44
+ function sectionFromPath(pathname: string): string {
45
+ const section = pathname.split("/settings/")[1]?.split("/")[0] ?? "";
46
+ return SETTINGS_SECTIONS.includes(section) ? section : "general";
47
+ }
48
+
49
+ export default function SettingsRoute() {
50
+ const t = useT();
51
+ const location = useLocation();
52
+ const agentSettingsTabs = useAgentSettingsTabs();
53
+ const tabs = useMemo<SettingsTabItem[]>(
54
+ () => [
55
+ {
56
+ id: "connection",
57
+ label: t("connection.tab"),
58
+ icon: IconPlugConnected,
59
+ keywords: "provider hubspot salesforce native mode mirror sync",
60
+ content: <ConnectionSettings />,
61
+ },
62
+ {
63
+ id: "fields",
64
+ label: t("fields.tab"),
65
+ icon: IconColumns3,
66
+ keywords:
67
+ "attributes schema columns slug type authority options status select stage",
68
+ content: <FieldsSettings />,
69
+ },
70
+ {
71
+ id: "lists",
72
+ label: t("lists.tab"),
73
+ icon: IconListDetails,
74
+ keywords: "lists entries pipeline workflow stage board",
75
+ content: <ListsSettings />,
76
+ },
77
+ {
78
+ id: "intelligence",
79
+ label: t("intelligence.tab"),
80
+ icon: IconWaveSine,
81
+ keywords: "signals trackers keywords smart detectors call evidence",
82
+ content: <IntelligenceSettings />,
83
+ },
84
+ {
85
+ id: "advanced",
86
+ label: t("advanced.tab"),
87
+ icon: IconAdjustments,
88
+ group: "workspace",
89
+ keywords: "danger reset reconfigure retention archive delete",
90
+ content: <AdvancedSettings />,
91
+ },
92
+ ...agentSettingsTabs,
93
+ ],
94
+ [agentSettingsTabs, t],
95
+ );
96
+
97
+ return (
98
+ <SettingsTabsPage
99
+ defaultTab={sectionFromPath(location.pathname)}
100
+ extraTabs={tabs}
101
+ general={
102
+ <div className="mx-auto w-full max-w-2xl space-y-3">
103
+ <h1 className="text-xl font-semibold tracking-tight">
104
+ {t("settings.title")}
105
+ </h1>
106
+ <p className="text-sm leading-6 text-muted-foreground">
107
+ {t("settings.description")}
108
+ </p>
109
+ </div>
110
+ }
111
+ whatsNew={
112
+ <div className="mx-auto w-full max-w-2xl">
113
+ <ChangelogSettingsCard markdown={changelog} />
114
+ </div>
115
+ }
116
+ />
117
+ );
118
+ }
@@ -0,0 +1,308 @@
1
+ import {
2
+ useActionMutation,
3
+ useActionQuery,
4
+ } from "@agent-native/core/client/hooks";
5
+ import { useMemo, useState } from "react";
6
+ import { Link, useNavigate } from "react-router";
7
+ import { toast } from "sonner";
8
+
9
+ import { PageHeader } from "@/components/crm/Surface";
10
+ import { Button } from "@/components/ui/button";
11
+ import { Input } from "@/components/ui/input";
12
+ import { Label } from "@/components/ui/label";
13
+ import {
14
+ Select,
15
+ SelectContent,
16
+ SelectItem,
17
+ SelectTrigger,
18
+ SelectValue,
19
+ } from "@/components/ui/select";
20
+
21
+ interface WorkspaceConnection {
22
+ id: string;
23
+ provider: "hubspot" | "salesforce";
24
+ label: string;
25
+ accountLabel?: string | null;
26
+ status: string;
27
+ }
28
+
29
+ export default function SetupRoute() {
30
+ const navigate = useNavigate();
31
+ const connectionsQuery = useActionQuery<unknown>(
32
+ "list-workspace-connections" as never,
33
+ { includeDisabled: false } as never,
34
+ );
35
+ const connections = useMemo(
36
+ () => crmConnections(connectionsQuery.data),
37
+ [connectionsQuery.data],
38
+ );
39
+ const [workspaceConnectionId, setWorkspaceConnectionId] = useState("");
40
+ const [pipelineIds, setPipelineIds] = useState("");
41
+ const [historyDays, setHistoryDays] = useState("90");
42
+ const configure = useActionMutation<
43
+ { id: string },
44
+ {
45
+ workspaceConnectionId: string;
46
+ provider: "hubspot" | "salesforce";
47
+ selectedPipelineIds: string[];
48
+ selectedObjectTypes: string[];
49
+ }
50
+ >("configure-crm-connection" as never);
51
+ const configureNative = useActionMutation<
52
+ { id: string },
53
+ Record<string, never>
54
+ >("configure-native-crm" as never);
55
+ const sync = useActionMutation<
56
+ unknown,
57
+ {
58
+ connectionId: string;
59
+ objectType: string;
60
+ scope: { updatedAfter: string; pipelineIds?: string[] };
61
+ maxPages: number;
62
+ }
63
+ >("sync-crm" as never);
64
+
65
+ const selected = connections.find(
66
+ (connection) => connection.id === workspaceConnectionId,
67
+ );
68
+
69
+ async function syncRecentRecords() {
70
+ if (!selected) return;
71
+ const selectedPipelines = pipelineIds
72
+ .split(",")
73
+ .map((value) => value.trim())
74
+ .filter(Boolean)
75
+ .slice(0, 50);
76
+ const days = Math.max(
77
+ 1,
78
+ Math.min(365, Number.parseInt(historyDays, 10) || 90),
79
+ );
80
+ try {
81
+ const connection = await configure.mutateAsync({
82
+ workspaceConnectionId,
83
+ provider: selected.provider,
84
+ selectedPipelineIds:
85
+ selected.provider === "hubspot" ? selectedPipelines : [],
86
+ selectedObjectTypes: objectTypesForProvider(selected.provider),
87
+ });
88
+ const updatedAfter = new Date(
89
+ Date.now() - days * 24 * 60 * 60 * 1_000,
90
+ ).toISOString();
91
+ for (const objectType of objectTypesForProvider(selected.provider)) {
92
+ await sync.mutateAsync({
93
+ connectionId: connection.id,
94
+ objectType,
95
+ scope: {
96
+ updatedAfter,
97
+ ...(selected.provider === "hubspot" &&
98
+ objectType === "deals" &&
99
+ selectedPipelines.length
100
+ ? { pipelineIds: selectedPipelines }
101
+ : {}),
102
+ },
103
+ maxPages: 2,
104
+ });
105
+ }
106
+ toast.success(
107
+ `Recent ${providerLabel(selected.provider)} records are ready.`,
108
+ );
109
+ navigate("/", { replace: true });
110
+ } catch (error) {
111
+ toast.error(error instanceof Error ? error.message : "CRM sync failed.");
112
+ }
113
+ }
114
+
115
+ async function startNativeCrm() {
116
+ try {
117
+ await configureNative.mutateAsync({});
118
+ toast.success("Your native CRM is ready.");
119
+ navigate("/", { replace: true });
120
+ } catch (error) {
121
+ toast.error(
122
+ error instanceof Error ? error.message : "Could not start native CRM.",
123
+ );
124
+ }
125
+ }
126
+
127
+ return (
128
+ <>
129
+ <PageHeader
130
+ eyebrow="CRM"
131
+ title="Set up CRM"
132
+ description="Start with CRM's built-in SQL store, or connect a scoped HubSpot or Salesforce companion."
133
+ />
134
+ <div className="mx-auto grid w-full max-w-xl gap-6 p-5 sm:p-7">
135
+ <section className="grid gap-4 rounded-lg border border-border/70 bg-card p-4">
136
+ <div className="grid gap-1">
137
+ <p className="text-sm font-medium">Start with Native SQL</p>
138
+ <p className="text-sm leading-6 text-muted-foreground">
139
+ Run accounts, people, opportunities, saved views, tasks, and
140
+ cadence without connecting another CRM. Your CRM is
141
+ local-authoritative and portable across SQLite, Postgres, and D1.
142
+ </p>
143
+ </div>
144
+ <Button
145
+ className="w-full sm:w-fit"
146
+ disabled={configureNative.isPending}
147
+ onClick={() => void startNativeCrm()}
148
+ >
149
+ {configureNative.isPending ? "Starting…" : "Start with Native SQL"}
150
+ </Button>
151
+ </section>
152
+ <div className="flex items-center gap-3 text-xs font-medium uppercase tracking-[0.12em] text-muted-foreground">
153
+ <span className="h-px flex-1 bg-border" />
154
+ Or connect an existing CRM
155
+ <span className="h-px flex-1 bg-border" />
156
+ </div>
157
+ <div className="grid gap-2">
158
+ <Label htmlFor="crm-connection">CRM connection</Label>
159
+ <Select
160
+ value={workspaceConnectionId}
161
+ onValueChange={setWorkspaceConnectionId}
162
+ disabled={connectionsQuery.isLoading}
163
+ >
164
+ <SelectTrigger id="crm-connection">
165
+ <SelectValue placeholder="Select HubSpot or Salesforce" />
166
+ </SelectTrigger>
167
+ <SelectContent>
168
+ {connections.map((connection) => (
169
+ <SelectItem key={connection.id} value={connection.id}>
170
+ {connection.label} · {providerLabel(connection.provider)}
171
+ {connection.accountLabel
172
+ ? ` · ${connection.accountLabel}`
173
+ : ""}
174
+ </SelectItem>
175
+ ))}
176
+ </SelectContent>
177
+ </Select>
178
+ <p className="text-xs text-muted-foreground">
179
+ Only workspace Connections granted to this app can be used. CRM
180
+ never stores provider tokens.
181
+ </p>
182
+ </div>
183
+ <div className="grid gap-4 rounded-lg border border-border/70 bg-card p-4 sm:grid-cols-2">
184
+ <div className="grid gap-2">
185
+ <Label htmlFor="history-days">Recent history</Label>
186
+ <Input
187
+ id="history-days"
188
+ type="number"
189
+ min={1}
190
+ max={365}
191
+ value={historyDays}
192
+ onChange={(event) => setHistoryDays(event.target.value)}
193
+ />
194
+ <p className="text-xs text-muted-foreground">
195
+ Days, capped at 365.
196
+ </p>
197
+ </div>
198
+ {selected?.provider === "hubspot" ? (
199
+ <div className="grid gap-2">
200
+ <Label htmlFor="pipeline-ids">Deal pipeline IDs</Label>
201
+ <Input
202
+ id="pipeline-ids"
203
+ value={pipelineIds}
204
+ maxLength={8_000}
205
+ placeholder="Optional, comma-separated"
206
+ onChange={(event) => setPipelineIds(event.target.value)}
207
+ />
208
+ <p className="text-xs text-muted-foreground">
209
+ Leave blank for recently updated deals.
210
+ </p>
211
+ </div>
212
+ ) : selected?.provider === "salesforce" ? (
213
+ <div className="grid gap-2">
214
+ <p className="text-sm font-medium">Salesforce objects</p>
215
+ <p className="text-xs leading-5 text-muted-foreground">
216
+ CRM mirrors recently updated Accounts, Contacts, and
217
+ Opportunities in this initial cohort.
218
+ </p>
219
+ </div>
220
+ ) : (
221
+ <div className="grid gap-2">
222
+ <p className="text-sm font-medium">Cohort objects</p>
223
+ <p className="text-xs leading-5 text-muted-foreground">
224
+ Choose a connection to see its initial object cohort.
225
+ </p>
226
+ </div>
227
+ )}
228
+ </div>
229
+ {connections.length ? (
230
+ <Button
231
+ disabled={
232
+ !selected ||
233
+ selected.status !== "connected" ||
234
+ configure.isPending ||
235
+ sync.isPending
236
+ }
237
+ onClick={() => void syncRecentRecords()}
238
+ >
239
+ {configure.isPending || sync.isPending
240
+ ? "Syncing…"
241
+ : "Configure and sync"}
242
+ </Button>
243
+ ) : (
244
+ <div className="rounded-lg border border-dashed border-border p-5 text-center">
245
+ <p className="text-sm font-medium">
246
+ No connected HubSpot or Salesforce account is available.
247
+ </p>
248
+ <p className="mt-1 text-sm text-muted-foreground">
249
+ You can start with Native SQL above, or authorize a provider and
250
+ grant it to CRM from shared settings.
251
+ </p>
252
+ <Button asChild variant="outline" className="mt-4">
253
+ <Link to="/settings/connections">Open shared connections</Link>
254
+ </Button>
255
+ <p className="mt-3 text-xs text-muted-foreground">
256
+ Using a Salesforce sandbox?{" "}
257
+ <a
258
+ className="font-medium text-foreground underline underline-offset-4"
259
+ href="/_agent-native/connections/oauth/salesforce/start?environment=sandbox"
260
+ >
261
+ Authorize the sandbox directly
262
+ </a>
263
+ .
264
+ </p>
265
+ </div>
266
+ )}
267
+ </div>
268
+ </>
269
+ );
270
+ }
271
+
272
+ function crmConnections(data: unknown): WorkspaceConnection[] {
273
+ if (!data || typeof data !== "object") return [];
274
+ const rows = (data as { connections?: unknown }).connections;
275
+ if (!Array.isArray(rows)) return [];
276
+ return rows.flatMap((row) => {
277
+ if (!row || typeof row !== "object") return [];
278
+ const item = row as Record<string, unknown>;
279
+ if (
280
+ (item.provider !== "hubspot" && item.provider !== "salesforce") ||
281
+ typeof item.id !== "string" ||
282
+ typeof item.label !== "string" ||
283
+ typeof item.status !== "string"
284
+ ) {
285
+ return [];
286
+ }
287
+ return [
288
+ {
289
+ id: item.id,
290
+ provider: item.provider,
291
+ label: item.label,
292
+ accountLabel:
293
+ typeof item.accountLabel === "string" ? item.accountLabel : null,
294
+ status: item.status,
295
+ },
296
+ ];
297
+ });
298
+ }
299
+
300
+ function objectTypesForProvider(provider: WorkspaceConnection["provider"]) {
301
+ return provider === "hubspot"
302
+ ? ["companies", "contacts", "deals"]
303
+ : ["Account", "Contact", "Opportunity"];
304
+ }
305
+
306
+ function providerLabel(provider: WorkspaceConnection["provider"]) {
307
+ return provider === "hubspot" ? "HubSpot" : "Salesforce";
308
+ }