@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,192 @@
1
+ import { defineAction } from "@agent-native/core/action";
2
+ import { accessFilter } from "@agent-native/core/sharing";
3
+ import { and, asc, eq, type SQL } from "drizzle-orm";
4
+ import { z } from "zod";
5
+
6
+ import { getDb, schema } from "../server/db/index.js";
7
+ import {
8
+ attributeSummary,
9
+ buildEntryFilter,
10
+ buildEntryOrder,
11
+ CRM_ENTRY_BUILTIN_FIELDS,
12
+ CRM_ENTRY_FILTER_OPERATORS,
13
+ CrmEntryFieldResolver,
14
+ decodeCrmCursor,
15
+ indexAttributes,
16
+ loadCrmEntryValues,
17
+ loadCrmListAttributes,
18
+ MAX_LIST_ENTRY_FILTERS,
19
+ MAX_LIST_ENTRY_LIMIT,
20
+ MAX_LIST_ENTRY_SORTS,
21
+ requireCrmList,
22
+ } from "./_crm-list-utils.js";
23
+
24
+ const filterValueSchema = z.union([
25
+ z.string().max(500),
26
+ z.number(),
27
+ z.boolean(),
28
+ z.null(),
29
+ z.array(z.union([z.string().max(500), z.number(), z.boolean()])).max(50),
30
+ ]);
31
+
32
+ export default defineAction({
33
+ description:
34
+ "Read a bounded page of entries in a CRM list with each entry's record summary and its own entry attribute values. Filters, sorting, and pagination are applied in SQL, so a filtered page is the real result set and not one page narrowed afterwards. Filter and sort fields are a list attribute's api_slug or one of the built-in fields.",
35
+ schema: z.object({
36
+ listId: z.string().trim().min(1).max(128),
37
+ filters: z
38
+ .array(
39
+ z.object({
40
+ attribute: z
41
+ .string()
42
+ .trim()
43
+ .min(1)
44
+ .max(120)
45
+ .describe(
46
+ `A list attribute api_slug, or one of: ${CRM_ENTRY_BUILTIN_FIELDS.join(", ")}.`,
47
+ ),
48
+ operator: z.enum(CRM_ENTRY_FILTER_OPERATORS),
49
+ value: filterValueSchema.optional(),
50
+ }),
51
+ )
52
+ .max(MAX_LIST_ENTRY_FILTERS)
53
+ .optional(),
54
+ sort: z
55
+ .array(
56
+ z.object({
57
+ attribute: z.string().trim().min(1).max(120),
58
+ direction: z.enum(["asc", "desc"]).default("asc"),
59
+ }),
60
+ )
61
+ .max(MAX_LIST_ENTRY_SORTS)
62
+ .optional(),
63
+ limit: z.coerce.number().int().min(1).max(MAX_LIST_ENTRY_LIMIT).default(50),
64
+ cursor: z
65
+ .string()
66
+ .regex(/^\d+$/)
67
+ .optional()
68
+ .describe("Cursor returned by a previous page."),
69
+ }),
70
+ http: { method: "POST" },
71
+ readOnly: true,
72
+ run: async (args) => {
73
+ const db = getDb();
74
+ const list = await requireCrmList(db, args.listId, "viewer");
75
+ const attributes = await loadCrmListAttributes(db, list.id);
76
+ const bySlug = indexAttributes(attributes);
77
+
78
+ const resolver = new CrmEntryFieldResolver(bySlug);
79
+ // Filters and sorts are resolved before the query is built: resolving a
80
+ // field is what registers the LEFT JOIN it needs.
81
+ const conditions: SQL[] = (args.filters ?? []).map((filter) =>
82
+ buildEntryFilter(resolver, filter),
83
+ );
84
+ const order = (args.sort ?? []).flatMap((sort) =>
85
+ buildEntryOrder(resolver, sort),
86
+ );
87
+
88
+ const offset = decodeCrmCursor(args.cursor);
89
+ const limit = Math.min(args.limit, MAX_LIST_ENTRY_LIMIT);
90
+
91
+ let query = db
92
+ .select({
93
+ entryId: schema.crmListEntries.id,
94
+ position: schema.crmListEntries.position,
95
+ createdAt: schema.crmListEntries.createdAt,
96
+ createdByActorType: schema.crmListEntries.createdByActorType,
97
+ createdByActorId: schema.crmListEntries.createdByActorId,
98
+ recordId: schema.crmRecords.id,
99
+ objectType: schema.crmRecords.objectType,
100
+ kind: schema.crmRecords.kind,
101
+ displayName: schema.crmRecords.displayName,
102
+ primaryEmail: schema.crmRecords.primaryEmail,
103
+ domain: schema.crmRecords.domain,
104
+ recordStage: schema.crmRecords.stage,
105
+ ownerName: schema.crmRecords.ownerName,
106
+ amount: schema.crmRecords.amount,
107
+ currencyCode: schema.crmRecords.currencyCode,
108
+ closeDate: schema.crmRecords.closeDate,
109
+ recordUpdatedAt: schema.crmRecords.updatedAt,
110
+ })
111
+ .from(schema.crmListEntries)
112
+ .innerJoin(
113
+ schema.crmRecords,
114
+ eq(schema.crmRecords.id, schema.crmListEntries.recordId),
115
+ )
116
+ .$dynamic();
117
+
118
+ for (const join of resolver.joins) {
119
+ query = query.leftJoin(join.table, join.on);
120
+ }
121
+
122
+ const rows = await query
123
+ .where(
124
+ and(
125
+ eq(schema.crmListEntries.listId, list.id),
126
+ ...conditions,
127
+ accessFilter(schema.crmListEntries, schema.crmListEntryShares),
128
+ accessFilter(schema.crmRecords, schema.crmRecordShares),
129
+ ),
130
+ )
131
+ .orderBy(
132
+ ...order,
133
+ asc(schema.crmListEntries.position),
134
+ asc(schema.crmListEntries.createdAt),
135
+ asc(schema.crmListEntries.id),
136
+ )
137
+ .limit(limit + 1)
138
+ .offset(offset);
139
+
140
+ const page = rows.slice(0, limit);
141
+ const values = await loadCrmEntryValues(
142
+ db,
143
+ page.map((row) => row.entryId),
144
+ bySlug,
145
+ );
146
+
147
+ return {
148
+ list: {
149
+ id: list.id,
150
+ name: list.name,
151
+ apiSlug: list.apiSlug,
152
+ parentObjectType: list.parentObjectType,
153
+ defaultViewId: list.defaultViewId,
154
+ archived: list.archived,
155
+ },
156
+ attributes: attributes.map(attributeSummary),
157
+ entries: page.map((row) => {
158
+ const entryValues = values.get(row.entryId) ?? {
159
+ values: {},
160
+ valuesSince: {},
161
+ };
162
+ return {
163
+ id: row.entryId,
164
+ listId: list.id,
165
+ recordId: row.recordId,
166
+ position: row.position,
167
+ createdAt: row.createdAt,
168
+ createdByActorType: row.createdByActorType,
169
+ createdByActorId: row.createdByActorId,
170
+ record: {
171
+ id: row.recordId,
172
+ objectType: row.objectType,
173
+ kind: row.kind,
174
+ displayName: row.displayName,
175
+ primaryEmail: row.primaryEmail,
176
+ domain: row.domain,
177
+ stage: row.recordStage,
178
+ ownerName: row.ownerName,
179
+ amount: row.amount,
180
+ currencyCode: row.currencyCode,
181
+ closeDate: row.closeDate,
182
+ updatedAt: row.recordUpdatedAt,
183
+ },
184
+ values: entryValues.values,
185
+ valuesSince: entryValues.valuesSince,
186
+ };
187
+ }),
188
+ nextCursor: rows.length > limit ? String(offset + limit) : undefined,
189
+ complete: rows.length <= limit,
190
+ };
191
+ },
192
+ });
@@ -0,0 +1,80 @@
1
+ import { defineAction } from "@agent-native/core/action";
2
+ import { accessFilter } from "@agent-native/core/sharing";
3
+ import { and, asc, count, eq, inArray } from "drizzle-orm";
4
+ import { z } from "zod";
5
+
6
+ import { getDb, schema } from "../server/db/index.js";
7
+ import { queryFlag } from "./_crm-action-utils.js";
8
+ import { decodeCrmCursor, MAX_LIST_LIMIT } from "./_crm-list-utils.js";
9
+
10
+ export default defineAction({
11
+ description:
12
+ "List the access-scoped CRM lists with their entry counts. A list is a workflow overlay over one object type; its entries and entry attribute values are always local, never provider rows.",
13
+ schema: z.object({
14
+ connectionId: z.string().trim().min(1).max(128).optional(),
15
+ includeArchived: queryFlag.describe(
16
+ "Include archived lists so they can be inspected or restored.",
17
+ ),
18
+ limit: z.coerce.number().int().min(1).max(MAX_LIST_LIMIT).default(50),
19
+ cursor: z
20
+ .string()
21
+ .regex(/^\d+$/)
22
+ .optional()
23
+ .describe("Cursor returned by a previous page."),
24
+ }),
25
+ http: { method: "GET" },
26
+ readOnly: true,
27
+ run: async (args) => {
28
+ const db = getDb();
29
+ const offset = decodeCrmCursor(args.cursor);
30
+ const limit = Math.min(args.limit, MAX_LIST_LIMIT);
31
+
32
+ const rows = await db
33
+ .select()
34
+ .from(schema.crmLists)
35
+ .where(
36
+ and(
37
+ ...(args.connectionId
38
+ ? [eq(schema.crmLists.connectionId, args.connectionId)]
39
+ : []),
40
+ ...(args.includeArchived
41
+ ? []
42
+ : [eq(schema.crmLists.archived, false)]),
43
+ accessFilter(schema.crmLists, schema.crmListShares),
44
+ ),
45
+ )
46
+ .orderBy(asc(schema.crmLists.position), asc(schema.crmLists.createdAt))
47
+ .limit(limit + 1)
48
+ .offset(offset);
49
+
50
+ const page = rows.slice(0, limit);
51
+ const listIds = page.map((list) => list.id);
52
+ const countRows = listIds.length
53
+ ? await db
54
+ .select({
55
+ listId: schema.crmListEntries.listId,
56
+ entries: count(),
57
+ })
58
+ .from(schema.crmListEntries)
59
+ .where(
60
+ and(
61
+ inArray(schema.crmListEntries.listId, listIds),
62
+ accessFilter(schema.crmListEntries, schema.crmListEntryShares),
63
+ ),
64
+ )
65
+ .groupBy(schema.crmListEntries.listId)
66
+ : [];
67
+ const entryCounts = new Map(
68
+ countRows.map((row) => [row.listId, Number(row.entries)]),
69
+ );
70
+
71
+ return {
72
+ lists: page.map((list) => ({
73
+ ...list,
74
+ entryCount: entryCounts.get(list.id) ?? 0,
75
+ })),
76
+ nextCursor: rows.length > limit ? String(offset + limit) : undefined,
77
+ complete: rows.length <= limit,
78
+ };
79
+ },
80
+ });
@@ -0,0 +1,55 @@
1
+ import { defineAction } from "@agent-native/core/action";
2
+ import { z } from "zod";
3
+
4
+ import { resolveProviderRecordLinks } from "../server/crm/provider-record-link.js";
5
+ import { listCrmProposals } from "../server/db/crm-store.js";
6
+
7
+ export default defineAction({
8
+ description:
9
+ "List bounded, access-scoped CRM mutation proposals, their review status, and the upstream record link for completing a provider change by hand. Proposal payloads are intentionally omitted from this list surface. A proposal with status `approved` and no `appliedAt` was prepared and handed off — CRM never wrote it upstream.",
10
+ schema: z.object({
11
+ recordId: z.string().min(1).optional(),
12
+ status: z
13
+ .enum([
14
+ "pending",
15
+ "approved",
16
+ "applied",
17
+ "rejected",
18
+ "conflict",
19
+ "failed",
20
+ ])
21
+ .optional(),
22
+ limit: z.coerce.number().int().min(1).max(100).default(50),
23
+ cursor: z
24
+ .string()
25
+ .regex(/^\d+$/)
26
+ .optional()
27
+ .describe("Cursor returned by a previous page."),
28
+ }),
29
+ http: { method: "GET" },
30
+ readOnly: true,
31
+ run: async (input) => {
32
+ const result = await listCrmProposals(input);
33
+ const links = await resolveProviderRecordLinks(
34
+ result.proposals
35
+ .map((proposal) => proposal.recordId)
36
+ .filter((recordId): recordId is string => Boolean(recordId)),
37
+ );
38
+ return {
39
+ ...result,
40
+ proposals: result.proposals.map((proposal) => {
41
+ const link = proposal.recordId
42
+ ? links.get(proposal.recordId)
43
+ : undefined;
44
+ // A missing link is reported, never blanked: the review surface has to
45
+ // say why it cannot hand off rather than show a dead button.
46
+ return {
47
+ ...proposal,
48
+ recordUrl: link?.available ? link.url : null,
49
+ recordUrlUnavailableReason:
50
+ link && !link.available ? link.reason : null,
51
+ };
52
+ }),
53
+ };
54
+ },
55
+ });
@@ -0,0 +1,201 @@
1
+ /**
2
+ * The bitemporal history of one attribute value.
3
+ *
4
+ * `crm_record_fields` keeps every superseded value as its own row stamped with
5
+ * when it stopped being current, so "changed from X to Y, by Z" is a read, not
6
+ * an audit-log join. An attribute with `history_tracked = false` is updated in
7
+ * place and therefore genuinely HAS no history — that is reported as
8
+ * `historyTracked: false`, which is a different answer from "nothing changed".
9
+ */
10
+
11
+ import { defineAction } from "@agent-native/core/action";
12
+ import { accessFilter } from "@agent-native/core/sharing";
13
+ import { and, desc, eq, isNull, sql } from "drizzle-orm";
14
+ import { z } from "zod";
15
+
16
+ import { getDb, schema } from "../server/db/index.js";
17
+ import { CrmAttributeValueError } from "../server/lib/record-fields.js";
18
+ import { storageColumnFor } from "../shared/crm-attributes.js";
19
+ import type { CrmValue } from "../shared/crm-contract.js";
20
+
21
+ const MAX_CHANGES = 50;
22
+
23
+ export default defineAction({
24
+ description:
25
+ "Return the bitemporal change history of one CRM attribute value, newest first: each stored value with the window it was current for and the actor that set it. Pass entryId to read a list-entry attribute (a stage history) instead of a record attribute. historyTracked=false means the attribute is updated in place and keeps no history, which is not the same as no changes.",
26
+ schema: z.object({
27
+ recordId: z.string().trim().min(1).max(128),
28
+ apiSlug: z.string().trim().min(1).max(120),
29
+ entryId: z
30
+ .string()
31
+ .trim()
32
+ .min(1)
33
+ .max(128)
34
+ .optional()
35
+ .describe(
36
+ "Read a list-entry attribute's history instead of the record's.",
37
+ ),
38
+ limit: z.coerce.number().int().min(1).max(MAX_CHANGES).default(20),
39
+ }),
40
+ http: { method: "GET" },
41
+ readOnly: true,
42
+ run: async (args) => {
43
+ const db = getDb();
44
+ const [record] = await db
45
+ .select({
46
+ id: schema.crmRecords.id,
47
+ connectionId: schema.crmRecords.connectionId,
48
+ objectType: schema.crmRecords.objectType,
49
+ })
50
+ .from(schema.crmRecords)
51
+ .where(
52
+ and(
53
+ eq(schema.crmRecords.id, args.recordId),
54
+ accessFilter(schema.crmRecords, schema.crmRecordShares),
55
+ ),
56
+ )
57
+ .limit(1);
58
+ if (!record) {
59
+ const error = new Error("CRM record not found") as Error & {
60
+ statusCode?: number;
61
+ };
62
+ error.statusCode = 404;
63
+ throw error;
64
+ }
65
+
66
+ let attributeScope: { target: "object" | "list"; targetId: string };
67
+ if (args.entryId) {
68
+ const [entry] = await db
69
+ .select({ listId: schema.crmListEntries.listId })
70
+ .from(schema.crmListEntries)
71
+ .where(
72
+ and(
73
+ eq(schema.crmListEntries.id, args.entryId),
74
+ eq(schema.crmListEntries.recordId, record.id),
75
+ accessFilter(schema.crmListEntries, schema.crmListEntryShares),
76
+ ),
77
+ )
78
+ .limit(1);
79
+ if (!entry) {
80
+ throw new CrmAttributeValueError(
81
+ "crm-list-entry-not-found",
82
+ `CRM list entry "${args.entryId}" was not found on this record.`,
83
+ );
84
+ }
85
+ attributeScope = { target: "list", targetId: entry.listId };
86
+ } else {
87
+ attributeScope = { target: "object", targetId: record.objectType };
88
+ }
89
+
90
+ const [attribute] = await db
91
+ .select({
92
+ id: schema.crmFieldPolicies.id,
93
+ apiSlug: schema.crmFieldPolicies.apiSlug,
94
+ fieldName: schema.crmFieldPolicies.fieldName,
95
+ label: schema.crmFieldPolicies.label,
96
+ attributeType: schema.crmFieldPolicies.attributeType,
97
+ multi: schema.crmFieldPolicies.multi,
98
+ historyTracked: schema.crmFieldPolicies.historyTracked,
99
+ })
100
+ .from(schema.crmFieldPolicies)
101
+ .where(
102
+ and(
103
+ eq(schema.crmFieldPolicies.target, attributeScope.target),
104
+ eq(schema.crmFieldPolicies.objectType, attributeScope.targetId),
105
+ eq(schema.crmFieldPolicies.fieldName, args.apiSlug),
106
+ accessFilter(schema.crmFieldPolicies, schema.crmFieldPolicyShares),
107
+ ),
108
+ )
109
+ .limit(1);
110
+ if (!attribute) {
111
+ throw new CrmAttributeValueError(
112
+ "crm-attribute-not-found",
113
+ `"${args.apiSlug}" is not an attribute of ${attributeScope.target} "${attributeScope.targetId}".`,
114
+ );
115
+ }
116
+
117
+ const column = storageColumnFor(attribute.attributeType, attribute.multi);
118
+ const rows = await db
119
+ .select({
120
+ id: schema.crmRecordFields.id,
121
+ stringValue: schema.crmRecordFields.stringValue,
122
+ numberValue: schema.crmRecordFields.numberValue,
123
+ booleanValue: schema.crmRecordFields.booleanValue,
124
+ jsonValue: schema.crmRecordFields.jsonValue,
125
+ activeFrom: schema.crmRecordFields.activeFrom,
126
+ activeUntil: schema.crmRecordFields.activeUntil,
127
+ actorType: schema.crmRecordFields.actorType,
128
+ actorId: schema.crmRecordFields.actorId,
129
+ })
130
+ .from(schema.crmRecordFields)
131
+ .where(
132
+ and(
133
+ eq(schema.crmRecordFields.recordId, record.id),
134
+ args.entryId
135
+ ? eq(schema.crmRecordFields.entryId, args.entryId)
136
+ : isNull(schema.crmRecordFields.entryId),
137
+ eq(schema.crmRecordFields.fieldName, args.apiSlug),
138
+ accessFilter(schema.crmRecordFields, schema.crmRecordFieldShares),
139
+ ),
140
+ )
141
+ // Two writes can land in the same millisecond, so `activeFrom` alone does
142
+ // not order the chain. The current row is first, then closed rows by when
143
+ // they were closed — a row's `activeUntil` is the next row's `activeFrom`,
144
+ // which is what actually orders same-instant edits. `(x is null) desc`
145
+ // puts the current row first in both SQLite (1 > 0) and PostgreSQL
146
+ // (true > false); NULLS FIRST/LAST defaults differ and cannot be relied on.
147
+ .orderBy(
148
+ desc(sql`(${schema.crmRecordFields.activeUntil} is null)`),
149
+ desc(schema.crmRecordFields.activeUntil),
150
+ desc(schema.crmRecordFields.activeFrom),
151
+ desc(schema.crmRecordFields.id),
152
+ )
153
+ .limit(args.limit);
154
+
155
+ return {
156
+ recordId: record.id,
157
+ entryId: args.entryId ?? null,
158
+ apiSlug: attribute.apiSlug ?? attribute.fieldName,
159
+ label: attribute.label,
160
+ attributeType: attribute.attributeType,
161
+ multi: attribute.multi,
162
+ historyTracked: attribute.historyTracked,
163
+ changes: rows.map((row) => ({
164
+ id: row.id,
165
+ value: decodeHistoryValue(column, args.apiSlug, row) as CrmValue,
166
+ activeFrom: row.activeFrom,
167
+ activeUntil: row.activeUntil,
168
+ current: row.activeUntil === null,
169
+ actorType: row.actorType,
170
+ actorId: row.actorId,
171
+ })),
172
+ complete: rows.length < args.limit,
173
+ };
174
+ },
175
+ });
176
+
177
+ function decodeHistoryValue(
178
+ column: ReturnType<typeof storageColumnFor>,
179
+ apiSlug: string,
180
+ row: {
181
+ stringValue: string | null;
182
+ numberValue: number | null;
183
+ booleanValue: boolean | null;
184
+ jsonValue: string | null;
185
+ },
186
+ ): CrmValue {
187
+ if (column === "numberValue") return row.numberValue;
188
+ if (column === "booleanValue") return row.booleanValue;
189
+ if (column === "jsonValue") {
190
+ if (row.jsonValue === null) return null;
191
+ try {
192
+ return JSON.parse(row.jsonValue) as CrmValue;
193
+ } catch {
194
+ throw new CrmAttributeValueError(
195
+ "crm-attribute-value-unreadable",
196
+ `A stored history value for "${apiSlug}" is not readable JSON. It was written outside the attribute writer.`,
197
+ );
198
+ }
199
+ }
200
+ return row.stringValue;
201
+ }