@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,200 @@
1
+ import { defineAction } from "@agent-native/core/action";
2
+ import { accessFilter } from "@agent-native/core/sharing";
3
+ import { and, eq, inArray, isNull } from "drizzle-orm";
4
+ import { z } from "zod";
5
+
6
+ import { getDb, schema } from "../server/db/index.js";
7
+ import { storageColumnFor } from "../shared/crm-attributes.js";
8
+ import type { CrmValue } from "../shared/crm-contract.js";
9
+ import {
10
+ CrmAttributeValueError,
11
+ loadAttributeOptions,
12
+ toAttributeDefinition,
13
+ type CrmAttributeRow,
14
+ } from "./_crm-attribute-utils.js";
15
+
16
+ const MAX_RECORDS = 200;
17
+
18
+ interface StoredValueRow {
19
+ stringValue: string | null;
20
+ numberValue: number | null;
21
+ booleanValue: boolean | null;
22
+ jsonValue: string | null;
23
+ }
24
+
25
+ /**
26
+ * Decode by the attribute's declared storage column, never by sniffing which
27
+ * column is non-null: a `false` checkbox and an empty text field are otherwise
28
+ * indistinguishable.
29
+ */
30
+ function decodeValue(
31
+ attribute: CrmAttributeRow,
32
+ row: StoredValueRow,
33
+ ): CrmValue {
34
+ const column = storageColumnFor(attribute.attributeType, attribute.multi);
35
+ if (column === "numberValue") return row.numberValue;
36
+ if (column === "booleanValue") return row.booleanValue;
37
+ if (column === "jsonValue") {
38
+ if (row.jsonValue === null) return null;
39
+ try {
40
+ return JSON.parse(row.jsonValue) as CrmValue;
41
+ } catch {
42
+ throw new CrmAttributeValueError(
43
+ "crm-attribute-value-unreadable",
44
+ `Stored value for "${attribute.apiSlug ?? attribute.fieldName}" is not readable JSON. It was written outside the attribute writer.`,
45
+ );
46
+ }
47
+ }
48
+ return row.stringValue;
49
+ }
50
+
51
+ export default defineAction({
52
+ description:
53
+ "Return the current typed attribute values for a bounded set of CRM records, with each value's actor and provenance blob and each record's current revision. This is the value payload behind the record grid: list-crm-records returns row summaries, this returns the cells. Only current values are returned (the bitemporal row whose activeUntil is null); list-entry values are excluded.",
54
+ schema: z.object({
55
+ recordIds: z
56
+ .array(z.string().trim().min(1).max(128))
57
+ .min(1)
58
+ .max(MAX_RECORDS)
59
+ .describe("Local CRM record IDs from list-crm-records."),
60
+ includeArchived: z
61
+ .boolean()
62
+ .default(false)
63
+ .describe("Include values of archived attributes."),
64
+ }),
65
+ readOnly: true,
66
+ run: async (args) => {
67
+ const db = getDb();
68
+ const recordIds = [...new Set(args.recordIds)];
69
+ const records = await db
70
+ .select({
71
+ id: schema.crmRecords.id,
72
+ connectionId: schema.crmRecords.connectionId,
73
+ objectType: schema.crmRecords.objectType,
74
+ provider: schema.crmRecords.provider,
75
+ remoteRevision: schema.crmRecords.remoteRevision,
76
+ })
77
+ .from(schema.crmRecords)
78
+ .where(
79
+ and(
80
+ inArray(schema.crmRecords.id, recordIds),
81
+ eq(schema.crmRecords.tombstone, false),
82
+ accessFilter(schema.crmRecords, schema.crmRecordShares),
83
+ ),
84
+ );
85
+ if (records.length === 0) {
86
+ return { records: [], attributes: [] };
87
+ }
88
+
89
+ const objectTypes = [...new Set(records.map((row) => row.objectType))];
90
+ const connectionIds = [...new Set(records.map((row) => row.connectionId))];
91
+ const attributeRows = await db
92
+ .select()
93
+ .from(schema.crmFieldPolicies)
94
+ .where(
95
+ and(
96
+ eq(schema.crmFieldPolicies.target, "object"),
97
+ inArray(schema.crmFieldPolicies.objectType, objectTypes),
98
+ inArray(schema.crmFieldPolicies.connectionId, connectionIds),
99
+ ...(args.includeArchived
100
+ ? []
101
+ : [eq(schema.crmFieldPolicies.archived, false)]),
102
+ accessFilter(schema.crmFieldPolicies, schema.crmFieldPolicyShares),
103
+ ),
104
+ );
105
+ const options = await loadAttributeOptions({
106
+ attributeIds: attributeRows.map((row) => row.id),
107
+ includeArchived: args.includeArchived,
108
+ });
109
+ // Keyed by field_name: that is the column the bitemporal writer keys on and
110
+ // the value `api_slug` was seeded from.
111
+ const attributeByKey = new Map(
112
+ attributeRows.map((row) => [
113
+ `${row.connectionId}:${row.objectType}:${row.fieldName}`,
114
+ row,
115
+ ]),
116
+ );
117
+
118
+ const valueRows = await db
119
+ .select({
120
+ recordId: schema.crmRecordFields.recordId,
121
+ fieldName: schema.crmRecordFields.fieldName,
122
+ stringValue: schema.crmRecordFields.stringValue,
123
+ numberValue: schema.crmRecordFields.numberValue,
124
+ booleanValue: schema.crmRecordFields.booleanValue,
125
+ jsonValue: schema.crmRecordFields.jsonValue,
126
+ activeFrom: schema.crmRecordFields.activeFrom,
127
+ actorType: schema.crmRecordFields.actorType,
128
+ actorId: schema.crmRecordFields.actorId,
129
+ provenanceJson: schema.crmRecordFields.provenanceJson,
130
+ })
131
+ .from(schema.crmRecordFields)
132
+ .where(
133
+ and(
134
+ inArray(schema.crmRecordFields.recordId, recordIds),
135
+ // A list-entry value lives in this same table; `entry_id IS NULL` is
136
+ // the record-vs-entry discriminator.
137
+ isNull(schema.crmRecordFields.entryId),
138
+ isNull(schema.crmRecordFields.activeUntil),
139
+ accessFilter(schema.crmRecordFields, schema.crmRecordFieldShares),
140
+ ),
141
+ );
142
+
143
+ const recordById = new Map(records.map((row) => [row.id, row]));
144
+ const buckets = new Map<
145
+ string,
146
+ {
147
+ values: Record<string, CrmValue>;
148
+ valuesSince: Record<string, string>;
149
+ provenance: Record<
150
+ string,
151
+ { actorType: string; actorId: string | null; provenanceJson: string }
152
+ >;
153
+ }
154
+ >(
155
+ records.map((row) => [
156
+ row.id,
157
+ { values: {}, valuesSince: {}, provenance: {} },
158
+ ]),
159
+ );
160
+
161
+ for (const row of valueRows) {
162
+ const record = recordById.get(row.recordId);
163
+ const bucket = buckets.get(row.recordId);
164
+ if (!record || !bucket) continue;
165
+ const attribute = attributeByKey.get(
166
+ `${record.connectionId}:${record.objectType}:${row.fieldName}`,
167
+ );
168
+ // A value with no surviving attribute definition is not a cell the grid
169
+ // can type; skipping it here is not data loss — the row is still stored.
170
+ if (!attribute) continue;
171
+ const slug = attribute.apiSlug ?? attribute.fieldName;
172
+ bucket.values[slug] = decodeValue(attribute, row);
173
+ bucket.valuesSince[slug] = row.activeFrom;
174
+ bucket.provenance[slug] = {
175
+ actorType: row.actorType,
176
+ actorId: row.actorId,
177
+ provenanceJson: row.provenanceJson,
178
+ };
179
+ }
180
+
181
+ return {
182
+ attributes: attributeRows.map((row) =>
183
+ toAttributeDefinition(row, options.get(row.id) ?? []),
184
+ ),
185
+ records: records.map((row) => {
186
+ const bucket = buckets.get(row.id)!;
187
+ return {
188
+ recordId: row.id,
189
+ connectionId: row.connectionId,
190
+ objectType: row.objectType,
191
+ provider: row.provider,
192
+ remoteRevision: row.remoteRevision,
193
+ values: bucket.values,
194
+ valuesSince: bucket.valuesSince,
195
+ provenance: bucket.provenance,
196
+ };
197
+ }),
198
+ };
199
+ },
200
+ });
@@ -0,0 +1,103 @@
1
+ import { defineAction, type ActionRunContext } from "@agent-native/core/action";
2
+ import { getRequestUserEmail } from "@agent-native/core/server";
3
+ import { z } from "zod";
4
+
5
+ import {
6
+ createConnectedCrmAdapter,
7
+ isConnectedCrmProvider,
8
+ } from "../server/crm/adapter.js";
9
+ import { resolveNativeCrmAccessScope } from "../server/crm/native-adapter.js";
10
+ import {
11
+ crmFilterSchema,
12
+ crmSortSchema,
13
+ queryCrmRecords,
14
+ } from "../server/lib/crm-query.js";
15
+
16
+ const kinds = [
17
+ "account",
18
+ "person",
19
+ "opportunity",
20
+ "activity",
21
+ "task",
22
+ "custom",
23
+ ] as const;
24
+
25
+ export default defineAction({
26
+ description:
27
+ 'List a bounded, access-scoped page of CRM records. Filters, sorting, and pagination all run in SQL: pass a typed filter tree (or a saved viewId whose filter is used), an ordered sort, and the cursor from the previous page. A condition value of "@currentUser" resolves to the calling user on actor-reference attributes and owner fields. A filter naming an unknown or archived attribute fails rather than returning unfiltered rows.',
28
+ schema: z.object({
29
+ kind: z
30
+ .enum(kinds)
31
+ .optional()
32
+ .describe("Optional canonical CRM record kind."),
33
+ connectionId: z
34
+ .string()
35
+ .min(1)
36
+ .optional()
37
+ .describe("Optional CRM connection ID."),
38
+ viewId: z
39
+ .string()
40
+ .min(1)
41
+ .max(128)
42
+ .optional()
43
+ .describe(
44
+ "Optional access-scoped saved CRM view ID; its stored filter, sort, and columns are applied. Cannot be combined with filter.",
45
+ ),
46
+ query: z
47
+ .string()
48
+ .trim()
49
+ .min(1)
50
+ .max(120)
51
+ .optional()
52
+ .describe("Optional display-name search, ANDed on top of the filter."),
53
+ filter: crmFilterSchema
54
+ .optional()
55
+ .describe(
56
+ 'Typed filter tree: {op:"and"|"or", conditions:[{attributeId|field, condition, value}]}, nestable one level. Date values accept the relative tokens today, this-week, last-N-days, and next-N-days.',
57
+ ),
58
+ sort: crmSortSchema
59
+ .optional()
60
+ .describe(
61
+ "Ordered sort keys; record id breaks ties so pagination cannot skip or repeat a row.",
62
+ ),
63
+ limit: z.coerce.number().int().min(1).max(100).default(50),
64
+ cursor: z
65
+ .string()
66
+ .max(4_000)
67
+ .optional()
68
+ .describe("Cursor returned by a previous page of this same query."),
69
+ includeTotal: z
70
+ .boolean()
71
+ .optional()
72
+ .describe(
73
+ "Include totalEstimate: the filtered row count before per-record provider scope revalidation.",
74
+ ),
75
+ }),
76
+ http: { method: "GET" },
77
+ readOnly: true,
78
+ run: (input, ctx?: ActionRunContext) =>
79
+ queryCrmRecords(input, {
80
+ actorEmail: ctx?.userEmail ?? getRequestUserEmail() ?? null,
81
+ resolveScope: async (target) => {
82
+ if (target.provider === "native") {
83
+ return resolveNativeCrmAccessScope({
84
+ connectionId: target.connectionId,
85
+ objectType: target.objectType,
86
+ });
87
+ }
88
+ if (
89
+ !isConnectedCrmProvider(target.provider) ||
90
+ !target.workspaceConnectionId
91
+ ) {
92
+ return null;
93
+ }
94
+ const adapter = await createConnectedCrmAdapter({
95
+ provider: target.provider,
96
+ connectionId: target.workspaceConnectionId,
97
+ ...(ctx?.userEmail ? { userEmail: ctx.userEmail } : {}),
98
+ ...(ctx?.orgId !== undefined ? { orgId: ctx.orgId } : {}),
99
+ });
100
+ return adapter.getAccessScope(target.objectType);
101
+ },
102
+ }),
103
+ });
@@ -0,0 +1,66 @@
1
+ import { defineAction } from "@agent-native/core/action";
2
+ import { accessFilter } from "@agent-native/core/sharing";
3
+ import { and, desc, eq } from "drizzle-orm";
4
+ import { z } from "zod";
5
+
6
+ import { getDb, schema } from "../server/db/index.js";
7
+ import { hydrateSavedViewRow } from "../server/lib/crm-query.js";
8
+
9
+ export default defineAction({
10
+ description:
11
+ "List access-scoped saved CRM views with their stored filter, sort, columns, board grouping, audience, and optional linked data-program ID.",
12
+ schema: z.object({
13
+ viewKind: z.enum(["table", "board"]).optional(),
14
+ targetKind: z.enum(["object", "list"]).optional(),
15
+ targetId: z.string().trim().min(1).max(128).optional(),
16
+ limit: z.coerce.number().int().min(1).max(100).default(50),
17
+ }),
18
+ http: { method: "GET" },
19
+ readOnly: true,
20
+ run: async (input) => {
21
+ const conditions = [
22
+ accessFilter(schema.crmSavedViews, schema.crmSavedViewShares),
23
+ ];
24
+ if (input.viewKind) {
25
+ conditions.push(eq(schema.crmSavedViews.viewKind, input.viewKind));
26
+ }
27
+ if (input.targetKind) {
28
+ conditions.push(eq(schema.crmSavedViews.targetKind, input.targetKind));
29
+ }
30
+ if (input.targetId) {
31
+ conditions.push(eq(schema.crmSavedViews.targetId, input.targetId));
32
+ }
33
+ const rows = await getDb()
34
+ .select()
35
+ .from(schema.crmSavedViews)
36
+ .where(and(...conditions))
37
+ .orderBy(
38
+ desc(schema.crmSavedViews.pinned),
39
+ desc(schema.crmSavedViews.updatedAt),
40
+ )
41
+ .limit(input.limit);
42
+
43
+ return {
44
+ views: rows.map((row) => {
45
+ const view = hydrateSavedViewRow(row);
46
+ return {
47
+ id: view.id,
48
+ name: view.name,
49
+ description: view.description || undefined,
50
+ kind: view.kind ?? undefined,
51
+ viewKind: view.viewKind,
52
+ targetKind: view.targetKind,
53
+ targetId: view.targetId ?? undefined,
54
+ groupByAttributeId: view.groupByAttributeId ?? undefined,
55
+ filters: view.filter,
56
+ sort: view.sort,
57
+ columns: view.columns,
58
+ dataProgramId: view.dataProgramId ?? undefined,
59
+ pinned: view.pinned,
60
+ audience: view.audience,
61
+ updatedAt: view.updatedAt,
62
+ };
63
+ }),
64
+ };
65
+ },
66
+ });
@@ -0,0 +1,43 @@
1
+ import { defineAction } from "@agent-native/core/action";
2
+ import { accessFilter } from "@agent-native/core/sharing";
3
+ import { and, desc, eq, like, or } from "drizzle-orm";
4
+ import { z } from "zod";
5
+
6
+ import { getDb, schema } from "../server/db/index.js";
7
+
8
+ export default defineAction({
9
+ description:
10
+ "List bounded, access-scoped CRM signals. Results contain evidence references and short excerpts, never transcripts or media.",
11
+ schema: z.object({
12
+ recordId: z.string().trim().min(1).max(128).optional(),
13
+ reviewStatus: z.enum(["unreviewed", "confirmed", "dismissed"]).optional(),
14
+ query: z.string().trim().max(120).optional(),
15
+ limit: z.coerce.number().int().min(1).max(100).default(50),
16
+ }),
17
+ http: { method: "GET" },
18
+ readOnly: true,
19
+ run: async (args) => {
20
+ const filters = [accessFilter(schema.crmSignals, schema.crmSignalShares)];
21
+ if (args.recordId)
22
+ filters.push(eq(schema.crmSignals.recordId, args.recordId));
23
+ if (args.reviewStatus)
24
+ filters.push(eq(schema.crmSignals.reviewStatus, args.reviewStatus));
25
+ if (args.query) {
26
+ const escaped = args.query.replace(/[\\%_]/g, "\\$&");
27
+ filters.push(
28
+ or(
29
+ like(schema.crmSignals.label, `%${escaped}%`),
30
+ like(schema.crmSignals.quote, `%${escaped}%`),
31
+ like(schema.crmSignals.summary, `%${escaped}%`),
32
+ )!,
33
+ );
34
+ }
35
+ const signals = await getDb()
36
+ .select()
37
+ .from(schema.crmSignals)
38
+ .where(and(...filters))
39
+ .orderBy(desc(schema.crmSignals.createdAt))
40
+ .limit(args.limit);
41
+ return { signals };
42
+ },
43
+ });
@@ -0,0 +1,32 @@
1
+ import { defineAction } from "@agent-native/core/action";
2
+ import { accessFilter } from "@agent-native/core/sharing";
3
+ import { asc } from "drizzle-orm";
4
+ import { z } from "zod";
5
+
6
+ import { getDb, schema } from "../server/db/index.js";
7
+ import { DEFAULT_CRM_DETECTORS } from "../server/lib/intelligence/default-detectors.js";
8
+
9
+ export default defineAction({
10
+ description:
11
+ "List access-scoped CRM signal detectors and the safe default pack when none are configured.",
12
+ schema: z.object({}),
13
+ http: { method: "GET" },
14
+ readOnly: true,
15
+ run: async () => {
16
+ const rows = await getDb()
17
+ .select()
18
+ .from(schema.crmSignalTrackers)
19
+ .where(
20
+ accessFilter(schema.crmSignalTrackers, schema.crmSignalTrackerShares),
21
+ )
22
+ .orderBy(asc(schema.crmSignalTrackers.name));
23
+ return {
24
+ trackers: rows.map((row) => ({
25
+ ...row,
26
+ keywords: JSON.parse(row.keywordsJson) as string[],
27
+ keywordsJson: undefined,
28
+ })),
29
+ suggestedDefaults: rows.length ? [] : DEFAULT_CRM_DETECTORS,
30
+ };
31
+ },
32
+ });
@@ -0,0 +1,22 @@
1
+ import { defineAction } from "@agent-native/core/action";
2
+ import { z } from "zod";
3
+
4
+ import { listCrmTasks } from "../server/db/crm-store.js";
5
+
6
+ export default defineAction({
7
+ description:
8
+ "List a bounded, access-scoped page of CRM follow-up tasks. Filter by the linked record or task status when needed.",
9
+ schema: z.object({
10
+ recordId: z.string().min(1).optional(),
11
+ status: z.enum(["open", "done", "cancelled"]).optional(),
12
+ limit: z.coerce.number().int().min(1).max(100).default(50),
13
+ cursor: z
14
+ .string()
15
+ .regex(/^\d+$/)
16
+ .optional()
17
+ .describe("Cursor returned by a previous page."),
18
+ }),
19
+ http: { method: "GET" },
20
+ readOnly: true,
21
+ run: (input) => listCrmTasks(input),
22
+ });
@@ -0,0 +1,14 @@
1
+ import { createListStagedDatasetsAction } from "@agent-native/core/provider-api/actions/staged-datasets";
2
+ import { getCredentialContext } from "@agent-native/core/server/request-context";
3
+ import { z } from "zod";
4
+
5
+ import { CRM_APP_ID } from "../server/lib/provider-api.js";
6
+
7
+ export default createListStagedDatasetsAction({
8
+ description:
9
+ "List caller-scoped staged CRM provider datasets created by provider-api-request. Use a dataset id with query-staged-dataset to reduce a result, or delete-staged-dataset after analysis.",
10
+ schema: z.object({}),
11
+ appId: CRM_APP_ID,
12
+ getOwnerEmail: () => getCredentialContext()?.userEmail ?? null,
13
+ http: { method: "GET" },
14
+ });
@@ -0,0 +1,47 @@
1
+ import { defineAction } from "@agent-native/core/action";
2
+ import { listWorkspaceConnectionsForApp } from "@agent-native/core/workspace-connections";
3
+ import { z } from "zod";
4
+
5
+ import { CRM_APP_ID } from "../server/lib/provider-api.js";
6
+
7
+ const httpBoolean = z.preprocess((value) => {
8
+ if (typeof value !== "string") return value;
9
+ if (["true", "1"].includes(value.toLowerCase())) return true;
10
+ if (["false", "0"].includes(value.toLowerCase())) return false;
11
+ return value;
12
+ }, z.boolean());
13
+
14
+ export default defineAction({
15
+ description:
16
+ "List safe metadata for HubSpot and Salesforce workspace connections currently granted to CRM. Provider credentials and credential references are never returned.",
17
+ schema: z.object({
18
+ includeDisabled: httpBoolean.default(false),
19
+ }),
20
+ http: { method: "GET" },
21
+ readOnly: true,
22
+ run: async ({ includeDisabled }) => {
23
+ const connectionGroups = await Promise.all(
24
+ (["hubspot", "salesforce"] as const).map((provider) =>
25
+ listWorkspaceConnectionsForApp({
26
+ appId: CRM_APP_ID,
27
+ provider,
28
+ includeDisabled,
29
+ }),
30
+ ),
31
+ );
32
+ const connections = connectionGroups.flat();
33
+ return {
34
+ connections: connections.map((connection) => ({
35
+ id: connection.id,
36
+ provider: connection.provider,
37
+ label: connection.label,
38
+ accountId: connection.accountId,
39
+ accountLabel: connection.accountLabel,
40
+ status: connection.status,
41
+ lastCheckedAt: connection.lastCheckedAt,
42
+ lastUsedAt: connection.lastUsedAt,
43
+ accessMode: connection.appAccess.mode,
44
+ })),
45
+ };
46
+ },
47
+ });