@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
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
ensureDatabaseSourceProperty,
|
|
23
23
|
importBuilderCmsEntriesAsDatabaseItems,
|
|
24
24
|
mapBuilderCmsEntriesToLocalItems,
|
|
25
|
+
mutateContentDatabaseSourceMetadata,
|
|
25
26
|
resolveDatabaseForSourceMutation,
|
|
26
27
|
seedMockSourceFields,
|
|
27
28
|
seedMockSourceRows,
|
|
@@ -134,26 +135,26 @@ function sourceRole(source: SourceRecord): "primary" | "secondary" | null {
|
|
|
134
135
|
}
|
|
135
136
|
|
|
136
137
|
async function clearSourceFederation(sourceId: string, now: string) {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
138
|
+
await mutateContentDatabaseSourceMetadata({
|
|
139
|
+
sourceId,
|
|
140
|
+
now,
|
|
141
|
+
buildPatch: (current) => {
|
|
142
|
+
let metadata: Record<string, unknown> = {};
|
|
143
|
+
try {
|
|
144
|
+
const parsed = JSON.parse(current.metadataJson ?? "{}") as unknown;
|
|
145
|
+
metadata =
|
|
146
|
+
parsed && typeof parsed === "object" && !Array.isArray(parsed)
|
|
147
|
+
? (parsed as Record<string, unknown>)
|
|
148
|
+
: {};
|
|
149
|
+
} catch {
|
|
150
|
+
metadata = {};
|
|
151
|
+
}
|
|
152
|
+
delete metadata.federation;
|
|
153
|
+
return { metadataJson: JSON.stringify(metadata) };
|
|
154
|
+
},
|
|
155
|
+
failureMessage:
|
|
156
|
+
"Source metadata changed repeatedly while clearing federation settings.",
|
|
157
|
+
});
|
|
157
158
|
}
|
|
158
159
|
|
|
159
160
|
async function removeRowsOwnedOnlyBySource(args: {
|
|
@@ -494,8 +494,12 @@ function livePreflightBlockMessage(args: {
|
|
|
494
494
|
) {
|
|
495
495
|
return "Builder body changed since this diff was approved; refresh and re-review.";
|
|
496
496
|
}
|
|
497
|
-
if (
|
|
498
|
-
|
|
497
|
+
if (
|
|
498
|
+
args.effect === "publish" &&
|
|
499
|
+
args.liveState.published !== "draft" &&
|
|
500
|
+
args.liveState.published !== "published"
|
|
501
|
+
) {
|
|
502
|
+
return "Builder publication state could not be verified; refresh and re-review.";
|
|
499
503
|
}
|
|
500
504
|
if (args.effect === "unpublish" && args.liveState.published !== "published") {
|
|
501
505
|
return "Entry is not currently published.";
|
|
@@ -637,6 +641,7 @@ async function reconcileBuilderCmsWrite(args: {
|
|
|
637
641
|
|
|
638
642
|
export function realExecutionDeps(
|
|
639
643
|
sourceId?: string,
|
|
644
|
+
changeSetId?: string,
|
|
640
645
|
): ExecuteBuilderSourceExecutionDeps {
|
|
641
646
|
return {
|
|
642
647
|
now: () => new Date().toISOString(),
|
|
@@ -644,8 +649,39 @@ export function realExecutionDeps(
|
|
|
644
649
|
assertEditor: async (database) => {
|
|
645
650
|
await assertAccess("document", database.documentId, "editor");
|
|
646
651
|
},
|
|
647
|
-
getSourceSnapshot: (database) =>
|
|
648
|
-
|
|
652
|
+
getSourceSnapshot: async (database) => {
|
|
653
|
+
// Execution always targets one prepared change set. Loading every
|
|
654
|
+
// Builder-backed document (and every heavy body baseline) here can spend
|
|
655
|
+
// the hosted request budget before the provider write is dispatched.
|
|
656
|
+
// Resolve the durable prepared target first, then build the authoritative
|
|
657
|
+
// write snapshot for that document only. The unscoped fallback preserves
|
|
658
|
+
// compatibility for legacy/non-persisted callers.
|
|
659
|
+
const [target] = changeSetId
|
|
660
|
+
? await getDb()
|
|
661
|
+
.select({
|
|
662
|
+
documentId: schema.contentDatabaseSourceChangeSets.documentId,
|
|
663
|
+
sourceId: schema.contentDatabaseSourceChangeSets.sourceId,
|
|
664
|
+
})
|
|
665
|
+
.from(schema.contentDatabaseSourceChangeSets)
|
|
666
|
+
.where(
|
|
667
|
+
sourceId
|
|
668
|
+
? and(
|
|
669
|
+
eq(schema.contentDatabaseSourceChangeSets.id, changeSetId),
|
|
670
|
+
eq(
|
|
671
|
+
schema.contentDatabaseSourceChangeSets.sourceId,
|
|
672
|
+
sourceId,
|
|
673
|
+
),
|
|
674
|
+
)
|
|
675
|
+
: eq(schema.contentDatabaseSourceChangeSets.id, changeSetId),
|
|
676
|
+
)
|
|
677
|
+
.limit(1)
|
|
678
|
+
: [];
|
|
679
|
+
return getContentDatabaseSourceSnapshotForWrite(
|
|
680
|
+
database,
|
|
681
|
+
sourceId ?? target?.sourceId,
|
|
682
|
+
target?.documentId ? [target.documentId] : undefined,
|
|
683
|
+
);
|
|
684
|
+
},
|
|
649
685
|
getExecution: async (args) => {
|
|
650
686
|
const [claim] = await getDb()
|
|
651
687
|
.select({
|
|
@@ -1188,9 +1224,14 @@ export async function executeBuilderSourceExecutionWithDeps(
|
|
|
1188
1224
|
now: deps.now(),
|
|
1189
1225
|
attemptToken,
|
|
1190
1226
|
});
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1227
|
+
if (writeResult.ambiguity) {
|
|
1228
|
+
throw builderExecutionConflict(lastError);
|
|
1229
|
+
}
|
|
1230
|
+
const error = new Error(lastError) as Error & { statusCode?: number };
|
|
1231
|
+
if (writeResult.status >= 400 && writeResult.status < 500) {
|
|
1232
|
+
error.statusCode = writeResult.status;
|
|
1233
|
+
}
|
|
1234
|
+
throw error;
|
|
1194
1235
|
}
|
|
1195
1236
|
|
|
1196
1237
|
const reconciliationStartedAt = timing.start();
|
|
@@ -1297,7 +1338,7 @@ export default defineAction({
|
|
|
1297
1338
|
): Promise<ContentDatabaseResponse> => {
|
|
1298
1339
|
return executeBuilderSourceExecutionWithDeps(
|
|
1299
1340
|
args,
|
|
1300
|
-
realExecutionDeps(args.sourceId),
|
|
1341
|
+
realExecutionDeps(args.sourceId, args.changeSetId),
|
|
1301
1342
|
);
|
|
1302
1343
|
},
|
|
1303
1344
|
});
|
|
@@ -340,11 +340,6 @@ export default defineAction({
|
|
|
340
340
|
? { ...field, model: reference.model }
|
|
341
341
|
: field;
|
|
342
342
|
});
|
|
343
|
-
const enrichedMetadataJson = JSON.stringify({
|
|
344
|
-
...parseMetadata(source.metadataJson),
|
|
345
|
-
builderModelFields: enrichedModelFields,
|
|
346
|
-
});
|
|
347
|
-
|
|
348
343
|
await db.transaction(async (tx) => {
|
|
349
344
|
const currentFields = await tx
|
|
350
345
|
.select()
|
|
@@ -539,10 +534,48 @@ export default defineAction({
|
|
|
539
534
|
);
|
|
540
535
|
}
|
|
541
536
|
}
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
537
|
+
for (let attempt = 0; attempt < 5; attempt += 1) {
|
|
538
|
+
const [currentSource] = await tx
|
|
539
|
+
.select({
|
|
540
|
+
metadataJson: schema.contentDatabaseSources.metadataJson,
|
|
541
|
+
})
|
|
542
|
+
.from(schema.contentDatabaseSources)
|
|
543
|
+
.where(eq(schema.contentDatabaseSources.id, source.id))
|
|
544
|
+
.limit(1);
|
|
545
|
+
if (!currentSource) throw new Error("Builder source not found.");
|
|
546
|
+
const currentModelFields =
|
|
547
|
+
parseMetadata(currentSource.metadataJson).builderModelFields ?? [];
|
|
548
|
+
if (
|
|
549
|
+
JSON.stringify(currentModelFields) !== JSON.stringify(modelFields)
|
|
550
|
+
) {
|
|
551
|
+
throw new Error(
|
|
552
|
+
"Builder model fields changed while required fields were being materialized. No fields were added.",
|
|
553
|
+
);
|
|
554
|
+
}
|
|
555
|
+
const nextMetadataJson = JSON.stringify({
|
|
556
|
+
...parseMetadata(currentSource.metadataJson),
|
|
557
|
+
builderModelFields: enrichedModelFields,
|
|
558
|
+
});
|
|
559
|
+
const updated = await tx
|
|
560
|
+
.update(schema.contentDatabaseSources)
|
|
561
|
+
.set({ metadataJson: nextMetadataJson, updatedAt: now })
|
|
562
|
+
.where(
|
|
563
|
+
and(
|
|
564
|
+
eq(schema.contentDatabaseSources.id, source.id),
|
|
565
|
+
currentSource.metadataJson === null
|
|
566
|
+
? isNull(schema.contentDatabaseSources.metadataJson)
|
|
567
|
+
: eq(
|
|
568
|
+
schema.contentDatabaseSources.metadataJson,
|
|
569
|
+
currentSource.metadataJson,
|
|
570
|
+
),
|
|
571
|
+
),
|
|
572
|
+
)
|
|
573
|
+
.returning({ id: schema.contentDatabaseSources.id });
|
|
574
|
+
if (updated.length > 0) return;
|
|
575
|
+
}
|
|
576
|
+
throw new Error(
|
|
577
|
+
"Builder source metadata changed repeatedly while materializing required fields. No fields were added.",
|
|
578
|
+
);
|
|
546
579
|
});
|
|
547
580
|
|
|
548
581
|
return getContentDatabaseResponse(database.id, { limit: 100, offset: 0 });
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
|
-
|
|
3
1
|
import { defineAction } from "@agent-native/core";
|
|
4
2
|
import { getRequestUserEmail } from "@agent-native/core/server/request-context";
|
|
5
3
|
import { assertAccess } from "@agent-native/core/sharing";
|
|
@@ -29,7 +27,9 @@ import { createBuilderSourceTiming } from "./_builder-source-timings.js";
|
|
|
29
27
|
import {
|
|
30
28
|
canRefreshLocallyBlockedBuilderReview,
|
|
31
29
|
findOpenSourceChangeSet,
|
|
30
|
+
getContentDatabaseSourceSnapshotForReview,
|
|
32
31
|
getContentDatabaseSourceSnapshotForWrite,
|
|
32
|
+
reviewedBuilderChangeSetRevisionId,
|
|
33
33
|
resolveDatabaseForSourceMutation,
|
|
34
34
|
serializeSourceRowRecord,
|
|
35
35
|
sourceChangeSetKey,
|
|
@@ -125,10 +125,20 @@ function maxRisk(
|
|
|
125
125
|
|
|
126
126
|
export function reviewPreparePriority(
|
|
127
127
|
changeSet: ContentDatabaseSourceChangeSet,
|
|
128
|
+
source?: ContentDatabaseSource,
|
|
128
129
|
) {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
130
|
+
const statePriority =
|
|
131
|
+
changeSet.state === "pending_push"
|
|
132
|
+
? 0
|
|
133
|
+
: changeSet.state === "staged_revision"
|
|
134
|
+
? 2
|
|
135
|
+
: 4;
|
|
136
|
+
const effectPriority =
|
|
137
|
+
source &&
|
|
138
|
+
resolveBuilderCmsWriteEffect({ source, changeSet }) === "create_draft"
|
|
139
|
+
? 0
|
|
140
|
+
: 1;
|
|
141
|
+
return statePriority + effectPriority;
|
|
132
142
|
}
|
|
133
143
|
|
|
134
144
|
function parsePayload(value: string) {
|
|
@@ -142,40 +152,6 @@ function parsePayload(value: string) {
|
|
|
142
152
|
}
|
|
143
153
|
}
|
|
144
154
|
|
|
145
|
-
function stableReviewValue(value: unknown): string {
|
|
146
|
-
if (value === null || typeof value !== "object") {
|
|
147
|
-
return JSON.stringify(value) ?? "undefined";
|
|
148
|
-
}
|
|
149
|
-
if (Array.isArray(value)) {
|
|
150
|
-
return `[${value.map(stableReviewValue).join(",")}]`;
|
|
151
|
-
}
|
|
152
|
-
const record = value as Record<string, unknown>;
|
|
153
|
-
return `{${Object.keys(record)
|
|
154
|
-
.sort()
|
|
155
|
-
.map((key) => `${JSON.stringify(key)}:${stableReviewValue(record[key])}`)
|
|
156
|
-
.join(",")}}`;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
function reviewedRevisionChangeSetId(
|
|
160
|
-
changeSet: ContentDatabaseSourceChangeSet,
|
|
161
|
-
) {
|
|
162
|
-
const revision = createHash("sha256")
|
|
163
|
-
.update(
|
|
164
|
-
stableReviewValue({
|
|
165
|
-
databaseItemId: changeSet.databaseItemId,
|
|
166
|
-
documentId: changeSet.documentId,
|
|
167
|
-
kind: changeSet.kind,
|
|
168
|
-
direction: "outbound",
|
|
169
|
-
pushMode: changeSet.pushMode ?? "autosave",
|
|
170
|
-
fieldChanges: changeSet.fieldChanges,
|
|
171
|
-
bodyChange: changeSet.bodyChange,
|
|
172
|
-
}),
|
|
173
|
-
)
|
|
174
|
-
.digest("hex")
|
|
175
|
-
.slice(0, 16);
|
|
176
|
-
return `${changeSet.id}-revision-${revision}`;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
155
|
function dryRunStatus(execution: ContentDatabaseSourceExecution | null) {
|
|
180
156
|
const dryRun =
|
|
181
157
|
execution?.payload.dryRun &&
|
|
@@ -360,6 +336,16 @@ async function approveChangeSetForReview(args: {
|
|
|
360
336
|
updatedAt: args.now,
|
|
361
337
|
});
|
|
362
338
|
|
|
339
|
+
const existingPayloadMatches = (row: {
|
|
340
|
+
fieldChangesJson: string;
|
|
341
|
+
bodyChangeJson: string | null;
|
|
342
|
+
}) =>
|
|
343
|
+
row.fieldChangesJson === JSON.stringify(args.changeSet.fieldChanges) &&
|
|
344
|
+
row.bodyChangeJson ===
|
|
345
|
+
(args.changeSet.bodyChange
|
|
346
|
+
? JSON.stringify(args.changeSet.bodyChange)
|
|
347
|
+
: null);
|
|
348
|
+
|
|
363
349
|
if (existing) {
|
|
364
350
|
const existingExecutions = await db
|
|
365
351
|
.select({
|
|
@@ -380,58 +366,64 @@ async function approveChangeSetForReview(args: {
|
|
|
380
366
|
// Once dispatch may have happened, the approved payload is evidence. Keep
|
|
381
367
|
// it byte-for-byte and let reconciliation decide what can happen next.
|
|
382
368
|
if (existing.state === "approved" && !mayRefreshApprovedPayload) {
|
|
369
|
+
if (existingPayloadMatches(existing)) {
|
|
370
|
+
return {
|
|
371
|
+
id: existing.id,
|
|
372
|
+
state: "approved" as const,
|
|
373
|
+
changeSet: {
|
|
374
|
+
...approvedChangeSet(existing.id),
|
|
375
|
+
summary: existing.summary,
|
|
376
|
+
fieldChanges: JSON.parse(
|
|
377
|
+
existing.fieldChangesJson,
|
|
378
|
+
) as ContentDatabaseSourceChangeSet["fieldChanges"],
|
|
379
|
+
bodyChange: existing.bodyChangeJson
|
|
380
|
+
? (JSON.parse(
|
|
381
|
+
existing.bodyChangeJson,
|
|
382
|
+
) as ContentDatabaseSourceChangeSet["bodyChange"])
|
|
383
|
+
: null,
|
|
384
|
+
updatedAt: existing.updatedAt,
|
|
385
|
+
},
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
// A failed, dispatched, or otherwise non-refreshable gate owns its exact
|
|
389
|
+
// approved payload forever. A materially different local edit must fall
|
|
390
|
+
// through to a new revision identity instead of aliasing that evidence.
|
|
391
|
+
} else {
|
|
392
|
+
await db
|
|
393
|
+
.update(schema.contentDatabaseSourceChangeSets)
|
|
394
|
+
.set({
|
|
395
|
+
direction: "outbound",
|
|
396
|
+
state: "approved",
|
|
397
|
+
pushMode: args.changeSet.pushMode ?? "autosave",
|
|
398
|
+
localOnly: 1,
|
|
399
|
+
summary,
|
|
400
|
+
fieldChangesJson: JSON.stringify(args.changeSet.fieldChanges),
|
|
401
|
+
bodyChangeJson: args.changeSet.bodyChange
|
|
402
|
+
? JSON.stringify(args.changeSet.bodyChange)
|
|
403
|
+
: null,
|
|
404
|
+
updatedAt: args.now,
|
|
405
|
+
})
|
|
406
|
+
.where(eq(schema.contentDatabaseSourceChangeSets.id, existing.id));
|
|
407
|
+
if (existing.state !== "approved") {
|
|
408
|
+
await db.insert(schema.contentDatabaseSourceChangeReviews).values({
|
|
409
|
+
id: crypto.randomUUID(),
|
|
410
|
+
ownerEmail: args.ownerEmail,
|
|
411
|
+
sourceId: args.sourceId,
|
|
412
|
+
changeSetId: existing.id,
|
|
413
|
+
reviewerEmail: args.reviewerEmail,
|
|
414
|
+
decision: "approved",
|
|
415
|
+
stateFrom: existing.state,
|
|
416
|
+
stateTo: "approved",
|
|
417
|
+
note: "Approved by Builder update review.",
|
|
418
|
+
createdAt: args.now,
|
|
419
|
+
});
|
|
420
|
+
}
|
|
383
421
|
return {
|
|
384
422
|
id: existing.id,
|
|
385
423
|
state: "approved" as const,
|
|
386
|
-
changeSet:
|
|
387
|
-
...approvedChangeSet(existing.id),
|
|
388
|
-
summary: existing.summary,
|
|
389
|
-
fieldChanges: JSON.parse(
|
|
390
|
-
existing.fieldChangesJson,
|
|
391
|
-
) as ContentDatabaseSourceChangeSet["fieldChanges"],
|
|
392
|
-
bodyChange: existing.bodyChangeJson
|
|
393
|
-
? (JSON.parse(
|
|
394
|
-
existing.bodyChangeJson,
|
|
395
|
-
) as ContentDatabaseSourceChangeSet["bodyChange"])
|
|
396
|
-
: null,
|
|
397
|
-
updatedAt: existing.updatedAt,
|
|
398
|
-
},
|
|
424
|
+
changeSet: approvedChangeSet(existing.id),
|
|
399
425
|
};
|
|
400
426
|
}
|
|
401
|
-
await db
|
|
402
|
-
.update(schema.contentDatabaseSourceChangeSets)
|
|
403
|
-
.set({
|
|
404
|
-
direction: "outbound",
|
|
405
|
-
state: "approved",
|
|
406
|
-
pushMode: args.changeSet.pushMode ?? "autosave",
|
|
407
|
-
localOnly: 1,
|
|
408
|
-
summary,
|
|
409
|
-
fieldChangesJson: JSON.stringify(args.changeSet.fieldChanges),
|
|
410
|
-
bodyChangeJson: args.changeSet.bodyChange
|
|
411
|
-
? JSON.stringify(args.changeSet.bodyChange)
|
|
412
|
-
: null,
|
|
413
|
-
updatedAt: args.now,
|
|
414
|
-
})
|
|
415
|
-
.where(eq(schema.contentDatabaseSourceChangeSets.id, existing.id));
|
|
416
|
-
if (existing.state !== "approved") {
|
|
417
|
-
await db.insert(schema.contentDatabaseSourceChangeReviews).values({
|
|
418
|
-
id: crypto.randomUUID(),
|
|
419
|
-
ownerEmail: args.ownerEmail,
|
|
420
|
-
sourceId: args.sourceId,
|
|
421
|
-
changeSetId: existing.id,
|
|
422
|
-
reviewerEmail: args.reviewerEmail,
|
|
423
|
-
decision: "approved",
|
|
424
|
-
stateFrom: existing.state,
|
|
425
|
-
stateTo: "approved",
|
|
426
|
-
note: "Approved by Builder update review.",
|
|
427
|
-
createdAt: args.now,
|
|
428
|
-
});
|
|
429
|
-
}
|
|
430
|
-
return {
|
|
431
|
-
id: existing.id,
|
|
432
|
-
state: "approved" as const,
|
|
433
|
-
changeSet: approvedChangeSet(existing.id),
|
|
434
|
-
};
|
|
435
427
|
}
|
|
436
428
|
|
|
437
429
|
// Local Builder diffs are materialized with deterministic IDs (for example,
|
|
@@ -442,7 +434,7 @@ async function approveChangeSetForReview(args: {
|
|
|
442
434
|
// Bind a materially changed follow-up to a deterministic payload revision so
|
|
443
435
|
// its review, execution, and idempotency evidence cannot alias the old row.
|
|
444
436
|
const changeSetId = selectedExisting
|
|
445
|
-
?
|
|
437
|
+
? reviewedBuilderChangeSetRevisionId(args.changeSet)
|
|
446
438
|
: args.changeSet.id;
|
|
447
439
|
await db.insert(schema.contentDatabaseSourceChangeSets).values({
|
|
448
440
|
id: changeSetId,
|
|
@@ -667,7 +659,7 @@ export default defineAction({
|
|
|
667
659
|
const database = await resolveDatabaseForSourceMutation(args);
|
|
668
660
|
if (!database) throw new Error("Database not found.");
|
|
669
661
|
await assertAccess("document", database.documentId, "editor");
|
|
670
|
-
const snapshot = await
|
|
662
|
+
const snapshot = await getContentDatabaseSourceSnapshotForReview(
|
|
671
663
|
database,
|
|
672
664
|
args.sourceId,
|
|
673
665
|
args.documentIds,
|
|
@@ -716,7 +708,11 @@ export default defineAction({
|
|
|
716
708
|
throw new Error("No pending local Builder changes to review.");
|
|
717
709
|
}
|
|
718
710
|
const reviewableChanges = [...allReviewableChanges]
|
|
719
|
-
.sort(
|
|
711
|
+
.sort(
|
|
712
|
+
(a, b) =>
|
|
713
|
+
reviewPreparePriority(a, snapshot) -
|
|
714
|
+
reviewPreparePriority(b, snapshot),
|
|
715
|
+
)
|
|
720
716
|
.slice(0, BUILDER_SOURCE_REVIEW_PREPARE_LIMIT);
|
|
721
717
|
const authoritativeSnapshot = await withAuthoritativeBuilderTargetRows({
|
|
722
718
|
source: snapshot,
|
|
@@ -773,7 +769,9 @@ export default defineAction({
|
|
|
773
769
|
reviewedSnapshot: await getContentDatabaseSourceSnapshotForWrite(
|
|
774
770
|
database,
|
|
775
771
|
args.sourceId,
|
|
776
|
-
|
|
772
|
+
reviewableChanges.every((changeSet) => changeSet.documentId)
|
|
773
|
+
? reviewableChanges.map((changeSet) => changeSet.documentId!)
|
|
774
|
+
: args.documentIds,
|
|
777
775
|
),
|
|
778
776
|
response: await getContentDatabaseResponse(database.id),
|
|
779
777
|
}),
|
|
@@ -7,7 +7,7 @@ import type {
|
|
|
7
7
|
PreviewBuilderSourceReviewResponse,
|
|
8
8
|
} from "../shared/api.js";
|
|
9
9
|
import {
|
|
10
|
-
|
|
10
|
+
getContentDatabaseSourceSnapshotForReview,
|
|
11
11
|
resolveDatabaseForSourceMutation,
|
|
12
12
|
} from "./_database-source-utils.js";
|
|
13
13
|
import {
|
|
@@ -45,7 +45,7 @@ export default defineAction({
|
|
|
45
45
|
throw new Error("Select at least one Builder row before reviewing it.");
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
const source = await
|
|
48
|
+
const source = await getContentDatabaseSourceSnapshotForReview(
|
|
49
49
|
database,
|
|
50
50
|
args.sourceId,
|
|
51
51
|
args.scope === "selected" ? args.documentIds : undefined,
|
|
@@ -72,7 +72,8 @@ export default defineAction({
|
|
|
72
72
|
const changeSets = [...allReviewableChanges]
|
|
73
73
|
.sort(
|
|
74
74
|
(left, right) =>
|
|
75
|
-
reviewPreparePriority(left) -
|
|
75
|
+
reviewPreparePriority(left, source) -
|
|
76
|
+
reviewPreparePriority(right, source),
|
|
76
77
|
)
|
|
77
78
|
.slice(0, BUILDER_SOURCE_REVIEW_PREPARE_LIMIT);
|
|
78
79
|
const authoritativeSource = await withAuthoritativeBuilderTargetRows({
|
|
@@ -5,9 +5,11 @@ import { z } from "zod";
|
|
|
5
5
|
import type { ContentDatabaseSourceStatusResponse } from "../shared/api.js";
|
|
6
6
|
import { getContentDatabaseSourceAdapter } from "./_content-database-source-adapters.js";
|
|
7
7
|
import {
|
|
8
|
+
claimBuilderCmsSourceRefresh,
|
|
8
9
|
getContentDatabaseSourceSnapshot,
|
|
9
10
|
getContentDatabaseSourceSnapshotById,
|
|
10
11
|
getExistingSourceForWrite,
|
|
12
|
+
releaseBuilderCmsSourceRefreshClaim,
|
|
11
13
|
resyncBuilderCmsSourceSnapshot,
|
|
12
14
|
resyncMockSourceSnapshot,
|
|
13
15
|
resolveDatabaseForSourceMutation,
|
|
@@ -33,6 +35,14 @@ export default defineAction({
|
|
|
33
35
|
.describe(
|
|
34
36
|
"For paginated Builder CMS or Notion sources, read a bounded multi-page snapshot in this refresh.",
|
|
35
37
|
),
|
|
38
|
+
expectedBuilderContinuationOffset: z
|
|
39
|
+
.number()
|
|
40
|
+
.int()
|
|
41
|
+
.nonnegative()
|
|
42
|
+
.optional()
|
|
43
|
+
.describe(
|
|
44
|
+
"Internal Builder pagination guard. Continue only when the persisted source still expects this offset.",
|
|
45
|
+
),
|
|
36
46
|
}),
|
|
37
47
|
run: async (args): Promise<ContentDatabaseSourceStatusResponse> => {
|
|
38
48
|
const database = await resolveDatabaseForSourceMutation(args);
|
|
@@ -50,15 +60,30 @@ export default defineAction({
|
|
|
50
60
|
}
|
|
51
61
|
|
|
52
62
|
const now = new Date().toISOString();
|
|
63
|
+
let skippedOverlappingBuilderRefresh = false;
|
|
53
64
|
if (source.sourceType === "mock-local") {
|
|
54
65
|
await resyncMockSourceSnapshot({ database, source, now });
|
|
55
66
|
} else if (source.sourceType === "builder-cms") {
|
|
56
|
-
await
|
|
57
|
-
database,
|
|
67
|
+
const claimedSource = await claimBuilderCmsSourceRefresh({
|
|
58
68
|
source,
|
|
59
|
-
|
|
60
|
-
runFullRefresh: args.fullRefresh === true,
|
|
69
|
+
expectedOffset: args.expectedBuilderContinuationOffset,
|
|
61
70
|
});
|
|
71
|
+
skippedOverlappingBuilderRefresh = !claimedSource;
|
|
72
|
+
if (claimedSource) {
|
|
73
|
+
await resyncBuilderCmsSourceSnapshot({
|
|
74
|
+
database,
|
|
75
|
+
source: claimedSource.source,
|
|
76
|
+
now,
|
|
77
|
+
runFullRefresh: args.fullRefresh === true,
|
|
78
|
+
refreshClaimId: claimedSource.claimId,
|
|
79
|
+
}).catch(async (error: unknown) => {
|
|
80
|
+
await releaseBuilderCmsSourceRefreshClaim({
|
|
81
|
+
sourceId: source.id,
|
|
82
|
+
claimId: claimedSource.claimId,
|
|
83
|
+
});
|
|
84
|
+
throw error;
|
|
85
|
+
});
|
|
86
|
+
}
|
|
62
87
|
} else if (source.sourceType === "local-table") {
|
|
63
88
|
// Read-only federated secondary; its rows are re-read on demand, nothing
|
|
64
89
|
// to resync against the primary's local snapshot here.
|
|
@@ -116,11 +141,13 @@ export default defineAction({
|
|
|
116
141
|
database: serializeDatabase(database),
|
|
117
142
|
mode: "source-backed",
|
|
118
143
|
summary: snapshot
|
|
119
|
-
?
|
|
120
|
-
? `${snapshot.sourceName}
|
|
121
|
-
:
|
|
122
|
-
? `${snapshot.sourceName}
|
|
123
|
-
:
|
|
144
|
+
? skippedOverlappingBuilderRefresh
|
|
145
|
+
? `${snapshot.sourceName} already advanced or has another refresh in progress; the current snapshot was preserved.`
|
|
146
|
+
: builderFetching
|
|
147
|
+
? `${snapshot.sourceName} fetched ${builderFetched ?? "some"} rows. Run refresh again to continue loading the remaining Builder rows.`
|
|
148
|
+
: snapshot.sourceType === "notion-database"
|
|
149
|
+
? `${snapshot.sourceName} refreshed read-only from Notion.`
|
|
150
|
+
: `${snapshot.sourceName} resynced locally; field mappings and row identity now reflect the current database snapshot.`
|
|
124
151
|
: "Source metadata refreshed.",
|
|
125
152
|
source: snapshot,
|
|
126
153
|
};
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { defineAction } from "@agent-native/core";
|
|
2
2
|
import { assertAccess } from "@agent-native/core/sharing";
|
|
3
|
-
import { eq } from "drizzle-orm";
|
|
4
3
|
import { z } from "zod";
|
|
5
4
|
|
|
6
|
-
import { getDb, schema } from "../server/db/index.js";
|
|
7
5
|
import {
|
|
8
6
|
type ContentDatabaseResponse,
|
|
9
7
|
type ContentDatabaseSourcePushMode,
|
|
@@ -16,6 +14,7 @@ import {
|
|
|
16
14
|
} from "./_builder-cms-write-settings.js";
|
|
17
15
|
import {
|
|
18
16
|
getExistingSourceForWrite,
|
|
17
|
+
mutateContentDatabaseSourceMetadata,
|
|
19
18
|
resolveDatabaseForSourceMutation,
|
|
20
19
|
} from "./_database-source-utils.js";
|
|
21
20
|
import { getContentDatabaseResponse } from "./_database-utils.js";
|
|
@@ -81,7 +80,6 @@ export default defineAction({
|
|
|
81
80
|
if (!database) throw new Error("Database not found.");
|
|
82
81
|
await assertAccess("document", database.documentId, "admin");
|
|
83
82
|
|
|
84
|
-
const db = getDb();
|
|
85
83
|
const source = await getExistingSourceForWrite(database.id, args.sourceId);
|
|
86
84
|
if (!source) {
|
|
87
85
|
throw new Error(
|
|
@@ -94,28 +92,34 @@ export default defineAction({
|
|
|
94
92
|
);
|
|
95
93
|
}
|
|
96
94
|
|
|
97
|
-
const next = buildBuilderCmsWriteModeJson({
|
|
98
|
-
sourceType: source.sourceType,
|
|
99
|
-
sourceTable: source.sourceTable,
|
|
100
|
-
capabilitiesJson: source.capabilitiesJson,
|
|
101
|
-
metadataJson: source.metadataJson,
|
|
102
|
-
liveWritesEnabled: args.liveWritesEnabled,
|
|
103
|
-
writeMode: args.writeMode as ContentDatabaseSourceWriteMode | undefined,
|
|
104
|
-
allowPublicationTransitions: args.allowPublicationTransitions,
|
|
105
|
-
allowedWriteModes: executableWriteModes(args.allowedWriteModes),
|
|
106
|
-
allowDraftWrites: args.allowDraftWrites,
|
|
107
|
-
allowPublishWrites: args.allowPublishWrites,
|
|
108
|
-
});
|
|
109
95
|
const now = new Date().toISOString();
|
|
110
|
-
await
|
|
111
|
-
.
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
96
|
+
await mutateContentDatabaseSourceMetadata({
|
|
97
|
+
sourceId: source.id,
|
|
98
|
+
now,
|
|
99
|
+
buildPatch: (current) => {
|
|
100
|
+
const next = buildBuilderCmsWriteModeJson({
|
|
101
|
+
sourceType: current.sourceType,
|
|
102
|
+
sourceTable: current.sourceTable,
|
|
103
|
+
capabilitiesJson: current.capabilitiesJson,
|
|
104
|
+
metadataJson: current.metadataJson,
|
|
105
|
+
liveWritesEnabled: args.liveWritesEnabled,
|
|
106
|
+
writeMode: args.writeMode as
|
|
107
|
+
| ContentDatabaseSourceWriteMode
|
|
108
|
+
| undefined,
|
|
109
|
+
allowPublicationTransitions: args.allowPublicationTransitions,
|
|
110
|
+
allowedWriteModes: executableWriteModes(args.allowedWriteModes),
|
|
111
|
+
allowDraftWrites: args.allowDraftWrites,
|
|
112
|
+
allowPublishWrites: args.allowPublishWrites,
|
|
113
|
+
});
|
|
114
|
+
return {
|
|
115
|
+
capabilitiesJson: next.capabilitiesJson,
|
|
116
|
+
metadataJson: next.metadataJson,
|
|
117
|
+
lastError: null,
|
|
118
|
+
};
|
|
119
|
+
},
|
|
120
|
+
failureMessage:
|
|
121
|
+
"Builder source settings changed repeatedly while saving write mode.",
|
|
122
|
+
});
|
|
119
123
|
|
|
120
124
|
return getContentDatabaseResponse(database.id);
|
|
121
125
|
},
|