@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.
- package/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +16 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/cli/templates-meta.ts +12 -0
- package/corpus/templates/crm/.agents/skills/agent-native-docs/SKILL.md +115 -0
- package/corpus/templates/crm/.agents/skills/agent-native-toolkit/SKILL.md +177 -0
- package/corpus/templates/crm/.agents/skills/crm/SKILL.md +205 -0
- package/corpus/templates/crm/.agents/skills/customizing-agent-native/SKILL.md +220 -0
- package/corpus/templates/crm/.agents/skills/feature-flags/SKILL.md +169 -0
- package/corpus/templates/crm/.agents/skills/sharing/SKILL.md +228 -0
- package/corpus/templates/crm/.agents/skills/storing-data/SKILL.md +178 -0
- package/corpus/templates/crm/.agents/skills/upgrade-agent-native/SKILL.md +122 -0
- package/corpus/templates/crm/.env.example +5 -0
- package/corpus/templates/crm/AGENTS.md +82 -0
- package/corpus/templates/crm/CHANGELOG.md +25 -0
- package/corpus/templates/crm/DEVELOPING.md +66 -0
- package/corpus/templates/crm/README.md +122 -0
- package/corpus/templates/crm/_gitignore +12 -0
- package/corpus/templates/crm/actions/_crm-action-utils.ts +98 -0
- package/corpus/templates/crm/actions/_crm-attribute-utils.ts +219 -0
- package/corpus/templates/crm/actions/_crm-dashboard.ts +69 -0
- package/corpus/templates/crm/actions/_crm-data-program-actions.ts +15 -0
- package/corpus/templates/crm/actions/_crm-list-utils.ts +775 -0
- package/corpus/templates/crm/actions/_crm-signal-utils.ts +79 -0
- package/corpus/templates/crm/actions/add-crm-record-to-list.ts +164 -0
- package/corpus/templates/crm/actions/apply-crm-proposals.ts +291 -0
- package/corpus/templates/crm/actions/archive-crm-attribute.ts +69 -0
- package/corpus/templates/crm/actions/attach-call-evidence.ts +170 -0
- package/corpus/templates/crm/actions/configure-crm-connection.ts +126 -0
- package/corpus/templates/crm/actions/configure-native-crm.ts +44 -0
- package/corpus/templates/crm/actions/create-crm-attribute.ts +350 -0
- package/corpus/templates/crm/actions/create-crm-list.ts +194 -0
- package/corpus/templates/crm/actions/create-crm-record.ts +148 -0
- package/corpus/templates/crm/actions/create-crm-signal-tracker.ts +58 -0
- package/corpus/templates/crm/actions/delete-crm-saved-view.ts +68 -0
- package/corpus/templates/crm/actions/delete-staged-dataset.ts +14 -0
- package/corpus/templates/crm/actions/estimate-crm-enrichment.ts +229 -0
- package/corpus/templates/crm/actions/find-crm-duplicates.ts +87 -0
- package/corpus/templates/crm/actions/get-crm-automation-recipe.ts +36 -0
- package/corpus/templates/crm/actions/get-crm-dashboard-panel.ts +35 -0
- package/corpus/templates/crm/actions/get-crm-dashboard.ts +14 -0
- package/corpus/templates/crm/actions/get-crm-overview.ts +13 -0
- package/corpus/templates/crm/actions/get-crm-pipeline-data.ts +61 -0
- package/corpus/templates/crm/actions/get-crm-record-page.ts +287 -0
- package/corpus/templates/crm/actions/get-crm-record.ts +75 -0
- package/corpus/templates/crm/actions/get-crm-workspace.ts +364 -0
- package/corpus/templates/crm/actions/install-crm-pipeline-dashboard.ts +81 -0
- package/corpus/templates/crm/actions/list-crm-attributes.ts +71 -0
- package/corpus/templates/crm/actions/list-crm-connections.ts +68 -0
- package/corpus/templates/crm/actions/list-crm-dashboard-revisions.ts +15 -0
- package/corpus/templates/crm/actions/list-crm-dashboards.ts +15 -0
- package/corpus/templates/crm/actions/list-crm-enrichment-slots.ts +28 -0
- package/corpus/templates/crm/actions/list-crm-list-entries.ts +192 -0
- package/corpus/templates/crm/actions/list-crm-lists.ts +80 -0
- package/corpus/templates/crm/actions/list-crm-proposals.ts +55 -0
- package/corpus/templates/crm/actions/list-crm-record-field-history.ts +201 -0
- package/corpus/templates/crm/actions/list-crm-record-values.ts +200 -0
- package/corpus/templates/crm/actions/list-crm-records.ts +103 -0
- package/corpus/templates/crm/actions/list-crm-saved-views.ts +66 -0
- package/corpus/templates/crm/actions/list-crm-signal-hits.ts +43 -0
- package/corpus/templates/crm/actions/list-crm-signal-trackers.ts +32 -0
- package/corpus/templates/crm/actions/list-crm-tasks.ts +22 -0
- package/corpus/templates/crm/actions/list-staged-datasets.ts +14 -0
- package/corpus/templates/crm/actions/list-workspace-connections.ts +47 -0
- package/corpus/templates/crm/actions/manage-crm-attribute-option.ts +272 -0
- package/corpus/templates/crm/actions/manage-crm-signal-tracker.ts +87 -0
- package/corpus/templates/crm/actions/manage-crm-task.ts +132 -0
- package/corpus/templates/crm/actions/merge-crm-records.ts +691 -0
- package/corpus/templates/crm/actions/navigate.ts +54 -0
- package/corpus/templates/crm/actions/provider-api-catalog.ts +22 -0
- package/corpus/templates/crm/actions/provider-api-docs.ts +54 -0
- package/corpus/templates/crm/actions/provider-api-request.ts +92 -0
- package/corpus/templates/crm/actions/query-staged-dataset.ts +44 -0
- package/corpus/templates/crm/actions/record-crm-call-insight.ts +160 -0
- package/corpus/templates/crm/actions/record-crm-smart-signal.ts +150 -0
- package/corpus/templates/crm/actions/remove-crm-list-entry.ts +79 -0
- package/corpus/templates/crm/actions/restore-crm-dashboard-revision.ts +23 -0
- package/corpus/templates/crm/actions/review-crm-signal.ts +31 -0
- package/corpus/templates/crm/actions/run-crm-attribute-fill.ts +443 -0
- package/corpus/templates/crm/actions/run-crm-enrichment.ts +647 -0
- package/corpus/templates/crm/actions/run-crm-saved-view-program.ts +103 -0
- package/corpus/templates/crm/actions/run-crm-signal-trackers.ts +265 -0
- package/corpus/templates/crm/actions/run.ts +7 -0
- package/corpus/templates/crm/actions/save-crm-dashboard.ts +51 -0
- package/corpus/templates/crm/actions/save-crm-saved-view.ts +296 -0
- package/corpus/templates/crm/actions/sync-crm.ts +146 -0
- package/corpus/templates/crm/actions/update-crm-attribute.ts +111 -0
- package/corpus/templates/crm/actions/update-crm-list-entry.ts +102 -0
- package/corpus/templates/crm/actions/update-crm-list.ts +97 -0
- package/corpus/templates/crm/actions/update-crm-record.ts +543 -0
- package/corpus/templates/crm/actions/view-screen.ts +324 -0
- package/corpus/templates/crm/agent-native.app-skill.json +69 -0
- package/corpus/templates/crm/app/components/crm/CreateCrmRecordDialog.tsx +158 -0
- package/corpus/templates/crm/app/components/crm/CrmDashboardPanel.tsx +78 -0
- package/corpus/templates/crm/app/components/crm/CrmSignalsPanel.tsx +220 -0
- package/corpus/templates/crm/app/components/crm/IntelligenceSettings.tsx +414 -0
- package/corpus/templates/crm/app/components/crm/RecordActions.tsx +648 -0
- package/corpus/templates/crm/app/components/crm/RecordGrid.tsx +488 -0
- package/corpus/templates/crm/app/components/crm/RecordWorkspace.tsx +232 -0
- package/corpus/templates/crm/app/components/crm/SavedViewDataProgram.tsx +93 -0
- package/corpus/templates/crm/app/components/crm/Surface.tsx +97 -0
- package/corpus/templates/crm/app/components/crm/WorkOverview.tsx +180 -0
- package/corpus/templates/crm/app/components/crm/board/CrmBoard.tsx +988 -0
- package/corpus/templates/crm/app/components/crm/board/board-model.ts +399 -0
- package/corpus/templates/crm/app/components/crm/board/entry-filter.ts +150 -0
- package/corpus/templates/crm/app/components/crm/board/view-draft.ts +227 -0
- package/corpus/templates/crm/app/components/crm/grid/CrmGrid.tsx +1033 -0
- package/corpus/templates/crm/app/components/crm/grid/GridCell.tsx +620 -0
- package/corpus/templates/crm/app/components/crm/grid/clipboard.ts +84 -0
- package/corpus/templates/crm/app/components/crm/grid/model.ts +304 -0
- package/corpus/templates/crm/app/components/crm/grid/navigation.ts +220 -0
- package/corpus/templates/crm/app/components/crm/grid/query.ts +214 -0
- package/corpus/templates/crm/app/components/crm/record/AttributePanel.tsx +192 -0
- package/corpus/templates/crm/app/components/crm/record/FieldHistory.tsx +179 -0
- package/corpus/templates/crm/app/components/crm/record/RecordHeader.tsx +103 -0
- package/corpus/templates/crm/app/components/crm/record/RecordLists.tsx +235 -0
- package/corpus/templates/crm/app/components/crm/record/RecordTabs.tsx +206 -0
- package/corpus/templates/crm/app/components/crm/record/field-editors.tsx +245 -0
- package/corpus/templates/crm/app/components/crm/record/record-data.ts +603 -0
- package/corpus/templates/crm/app/components/crm/settings/AdvancedSettings.tsx +49 -0
- package/corpus/templates/crm/app/components/crm/settings/AttributeOptionsEditor.tsx +441 -0
- package/corpus/templates/crm/app/components/crm/settings/ConnectionSettings.tsx +169 -0
- package/corpus/templates/crm/app/components/crm/settings/FieldsSettings.tsx +935 -0
- package/corpus/templates/crm/app/components/crm/settings/ListsSettings.tsx +339 -0
- package/corpus/templates/crm/app/components/crm/settings/settings-admin.ts +377 -0
- package/corpus/templates/crm/app/components/crm/shared/AttributeValueParts.tsx +69 -0
- package/corpus/templates/crm/app/components/crm/shared/attribute-value.ts +591 -0
- package/corpus/templates/crm/app/components/layout/CrmCommandMenu.tsx +556 -0
- package/corpus/templates/crm/app/components/layout/CrmLayout.tsx +130 -0
- package/corpus/templates/crm/app/components/layout/CrmSidebar.tsx +407 -0
- package/corpus/templates/crm/app/components/layout/command-palette-search.ts +126 -0
- package/corpus/templates/crm/app/components/layout/crm-ui-intents.ts +41 -0
- package/corpus/templates/crm/app/components/layout/sidebar-collapse.ts +40 -0
- package/corpus/templates/crm/app/components/layout/sidebar-lists.ts +109 -0
- package/corpus/templates/crm/app/components/ui/alert-dialog.tsx +1 -0
- package/corpus/templates/crm/app/components/ui/badge.tsx +1 -0
- package/corpus/templates/crm/app/components/ui/button.tsx +1 -0
- package/corpus/templates/crm/app/components/ui/card.tsx +1 -0
- package/corpus/templates/crm/app/components/ui/checkbox.tsx +1 -0
- package/corpus/templates/crm/app/components/ui/dialog.tsx +1 -0
- package/corpus/templates/crm/app/components/ui/dropdown-menu.tsx +1 -0
- package/corpus/templates/crm/app/components/ui/hover-card.tsx +1 -0
- package/corpus/templates/crm/app/components/ui/input.tsx +1 -0
- package/corpus/templates/crm/app/components/ui/label.tsx +1 -0
- package/corpus/templates/crm/app/components/ui/popover.tsx +1 -0
- package/corpus/templates/crm/app/components/ui/select.tsx +1 -0
- package/corpus/templates/crm/app/components/ui/sheet.tsx +1 -0
- package/corpus/templates/crm/app/components/ui/skeleton.tsx +1 -0
- package/corpus/templates/crm/app/components/ui/switch.tsx +1 -0
- package/corpus/templates/crm/app/components/ui/table.tsx +1 -0
- package/corpus/templates/crm/app/components/ui/tabs.tsx +1 -0
- package/corpus/templates/crm/app/components/ui/textarea.tsx +1 -0
- package/corpus/templates/crm/app/components/ui/toolkit-provider.tsx +17 -0
- package/corpus/templates/crm/app/components/ui/tooltip.tsx +1 -0
- package/corpus/templates/crm/app/design-system.ts +3 -0
- package/corpus/templates/crm/app/entry.client.tsx +19 -0
- package/corpus/templates/crm/app/entry.server.tsx +10 -0
- package/corpus/templates/crm/app/global.css +167 -0
- package/corpus/templates/crm/app/hooks/use-keyboard-shortcuts.ts +206 -0
- package/corpus/templates/crm/app/hooks/use-navigation-state.ts +48 -0
- package/corpus/templates/crm/app/i18n/en-US.ts +684 -0
- package/corpus/templates/crm/app/i18n/index.ts +34 -0
- package/corpus/templates/crm/app/lib/dashboard.ts +13 -0
- package/corpus/templates/crm/app/lib/navigation.ts +17 -0
- package/corpus/templates/crm/app/lib/tab-id.ts +1 -0
- package/corpus/templates/crm/app/lib/types.ts +172 -0
- package/corpus/templates/crm/app/lib/utils.ts +1 -0
- package/corpus/templates/crm/app/root.tsx +93 -0
- package/corpus/templates/crm/app/routes/_index.tsx +26 -0
- package/corpus/templates/crm/app/routes/accounts.tsx +41 -0
- package/corpus/templates/crm/app/routes/agent.tsx +5 -0
- package/corpus/templates/crm/app/routes/ask.tsx +53 -0
- package/corpus/templates/crm/app/routes/dashboard.tsx +118 -0
- package/corpus/templates/crm/app/routes/lists.tsx +228 -0
- package/corpus/templates/crm/app/routes/opportunities.tsx +41 -0
- package/corpus/templates/crm/app/routes/people.tsx +41 -0
- package/corpus/templates/crm/app/routes/proposals.tsx +388 -0
- package/corpus/templates/crm/app/routes/records.$recordId.tsx +53 -0
- package/corpus/templates/crm/app/routes/records.tsx +64 -0
- package/corpus/templates/crm/app/routes/settings.tsx +118 -0
- package/corpus/templates/crm/app/routes/setup.tsx +308 -0
- package/corpus/templates/crm/app/routes/tasks.tsx +252 -0
- package/corpus/templates/crm/app/routes/views.tsx +683 -0
- package/corpus/templates/crm/app/routes.ts +19 -0
- package/corpus/templates/crm/app/vite-env.d.ts +6 -0
- 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
- package/corpus/templates/crm/changelog/2026-07-26-crm-now-finds-likely-duplicate-records-and-tells-you-exactly.md +6 -0
- package/corpus/templates/crm/changelog/2026-07-26-enrichment-now-shows-what-a-run-will-cost-before-it-starts-g.md +6 -0
- package/corpus/templates/crm/changelog/2026-07-26-lists-turn-any-set-of-records-into-a-pipeline-with-its-own-s.md +6 -0
- package/corpus/templates/crm/changelog/2026-07-26-moving-a-record-or-list-entry-into-a-stage-that-was-retired-.md +6 -0
- package/corpus/templates/crm/changelog/2026-07-26-opening-a-record-now-shows-one-page-with-its-fields-the-list.md +6 -0
- 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
- package/corpus/templates/crm/changelog/2026-07-26-provider-record-edits-now-show-an-exact-before-after-diff-an.md +6 -0
- package/corpus/templates/crm/changelog/2026-07-26-record-page-attribute-labels-now-read-in-full-instead-of-bei.md +6 -0
- package/corpus/templates/crm/changelog/2026-07-26-records-now-use-typed-fields-currency-dates-ratings-status-s.md +6 -0
- package/corpus/templates/crm/changelog/2026-07-26-retyping-a-value-on-the-record-page-now-replaces-it-instead-.md +6 -0
- package/corpus/templates/crm/changelog/2026-07-26-settings-now-has-fields-and-lists-tabs-for-adding-fields-edi.md +6 -0
- package/corpus/templates/crm/changelog/2026-07-26-the-proposal-review-now-shows-the-field-that-actually-change.md +6 -0
- package/corpus/templates/crm/changelog/2026-07-26-the-record-grid-is-now-a-real-spreadsheet-filter-sort-and-pa.md +6 -0
- package/corpus/templates/crm/components.json +20 -0
- package/corpus/templates/crm/docs/architecture/crm-contract.md +214 -0
- package/corpus/templates/crm/docs/features/README.md +55 -0
- package/corpus/templates/crm/docs/features/f1-records-and-fields.mdx +52 -0
- package/corpus/templates/crm/docs/features/f2-lists-pipelines-and-views.mdx +48 -0
- package/corpus/templates/crm/docs/features/f3-work-and-record-quality.mdx +40 -0
- package/corpus/templates/crm/docs/features/f4-evidence-and-chat.mdx +47 -0
- package/corpus/templates/crm/learnings.defaults.md +5 -0
- package/corpus/templates/crm/netlify.toml +11 -0
- package/corpus/templates/crm/package.json +61 -0
- package/corpus/templates/crm/public/favicon.svg +1 -0
- package/corpus/templates/crm/public/icon-180.svg +1 -0
- package/corpus/templates/crm/public/manifest.json +11 -0
- package/corpus/templates/crm/react-router.config.ts +7 -0
- package/corpus/templates/crm/server/crm/adapter.ts +32 -0
- package/corpus/templates/crm/server/crm/crm-field-firewall.ts +64 -0
- package/corpus/templates/crm/server/crm/crm-mirror.ts +721 -0
- package/corpus/templates/crm/server/crm/field-policy-attributes.ts +48 -0
- package/corpus/templates/crm/server/crm/hubspot-adapter.ts +1146 -0
- package/corpus/templates/crm/server/crm/native-adapter.ts +1810 -0
- package/corpus/templates/crm/server/crm/provider-record-link.ts +264 -0
- package/corpus/templates/crm/server/crm/read-through.ts +207 -0
- package/corpus/templates/crm/server/crm/salesforce-adapter.ts +1255 -0
- package/corpus/templates/crm/server/db/crm-store.ts +1112 -0
- package/corpus/templates/crm/server/db/index.ts +191 -0
- package/corpus/templates/crm/server/db/schema.ts +647 -0
- package/corpus/templates/crm/server/lib/crm-query.ts +1558 -0
- package/corpus/templates/crm/server/lib/dedupe.ts +714 -0
- package/corpus/templates/crm/server/lib/enrichment-cost.ts +381 -0
- package/corpus/templates/crm/server/lib/enrichment-slots.ts +519 -0
- package/corpus/templates/crm/server/lib/intelligence/default-detectors.ts +65 -0
- package/corpus/templates/crm/server/lib/intelligence/evidence.ts +105 -0
- package/corpus/templates/crm/server/lib/intelligence/keyword-detector.ts +94 -0
- package/corpus/templates/crm/server/lib/intelligence/smart-detector.ts +39 -0
- package/corpus/templates/crm/server/lib/intelligence/summary.ts +19 -0
- package/corpus/templates/crm/server/lib/lifecycle.ts +634 -0
- package/corpus/templates/crm/server/lib/provider-api.ts +64 -0
- package/corpus/templates/crm/server/middleware/auth.ts +4 -0
- package/corpus/templates/crm/server/plugins/agent-chat.ts +65 -0
- package/corpus/templates/crm/server/plugins/auth.ts +14 -0
- package/corpus/templates/crm/server/plugins/core-routes.ts +17 -0
- package/corpus/templates/crm/server/plugins/db.ts +738 -0
- package/corpus/templates/crm/server/routes/[...page].get.ts +5 -0
- package/corpus/templates/crm/shared/crm-attributes.ts +469 -0
- package/corpus/templates/crm/shared/crm-automation-recipes.ts +132 -0
- package/corpus/templates/crm/shared/crm-contract.ts +417 -0
- package/corpus/templates/crm/shared/crm-navigation.ts +190 -0
- package/corpus/templates/crm/shared/crm-sales-config.ts +63 -0
- package/corpus/templates/crm/ssr-entry.ts +11 -0
- package/corpus/templates/crm/tsconfig.json +24 -0
- package/corpus/templates/crm/vite.config.ts +15 -0
- package/corpus/templates/crm/vitest.config.ts +5 -0
- package/corpus/templates/design/app/i18n-data.ts +12 -12
- package/corpus/templates/design/changelog/2026-07-26-apply-design-updates-from-a-centered-top-toolbar-without-cov.md +6 -0
- package/dist/cli/templates-meta.d.ts.map +1 -1
- package/dist/cli/templates-meta.js +12 -0
- package/dist/cli/templates-meta.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +3 -3
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/package.json +1 -1
- package/src/cli/templates-meta.ts +12 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { defineAction } from "@agent-native/core/action";
|
|
2
|
+
import { writeAppStateForCurrentTab } from "@agent-native/core/application-state";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
CRM_RECORD_KINDS,
|
|
7
|
+
CRM_SETTINGS_SECTIONS,
|
|
8
|
+
CRM_VIEWS,
|
|
9
|
+
crmNavigationPath,
|
|
10
|
+
} from "../shared/crm-navigation.js";
|
|
11
|
+
|
|
12
|
+
export default defineAction({
|
|
13
|
+
description:
|
|
14
|
+
"Navigate the CRM UI to work, the record grid, one record, lists, a saved view, a list or view board, tasks, proposals, a dashboard, Ask CRM, setup, or one settings tab.",
|
|
15
|
+
schema: z.object({
|
|
16
|
+
view: z.enum(CRM_VIEWS),
|
|
17
|
+
recordId: z
|
|
18
|
+
.string()
|
|
19
|
+
.trim()
|
|
20
|
+
.min(1)
|
|
21
|
+
.max(200)
|
|
22
|
+
.optional()
|
|
23
|
+
.describe("Required for view=record."),
|
|
24
|
+
listId: z
|
|
25
|
+
.string()
|
|
26
|
+
.trim()
|
|
27
|
+
.min(1)
|
|
28
|
+
.max(200)
|
|
29
|
+
.optional()
|
|
30
|
+
.describe(
|
|
31
|
+
"Opens that list on the /views surface. A board needs this or viewId.",
|
|
32
|
+
),
|
|
33
|
+
viewId: z
|
|
34
|
+
.string()
|
|
35
|
+
.trim()
|
|
36
|
+
.min(1)
|
|
37
|
+
.max(200)
|
|
38
|
+
.optional()
|
|
39
|
+
.describe("Saved view to open. A board needs this or listId."),
|
|
40
|
+
dashboardId: z.string().trim().min(1).max(200).optional(),
|
|
41
|
+
kind: z
|
|
42
|
+
.enum(CRM_RECORD_KINDS)
|
|
43
|
+
.optional()
|
|
44
|
+
.describe("Record kind tab for view=records."),
|
|
45
|
+
query: z.string().trim().max(200).optional(),
|
|
46
|
+
settingsSection: z.enum(CRM_SETTINGS_SECTIONS).optional(),
|
|
47
|
+
}),
|
|
48
|
+
http: false,
|
|
49
|
+
run: async (args) => {
|
|
50
|
+
const path = crmNavigationPath(args);
|
|
51
|
+
await writeAppStateForCurrentTab("navigate", { ...args, path });
|
|
52
|
+
return { navigatingTo: path };
|
|
53
|
+
},
|
|
54
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { createProviderApiCatalogAction } from "@agent-native/core/provider-api/actions/provider-api";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
CRM_PROVIDER_API_IDS,
|
|
6
|
+
getCrmProviderApiRuntime,
|
|
7
|
+
} from "../server/lib/provider-api.js";
|
|
8
|
+
|
|
9
|
+
const ProviderSchema = z.enum(CRM_PROVIDER_API_IDS);
|
|
10
|
+
|
|
11
|
+
export default createProviderApiCatalogAction(getCrmProviderApiRuntime(), {
|
|
12
|
+
description:
|
|
13
|
+
"List the raw HubSpot and Salesforce API capabilities available through CRM's granted workspace connections. Use before provider-api-request when a CRM convenience action cannot express an endpoint, filter, custom object, pagination mode, or API version. Returns metadata and examples, never secret values.",
|
|
14
|
+
schema: z.object({
|
|
15
|
+
provider: ProviderSchema.optional().describe(
|
|
16
|
+
"Optional provider id to inspect: hubspot or salesforce.",
|
|
17
|
+
),
|
|
18
|
+
}),
|
|
19
|
+
http: { method: "GET" },
|
|
20
|
+
guidance:
|
|
21
|
+
"CRM actions are convenience workflows, not a provider capability limit. For an exact HubSpot or Salesforce endpoint, inspect its catalog and docs before provider-api-request. For broad work, stage a bounded paginated result, reduce it with query-staged-dataset or a data program, and report the scope, row count, pagination status, truncation, and gaps.",
|
|
22
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { createProviderApiDocsAction } from "@agent-native/core/provider-api/actions/provider-api";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
CRM_PROVIDER_API_IDS,
|
|
6
|
+
getCrmProviderApiRuntime,
|
|
7
|
+
} from "../server/lib/provider-api.js";
|
|
8
|
+
|
|
9
|
+
const ProviderSchema = z.enum(CRM_PROVIDER_API_IDS);
|
|
10
|
+
const BooleanFromQuerySchema = z.preprocess(
|
|
11
|
+
(value) => (typeof value === "string" ? value === "true" : value),
|
|
12
|
+
z.boolean(),
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
export default createProviderApiDocsAction(getCrmProviderApiRuntime(), {
|
|
16
|
+
description:
|
|
17
|
+
"Inspect HubSpot or Salesforce API documentation before an exact provider-api-request when the endpoint, object schema, field, filter operator, relationship, or pagination contract is uncertain.",
|
|
18
|
+
schema: z.object({
|
|
19
|
+
provider: ProviderSchema.describe("Provider whose API docs to inspect."),
|
|
20
|
+
url: z
|
|
21
|
+
.string()
|
|
22
|
+
.url()
|
|
23
|
+
.optional()
|
|
24
|
+
.describe(
|
|
25
|
+
"Optional public provider docs/spec URL. The provider catalog returns curated starting URLs.",
|
|
26
|
+
),
|
|
27
|
+
maxBytes: z.coerce
|
|
28
|
+
.number()
|
|
29
|
+
.int()
|
|
30
|
+
.min(1_000)
|
|
31
|
+
.max(4 * 1024 * 1024)
|
|
32
|
+
.optional(),
|
|
33
|
+
maxChars: z.coerce.number().int().min(1).max(200_000).optional(),
|
|
34
|
+
responseMode: z
|
|
35
|
+
.enum(["auto", "raw", "text", "markdown", "links", "metadata", "matches"])
|
|
36
|
+
.optional(),
|
|
37
|
+
extract: z.enum(["readability", "all-visible", "none"]).optional(),
|
|
38
|
+
includeLinks: BooleanFromQuerySchema.optional(),
|
|
39
|
+
search: z
|
|
40
|
+
.object({
|
|
41
|
+
query: z.union([z.string(), z.array(z.string())]).optional(),
|
|
42
|
+
queries: z.array(z.string()).optional(),
|
|
43
|
+
terms: z.array(z.string()).optional(),
|
|
44
|
+
regex: z.string().optional(),
|
|
45
|
+
regexFlags: z.string().optional(),
|
|
46
|
+
source: z.enum(["extracted", "raw"]).optional(),
|
|
47
|
+
maxMatches: z.coerce.number().int().min(1).max(500).optional(),
|
|
48
|
+
contextChars: z.coerce.number().int().min(0).max(1_000).optional(),
|
|
49
|
+
caseSensitive: BooleanFromQuerySchema.optional(),
|
|
50
|
+
})
|
|
51
|
+
.optional(),
|
|
52
|
+
}),
|
|
53
|
+
http: { method: "GET" },
|
|
54
|
+
});
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { createProviderApiRequestAction } from "@agent-native/core/provider-api/actions/provider-api";
|
|
2
|
+
import { getCredentialContext } from "@agent-native/core/server/request-context";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
CRM_APP_ID,
|
|
7
|
+
CRM_PROVIDER_API_IDS,
|
|
8
|
+
getCrmProviderApiRuntime,
|
|
9
|
+
} from "../server/lib/provider-api.js";
|
|
10
|
+
|
|
11
|
+
const ProviderSchema = z.enum(CRM_PROVIDER_API_IDS);
|
|
12
|
+
const MethodSchema = z.enum(["GET", "HEAD"]);
|
|
13
|
+
|
|
14
|
+
const PaginationSchema = z
|
|
15
|
+
.object({
|
|
16
|
+
nextCursorPath: z.string().optional(),
|
|
17
|
+
cursorParam: z.string().optional(),
|
|
18
|
+
pageParam: z.string().optional(),
|
|
19
|
+
startPage: z.coerce.number().int().optional(),
|
|
20
|
+
offsetParam: z.string().optional(),
|
|
21
|
+
pageSize: z.coerce.number().int().min(1).optional(),
|
|
22
|
+
maxPages: z.coerce.number().int().min(1).max(200).optional(),
|
|
23
|
+
})
|
|
24
|
+
.optional();
|
|
25
|
+
|
|
26
|
+
export default createProviderApiRequestAction(getCrmProviderApiRuntime(), {
|
|
27
|
+
description:
|
|
28
|
+
"Make an exact read-only authenticated HubSpot or Salesforce GET or HEAD request through a CRM-granted workspace connection. This is the flexible escape hatch for custom object, field, relationship, pagination, and API-version reads that CRM convenience actions do not model. It is host-constrained, access-scoped to the granted connection, and redacts secrets. Provider writes must use revision-aware CRM proposals, never this action. Use stageAs for large results instead of returning raw provider payloads to chat.",
|
|
29
|
+
schema: z
|
|
30
|
+
.object({
|
|
31
|
+
provider: ProviderSchema.describe("CRM provider: hubspot or salesforce."),
|
|
32
|
+
method: MethodSchema.default("GET"),
|
|
33
|
+
path: z
|
|
34
|
+
.string()
|
|
35
|
+
.min(1)
|
|
36
|
+
.describe(
|
|
37
|
+
"Provider API path, or a full URL on the selected connection's allowed provider host.",
|
|
38
|
+
),
|
|
39
|
+
query: z.unknown().optional(),
|
|
40
|
+
headers: z.record(z.string(), z.unknown()).optional(),
|
|
41
|
+
auth: z.enum(["default", "none"]).default("default"),
|
|
42
|
+
connectionId: z
|
|
43
|
+
.string()
|
|
44
|
+
.trim()
|
|
45
|
+
.min(1)
|
|
46
|
+
.optional()
|
|
47
|
+
.describe(
|
|
48
|
+
"Granted workspace connection id. Required for Salesforce so its actor-bound token and instance URL stay coupled.",
|
|
49
|
+
),
|
|
50
|
+
timeoutMs: z.coerce.number().int().min(1_000).max(120_000).optional(),
|
|
51
|
+
maxBytes: z.coerce
|
|
52
|
+
.number()
|
|
53
|
+
.int()
|
|
54
|
+
.min(1_000)
|
|
55
|
+
.max(4 * 1024 * 1024)
|
|
56
|
+
.optional(),
|
|
57
|
+
stageAs: z
|
|
58
|
+
.string()
|
|
59
|
+
.min(1)
|
|
60
|
+
.optional()
|
|
61
|
+
.describe(
|
|
62
|
+
"Store response items as a caller-scoped scratch dataset and return a compact summary.",
|
|
63
|
+
),
|
|
64
|
+
itemsPath: z.string().optional(),
|
|
65
|
+
pagination: PaginationSchema.describe(
|
|
66
|
+
"Server-side query-string pagination for a staged response. Use the provider's catalog/docs to choose its cursor fields.",
|
|
67
|
+
),
|
|
68
|
+
saveToFile: z.string().optional(),
|
|
69
|
+
fetchAllPages: z
|
|
70
|
+
.object({
|
|
71
|
+
cursorPath: z.string(),
|
|
72
|
+
cursorParam: z.string().optional(),
|
|
73
|
+
itemsPath: z.string().optional(),
|
|
74
|
+
maxPages: z.coerce.number().int().min(1).max(50).optional(),
|
|
75
|
+
})
|
|
76
|
+
.optional(),
|
|
77
|
+
})
|
|
78
|
+
.superRefine((value, ctx) => {
|
|
79
|
+
if (value.provider === "salesforce" && !value.connectionId) {
|
|
80
|
+
ctx.addIssue({
|
|
81
|
+
code: "custom",
|
|
82
|
+
path: ["connectionId"],
|
|
83
|
+
message:
|
|
84
|
+
"Salesforce provider reads require the granted workspace connection id.",
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}),
|
|
88
|
+
appId: CRM_APP_ID,
|
|
89
|
+
getOwnerEmail: () => getCredentialContext()?.userEmail ?? null,
|
|
90
|
+
http: false,
|
|
91
|
+
toolCallable: false,
|
|
92
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { createQueryStagedDatasetAction } from "@agent-native/core/provider-api/actions/staged-datasets";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
import { CRM_APP_ID } from "../server/lib/provider-api.js";
|
|
5
|
+
|
|
6
|
+
const WhereSchema = z.object({
|
|
7
|
+
column: z.string().min(1),
|
|
8
|
+
op: z.enum([
|
|
9
|
+
"equals",
|
|
10
|
+
"not_equals",
|
|
11
|
+
"contains",
|
|
12
|
+
"not_contains",
|
|
13
|
+
"gt",
|
|
14
|
+
"gte",
|
|
15
|
+
"lt",
|
|
16
|
+
"lte",
|
|
17
|
+
"exists",
|
|
18
|
+
"not_exists",
|
|
19
|
+
]),
|
|
20
|
+
value: z.unknown().optional(),
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const AggregateSchema = z.object({
|
|
24
|
+
column: z.string().min(1),
|
|
25
|
+
op: z.enum(["sum", "avg", "count", "min", "max", "count_distinct"]),
|
|
26
|
+
as: z.string().optional(),
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export default createQueryStagedDatasetAction({
|
|
30
|
+
description:
|
|
31
|
+
"Filter, project, sort, or aggregate a caller-scoped staged CRM provider dataset without re-fetching the provider. For exhaustive CRM analysis, stage every bounded page first and report the dataset's source scope, row count, pagination status, truncation, and gaps.",
|
|
32
|
+
schema: z.object({
|
|
33
|
+
datasetId: z.string().min(1),
|
|
34
|
+
where: z.array(WhereSchema).optional(),
|
|
35
|
+
groupBy: z.array(z.string().min(1)).optional(),
|
|
36
|
+
aggregate: z.array(AggregateSchema).optional(),
|
|
37
|
+
select: z.array(z.string().min(1)).optional(),
|
|
38
|
+
orderBy: z.string().optional(),
|
|
39
|
+
orderDir: z.enum(["asc", "desc"]).optional(),
|
|
40
|
+
limit: z.coerce.number().int().min(1).max(10_000).optional(),
|
|
41
|
+
}),
|
|
42
|
+
http: false,
|
|
43
|
+
appId: CRM_APP_ID,
|
|
44
|
+
});
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { defineAction } from "@agent-native/core/action";
|
|
2
|
+
import { accessFilter, assertAccess } from "@agent-native/core/sharing";
|
|
3
|
+
import { and, eq, inArray } from "drizzle-orm";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
|
|
6
|
+
import { isSafeCrmEvidenceExcerpt } from "../server/crm/crm-field-firewall.js";
|
|
7
|
+
import { getDb, schema } from "../server/db/index.js";
|
|
8
|
+
import {
|
|
9
|
+
crmSignalIdempotencyKey,
|
|
10
|
+
signalTupleError,
|
|
11
|
+
} from "./_crm-signal-utils.js";
|
|
12
|
+
|
|
13
|
+
const insightSchema = z.object({
|
|
14
|
+
evidenceId: z.string().trim().min(1).max(128),
|
|
15
|
+
kind: z.enum(["call-summary", "next-step"]),
|
|
16
|
+
label: z.string().trim().min(1).max(120),
|
|
17
|
+
summary: z.string().trim().min(1).max(1_200).refine(isSafeCrmEvidenceExcerpt),
|
|
18
|
+
quote: z
|
|
19
|
+
.string()
|
|
20
|
+
.trim()
|
|
21
|
+
.max(1_200)
|
|
22
|
+
.refine(isSafeCrmEvidenceExcerpt)
|
|
23
|
+
.optional(),
|
|
24
|
+
quoteSeconds: z.coerce.number().finite().min(0).max(86_400),
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
export default defineAction({
|
|
28
|
+
description:
|
|
29
|
+
"Atomically record a bounded, evidence-cited call recap or next-step set produced through agent chat. No transcript body or raw model response is accepted.",
|
|
30
|
+
schema: z.object({
|
|
31
|
+
runId: z.string().trim().min(1).max(128),
|
|
32
|
+
recordId: z.string().trim().min(1).max(128),
|
|
33
|
+
insights: z.array(insightSchema).min(1).max(20),
|
|
34
|
+
model: z.string().trim().min(1).max(120),
|
|
35
|
+
modelVersion: z.string().trim().max(120).optional(),
|
|
36
|
+
idempotencyKey: z.string().trim().min(1).max(180),
|
|
37
|
+
}),
|
|
38
|
+
run: async (args, ctx) => {
|
|
39
|
+
await Promise.all([
|
|
40
|
+
assertAccess("crm-record", args.recordId, "editor"),
|
|
41
|
+
assertAccess("crm-signal-run", args.runId, "editor"),
|
|
42
|
+
...args.insights.map((item) =>
|
|
43
|
+
assertAccess("crm-call-evidence", item.evidenceId, "viewer"),
|
|
44
|
+
),
|
|
45
|
+
]);
|
|
46
|
+
const db = getDb();
|
|
47
|
+
const [run] = await db
|
|
48
|
+
.select()
|
|
49
|
+
.from(schema.crmSignalRuns)
|
|
50
|
+
.where(
|
|
51
|
+
and(
|
|
52
|
+
eq(schema.crmSignalRuns.id, args.runId),
|
|
53
|
+
accessFilter(schema.crmSignalRuns, schema.crmSignalRunShares),
|
|
54
|
+
),
|
|
55
|
+
)
|
|
56
|
+
.limit(1);
|
|
57
|
+
const evidenceIds = [
|
|
58
|
+
...new Set(args.insights.map((item) => item.evidenceId)),
|
|
59
|
+
];
|
|
60
|
+
const evidence = await db
|
|
61
|
+
.select()
|
|
62
|
+
.from(schema.crmCallEvidence)
|
|
63
|
+
.where(
|
|
64
|
+
and(
|
|
65
|
+
inArray(schema.crmCallEvidence.id, evidenceIds),
|
|
66
|
+
accessFilter(schema.crmCallEvidence, schema.crmCallEvidenceShares),
|
|
67
|
+
),
|
|
68
|
+
)
|
|
69
|
+
.limit(20);
|
|
70
|
+
if (
|
|
71
|
+
!run ||
|
|
72
|
+
run.kind !== "summary" ||
|
|
73
|
+
run.recordId !== args.recordId ||
|
|
74
|
+
evidence.length !== evidenceIds.length ||
|
|
75
|
+
evidence.some((item) => item.recordId !== args.recordId)
|
|
76
|
+
)
|
|
77
|
+
signalTupleError(
|
|
78
|
+
"The summary run, evidence, and record do not form an accessible tuple.",
|
|
79
|
+
);
|
|
80
|
+
const byId = new Map(evidence.map((item) => [item.id, item]));
|
|
81
|
+
const now = new Date().toISOString();
|
|
82
|
+
const rows: Array<typeof schema.crmSignals.$inferInsert> = [];
|
|
83
|
+
for (const [index, item] of args.insights.entries()) {
|
|
84
|
+
const source = byId.get(item.evidenceId)!;
|
|
85
|
+
const excerpt = source.quote.trim() || source.summary.trim();
|
|
86
|
+
if (item.quote && !excerpt.includes(item.quote))
|
|
87
|
+
throw new Error(
|
|
88
|
+
"Every insight quote must be an exact substring of its stored bounded evidence excerpt.",
|
|
89
|
+
);
|
|
90
|
+
const start = source.startSeconds ?? 0;
|
|
91
|
+
if (
|
|
92
|
+
item.quoteSeconds < start ||
|
|
93
|
+
(source.endSeconds !== null && item.quoteSeconds > source.endSeconds)
|
|
94
|
+
)
|
|
95
|
+
throw new Error(
|
|
96
|
+
"Every insight timestamp must fall inside its evidence excerpt.",
|
|
97
|
+
);
|
|
98
|
+
const idem = await crmSignalIdempotencyKey(
|
|
99
|
+
ctx,
|
|
100
|
+
args.recordId,
|
|
101
|
+
`${args.idempotencyKey}:${index}`,
|
|
102
|
+
);
|
|
103
|
+
rows.push({
|
|
104
|
+
id: crypto.randomUUID(),
|
|
105
|
+
runId: args.runId,
|
|
106
|
+
trackerId: null,
|
|
107
|
+
recordId: args.recordId,
|
|
108
|
+
evidenceId: item.evidenceId,
|
|
109
|
+
kind: item.kind,
|
|
110
|
+
label: item.label,
|
|
111
|
+
quote: item.quote ?? "",
|
|
112
|
+
speaker: source.speaker,
|
|
113
|
+
startSeconds: item.quoteSeconds,
|
|
114
|
+
endSeconds: source.endSeconds,
|
|
115
|
+
summary: item.summary,
|
|
116
|
+
confidence: 100,
|
|
117
|
+
detector: "agent",
|
|
118
|
+
model: args.model,
|
|
119
|
+
modelVersion: args.modelVersion ?? null,
|
|
120
|
+
reviewStatus: "unreviewed",
|
|
121
|
+
idempotencyKey: idem.key,
|
|
122
|
+
...idem.scope,
|
|
123
|
+
createdAt: now,
|
|
124
|
+
updatedAt: now,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
const existing = await db
|
|
128
|
+
.select({ key: schema.crmSignals.idempotencyKey })
|
|
129
|
+
.from(schema.crmSignals)
|
|
130
|
+
.where(
|
|
131
|
+
and(
|
|
132
|
+
inArray(
|
|
133
|
+
schema.crmSignals.idempotencyKey,
|
|
134
|
+
rows.map((row) => row.idempotencyKey),
|
|
135
|
+
),
|
|
136
|
+
accessFilter(schema.crmSignals, schema.crmSignalShares),
|
|
137
|
+
),
|
|
138
|
+
);
|
|
139
|
+
const existingKeys = new Set(existing.map((item) => item.key));
|
|
140
|
+
const pending = rows.filter((row) => !existingKeys.has(row.idempotencyKey));
|
|
141
|
+
await db.transaction(async (tx) => {
|
|
142
|
+
if (pending.length) await tx.insert(schema.crmSignals).values(pending);
|
|
143
|
+
await tx
|
|
144
|
+
.update(schema.crmSignalRuns)
|
|
145
|
+
.set({
|
|
146
|
+
status: "completed",
|
|
147
|
+
model: args.model,
|
|
148
|
+
modelVersion: args.modelVersion ?? null,
|
|
149
|
+
completedAt: now,
|
|
150
|
+
updatedAt: now,
|
|
151
|
+
})
|
|
152
|
+
.where(eq(schema.crmSignalRuns.id, args.runId));
|
|
153
|
+
});
|
|
154
|
+
return {
|
|
155
|
+
created: pending.length,
|
|
156
|
+
replayed: rows.length - pending.length,
|
|
157
|
+
signalIds: pending.map((row) => row.id),
|
|
158
|
+
};
|
|
159
|
+
},
|
|
160
|
+
});
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { defineAction } from "@agent-native/core/action";
|
|
2
|
+
import { accessFilter, assertAccess } from "@agent-native/core/sharing";
|
|
3
|
+
import { and, eq } from "drizzle-orm";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
|
|
6
|
+
import { isSafeCrmEvidenceExcerpt } from "../server/crm/crm-field-firewall.js";
|
|
7
|
+
import { getDb, schema } from "../server/db/index.js";
|
|
8
|
+
import {
|
|
9
|
+
crmSignalIdempotencyKey,
|
|
10
|
+
signalTupleError,
|
|
11
|
+
} from "./_crm-signal-utils.js";
|
|
12
|
+
|
|
13
|
+
export default defineAction({
|
|
14
|
+
description:
|
|
15
|
+
"Record one delegated-agent detector hit only after grounding it to an access-scoped evidence excerpt and queued smart run.",
|
|
16
|
+
schema: z.object({
|
|
17
|
+
runId: z.string().trim().min(1).max(128),
|
|
18
|
+
trackerId: z.string().trim().min(1).max(128),
|
|
19
|
+
recordId: z.string().trim().min(1).max(128),
|
|
20
|
+
evidenceId: z.string().trim().min(1).max(128),
|
|
21
|
+
quote: z.string().trim().min(1).max(1_200).refine(isSafeCrmEvidenceExcerpt),
|
|
22
|
+
confidence: z.coerce.number().finite().min(0).max(100),
|
|
23
|
+
model: z.string().trim().min(1).max(120),
|
|
24
|
+
modelVersion: z.string().trim().max(120).optional(),
|
|
25
|
+
idempotencyKey: z.string().trim().min(1).max(180),
|
|
26
|
+
}),
|
|
27
|
+
run: async (args, ctx) => {
|
|
28
|
+
await Promise.all([
|
|
29
|
+
assertAccess("crm-record", args.recordId, "editor"),
|
|
30
|
+
assertAccess("crm-signal-run", args.runId, "editor"),
|
|
31
|
+
assertAccess("crm-signal-tracker", args.trackerId, "viewer"),
|
|
32
|
+
assertAccess("crm-call-evidence", args.evidenceId, "viewer"),
|
|
33
|
+
]);
|
|
34
|
+
const db = getDb();
|
|
35
|
+
const [[run], [tracker], [evidence]] = await Promise.all([
|
|
36
|
+
db
|
|
37
|
+
.select()
|
|
38
|
+
.from(schema.crmSignalRuns)
|
|
39
|
+
.where(
|
|
40
|
+
and(
|
|
41
|
+
eq(schema.crmSignalRuns.id, args.runId),
|
|
42
|
+
accessFilter(schema.crmSignalRuns, schema.crmSignalRunShares),
|
|
43
|
+
),
|
|
44
|
+
)
|
|
45
|
+
.limit(1),
|
|
46
|
+
db
|
|
47
|
+
.select()
|
|
48
|
+
.from(schema.crmSignalTrackers)
|
|
49
|
+
.where(
|
|
50
|
+
and(
|
|
51
|
+
eq(schema.crmSignalTrackers.id, args.trackerId),
|
|
52
|
+
accessFilter(
|
|
53
|
+
schema.crmSignalTrackers,
|
|
54
|
+
schema.crmSignalTrackerShares,
|
|
55
|
+
),
|
|
56
|
+
),
|
|
57
|
+
)
|
|
58
|
+
.limit(1),
|
|
59
|
+
db
|
|
60
|
+
.select()
|
|
61
|
+
.from(schema.crmCallEvidence)
|
|
62
|
+
.where(
|
|
63
|
+
and(
|
|
64
|
+
eq(schema.crmCallEvidence.id, args.evidenceId),
|
|
65
|
+
accessFilter(schema.crmCallEvidence, schema.crmCallEvidenceShares),
|
|
66
|
+
),
|
|
67
|
+
)
|
|
68
|
+
.limit(1),
|
|
69
|
+
]);
|
|
70
|
+
if (
|
|
71
|
+
!run ||
|
|
72
|
+
!tracker ||
|
|
73
|
+
!evidence ||
|
|
74
|
+
run.kind !== "smart" ||
|
|
75
|
+
run.recordId !== args.recordId ||
|
|
76
|
+
run.trackerId !== args.trackerId ||
|
|
77
|
+
tracker.kind !== "smart" ||
|
|
78
|
+
evidence.recordId !== args.recordId
|
|
79
|
+
)
|
|
80
|
+
signalTupleError(
|
|
81
|
+
"The signal run, tracker, evidence, and record do not form an accessible tuple.",
|
|
82
|
+
);
|
|
83
|
+
const source = evidence.quote.trim() || evidence.summary.trim();
|
|
84
|
+
if (!source.includes(args.quote))
|
|
85
|
+
throw new Error(
|
|
86
|
+
"The signal quote must be an exact substring of the stored bounded evidence excerpt.",
|
|
87
|
+
);
|
|
88
|
+
const idem = await crmSignalIdempotencyKey(
|
|
89
|
+
ctx,
|
|
90
|
+
args.recordId,
|
|
91
|
+
args.idempotencyKey,
|
|
92
|
+
);
|
|
93
|
+
const [existing] = await db
|
|
94
|
+
.select()
|
|
95
|
+
.from(schema.crmSignals)
|
|
96
|
+
.where(
|
|
97
|
+
and(
|
|
98
|
+
eq(schema.crmSignals.idempotencyKey, idem.key),
|
|
99
|
+
accessFilter(schema.crmSignals, schema.crmSignalShares),
|
|
100
|
+
),
|
|
101
|
+
)
|
|
102
|
+
.limit(1);
|
|
103
|
+
if (existing) return existing;
|
|
104
|
+
const now = new Date().toISOString();
|
|
105
|
+
const id = crypto.randomUUID();
|
|
106
|
+
await db.transaction(async (tx) => {
|
|
107
|
+
await tx.insert(schema.crmSignals).values({
|
|
108
|
+
id,
|
|
109
|
+
runId: args.runId,
|
|
110
|
+
trackerId: args.trackerId,
|
|
111
|
+
recordId: args.recordId,
|
|
112
|
+
evidenceId: args.evidenceId,
|
|
113
|
+
kind: "moment",
|
|
114
|
+
label: tracker.name,
|
|
115
|
+
quote: args.quote,
|
|
116
|
+
speaker: evidence.speaker,
|
|
117
|
+
startSeconds: evidence.startSeconds,
|
|
118
|
+
endSeconds: evidence.endSeconds,
|
|
119
|
+
summary: "",
|
|
120
|
+
confidence: args.confidence,
|
|
121
|
+
detector: "agent",
|
|
122
|
+
model: args.model,
|
|
123
|
+
modelVersion: args.modelVersion ?? null,
|
|
124
|
+
reviewStatus: "unreviewed",
|
|
125
|
+
idempotencyKey: idem.key,
|
|
126
|
+
...idem.scope,
|
|
127
|
+
createdAt: now,
|
|
128
|
+
updatedAt: now,
|
|
129
|
+
});
|
|
130
|
+
await tx
|
|
131
|
+
.update(schema.crmSignalRuns)
|
|
132
|
+
.set({
|
|
133
|
+
status: "completed",
|
|
134
|
+
model: args.model,
|
|
135
|
+
modelVersion: args.modelVersion ?? null,
|
|
136
|
+
completedAt: now,
|
|
137
|
+
updatedAt: now,
|
|
138
|
+
})
|
|
139
|
+
.where(eq(schema.crmSignalRuns.id, args.runId));
|
|
140
|
+
});
|
|
141
|
+
const [saved] = await db
|
|
142
|
+
.select()
|
|
143
|
+
.from(schema.crmSignals)
|
|
144
|
+
.where(eq(schema.crmSignals.id, id))
|
|
145
|
+
.limit(1);
|
|
146
|
+
if (!saved)
|
|
147
|
+
throw new Error("CRM signal could not be verified after saving.");
|
|
148
|
+
return saved;
|
|
149
|
+
},
|
|
150
|
+
});
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { defineAction } from "@agent-native/core/action";
|
|
2
|
+
import { accessFilter } from "@agent-native/core/sharing";
|
|
3
|
+
import { and, eq } from "drizzle-orm";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
|
|
6
|
+
import { getDb, schema } from "../server/db/index.js";
|
|
7
|
+
import { requireCrmListEntry } from "./_crm-list-utils.js";
|
|
8
|
+
|
|
9
|
+
export default defineAction({
|
|
10
|
+
description:
|
|
11
|
+
"Remove one entry from a CRM list. This deletes the list membership and the values held on that entry only. The underlying CRM record, its own attributes, and any other entries it has — in this list or another — are left completely intact. It is NOT a way to delete a record.",
|
|
12
|
+
schema: z.object({
|
|
13
|
+
entryId: z.string().trim().min(1).max(128),
|
|
14
|
+
}),
|
|
15
|
+
audit: {
|
|
16
|
+
target: (_args, result) => {
|
|
17
|
+
const entry = result as {
|
|
18
|
+
entryId: string;
|
|
19
|
+
ownerEmail: string;
|
|
20
|
+
orgId: string | null;
|
|
21
|
+
visibility: "private" | "org" | "public";
|
|
22
|
+
};
|
|
23
|
+
return {
|
|
24
|
+
type: "crm-list-entry",
|
|
25
|
+
id: entry.entryId,
|
|
26
|
+
ownerEmail: entry.ownerEmail,
|
|
27
|
+
orgId: entry.orgId,
|
|
28
|
+
visibility: entry.visibility,
|
|
29
|
+
};
|
|
30
|
+
},
|
|
31
|
+
summary: (args) => `Removed CRM list entry ${args.entryId}`,
|
|
32
|
+
},
|
|
33
|
+
run: async (args) => {
|
|
34
|
+
const db = getDb();
|
|
35
|
+
const entry = await requireCrmListEntry(db, args.entryId, "editor");
|
|
36
|
+
|
|
37
|
+
await db.transaction(async (tx) => {
|
|
38
|
+
// Entry-scoped value rows only: `entry_id` is the record-vs-entry
|
|
39
|
+
// discriminator, so the record's own attribute history is untouched.
|
|
40
|
+
await tx
|
|
41
|
+
.delete(schema.crmRecordFields)
|
|
42
|
+
.where(
|
|
43
|
+
and(
|
|
44
|
+
eq(schema.crmRecordFields.entryId, entry.id),
|
|
45
|
+
accessFilter(
|
|
46
|
+
schema.crmRecordFields,
|
|
47
|
+
schema.crmRecordFieldShares,
|
|
48
|
+
undefined,
|
|
49
|
+
"editor",
|
|
50
|
+
),
|
|
51
|
+
),
|
|
52
|
+
);
|
|
53
|
+
await tx
|
|
54
|
+
.delete(schema.crmListEntries)
|
|
55
|
+
.where(
|
|
56
|
+
and(
|
|
57
|
+
eq(schema.crmListEntries.id, entry.id),
|
|
58
|
+
accessFilter(
|
|
59
|
+
schema.crmListEntries,
|
|
60
|
+
schema.crmListEntryShares,
|
|
61
|
+
undefined,
|
|
62
|
+
"editor",
|
|
63
|
+
),
|
|
64
|
+
),
|
|
65
|
+
);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
entryId: entry.id,
|
|
70
|
+
listId: entry.listId,
|
|
71
|
+
recordId: entry.recordId,
|
|
72
|
+
removed: true as const,
|
|
73
|
+
recordDeleted: false as const,
|
|
74
|
+
ownerEmail: entry.ownerEmail,
|
|
75
|
+
orgId: entry.orgId,
|
|
76
|
+
visibility: entry.visibility,
|
|
77
|
+
};
|
|
78
|
+
},
|
|
79
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { defineAction, type ActionRunContext } from "@agent-native/core/action";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
import { crmDashboardStore } from "../server/db/index.js";
|
|
5
|
+
import { requireDashboardAccess } from "./_crm-dashboard.js";
|
|
6
|
+
|
|
7
|
+
export default defineAction({
|
|
8
|
+
description:
|
|
9
|
+
"Restore one revision of an access-scoped CRM dashboard. The restore is revision-aware and fails if the dashboard changed concurrently.",
|
|
10
|
+
schema: z.object({
|
|
11
|
+
id: z.string().trim().min(1).max(200),
|
|
12
|
+
revisionId: z.string().trim().min(1).max(200),
|
|
13
|
+
}),
|
|
14
|
+
run: async ({ id, revisionId }, ctx?: ActionRunContext) => {
|
|
15
|
+
const dashboard = await crmDashboardStore.restore(
|
|
16
|
+
id,
|
|
17
|
+
revisionId,
|
|
18
|
+
requireDashboardAccess(ctx),
|
|
19
|
+
);
|
|
20
|
+
if (!dashboard) throw new Error("CRM dashboard revision was not found.");
|
|
21
|
+
return dashboard;
|
|
22
|
+
},
|
|
23
|
+
});
|