@agent-native/core 0.123.0 → 0.123.2
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 +24 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/cli/templates-meta.ts +12 -0
- package/corpus/core/src/deploy/build.ts +8 -0
- package/corpus/core/src/server/builder-browser.ts +76 -0
- package/corpus/core/src/server/core-routes-plugin.ts +15 -5
- package/corpus/templates/assets/app/global.css +0 -4
- package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +195 -167
- package/corpus/templates/assets/changelog/2026-07-23-drafts-preview-panel-now-fills-the-full-width-with-a-large-p.md +6 -0
- package/corpus/templates/content/actions/_builder-cms-source-adapter.ts +1 -0
- package/corpus/templates/content/actions/_builder-cms-write-client.ts +55 -9
- package/corpus/templates/content/actions/_database-source-utils.ts +756 -182
- package/corpus/templates/content/actions/change-content-database-source-role.ts +21 -20
- package/corpus/templates/content/actions/execute-builder-source-execution.ts +49 -8
- package/corpus/templates/content/actions/materialize-builder-required-fields.ts +42 -9
- package/corpus/templates/content/actions/prepare-builder-source-review.ts +88 -90
- package/corpus/templates/content/actions/preview-builder-source-review.ts +4 -3
- package/corpus/templates/content/actions/refresh-content-database-source.ts +36 -9
- package/corpus/templates/content/actions/set-content-database-source-write-mode.ts +28 -24
- package/corpus/templates/content/app/components/editor/BubbleToolbar.tsx +62 -12
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +91 -16
- package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +15 -1
- package/corpus/templates/content/app/components/editor/SlashCommandMenu.tsx +234 -44
- package/corpus/templates/content/app/components/editor/VisualEditor.tsx +194 -16
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +95 -42
- package/corpus/templates/content/app/components/editor/database-sources/BuilderSourceReviewDialog.tsx +4 -2
- package/corpus/templates/content/app/components/editor/extensions/CodeBlockNode.tsx +10 -166
- package/corpus/templates/content/app/components/editor/extensions/VideoBlock.tsx +32 -8
- package/corpus/templates/content/app/components/editor/extensions/VideoNode.ts +14 -0
- package/corpus/templates/content/app/components/editor/registrySlashItems.ts +5 -1
- package/corpus/templates/content/app/hooks/use-content-database.ts +59 -2
- package/corpus/templates/content/app/hooks/use-documents.ts +18 -26
- package/corpus/templates/content/app/root.tsx +1 -6
- package/corpus/templates/content/changelog/2026-07-24-builder-can-now-publish-approved-updates-to-entries-that-are.md +6 -0
- package/corpus/templates/content/changelog/2026-07-24-builder-collection-loading-no-longer-restarts-from-the-begin.md +6 -0
- package/corpus/templates/content/changelog/2026-07-24-builder-connections-now-complete-securely-from-netlify-deplo.md +6 -0
- package/corpus/templates/content/changelog/2026-07-24-builder-draft-creation-now-scopes-execution-to-the-selected-.md +6 -0
- package/corpus/templates/content/changelog/2026-07-24-builder-review-now-distinguishes-preparing-a-safe-dry-run-from.md +6 -0
- package/corpus/templates/content/changelog/2026-07-24-builder-source-imports-now-recover-from-a-transient-paginati.md +6 -0
- package/corpus/templates/content/changelog/2026-07-24-builder-source-refresh-now-preserves-the-provider-bound-fiel.md +6 -0
- package/corpus/templates/content/changelog/2026-07-24-builder-update-reviews-now-open-reliably-on-large-collection.md +6 -0
- package/corpus/templates/content/changelog/2026-07-24-builder-write-policy-controls-now-reflect-successful-changes.md +6 -0
- package/corpus/templates/content/changelog/2026-07-24-date-properties-now-save-reliably-when-selected-with-the-nat.md +6 -0
- package/corpus/templates/content/changelog/2026-07-24-editor-slash-search-now-keeps-multi-word-block-names-open.md +6 -0
- package/corpus/templates/content/changelog/2026-07-24-exact-editor-slash-commands-now-execute-on-enter-even-if-the.md +6 -0
- package/corpus/templates/content/changelog/2026-07-24-new-collaborative-pages-now-save-their-first-edits.md +6 -0
- package/corpus/templates/content/changelog/2026-07-24-new-local-builder-rows-no-longer-remain-locked-behind-a-none.md +6 -0
- package/corpus/templates/content/changelog/2026-07-24-opening-or-closing-a-collaborative-document-no-longer-lets-u.md +6 -0
- package/corpus/templates/content/changelog/2026-07-24-selected-text-now-opens-the-link-editor-with-command-k-or-co.md +6 -0
- package/corpus/templates/content/changelog/2026-07-24-slash-menu-block-choices-no-longer-stall-pointer-interaction.md +6 -0
- package/corpus/templates/content/changelog/2026-07-24-uploaded-and-embedded-media-now-stay-intact-while-collaborative-document-drafts-save.md +6 -0
- package/corpus/templates/content/changelog/2026-07-24-video-link-insertion-now-keeps-its-source-panel-open-while-t.md +6 -0
- package/corpus/templates/content/changelog/2026-07-26-builder-publishing-now-shows-safe-validation-details-instead.md +6 -0
- package/corpus/templates/content/changelog/2026-07-26-builder-reviews-stay-responsive-on-large-collections-and-pre.md +6 -0
- package/corpus/templates/content/changelog/2026-07-26-collaborative-documents-no-longer-flash-duplicated-recent-bl.md +6 -0
- package/corpus/templates/content/changelog/2026-07-26-corrected-builder-updates-are-now-reviewed-separately-from-f.md +6 -0
- package/corpus/templates/content/changelog/2026-07-26-documents-now-remain-read-only-until-their-live-collaborativ.md +6 -0
- package/corpus/templates/content/changelog/2026-07-26-slash-command-menus-now-stay-reachable-when-editing-near-the.md +6 -0
- package/corpus/templates/content/changelog/2026-07-26-the-slash-menu-now-shows-one-clear-callout-choice-instead-of.md +6 -0
- package/corpus/templates/content/shared/api.ts +1 -0
- package/corpus/templates/content/shared/builder-mdx.ts +73 -16
- 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/corpus/toolkit/CHANGELOG.md +6 -0
- package/corpus/toolkit/package.json +1 -1
- package/corpus/toolkit/src/editor/useCollabReconcile.ts +14 -1
- 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 +2 -2
- package/dist/deploy/build.js +6 -0
- package/dist/deploy/build.js.map +1 -1
- package/dist/observability/routes.d.ts +3 -3
- package/dist/server/builder-browser.d.ts +13 -0
- package/dist/server/builder-browser.d.ts.map +1 -1
- package/dist/server/builder-browser.js +59 -0
- package/dist/server/builder-browser.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +10 -5
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/package.json +2 -2
- package/src/cli/templates-meta.ts +12 -0
- package/src/deploy/build.ts +8 -0
- package/src/server/builder-browser.ts +76 -0
- package/src/server/core-routes-plugin.ts +15 -5
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The enrichment spend gate: what a run will cost, what has been spent this
|
|
3
|
+
* period, whether the cap allows it, and whether the run may launch at all.
|
|
4
|
+
*
|
|
5
|
+
* Costs are provider-neutral "units", not any vendor's credits — a slot rebound
|
|
6
|
+
* to a different provider changes its unit price here and nowhere else.
|
|
7
|
+
*
|
|
8
|
+
* Two things this fixes relative to the app it was ported from:
|
|
9
|
+
* - period-to-date is reported per ACTOR and per WORKSPACE as two separate
|
|
10
|
+
* numbers. One number labelled ambiguously reads as personal spend while
|
|
11
|
+
* actually summing the whole org, and users budget against it.
|
|
12
|
+
* - there is a cap, and passing it refuses the run. Refusing to spend is a
|
|
13
|
+
* feature; an estimate nobody enforces is decoration.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import {
|
|
17
|
+
spendSlots,
|
|
18
|
+
verifySlots,
|
|
19
|
+
type CrmEnrichmentPhase,
|
|
20
|
+
type CrmEnrichmentSlot,
|
|
21
|
+
type CrmEnrichmentSlotOutcome,
|
|
22
|
+
type CrmEnrichmentTarget,
|
|
23
|
+
} from "./enrichment-slots.js";
|
|
24
|
+
|
|
25
|
+
/** Units one record costs in one slot. Phase A slots are the cheap ones. */
|
|
26
|
+
export const CRM_ENRICHMENT_SLOT_UNIT_COST: Record<CrmEnrichmentSlot, number> =
|
|
27
|
+
{
|
|
28
|
+
company: 1,
|
|
29
|
+
person: 1,
|
|
30
|
+
// The reveal pass. An order of magnitude above verification is the whole
|
|
31
|
+
// reason the approval gate sits between them.
|
|
32
|
+
contact: 10,
|
|
33
|
+
web: 2,
|
|
34
|
+
calls: 0,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/** Units per actor per calendar month before a run is refused. */
|
|
38
|
+
export const CRM_DEFAULT_ENRICHMENT_BUDGET_UNITS = 500;
|
|
39
|
+
|
|
40
|
+
const BUDGET_ENV_KEY = "CRM_ENRICHMENT_BUDGET_UNITS";
|
|
41
|
+
|
|
42
|
+
/** A spend the caller must change before retrying — surfaces as HTTP 422. */
|
|
43
|
+
export class CrmEnrichmentBudgetError extends Error {
|
|
44
|
+
readonly statusCode = 422;
|
|
45
|
+
readonly code: string;
|
|
46
|
+
readonly decision: CrmBudgetDecision;
|
|
47
|
+
|
|
48
|
+
constructor(code: string, message: string, decision: CrmBudgetDecision) {
|
|
49
|
+
super(message);
|
|
50
|
+
this.name = "CrmEnrichmentBudgetError";
|
|
51
|
+
this.code = code;
|
|
52
|
+
this.decision = decision;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface CrmEnrichmentLineItem {
|
|
57
|
+
slot: CrmEnrichmentSlot;
|
|
58
|
+
recordCount: number;
|
|
59
|
+
unitCost: number;
|
|
60
|
+
cost: number;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface CrmEnrichmentEstimate {
|
|
64
|
+
phase: CrmEnrichmentPhase;
|
|
65
|
+
recordCount: number;
|
|
66
|
+
/** The slots this phase will actually run — not the slots asked for. */
|
|
67
|
+
slots: CrmEnrichmentSlot[];
|
|
68
|
+
lineItems: CrmEnrichmentLineItem[];
|
|
69
|
+
totalCost: number;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Line-item cost for one phase.
|
|
74
|
+
*
|
|
75
|
+
* The slot set is derived from the phase, not taken on trust, so an estimate can
|
|
76
|
+
* never quote a cheap verification pass for a run that will buy contact data.
|
|
77
|
+
*/
|
|
78
|
+
export function estimateEnrichment(input: {
|
|
79
|
+
phase: CrmEnrichmentPhase;
|
|
80
|
+
slots: readonly CrmEnrichmentSlot[];
|
|
81
|
+
recordCount: number;
|
|
82
|
+
}): CrmEnrichmentEstimate {
|
|
83
|
+
const slots =
|
|
84
|
+
input.phase === "verify"
|
|
85
|
+
? verifySlots(input.slots)
|
|
86
|
+
: spendSlots(input.slots);
|
|
87
|
+
const lineItems = slots.map((slot) => {
|
|
88
|
+
const unitCost = CRM_ENRICHMENT_SLOT_UNIT_COST[slot];
|
|
89
|
+
return {
|
|
90
|
+
slot,
|
|
91
|
+
recordCount: input.recordCount,
|
|
92
|
+
unitCost,
|
|
93
|
+
cost: unitCost * input.recordCount,
|
|
94
|
+
};
|
|
95
|
+
});
|
|
96
|
+
return {
|
|
97
|
+
phase: input.phase,
|
|
98
|
+
recordCount: input.recordCount,
|
|
99
|
+
slots,
|
|
100
|
+
lineItems,
|
|
101
|
+
totalCost: lineItems.reduce((sum, item) => sum + item.cost, 0),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* What a finished run actually cost, from its outcomes rather than its quote.
|
|
107
|
+
*
|
|
108
|
+
* Only a slot that reached the provider is billable. An `unconfigured` or
|
|
109
|
+
* `skipped` slot made no call at all, so booking the quoted price for it would
|
|
110
|
+
* record spend that never happened — and period-to-date is summed from these
|
|
111
|
+
* numbers, so that error compounds into a budget nobody can reconcile.
|
|
112
|
+
*
|
|
113
|
+
* ponytail: an `error` outcome counts as free. True for a request that never
|
|
114
|
+
* completed, optimistic for a provider that bills a rejected call; if one does,
|
|
115
|
+
* bill errors here rather than at the call site.
|
|
116
|
+
*/
|
|
117
|
+
export function actualEnrichmentCost(
|
|
118
|
+
outcomes: ReadonlyArray<{ slots: readonly CrmEnrichmentSlotOutcome[] }>,
|
|
119
|
+
): number {
|
|
120
|
+
let total = 0;
|
|
121
|
+
for (const record of outcomes) {
|
|
122
|
+
for (const slot of record.slots) {
|
|
123
|
+
if (slot.status !== "ok" && slot.status !== "empty") continue;
|
|
124
|
+
total += CRM_ENRICHMENT_SLOT_UNIT_COST[slot.slot];
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return total;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Start of the calendar month a timestamp falls in, in UTC. */
|
|
131
|
+
export function currentSpendPeriodStart(now: Date = new Date()): string {
|
|
132
|
+
return new Date(
|
|
133
|
+
Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1),
|
|
134
|
+
).toISOString();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* The configured cap.
|
|
139
|
+
*
|
|
140
|
+
* An unreadable override throws instead of falling back to the default: a typo
|
|
141
|
+
* in the env var must not silently restore a cap the operator meant to lower.
|
|
142
|
+
*/
|
|
143
|
+
export function resolveEnrichmentBudgetUnits(
|
|
144
|
+
env: Record<string, string | undefined> = process.env,
|
|
145
|
+
): number {
|
|
146
|
+
const raw = env[BUDGET_ENV_KEY];
|
|
147
|
+
if (raw === undefined || raw.trim() === "") {
|
|
148
|
+
return CRM_DEFAULT_ENRICHMENT_BUDGET_UNITS;
|
|
149
|
+
}
|
|
150
|
+
const parsed = Number(raw);
|
|
151
|
+
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
152
|
+
throw new Error(
|
|
153
|
+
`${BUDGET_ENV_KEY} must be a non-negative number of units; it is "${raw}".`,
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
return parsed;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export interface CrmSpendToDate {
|
|
160
|
+
/** Spend attributed to the calling actor. This is the one the cap applies to. */
|
|
161
|
+
actorUnits: number;
|
|
162
|
+
/** Spend across every actor whose runs the caller can see. Context, not a cap. */
|
|
163
|
+
workspaceUnits: number;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export interface CrmBudgetDecision {
|
|
167
|
+
allowed: boolean;
|
|
168
|
+
capUnits: number;
|
|
169
|
+
periodStart: string;
|
|
170
|
+
spendToDate: CrmSpendToDate;
|
|
171
|
+
estimatedUnits: number;
|
|
172
|
+
/** Units left for this actor after the run. Negative when it would overrun. */
|
|
173
|
+
remainingUnits: number;
|
|
174
|
+
reason?: string;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export function evaluateEnrichmentBudget(input: {
|
|
178
|
+
estimatedUnits: number;
|
|
179
|
+
spendToDate: CrmSpendToDate;
|
|
180
|
+
capUnits: number;
|
|
181
|
+
periodStart: string;
|
|
182
|
+
}): CrmBudgetDecision {
|
|
183
|
+
const remainingUnits =
|
|
184
|
+
input.capUnits - input.spendToDate.actorUnits - input.estimatedUnits;
|
|
185
|
+
const allowed = remainingUnits >= 0;
|
|
186
|
+
return {
|
|
187
|
+
allowed,
|
|
188
|
+
capUnits: input.capUnits,
|
|
189
|
+
periodStart: input.periodStart,
|
|
190
|
+
spendToDate: input.spendToDate,
|
|
191
|
+
estimatedUnits: input.estimatedUnits,
|
|
192
|
+
remainingUnits,
|
|
193
|
+
...(allowed
|
|
194
|
+
? {}
|
|
195
|
+
: {
|
|
196
|
+
reason: `This run costs ${input.estimatedUnits} units. You have spent ${input.spendToDate.actorUnits} of your ${input.capUnits}-unit budget since ${input.periodStart}, which leaves ${Math.max(
|
|
197
|
+
0,
|
|
198
|
+
input.capUnits - input.spendToDate.actorUnits,
|
|
199
|
+
)}. Approve fewer records or raise ${BUDGET_ENV_KEY}.`,
|
|
200
|
+
}),
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/** Throw the typed 422 unless the decision allows the spend. */
|
|
205
|
+
export function assertWithinEnrichmentBudget(
|
|
206
|
+
decision: CrmBudgetDecision,
|
|
207
|
+
): void {
|
|
208
|
+
if (decision.allowed) return;
|
|
209
|
+
throw new CrmEnrichmentBudgetError(
|
|
210
|
+
"crm-enrichment-budget-exceeded",
|
|
211
|
+
decision.reason ?? "This run exceeds the enrichment budget.",
|
|
212
|
+
decision,
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// ---------------------------------------------------------------------------
|
|
217
|
+
// The launch gate
|
|
218
|
+
// ---------------------------------------------------------------------------
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Hard ceiling on the records one run may touch.
|
|
222
|
+
*
|
|
223
|
+
* `input_record_ids_json` is a text column, and an unbounded id array in it
|
|
224
|
+
* erodes the no-payloads-in-SQL rule one row at a time. Exceeding this is a
|
|
225
|
+
* typed refusal, never a silent slice — a truncated run is not a completed one.
|
|
226
|
+
*/
|
|
227
|
+
export const MAX_ENRICHMENT_RECORDS_PER_RUN = 2000;
|
|
228
|
+
|
|
229
|
+
export type CrmEnrichmentScopeKind = "object" | "list" | "records";
|
|
230
|
+
|
|
231
|
+
export interface CrmEnrichmentScope {
|
|
232
|
+
kind: CrmEnrichmentScopeKind;
|
|
233
|
+
id: string;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/** A launch the caller must change before retrying — surfaces as HTTP 409. */
|
|
237
|
+
export class CrmEnrichmentRunConflictError extends Error {
|
|
238
|
+
readonly statusCode = 409;
|
|
239
|
+
readonly code = "crm-enrichment-run-in-flight";
|
|
240
|
+
readonly runId: string;
|
|
241
|
+
|
|
242
|
+
constructor(message: string, runId: string) {
|
|
243
|
+
super(message);
|
|
244
|
+
this.name = "CrmEnrichmentRunConflictError";
|
|
245
|
+
this.runId = runId;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/** A scope this run cannot be launched over — surfaces as HTTP 422. */
|
|
250
|
+
export class CrmEnrichmentScopeError extends Error {
|
|
251
|
+
readonly statusCode = 422;
|
|
252
|
+
readonly code: string;
|
|
253
|
+
|
|
254
|
+
constructor(code: string, message: string) {
|
|
255
|
+
super(message);
|
|
256
|
+
this.name = "CrmEnrichmentScopeError";
|
|
257
|
+
this.code = code;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export function assertRecordCountWithinCap(count: number): void {
|
|
262
|
+
if (count <= MAX_ENRICHMENT_RECORDS_PER_RUN) return;
|
|
263
|
+
throw new CrmEnrichmentScopeError(
|
|
264
|
+
"crm-enrichment-scope-too-large",
|
|
265
|
+
`This run would touch ${count} records; the cap is ${MAX_ENRICHMENT_RECORDS_PER_RUN}. Narrow the scope and run it again.`,
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* The scope an in-flight run is compared against.
|
|
271
|
+
*
|
|
272
|
+
* An ad-hoc record set has no natural id, so it gets a deterministic digest of
|
|
273
|
+
* its sorted record ids. Two identical ad-hoc launches therefore collide (which
|
|
274
|
+
* is the point — the second is a double-click), and two different ones do not.
|
|
275
|
+
*/
|
|
276
|
+
export async function resolveEnrichmentScope(input: {
|
|
277
|
+
kind: CrmEnrichmentScopeKind;
|
|
278
|
+
targetId?: string | null;
|
|
279
|
+
recordIds: readonly string[];
|
|
280
|
+
}): Promise<CrmEnrichmentScope> {
|
|
281
|
+
if (input.kind !== "records") {
|
|
282
|
+
const id = input.targetId?.trim();
|
|
283
|
+
if (!id) {
|
|
284
|
+
throw new CrmEnrichmentScopeError(
|
|
285
|
+
"crm-enrichment-scope-target-required",
|
|
286
|
+
`A ${input.kind} scope needs a targetId.`,
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
return { kind: input.kind, id };
|
|
290
|
+
}
|
|
291
|
+
const canonical = [...new Set(input.recordIds)].sort().join("\u0000");
|
|
292
|
+
const digest = await crypto.subtle.digest(
|
|
293
|
+
"SHA-256",
|
|
294
|
+
new TextEncoder().encode(canonical),
|
|
295
|
+
);
|
|
296
|
+
return {
|
|
297
|
+
kind: "records",
|
|
298
|
+
id: `sha256:${Array.from(new Uint8Array(digest), (byte) =>
|
|
299
|
+
byte.toString(16).padStart(2, "0"),
|
|
300
|
+
)
|
|
301
|
+
.join("")
|
|
302
|
+
.slice(0, 32)}`,
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export function enrichmentScopeKey(scope: CrmEnrichmentScope): string {
|
|
307
|
+
return `${scope.kind}:${scope.id}`;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/** Refuse to launch while another run for the same scope and phase is live. */
|
|
311
|
+
export function assertNoInFlightRun(input: {
|
|
312
|
+
scope: CrmEnrichmentScope;
|
|
313
|
+
phase: CrmEnrichmentPhase;
|
|
314
|
+
inFlight: ReadonlyArray<{ id: string }>;
|
|
315
|
+
}): void {
|
|
316
|
+
const existing = input.inFlight[0];
|
|
317
|
+
if (!existing) return;
|
|
318
|
+
throw new CrmEnrichmentRunConflictError(
|
|
319
|
+
`A ${input.phase} enrichment run for ${enrichmentScopeKey(input.scope)} is already in progress (run ${existing.id}). Wait for it to finish before starting another.`,
|
|
320
|
+
existing.id,
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/** One record's result from the free verification pass, plus the human verdict. */
|
|
325
|
+
export interface CrmVerifiedRecord {
|
|
326
|
+
target: CrmEnrichmentTarget;
|
|
327
|
+
outcomes: CrmEnrichmentSlotOutcome[];
|
|
328
|
+
approved: boolean;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* The input set for the paid pass.
|
|
333
|
+
*
|
|
334
|
+
* Frozen, and readonly in the type, because this value IS the spend bound. A
|
|
335
|
+
* caller that could push one more target into it after the approval gate would
|
|
336
|
+
* have turned a structural guarantee back into a convention.
|
|
337
|
+
*/
|
|
338
|
+
export interface CrmPhaseBInput {
|
|
339
|
+
readonly recordIds: readonly string[];
|
|
340
|
+
readonly targets: readonly CrmEnrichmentTarget[];
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Build the paid pass's input set from the approvals.
|
|
345
|
+
*
|
|
346
|
+
* The result is the ONLY thing phase B is handed, and it is constructed from
|
|
347
|
+
* approved entries rather than filtered from all of them. An unapproved record
|
|
348
|
+
* is not excluded by a condition somebody could later invert — it was never
|
|
349
|
+
* placed in the set to begin with.
|
|
350
|
+
*/
|
|
351
|
+
export function buildPhaseBInput(
|
|
352
|
+
verified: readonly CrmVerifiedRecord[],
|
|
353
|
+
): CrmPhaseBInput {
|
|
354
|
+
const targets: CrmEnrichmentTarget[] = [];
|
|
355
|
+
for (const entry of verified) {
|
|
356
|
+
if (!entry.approved) continue;
|
|
357
|
+
targets.push(entry.target);
|
|
358
|
+
}
|
|
359
|
+
return Object.freeze({
|
|
360
|
+
recordIds: Object.freeze(targets.map((target) => target.recordId)),
|
|
361
|
+
targets: Object.freeze(targets),
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Win or lose the right to make this run's provider calls.
|
|
367
|
+
*
|
|
368
|
+
* Write a unique nonce, read it back, proceed only as the winner. The run row
|
|
369
|
+
* is persisted before any provider call, so a crash between insert and call
|
|
370
|
+
* leaves a claimable row rather than a paid job nobody recorded. A read-back
|
|
371
|
+
* that does not match means another worker won; returning false is the point,
|
|
372
|
+
* not a failure.
|
|
373
|
+
*/
|
|
374
|
+
export async function claimEnrichmentRun(input: {
|
|
375
|
+
nonce: string;
|
|
376
|
+
write(nonce: string): Promise<void>;
|
|
377
|
+
readBack(): Promise<string | null>;
|
|
378
|
+
}): Promise<boolean> {
|
|
379
|
+
await input.write(input.nonce);
|
|
380
|
+
return (await input.readBack()) === input.nonce;
|
|
381
|
+
}
|