@prenta/core 6.0.0 → 7.0.0
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/CHANGELOG.md +32 -0
- package/dist/ai/brand-voice-analyze.d.ts +1 -4
- package/dist/ai/brand-voice-analyze.d.ts.map +1 -1
- package/dist/ai/brand-voice-analyze.js +1 -2
- package/dist/ai/brand-voice-analyze.js.map +1 -1
- package/dist/ai/brand-voice-sources.d.ts +0 -2
- package/dist/ai/brand-voice-sources.d.ts.map +1 -1
- package/dist/ai/brand-voice-sources.js +2 -2
- package/dist/ai/brand-voice-sources.js.map +1 -1
- package/dist/ai/brand-voice-structured.d.ts +6 -6
- package/dist/ai/brand-voice-structured.d.ts.map +1 -1
- package/dist/ai/catalog.d.ts +8 -0
- package/dist/ai/catalog.d.ts.map +1 -1
- package/dist/ai/catalog.js +22 -0
- package/dist/ai/catalog.js.map +1 -1
- package/dist/ai/config-store.d.ts +18 -4
- package/dist/ai/config-store.d.ts.map +1 -1
- package/dist/ai/config-store.js +115 -20
- package/dist/ai/config-store.js.map +1 -1
- package/dist/ai/content-writing-generate.d.ts +10 -8
- package/dist/ai/content-writing-generate.d.ts.map +1 -1
- package/dist/ai/content-writing-generate.js +24 -56
- package/dist/ai/content-writing-generate.js.map +1 -1
- package/dist/ai/embeddings.d.ts +28 -0
- package/dist/ai/embeddings.d.ts.map +1 -0
- package/dist/ai/embeddings.js +36 -0
- package/dist/ai/embeddings.js.map +1 -0
- package/dist/ai/feature-defaults.d.ts +3 -3
- package/dist/ai/feature-defaults.d.ts.map +1 -1
- package/dist/ai/feature-defaults.js +24 -14
- package/dist/ai/feature-defaults.js.map +1 -1
- package/dist/ai/index.d.ts +15 -16
- package/dist/ai/index.d.ts.map +1 -1
- package/dist/ai/index.js +14 -15
- package/dist/ai/index.js.map +1 -1
- package/dist/ai/models.d.ts +7 -3
- package/dist/ai/models.d.ts.map +1 -1
- package/dist/ai/models.js +10 -5
- package/dist/ai/models.js.map +1 -1
- package/dist/ai/plugin-errors.d.ts +9 -3
- package/dist/ai/plugin-errors.d.ts.map +1 -1
- package/dist/ai/plugin-errors.js +9 -3
- package/dist/ai/plugin-errors.js.map +1 -1
- package/dist/ai/plugin-import.d.ts +9 -1
- package/dist/ai/plugin-import.d.ts.map +1 -1
- package/dist/ai/plugin-import.js +9 -1
- package/dist/ai/plugin-import.js.map +1 -1
- package/dist/ai/prompt-safety.d.ts +23 -0
- package/dist/ai/prompt-safety.d.ts.map +1 -0
- package/dist/ai/prompt-safety.js +34 -0
- package/dist/ai/prompt-safety.js.map +1 -0
- package/dist/ai/providers/anthropic.d.ts.map +1 -1
- package/dist/ai/providers/anthropic.js +16 -1
- package/dist/ai/providers/anthropic.js.map +1 -1
- package/dist/ai/providers/http.d.ts +7 -2
- package/dist/ai/providers/http.d.ts.map +1 -1
- package/dist/ai/providers/http.js +13 -2
- package/dist/ai/providers/http.js.map +1 -1
- package/dist/ai/providers/index.d.ts +0 -7
- package/dist/ai/providers/index.d.ts.map +1 -1
- package/dist/ai/providers/index.js +1 -1
- package/dist/ai/providers/index.js.map +1 -1
- package/dist/ai/resolve.d.ts +7 -1
- package/dist/ai/resolve.d.ts.map +1 -1
- package/dist/ai/resolve.js +36 -3
- package/dist/ai/resolve.js.map +1 -1
- package/dist/ai/runtime-response.d.ts +27 -0
- package/dist/ai/runtime-response.d.ts.map +1 -0
- package/dist/ai/runtime-response.js +75 -0
- package/dist/ai/runtime-response.js.map +1 -0
- package/dist/ai/runtime.d.ts +9 -7
- package/dist/ai/runtime.d.ts.map +1 -1
- package/dist/ai/runtime.js +14 -19
- package/dist/ai/runtime.js.map +1 -1
- package/dist/ai/tasks/alt-text.d.ts +82 -0
- package/dist/ai/tasks/alt-text.d.ts.map +1 -0
- package/dist/ai/tasks/alt-text.js +171 -0
- package/dist/ai/tasks/alt-text.js.map +1 -0
- package/dist/ai/tasks/document-generation.d.ts +102 -0
- package/dist/ai/tasks/document-generation.d.ts.map +1 -0
- package/dist/ai/tasks/document-generation.js +321 -0
- package/dist/ai/tasks/document-generation.js.map +1 -0
- package/dist/ai/tasks/link-embeddings.d.ts +55 -0
- package/dist/ai/tasks/link-embeddings.d.ts.map +1 -0
- package/dist/ai/tasks/link-embeddings.js +160 -0
- package/dist/ai/tasks/link-embeddings.js.map +1 -0
- package/dist/ai/types.d.ts +15 -19
- package/dist/ai/types.d.ts.map +1 -1
- package/dist/ai/types.js +6 -7
- package/dist/ai/types.js.map +1 -1
- package/dist/ai/usage.d.ts +6 -4
- package/dist/ai/usage.d.ts.map +1 -1
- package/dist/ai/usage.js +6 -4
- package/dist/ai/usage.js.map +1 -1
- package/dist/ai/vector.d.ts +8 -0
- package/dist/ai/vector.d.ts.map +1 -0
- package/dist/ai/vector.js +24 -0
- package/dist/ai/vector.js.map +1 -0
- package/dist/api/responses.d.ts +1 -1
- package/dist/api/responses.d.ts.map +1 -1
- package/dist/api/responses.js +1 -1
- package/dist/api/responses.js.map +1 -1
- package/dist/api/route-helpers.d.ts +2 -19
- package/dist/api/route-helpers.d.ts.map +1 -1
- package/dist/api/route-helpers.js +11 -22
- package/dist/api/route-helpers.js.map +1 -1
- package/dist/api/routes/ai-authoring.d.ts.map +1 -1
- package/dist/api/routes/ai-authoring.js +132 -144
- package/dist/api/routes/ai-authoring.js.map +1 -1
- package/dist/api/routes/ai-seo.d.ts.map +1 -1
- package/dist/api/routes/ai-seo.js +44 -120
- package/dist/api/routes/ai-seo.js.map +1 -1
- package/dist/api/routes/ai-settings.d.ts.map +1 -1
- package/dist/api/routes/ai-settings.js +76 -28
- package/dist/api/routes/ai-settings.js.map +1 -1
- package/dist/api/routes/security-center.d.ts.map +1 -1
- package/dist/api/routes/security-center.js +5 -39
- package/dist/api/routes/security-center.js.map +1 -1
- package/dist/api/routes/seo-public.d.ts +0 -5
- package/dist/api/routes/seo-public.d.ts.map +1 -1
- package/dist/api/routes/seo-public.js +1 -1
- package/dist/api/routes/seo-public.js.map +1 -1
- package/dist/api/routes/seo.d.ts.map +1 -1
- package/dist/api/routes/seo.js +11 -10
- package/dist/api/routes/seo.js.map +1 -1
- package/dist/api/security-headers.d.ts +8 -0
- package/dist/api/security-headers.d.ts.map +1 -0
- package/dist/api/security-headers.js +13 -0
- package/dist/api/security-headers.js.map +1 -0
- package/dist/appearance/favicon-icon.d.ts +1 -1
- package/dist/appearance/favicon-icon.d.ts.map +1 -1
- package/dist/appearance/favicon-icon.js +1 -1
- package/dist/appearance/favicon-icon.js.map +1 -1
- package/dist/auth/index.d.ts +0 -6
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +0 -3
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/invites.d.ts +0 -5
- package/dist/auth/invites.d.ts.map +1 -1
- package/dist/auth/invites.js +3 -3
- package/dist/auth/invites.js.map +1 -1
- package/dist/auth/team.d.ts +4 -3
- package/dist/auth/team.d.ts.map +1 -1
- package/dist/auth/user-admin.d.ts +2 -1
- package/dist/auth/user-admin.d.ts.map +1 -1
- package/dist/billing/stripe-meter.d.ts +2 -1
- package/dist/billing/stripe-meter.d.ts.map +1 -1
- package/dist/cache/http.d.ts +0 -11
- package/dist/cache/http.d.ts.map +1 -1
- package/dist/cache/http.js +1 -1
- package/dist/cache/http.js.map +1 -1
- package/dist/cache/index.d.ts +0 -5
- package/dist/cache/index.d.ts.map +1 -1
- package/dist/config/types.d.ts +19 -19
- package/dist/config/types.d.ts.map +1 -1
- package/dist/content/index.d.ts +2 -3
- package/dist/content/index.d.ts.map +1 -1
- package/dist/content/index.js +1 -2
- package/dist/content/index.js.map +1 -1
- package/dist/content/structured-data.d.ts +0 -14
- package/dist/content/structured-data.d.ts.map +1 -1
- package/dist/content/structured-data.js +3 -14
- package/dist/content/structured-data.js.map +1 -1
- package/dist/cron/index.d.ts +2 -3
- package/dist/cron/index.d.ts.map +1 -1
- package/dist/cron/index.js +0 -2
- package/dist/cron/index.js.map +1 -1
- package/dist/db/client-types.d.ts +2 -1
- package/dist/db/client-types.d.ts.map +1 -1
- package/dist/db.d.ts +0 -2
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +0 -1
- package/dist/db.js.map +1 -1
- package/dist/diagnostics/env.d.ts +1 -1
- package/dist/diagnostics/env.d.ts.map +1 -1
- package/dist/diagnostics/logger.d.ts +0 -24
- package/dist/diagnostics/logger.d.ts.map +1 -1
- package/dist/forms/entries.d.ts +0 -7
- package/dist/forms/entries.d.ts.map +1 -1
- package/dist/forms/entries.js +1 -19
- package/dist/forms/entries.js.map +1 -1
- package/dist/forms/files.d.ts +1 -1
- package/dist/forms/files.d.ts.map +1 -1
- package/dist/forms/index.d.ts +4 -6
- package/dist/forms/index.d.ts.map +1 -1
- package/dist/forms/index.js +3 -5
- package/dist/forms/index.js.map +1 -1
- package/dist/forms/prefetch.d.ts +0 -6
- package/dist/forms/prefetch.d.ts.map +1 -1
- package/dist/forms/service.d.ts +0 -4
- package/dist/forms/service.d.ts.map +1 -1
- package/dist/forms/service.js +2 -8
- package/dist/forms/service.js.map +1 -1
- package/dist/forms/webmcp.d.ts +0 -2
- package/dist/forms/webmcp.d.ts.map +1 -1
- package/dist/forms/webmcp.js +1 -1
- package/dist/forms/webmcp.js.map +1 -1
- package/dist/media/denormalize.d.ts +0 -7
- package/dist/media/denormalize.d.ts.map +1 -1
- package/dist/media/index.d.ts +0 -8
- package/dist/media/index.d.ts.map +1 -1
- package/dist/media/index.js +0 -5
- package/dist/media/index.js.map +1 -1
- package/dist/media/optimize.d.ts +0 -4
- package/dist/media/optimize.d.ts.map +1 -1
- package/dist/media/optimize.js +4 -4
- package/dist/media/optimize.js.map +1 -1
- package/dist/media/reoptimize.d.ts +2 -3
- package/dist/media/reoptimize.d.ts.map +1 -1
- package/dist/media/reoptimize.js +2 -2
- package/dist/media/reoptimize.js.map +1 -1
- package/dist/media/seo.d.ts +3 -8
- package/dist/media/seo.d.ts.map +1 -1
- package/dist/media/seo.js +1 -1
- package/dist/media/seo.js.map +1 -1
- package/dist/plan/features.d.ts +0 -1
- package/dist/plan/features.d.ts.map +1 -1
- package/dist/plan/features.js +0 -16
- package/dist/plan/features.js.map +1 -1
- package/dist/preview/event-bus.d.ts +2 -1
- package/dist/preview/event-bus.d.ts.map +1 -1
- package/dist/preview/index.d.ts +2 -1
- package/dist/preview/index.d.ts.map +1 -1
- package/dist/preview/sse-stream.d.ts +0 -13
- package/dist/preview/sse-stream.d.ts.map +1 -1
- package/dist/preview/sse-stream.js +2 -2
- package/dist/preview/sse-stream.js.map +1 -1
- package/dist/realtime/notifications.d.ts +4 -3
- package/dist/realtime/notifications.d.ts.map +1 -1
- package/dist/redirects/index.d.ts +4 -16
- package/dist/redirects/index.d.ts.map +1 -1
- package/dist/redirects/index.js +2 -9
- package/dist/redirects/index.js.map +1 -1
- package/dist/redirects/match.d.ts +0 -2
- package/dist/redirects/match.d.ts.map +1 -1
- package/dist/redirects/match.js +1 -1
- package/dist/redirects/match.js.map +1 -1
- package/dist/redirects/recovery.d.ts +0 -2
- package/dist/redirects/recovery.d.ts.map +1 -1
- package/dist/redirects/recovery.js +1 -1
- package/dist/redirects/recovery.js.map +1 -1
- package/dist/redirects/rerank.d.ts +4 -3
- package/dist/redirects/rerank.d.ts.map +1 -1
- package/dist/redirects/rerank.js +3 -18
- package/dist/redirects/rerank.js.map +1 -1
- package/dist/sections/validate.d.ts +0 -2
- package/dist/sections/validate.d.ts.map +1 -1
- package/dist/sections/validate.js +1 -1
- package/dist/sections/validate.js.map +1 -1
- package/dist/security/access.d.ts +0 -9
- package/dist/security/access.d.ts.map +1 -1
- package/dist/security/access.js +1 -1
- package/dist/security/access.js.map +1 -1
- package/dist/security/audit.d.ts +0 -20
- package/dist/security/audit.d.ts.map +1 -1
- package/dist/security/audit.js.map +1 -1
- package/dist/security/index.d.ts +2 -20
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/index.js +2 -7
- package/dist/security/index.js.map +1 -1
- package/dist/security/internal-keys.d.ts +0 -10
- package/dist/security/internal-keys.d.ts.map +1 -1
- package/dist/security/internal-keys.js +2 -2
- package/dist/security/internal-keys.js.map +1 -1
- package/dist/security/rate-limit-policy.d.ts +0 -2
- package/dist/security/rate-limit-policy.d.ts.map +1 -1
- package/dist/security/rate-limit-policy.js +1 -1
- package/dist/security/rate-limit-policy.js.map +1 -1
- package/dist/security/rate-limit.d.ts +2 -5
- package/dist/security/rate-limit.d.ts.map +1 -1
- package/dist/security/rate-limit.js +2 -2
- package/dist/security/rate-limit.js.map +1 -1
- package/dist/security/sanitize.d.ts +0 -3
- package/dist/security/sanitize.d.ts.map +1 -1
- package/dist/security/sanitize.js +0 -1
- package/dist/security/sanitize.js.map +1 -1
- package/dist/security/upload.d.ts +0 -3
- package/dist/security/upload.d.ts.map +1 -1
- package/dist/security/upload.js +0 -1
- package/dist/security/upload.js.map +1 -1
- package/dist/seo/ai-crawler-log.d.ts +0 -2
- package/dist/seo/ai-crawler-log.d.ts.map +1 -1
- package/dist/seo/ai-crawler-log.js +1 -1
- package/dist/seo/ai-crawler-log.js.map +1 -1
- package/dist/seo/audit-engine.d.ts +5 -5
- package/dist/seo/audit-engine.d.ts.map +1 -1
- package/dist/seo/audit-engine.js +1 -2
- package/dist/seo/audit-engine.js.map +1 -1
- package/dist/seo/audit-runner.d.ts +0 -7
- package/dist/seo/audit-runner.d.ts.map +1 -1
- package/dist/seo/audit-runner.js +1 -1
- package/dist/seo/audit-runner.js.map +1 -1
- package/dist/seo/audit-signals.d.ts +0 -2
- package/dist/seo/audit-signals.d.ts.map +1 -1
- package/dist/seo/audit-signals.js +1 -1
- package/dist/seo/audit-signals.js.map +1 -1
- package/dist/seo/autopilot-gate.d.ts +0 -1
- package/dist/seo/autopilot-gate.d.ts.map +1 -1
- package/dist/seo/autopilot-gate.js +1 -1
- package/dist/seo/autopilot-gate.js.map +1 -1
- package/dist/seo/autopilot-ledger.d.ts +2 -1
- package/dist/seo/autopilot-ledger.d.ts.map +1 -1
- package/dist/seo/autopilot-outcome.d.ts +0 -2
- package/dist/seo/autopilot-outcome.d.ts.map +1 -1
- package/dist/seo/autopilot-outcome.js +1 -1
- package/dist/seo/autopilot-outcome.js.map +1 -1
- package/dist/seo/autopilot-policy.d.ts +3 -38
- package/dist/seo/autopilot-policy.d.ts.map +1 -1
- package/dist/seo/autopilot-policy.js +3 -3
- package/dist/seo/autopilot-policy.js.map +1 -1
- package/dist/seo/autopilot.d.ts +4 -12
- package/dist/seo/autopilot.d.ts.map +1 -1
- package/dist/seo/autopilot.js +3 -3
- package/dist/seo/autopilot.js.map +1 -1
- package/dist/seo/config-store.d.ts +0 -2
- package/dist/seo/config-store.d.ts.map +1 -1
- package/dist/seo/config-store.js +1 -1
- package/dist/seo/config-store.js.map +1 -1
- package/dist/seo/digest.d.ts +8 -7
- package/dist/seo/digest.d.ts.map +1 -1
- package/dist/seo/faq-signals.d.ts +0 -4
- package/dist/seo/faq-signals.d.ts.map +1 -1
- package/dist/seo/faq-signals.js +4 -4
- package/dist/seo/faq-signals.js.map +1 -1
- package/dist/seo/fix-brief-export.d.ts +0 -2
- package/dist/seo/fix-brief-export.d.ts.map +1 -1
- package/dist/seo/fix-brief-export.js +1 -1
- package/dist/seo/fix-brief-export.js.map +1 -1
- package/dist/seo/geo-record.d.ts +0 -4
- package/dist/seo/geo-record.d.ts.map +1 -1
- package/dist/seo/geo-record.js +2 -2
- package/dist/seo/geo-record.js.map +1 -1
- package/dist/seo/geo-report.d.ts +4 -3
- package/dist/seo/geo-report.d.ts.map +1 -1
- package/dist/seo/geo-report.js +1 -1
- package/dist/seo/geo-report.js.map +1 -1
- package/dist/seo/index-status.d.ts +6 -5
- package/dist/seo/index-status.d.ts.map +1 -1
- package/dist/seo/index-status.js.map +1 -1
- package/dist/seo/index.d.ts +15 -90
- package/dist/seo/index.d.ts.map +1 -1
- package/dist/seo/index.js +10 -49
- package/dist/seo/index.js.map +1 -1
- package/dist/seo/issue-batch-briefs.d.ts +2 -3
- package/dist/seo/issue-batch-briefs.d.ts.map +1 -1
- package/dist/seo/issue-batch-briefs.js +2 -2
- package/dist/seo/issue-batch-briefs.js.map +1 -1
- package/dist/seo/issue-content-brief.d.ts +2 -1
- package/dist/seo/issue-content-brief.d.ts.map +1 -1
- package/dist/seo/issue-content-research.d.ts +2 -2
- package/dist/seo/issue-content-research.d.ts.map +1 -1
- package/dist/seo/issue-content-research.js +1 -1
- package/dist/seo/issue-content-research.js.map +1 -1
- package/dist/seo/issue-fix-ai.d.ts +5 -4
- package/dist/seo/issue-fix-ai.d.ts.map +1 -1
- package/dist/seo/issue-fix-ai.js +4 -19
- package/dist/seo/issue-fix-ai.js.map +1 -1
- package/dist/seo/issue-fix-apply.d.ts +2 -11
- package/dist/seo/issue-fix-apply.d.ts.map +1 -1
- package/dist/seo/issue-fix-apply.js +4 -4
- package/dist/seo/issue-fix-apply.js.map +1 -1
- package/dist/seo/issue-fix-faq.d.ts +4 -7
- package/dist/seo/issue-fix-faq.d.ts.map +1 -1
- package/dist/seo/issue-fix-faq.js +4 -4
- package/dist/seo/issue-fix-faq.js.map +1 -1
- package/dist/seo/issue-fix-link-insert.d.ts +4 -14
- package/dist/seo/issue-fix-link-insert.d.ts.map +1 -1
- package/dist/seo/issue-fix-link-insert.js +5 -4
- package/dist/seo/issue-fix-link-insert.js.map +1 -1
- package/dist/seo/issue-fix-redirect.d.ts +0 -1
- package/dist/seo/issue-fix-redirect.d.ts.map +1 -1
- package/dist/seo/issue-fix.d.ts.map +1 -1
- package/dist/seo/issue-fix.js +1 -0
- package/dist/seo/issue-fix.js.map +1 -1
- package/dist/seo/link-graph.d.ts +2 -3
- package/dist/seo/link-graph.d.ts.map +1 -1
- package/dist/seo/link-graph.js +0 -0
- package/dist/seo/link-graph.js.map +1 -1
- package/dist/seo/link-suggest.d.ts +8 -2
- package/dist/seo/link-suggest.d.ts.map +1 -1
- package/dist/seo/link-suggest.js +38 -50
- package/dist/seo/link-suggest.js.map +1 -1
- package/dist/seo/page-meta.d.ts +1 -10
- package/dist/seo/page-meta.d.ts.map +1 -1
- package/dist/seo/page-meta.js +1 -1
- package/dist/seo/page-meta.js.map +1 -1
- package/dist/seo/proposals-bulk-apply.d.ts +3 -2
- package/dist/seo/proposals-bulk-apply.d.ts.map +1 -1
- package/dist/seo/proposals.d.ts +2 -2
- package/dist/seo/proposals.d.ts.map +1 -1
- package/dist/seo/rendered-fetch.d.ts +2 -1
- package/dist/seo/rendered-fetch.d.ts.map +1 -1
- package/dist/seo/score.d.ts +3 -7
- package/dist/seo/score.d.ts.map +1 -1
- package/dist/seo/score.js.map +1 -1
- package/dist/seo/search-console/client.d.ts +0 -4
- package/dist/seo/search-console/client.d.ts.map +1 -1
- package/dist/seo/search-console/client.js +2 -2
- package/dist/seo/search-console/client.js.map +1 -1
- package/dist/seo/search-console/sync.d.ts +0 -4
- package/dist/seo/search-console/sync.d.ts.map +1 -1
- package/dist/seo/search-console/sync.js +2 -2
- package/dist/seo/search-console/sync.js.map +1 -1
- package/dist/seo/search-console/types.d.ts +2 -1
- package/dist/seo/search-console/types.d.ts.map +1 -1
- package/dist/seo/sitemap-notify.d.ts +0 -8
- package/dist/seo/sitemap-notify.d.ts.map +1 -1
- package/dist/seo/sitemap-notify.js +1 -1
- package/dist/seo/sitemap-notify.js.map +1 -1
- package/dist/seo/title-templates.d.ts +2 -5
- package/dist/seo/title-templates.d.ts.map +1 -1
- package/dist/seo/title-templates.js +2 -2
- package/dist/seo/title-templates.js.map +1 -1
- package/dist/server-site.d.ts +0 -1
- package/dist/server-site.d.ts.map +1 -1
- package/dist/server-site.js +0 -1
- package/dist/server-site.js.map +1 -1
- package/dist/storage/index.d.ts +0 -2
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +0 -4
- package/dist/storage/index.js.map +1 -1
- package/dist/upgrade/version-check.d.ts +0 -2
- package/dist/upgrade/version-check.d.ts.map +1 -1
- package/dist/upgrade/version-check.js +1 -1
- package/dist/upgrade/version-check.js.map +1 -1
- package/dist/webhooks/index.d.ts +0 -8
- package/dist/webhooks/index.d.ts.map +1 -1
- package/dist/webhooks/index.js +1 -16
- package/dist/webhooks/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider-free prompt + coercion for whole-document generation. Moved from
|
|
3
|
+
* `@prenta/plugin-ai` (`writing/content-generator.ts`) in Spec B; the model
|
|
4
|
+
* call runs through `generateJsonForFeature` under the
|
|
5
|
+
* `ai.features.documentGeneration` feature (toggle, daily cap, budget, brand
|
|
6
|
+
* voice, usage metering), so this module never touches a provider.
|
|
7
|
+
*
|
|
8
|
+
* Given a prompt + a minimal collection schema (slug, labels, fields) it
|
|
9
|
+
* builds the system/user prompts that ask the model for JSON matching the
|
|
10
|
+
* field names and types, and coerces the validated reply into field data
|
|
11
|
+
* ready for `createDocument`.
|
|
12
|
+
*
|
|
13
|
+
* Intentionally pragmatic — it does *not* invent a page `data.sections`
|
|
14
|
+
* list. Author pages with `add_page_section` or `createDocument` using
|
|
15
|
+
* `data.sections`. This is for non-page collections (blog posts, case
|
|
16
|
+
* studies, services, locations, team members, …) with structured field data.
|
|
17
|
+
*/
|
|
18
|
+
import { z } from 'zod';
|
|
19
|
+
import { clampAiContent, fenceUntrusted } from '../prompt-safety.js';
|
|
20
|
+
export const DOCUMENT_GENERATION_FEATURE = 'ai.features.documentGeneration';
|
|
21
|
+
/** Default output cap — enough for ~2000-word posts. */
|
|
22
|
+
export const DOCUMENT_GENERATION_MAX_TOKENS = 3000;
|
|
23
|
+
/** Reference material is clamped before fencing so it cannot dominate the prompt. */
|
|
24
|
+
const EXISTING_CONTENT_MAX_CHARS = 2000;
|
|
25
|
+
/** Caller-supplied modifiers land in the *system* prompt: clamp and keep to one line. */
|
|
26
|
+
const MODIFIER_MAX_CHARS = 200;
|
|
27
|
+
/**
|
|
28
|
+
* Sanitize a free-form modifier (`tone`, `targetAudience`) before it becomes a
|
|
29
|
+
* system-prompt rule: clamp, collapse every line break / whitespace run to one
|
|
30
|
+
* space so the caller cannot inject additional `- Rule` lines, and trim.
|
|
31
|
+
* Returns `undefined` for blank input so the rule line is omitted.
|
|
32
|
+
*/
|
|
33
|
+
function systemPromptModifier(value) {
|
|
34
|
+
if (!value)
|
|
35
|
+
return undefined;
|
|
36
|
+
const flat = clampAiContent(value, MODIFIER_MAX_CHARS).replace(/\s+/g, ' ').trim();
|
|
37
|
+
return flat || undefined;
|
|
38
|
+
}
|
|
39
|
+
/** Any JSON object; field-level coercion happens in {@link coerceDocument}. */
|
|
40
|
+
export const DocumentDraftSchema = z
|
|
41
|
+
.record(z.string(), z.unknown())
|
|
42
|
+
.refine((v) => v !== null && typeof v === 'object' && !Array.isArray(v), {
|
|
43
|
+
message: 'expected a JSON object',
|
|
44
|
+
});
|
|
45
|
+
// ─── Core type adapters ───────────────────────────────────────────────────
|
|
46
|
+
/**
|
|
47
|
+
* Project a core `FieldDefinition` onto {@link FieldShape}. Only the
|
|
48
|
+
* prompt-relevant, serializable subset is carried (no validators, hooks,
|
|
49
|
+
* access functions, or admin conditions), so the shape stays safe to log and
|
|
50
|
+
* cheap to describe to the model.
|
|
51
|
+
*/
|
|
52
|
+
export function toFieldShape(field) {
|
|
53
|
+
const shape = { type: field.type };
|
|
54
|
+
if (field.label)
|
|
55
|
+
shape.label = field.label;
|
|
56
|
+
if (field.required)
|
|
57
|
+
shape.required = true;
|
|
58
|
+
if (field.admin?.description)
|
|
59
|
+
shape.admin = { description: field.admin.description };
|
|
60
|
+
switch (field.type) {
|
|
61
|
+
case 'text':
|
|
62
|
+
if (typeof field.maxLength === 'number')
|
|
63
|
+
shape.maxLength = field.maxLength;
|
|
64
|
+
break;
|
|
65
|
+
case 'number':
|
|
66
|
+
if (typeof field.min === 'number')
|
|
67
|
+
shape.min = field.min;
|
|
68
|
+
if (typeof field.max === 'number')
|
|
69
|
+
shape.max = field.max;
|
|
70
|
+
if (typeof field.step === 'number')
|
|
71
|
+
shape.step = field.step;
|
|
72
|
+
break;
|
|
73
|
+
case 'select':
|
|
74
|
+
shape.options = field.options.map((o) => ({ label: o.label, value: o.value }));
|
|
75
|
+
break;
|
|
76
|
+
case 'richText':
|
|
77
|
+
if (field.features)
|
|
78
|
+
shape.features = [...field.features];
|
|
79
|
+
break;
|
|
80
|
+
case 'array':
|
|
81
|
+
shape.fields = Object.fromEntries(Object.entries(field.fields).map(([name, sub]) => [name, toFieldShape(sub)]));
|
|
82
|
+
break;
|
|
83
|
+
case 'boolean':
|
|
84
|
+
case 'date':
|
|
85
|
+
case 'slug':
|
|
86
|
+
case 'relationship':
|
|
87
|
+
case 'media':
|
|
88
|
+
case 'json':
|
|
89
|
+
case 'nav':
|
|
90
|
+
case 'color':
|
|
91
|
+
case 'url':
|
|
92
|
+
case 'email':
|
|
93
|
+
case 'blocks':
|
|
94
|
+
case 'componentBlock':
|
|
95
|
+
break;
|
|
96
|
+
default: {
|
|
97
|
+
const _exhaustive = field;
|
|
98
|
+
return _exhaustive;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return shape;
|
|
102
|
+
}
|
|
103
|
+
/** Project a core `CollectionDefinition` onto {@link CollectionShape}. */
|
|
104
|
+
export function toCollectionShape(collection) {
|
|
105
|
+
const shape = {
|
|
106
|
+
slug: collection.slug,
|
|
107
|
+
labels: collection.labels,
|
|
108
|
+
fields: Object.fromEntries(Object.entries(collection.fields).map(([name, f]) => [name, toFieldShape(f)])),
|
|
109
|
+
};
|
|
110
|
+
if (collection.type)
|
|
111
|
+
shape.type = collection.type;
|
|
112
|
+
if (collection.seo?.defaultSchemaType) {
|
|
113
|
+
shape.seo = { defaultSchemaType: collection.seo.defaultSchemaType };
|
|
114
|
+
}
|
|
115
|
+
return shape;
|
|
116
|
+
}
|
|
117
|
+
// ─── Prompt construction ──────────────────────────────────────────────────
|
|
118
|
+
function describeField(name, field) {
|
|
119
|
+
const parts = [` "${name}"`];
|
|
120
|
+
parts.push(`(${field.type}${field.required ? ', required' : ''})`);
|
|
121
|
+
if (field.label && field.label !== name)
|
|
122
|
+
parts.push(`label: ${field.label}`);
|
|
123
|
+
if (field.maxLength)
|
|
124
|
+
parts.push(`max ${field.maxLength} chars`);
|
|
125
|
+
if (typeof field.min === 'number' || typeof field.max === 'number') {
|
|
126
|
+
parts.push(`range ${field.min ?? '∞'}…${field.max ?? '∞'}`);
|
|
127
|
+
}
|
|
128
|
+
if (field.options && field.options.length) {
|
|
129
|
+
parts.push(`one of: ${field.options.map((o) => o.value).join(', ')}`);
|
|
130
|
+
}
|
|
131
|
+
if (field.type === 'array' && field.fields) {
|
|
132
|
+
const subFields = Object.entries(field.fields)
|
|
133
|
+
.map(([n, f]) => `${n}:${f.type}`)
|
|
134
|
+
.join(', ');
|
|
135
|
+
parts.push(`array of { ${subFields} }`);
|
|
136
|
+
}
|
|
137
|
+
if (field.admin?.description) {
|
|
138
|
+
parts.push(`— ${field.admin.description}`);
|
|
139
|
+
}
|
|
140
|
+
return parts.join(' ');
|
|
141
|
+
}
|
|
142
|
+
function buildSystemPrompt(collection, tone, targetAudience) {
|
|
143
|
+
const lines = [
|
|
144
|
+
`You are an expert content writer creating a "${collection.labels.singular}" document for a CMS.`,
|
|
145
|
+
'You generate valid JSON matching the requested field schema. NEVER include markdown, code fences, or commentary.',
|
|
146
|
+
'',
|
|
147
|
+
'Rules:',
|
|
148
|
+
'- Output a single JSON object whose top-level keys exactly match the field names below.',
|
|
149
|
+
'- Match field types: text/richText/email/url are strings; number is a number; boolean is a boolean; date is an ISO-8601 string; array is a JSON array of objects; select must use one of the listed option values; media fields should be left as null (a human will upload the asset).',
|
|
150
|
+
'- Respect maxLength constraints. Quality over quantity — never pad.',
|
|
151
|
+
'- For slug fields, generate a URL-safe lowercase string with words joined by hyphens.',
|
|
152
|
+
'- For richText fields, return well-structured prose with paragraph breaks, but no HTML tags.',
|
|
153
|
+
'- For seo fields (metaTitle, metaDescription) compose SEO-optimised copy distinct from the page title.',
|
|
154
|
+
"- Never invent facts. If you don't know something the prompt didn't supply, leave the field blank or null.",
|
|
155
|
+
];
|
|
156
|
+
if (collection.seo?.defaultSchemaType) {
|
|
157
|
+
lines.push(`- This collection maps to Schema.org "${collection.seo.defaultSchemaType}".`);
|
|
158
|
+
}
|
|
159
|
+
if (tone)
|
|
160
|
+
lines.push(`- Tone: ${tone}.`);
|
|
161
|
+
if (targetAudience)
|
|
162
|
+
lines.push(`- Audience: ${targetAudience}.`);
|
|
163
|
+
return lines.join('\n');
|
|
164
|
+
}
|
|
165
|
+
function buildUserPrompt(input) {
|
|
166
|
+
const { collection, prompt, existingContent } = input;
|
|
167
|
+
const fieldDescriptions = Object.entries(collection.fields)
|
|
168
|
+
// Media is supplied by the human reviewer.
|
|
169
|
+
// Relations are excluded because the LLM has no view of existing
|
|
170
|
+
// document IDs — guessing would create dangling references that the
|
|
171
|
+
// validator would then reject. Humans / orchestration code wire those
|
|
172
|
+
// in after creation.
|
|
173
|
+
.filter(([, f]) => f.type !== 'media' && f.type !== 'relationship')
|
|
174
|
+
.map(([name, field]) => describeField(name, field))
|
|
175
|
+
.join('\n');
|
|
176
|
+
const lines = [
|
|
177
|
+
`Create a ${collection.labels.singular} matching this brief:`,
|
|
178
|
+
'',
|
|
179
|
+
prompt.trim(),
|
|
180
|
+
'',
|
|
181
|
+
'Field schema:',
|
|
182
|
+
fieldDescriptions,
|
|
183
|
+
'',
|
|
184
|
+
'Output ONLY a JSON object with the field values. No prose. No markdown fences.',
|
|
185
|
+
];
|
|
186
|
+
if (existingContent) {
|
|
187
|
+
lines.push('', 'Existing related content for voice / fact reference:');
|
|
188
|
+
// Untrusted reference content — fence it so an injected instruction inside
|
|
189
|
+
// a referenced document can't redirect the generation.
|
|
190
|
+
lines.push(fenceUntrusted(existingContent, EXISTING_CONTENT_MAX_CHARS));
|
|
191
|
+
}
|
|
192
|
+
return lines.join('\n');
|
|
193
|
+
}
|
|
194
|
+
export function buildDocumentPrompt(input) {
|
|
195
|
+
return {
|
|
196
|
+
system: buildSystemPrompt(input.collection, systemPromptModifier(input.tone), systemPromptModifier(input.targetAudience)),
|
|
197
|
+
prompt: buildUserPrompt(input),
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
// ─── Output coercion ──────────────────────────────────────────────────────
|
|
201
|
+
function slugify(title) {
|
|
202
|
+
return (title
|
|
203
|
+
.toLowerCase()
|
|
204
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
205
|
+
.replace(/^-+|-+$/g, '')
|
|
206
|
+
.slice(0, 80) || 'untitled');
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Coerce the LLM output into shapes that match each field's declared type.
|
|
210
|
+
* The LLM occasionally returns numbers as strings, booleans as "true"/"false",
|
|
211
|
+
* etc. We don't try to recover unrecoverable values — we leave bad fields
|
|
212
|
+
* `undefined` so the downstream field validator surfaces the error to the
|
|
213
|
+
* caller instead of silently persisting garbage.
|
|
214
|
+
*/
|
|
215
|
+
function coerceFieldValue(value, field) {
|
|
216
|
+
if (value === null || value === undefined)
|
|
217
|
+
return value;
|
|
218
|
+
switch (field.type) {
|
|
219
|
+
case 'text':
|
|
220
|
+
case 'richText':
|
|
221
|
+
case 'email':
|
|
222
|
+
case 'url':
|
|
223
|
+
case 'slug':
|
|
224
|
+
case 'color':
|
|
225
|
+
return typeof value === 'string' ? value : String(value);
|
|
226
|
+
case 'number':
|
|
227
|
+
if (typeof value === 'number')
|
|
228
|
+
return value;
|
|
229
|
+
if (typeof value === 'string') {
|
|
230
|
+
const n = Number(value);
|
|
231
|
+
return Number.isFinite(n) ? n : undefined;
|
|
232
|
+
}
|
|
233
|
+
return undefined;
|
|
234
|
+
case 'boolean':
|
|
235
|
+
if (typeof value === 'boolean')
|
|
236
|
+
return value;
|
|
237
|
+
if (value === 'true')
|
|
238
|
+
return true;
|
|
239
|
+
if (value === 'false')
|
|
240
|
+
return false;
|
|
241
|
+
return undefined;
|
|
242
|
+
case 'date':
|
|
243
|
+
if (value instanceof Date)
|
|
244
|
+
return value.toISOString();
|
|
245
|
+
if (typeof value === 'string') {
|
|
246
|
+
const d = new Date(value);
|
|
247
|
+
return isNaN(d.getTime()) ? undefined : d.toISOString();
|
|
248
|
+
}
|
|
249
|
+
return undefined;
|
|
250
|
+
case 'select':
|
|
251
|
+
if (typeof value === 'string' && field.options?.some((o) => o.value === value))
|
|
252
|
+
return value;
|
|
253
|
+
return undefined;
|
|
254
|
+
case 'array':
|
|
255
|
+
if (!Array.isArray(value))
|
|
256
|
+
return [];
|
|
257
|
+
if (!field.fields)
|
|
258
|
+
return value;
|
|
259
|
+
return value.map((item) => {
|
|
260
|
+
if (item === null || typeof item !== 'object')
|
|
261
|
+
return item;
|
|
262
|
+
const obj = item;
|
|
263
|
+
const coerced = {};
|
|
264
|
+
for (const [name, sub] of Object.entries(field.fields)) {
|
|
265
|
+
if (obj[name] !== undefined)
|
|
266
|
+
coerced[name] = coerceFieldValue(obj[name], sub);
|
|
267
|
+
}
|
|
268
|
+
return coerced;
|
|
269
|
+
});
|
|
270
|
+
case 'json':
|
|
271
|
+
return value;
|
|
272
|
+
case 'media':
|
|
273
|
+
// The LLM doesn't have asset IDs. Leave for a human to fill.
|
|
274
|
+
return null;
|
|
275
|
+
case 'relationship':
|
|
276
|
+
// Same rationale as media — IDs are out-of-band knowledge. If the
|
|
277
|
+
// model did emit something (e.g. echoed an ID from the prompt) we
|
|
278
|
+
// accept strings and string arrays verbatim; otherwise drop.
|
|
279
|
+
if (typeof value === 'string')
|
|
280
|
+
return value;
|
|
281
|
+
if (Array.isArray(value))
|
|
282
|
+
return value.filter((v) => typeof v === 'string');
|
|
283
|
+
return undefined;
|
|
284
|
+
default:
|
|
285
|
+
return value;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Coerce a schema-validated model reply into persisted field data plus the
|
|
290
|
+
* derived title / slug / SEO bundle. Unknown keys (anything the collection
|
|
291
|
+
* doesn't declare) are dropped to keep persisted data clean.
|
|
292
|
+
*/
|
|
293
|
+
export function coerceDocument(parsed, collection) {
|
|
294
|
+
const data = {};
|
|
295
|
+
for (const [name, field] of Object.entries(collection.fields)) {
|
|
296
|
+
const raw = parsed[name];
|
|
297
|
+
if (raw === undefined)
|
|
298
|
+
continue;
|
|
299
|
+
const coerced = coerceFieldValue(raw, field);
|
|
300
|
+
if (coerced !== undefined)
|
|
301
|
+
data[name] = coerced;
|
|
302
|
+
}
|
|
303
|
+
// Derive title + slug.
|
|
304
|
+
const title = (typeof data.title === 'string' && data.title) ||
|
|
305
|
+
(typeof data.question === 'string' && data.question) ||
|
|
306
|
+
(typeof data.name === 'string' && data.name) ||
|
|
307
|
+
'Untitled';
|
|
308
|
+
const slug = (typeof data.slug === 'string' && data.slug.trim()) || slugify(title);
|
|
309
|
+
data.slug = slug;
|
|
310
|
+
const seo = {
|
|
311
|
+
metaTitle: (typeof data.metaTitle === 'string' && data.metaTitle) ||
|
|
312
|
+
(typeof parsed.metaTitle === 'string' && parsed.metaTitle) ||
|
|
313
|
+
title,
|
|
314
|
+
metaDescription: (typeof data.metaDescription === 'string' && data.metaDescription) ||
|
|
315
|
+
(typeof parsed.metaDescription === 'string' && parsed.metaDescription) ||
|
|
316
|
+
undefined,
|
|
317
|
+
schemaType: collection.seo?.defaultSchemaType,
|
|
318
|
+
};
|
|
319
|
+
return { data, title, slug, seo };
|
|
320
|
+
}
|
|
321
|
+
//# sourceMappingURL=document-generation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"document-generation.js","sourceRoot":"","sources":["../../../src/ai/tasks/document-generation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Embedding-ranked internal-link candidates. The query and the corpus are
|
|
3
|
+
* embedded through governed `embedForFeature` calls in batches of
|
|
4
|
+
* {@link EMBED_BATCH_SIZE} inputs (plugin-ai's `semanticSearch` made N+1
|
|
5
|
+
* ungoverned calls; a single request for a 5000-page site blew the provider's
|
|
6
|
+
* per-request input limit). Returns `null` when the runtime refuses so callers
|
|
7
|
+
* fall back to {@link phraseMatchLinks}.
|
|
8
|
+
*/
|
|
9
|
+
export declare const LINK_SUGGEST_FEATURE = "ai.features.seoInternalLinks";
|
|
10
|
+
export declare const LINK_SIMILARITY_THRESHOLD = 0.35;
|
|
11
|
+
/** Per-item content cap before embedding — enough context, bounded token cost. */
|
|
12
|
+
export declare const CONTENT_MAX_CHARS = 2000;
|
|
13
|
+
/** Max inputs per embeddings request (the first request also carries the query). */
|
|
14
|
+
export declare const EMBED_BATCH_SIZE = 256;
|
|
15
|
+
/** Largest corpus we embed; bigger ones are pre-trimmed by {@link trimCorpusForEmbedding}. */
|
|
16
|
+
export declare const MAX_CORPUS_ITEMS = 1000;
|
|
17
|
+
export interface LinkCorpusItem {
|
|
18
|
+
id: string;
|
|
19
|
+
title: string;
|
|
20
|
+
content: string;
|
|
21
|
+
/** Recency tie-breaker for the pre-trim; optional for callers without it. */
|
|
22
|
+
updatedAt?: string | Date;
|
|
23
|
+
}
|
|
24
|
+
export interface RankedLink {
|
|
25
|
+
id: string;
|
|
26
|
+
title: string;
|
|
27
|
+
score: number;
|
|
28
|
+
excerpt: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Deterministic pre-trim for corpora above the embedding cap: items whose
|
|
32
|
+
* title/content share the most query tokens come first (a `phraseMatchLinks`-
|
|
33
|
+
* style overlap), then the most recently updated. Stable for ties so the
|
|
34
|
+
* result is reproducible. Returns the input untouched when it already fits.
|
|
35
|
+
*/
|
|
36
|
+
export declare function trimCorpusForEmbedding(query: string, corpus: LinkCorpusItem[], max?: number): LinkCorpusItem[];
|
|
37
|
+
/**
|
|
38
|
+
* Rank `corpus` against `query` by embedding cosine similarity. Everything
|
|
39
|
+
* (toggle, budget, daily cap, key resolution, metering) is enforced inside
|
|
40
|
+
* `embedForFeature`; any refusal or provider failure yields `null`. Corpora
|
|
41
|
+
* above {@link MAX_CORPUS_ITEMS} are pre-trimmed deterministically, every item
|
|
42
|
+
* is clamped to {@link CONTENT_MAX_CHARS}, and requests are chunked.
|
|
43
|
+
*/
|
|
44
|
+
export declare function rankByEmbedding(db: unknown, query: string, corpus: LinkCorpusItem[], opts?: {
|
|
45
|
+
limit?: number;
|
|
46
|
+
userId?: string;
|
|
47
|
+
fetchImpl?: typeof fetch;
|
|
48
|
+
}): Promise<RankedLink[] | null>;
|
|
49
|
+
/**
|
|
50
|
+
* Deterministic fallback: a source qualifies when its content contains the
|
|
51
|
+
* target title or one of its keywords. Score 0.6 for a title hit, 0.4 for a
|
|
52
|
+
* keyword-only hit (both clear the 40% wrappable floor in link-suggest).
|
|
53
|
+
*/
|
|
54
|
+
export declare function phraseMatchLinks(targetTitle: string, keywords: string[], corpus: LinkCorpusItem[], limit: number): RankedLink[];
|
|
55
|
+
//# sourceMappingURL=link-embeddings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"link-embeddings.d.ts","sourceRoot":"","sources":["../../../src/ai/tasks/link-embeddings.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,eAAO,MAAM,oBAAoB,iCAAiC,CAAA;AAClE,eAAO,MAAM,yBAAyB,OAAO,CAAA;AAC7C,kFAAkF;AAClF,eAAO,MAAM,iBAAiB,OAAO,CAAA;AACrC,oFAAoF;AACpF,eAAO,MAAM,gBAAgB,MAAM,CAAA;AACnC,8FAA8F;AAC9F,eAAO,MAAM,gBAAgB,OAAO,CAAA;AAMpC,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,6EAA6E;IAC7E,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1B;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;CAChB;AAwBD;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,cAAc,EAAE,EACxB,GAAG,SAAmB,GACrB,cAAc,EAAE,CAkBlB;AA+BD;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,EAAE,EAAE,OAAO,EACX,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,cAAc,EAAE,EACxB,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;CAAO,GACvE,OAAO,CAAC,UAAU,EAAE,GAAG,IAAI,CAAC,CA2B9B;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAAE,EAClB,MAAM,EAAE,cAAc,EAAE,EACxB,KAAK,EAAE,MAAM,GACZ,UAAU,EAAE,CAmBd"}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Embedding-ranked internal-link candidates. The query and the corpus are
|
|
3
|
+
* embedded through governed `embedForFeature` calls in batches of
|
|
4
|
+
* {@link EMBED_BATCH_SIZE} inputs (plugin-ai's `semanticSearch` made N+1
|
|
5
|
+
* ungoverned calls; a single request for a 5000-page site blew the provider's
|
|
6
|
+
* per-request input limit). Returns `null` when the runtime refuses so callers
|
|
7
|
+
* fall back to {@link phraseMatchLinks}.
|
|
8
|
+
*/
|
|
9
|
+
import { embedForFeature } from '../runtime.js';
|
|
10
|
+
import { cosineSimilarity } from '../vector.js';
|
|
11
|
+
export const LINK_SUGGEST_FEATURE = 'ai.features.seoInternalLinks';
|
|
12
|
+
export const LINK_SIMILARITY_THRESHOLD = 0.35;
|
|
13
|
+
/** Per-item content cap before embedding — enough context, bounded token cost. */
|
|
14
|
+
export const CONTENT_MAX_CHARS = 2000;
|
|
15
|
+
/** Max inputs per embeddings request (the first request also carries the query). */
|
|
16
|
+
export const EMBED_BATCH_SIZE = 256;
|
|
17
|
+
/** Largest corpus we embed; bigger ones are pre-trimmed by {@link trimCorpusForEmbedding}. */
|
|
18
|
+
export const MAX_CORPUS_ITEMS = 1000;
|
|
19
|
+
const EXCERPT_CHARS = 200;
|
|
20
|
+
const DEFAULT_LIMIT = 10;
|
|
21
|
+
/** Query tokens shorter than this carry no signal for the pre-trim. */
|
|
22
|
+
const MIN_TOKEN_CHARS = 3;
|
|
23
|
+
function excerptAround(content, terms) {
|
|
24
|
+
const lower = content.toLowerCase();
|
|
25
|
+
let start = 0;
|
|
26
|
+
for (const term of terms) {
|
|
27
|
+
const idx = lower.indexOf(term);
|
|
28
|
+
if (idx >= 0) {
|
|
29
|
+
start = Math.max(0, idx - 50);
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return content.slice(start, start + EXCERPT_CHARS);
|
|
34
|
+
}
|
|
35
|
+
function updatedAtMs(item) {
|
|
36
|
+
if (item.updatedAt instanceof Date)
|
|
37
|
+
return item.updatedAt.getTime();
|
|
38
|
+
if (typeof item.updatedAt === 'string') {
|
|
39
|
+
const t = Date.parse(item.updatedAt);
|
|
40
|
+
return Number.isNaN(t) ? 0 : t;
|
|
41
|
+
}
|
|
42
|
+
return 0;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Deterministic pre-trim for corpora above the embedding cap: items whose
|
|
46
|
+
* title/content share the most query tokens come first (a `phraseMatchLinks`-
|
|
47
|
+
* style overlap), then the most recently updated. Stable for ties so the
|
|
48
|
+
* result is reproducible. Returns the input untouched when it already fits.
|
|
49
|
+
*/
|
|
50
|
+
export function trimCorpusForEmbedding(query, corpus, max = MAX_CORPUS_ITEMS) {
|
|
51
|
+
if (corpus.length <= max)
|
|
52
|
+
return corpus;
|
|
53
|
+
const tokens = [
|
|
54
|
+
...new Set(query
|
|
55
|
+
.toLowerCase()
|
|
56
|
+
.split(/[^\p{L}\p{N}]+/u)
|
|
57
|
+
.filter((t) => t.length >= MIN_TOKEN_CHARS)),
|
|
58
|
+
];
|
|
59
|
+
const scored = corpus.map((item, index) => {
|
|
60
|
+
const hay = `${item.title} ${item.content}`.toLowerCase();
|
|
61
|
+
let overlap = 0;
|
|
62
|
+
for (const t of tokens)
|
|
63
|
+
if (hay.includes(t))
|
|
64
|
+
overlap++;
|
|
65
|
+
return { item, index, overlap, recency: updatedAtMs(item) };
|
|
66
|
+
});
|
|
67
|
+
scored.sort((a, b) => b.overlap - a.overlap || b.recency - a.recency || a.index - b.index);
|
|
68
|
+
return scored.slice(0, max).map((s) => s.item);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Embed `texts` in sequential batches of {@link EMBED_BATCH_SIZE}. The first
|
|
72
|
+
* batch also carries the query (so it has one extra input). Stops at the first
|
|
73
|
+
* refusal and returns `null` — a partially embedded corpus is not rankable.
|
|
74
|
+
*/
|
|
75
|
+
async function embedInBatches(db, query, contents, opts) {
|
|
76
|
+
let queryVec;
|
|
77
|
+
const vecs = [];
|
|
78
|
+
for (let start = 0; start < contents.length; start += EMBED_BATCH_SIZE) {
|
|
79
|
+
const chunk = contents.slice(start, start + EMBED_BATCH_SIZE);
|
|
80
|
+
const texts = start === 0 ? [query, ...chunk] : chunk;
|
|
81
|
+
const res = await embedForFeature(db, LINK_SUGGEST_FEATURE, texts, {
|
|
82
|
+
userId: opts.userId,
|
|
83
|
+
fetchImpl: opts.fetchImpl,
|
|
84
|
+
});
|
|
85
|
+
if (!res.ok)
|
|
86
|
+
return null;
|
|
87
|
+
const out = start === 0 ? res.vectors.slice(1) : res.vectors;
|
|
88
|
+
if (start === 0)
|
|
89
|
+
queryVec = res.vectors[0];
|
|
90
|
+
vecs.push(...out);
|
|
91
|
+
}
|
|
92
|
+
if (!queryVec)
|
|
93
|
+
return null;
|
|
94
|
+
return { queryVec, vecs };
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Rank `corpus` against `query` by embedding cosine similarity. Everything
|
|
98
|
+
* (toggle, budget, daily cap, key resolution, metering) is enforced inside
|
|
99
|
+
* `embedForFeature`; any refusal or provider failure yields `null`. Corpora
|
|
100
|
+
* above {@link MAX_CORPUS_ITEMS} are pre-trimmed deterministically, every item
|
|
101
|
+
* is clamped to {@link CONTENT_MAX_CHARS}, and requests are chunked.
|
|
102
|
+
*/
|
|
103
|
+
export async function rankByEmbedding(db, query, corpus, opts = {}) {
|
|
104
|
+
if (corpus.length === 0)
|
|
105
|
+
return [];
|
|
106
|
+
const trimmed = trimCorpusForEmbedding(query, corpus);
|
|
107
|
+
const contents = trimmed.map((c) => c.content.slice(0, CONTENT_MAX_CHARS));
|
|
108
|
+
const embedded = await embedInBatches(db, query.slice(0, CONTENT_MAX_CHARS), contents, {
|
|
109
|
+
userId: opts.userId,
|
|
110
|
+
fetchImpl: opts.fetchImpl,
|
|
111
|
+
});
|
|
112
|
+
if (!embedded)
|
|
113
|
+
return null;
|
|
114
|
+
const { queryVec, vecs } = embedded;
|
|
115
|
+
const terms = query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
116
|
+
const ranked = [];
|
|
117
|
+
trimmed.forEach((item, i) => {
|
|
118
|
+
const vec = vecs[i];
|
|
119
|
+
if (!vec)
|
|
120
|
+
return;
|
|
121
|
+
const score = cosineSimilarity(queryVec, vec);
|
|
122
|
+
if (score >= LINK_SIMILARITY_THRESHOLD) {
|
|
123
|
+
ranked.push({
|
|
124
|
+
id: item.id,
|
|
125
|
+
title: item.title,
|
|
126
|
+
score,
|
|
127
|
+
excerpt: excerptAround(item.content, terms),
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
ranked.sort((a, b) => b.score - a.score);
|
|
132
|
+
return ranked.slice(0, opts.limit ?? DEFAULT_LIMIT);
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Deterministic fallback: a source qualifies when its content contains the
|
|
136
|
+
* target title or one of its keywords. Score 0.6 for a title hit, 0.4 for a
|
|
137
|
+
* keyword-only hit (both clear the 40% wrappable floor in link-suggest).
|
|
138
|
+
*/
|
|
139
|
+
export function phraseMatchLinks(targetTitle, keywords, corpus, limit) {
|
|
140
|
+
const title = targetTitle.trim().toLowerCase();
|
|
141
|
+
const kws = keywords.map((k) => k.trim().toLowerCase()).filter(Boolean);
|
|
142
|
+
const out = [];
|
|
143
|
+
for (const item of corpus) {
|
|
144
|
+
const lower = item.content.toLowerCase();
|
|
145
|
+
const titleHit = title.length > 0 && lower.includes(title);
|
|
146
|
+
const kwHit = kws.find((k) => lower.includes(k));
|
|
147
|
+
if (!titleHit && !kwHit)
|
|
148
|
+
continue;
|
|
149
|
+
const term = titleHit ? title : kwHit;
|
|
150
|
+
out.push({
|
|
151
|
+
id: item.id,
|
|
152
|
+
title: item.title,
|
|
153
|
+
score: titleHit ? 0.6 : 0.4,
|
|
154
|
+
excerpt: excerptAround(item.content, term ? [term] : []),
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
out.sort((a, b) => b.score - a.score);
|
|
158
|
+
return out.slice(0, limit);
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=link-embeddings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"link-embeddings.js","sourceRoot":"","sources":["../../../src/ai/tasks/link-embeddings.ts"],"names":[],"mappings":"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"}
|
package/dist/ai/types.d.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Shared types for the Settings > AI governance layer
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* phases
|
|
8
|
-
* wired into the UI for now.
|
|
2
|
+
* Shared types for the Settings > AI governance layer: provider connections
|
|
3
|
+
* (encrypted or env-managed keys), the normalized model catalog, per-feature
|
|
4
|
+
* toggles / routing / approval / daily caps, the monthly token budget, and the
|
|
5
|
+
* brand voice profile. Everything declared here is read by the governed
|
|
6
|
+
* runtime (`runtime.ts`) or the admin UI — fields that were once reserved for
|
|
7
|
+
* future phases were removed in Spec B rather than left as silent no-ops.
|
|
9
8
|
*/
|
|
10
9
|
/** Provider keys are stable code identifiers. `openai` covers OpenAI/ChatGPT. */
|
|
11
10
|
export type AIProviderKey = 'openai' | 'anthropic' | 'xai' | 'custom';
|
|
@@ -82,7 +81,7 @@ export type AIProviderConnectionPublic = Omit<AIProviderConnection, 'apiKeyEncry
|
|
|
82
81
|
keyManagedExternally: boolean;
|
|
83
82
|
hasKey: boolean;
|
|
84
83
|
};
|
|
85
|
-
|
|
84
|
+
type AIFeatureCategory = 'seo' | 'media' | 'content' | 'forms' | 'security' | 'developer' | 'search';
|
|
86
85
|
export interface AIFeatureConfig {
|
|
87
86
|
key: string;
|
|
88
87
|
label: string;
|
|
@@ -95,19 +94,11 @@ export interface AIFeatureConfig {
|
|
|
95
94
|
fallbackModelIds?: string[];
|
|
96
95
|
approvalRequired: boolean;
|
|
97
96
|
allowBulkApply: boolean;
|
|
98
|
-
maxMonthlyTokens?: number;
|
|
99
97
|
maxRequestsPerDay?: number;
|
|
100
|
-
rolesAllowed: string[];
|
|
101
|
-
permissionsRequired: string[];
|
|
102
|
-
dataPolicyKey?: string;
|
|
103
98
|
}
|
|
104
99
|
export interface AIBudgetSettings {
|
|
105
100
|
monthlyTokenLimit?: number;
|
|
106
|
-
monthlyCostLimit?: number;
|
|
107
|
-
alertAtPercentages: number[];
|
|
108
101
|
disableWhenExceeded: boolean;
|
|
109
|
-
perFeatureLimits?: Record<string, number>;
|
|
110
|
-
perUserLimits?: Record<string, number>;
|
|
111
102
|
}
|
|
112
103
|
/**
|
|
113
104
|
* Brand voice profile. A reusable description of how generated copy should
|
|
@@ -145,11 +136,9 @@ export interface AISettings {
|
|
|
145
136
|
defaultTextModelId?: string;
|
|
146
137
|
defaultReasoningModelId?: string;
|
|
147
138
|
defaultVisionModelId?: string;
|
|
148
|
-
defaultEmbeddingModelId?: string;
|
|
149
139
|
defaultImageGenerationModelId?: string;
|
|
150
140
|
features: AIFeatureConfig[];
|
|
151
141
|
budget: AIBudgetSettings;
|
|
152
|
-
logsRetentionDays: number;
|
|
153
142
|
/** Active brand voice. Absent until an admin configures it. */
|
|
154
143
|
brandVoice?: BrandVoiceProfile;
|
|
155
144
|
}
|
|
@@ -171,7 +160,13 @@ export interface AIConfigDocument {
|
|
|
171
160
|
updatedAt?: string;
|
|
172
161
|
updatedBy?: string;
|
|
173
162
|
}
|
|
174
|
-
/**
|
|
163
|
+
/**
|
|
164
|
+
* Monthly usage summary for the budget meter. Sums the token fields the
|
|
165
|
+
* governed runtime writes to `prenta_audit_logs` (`ai_usage` rows:
|
|
166
|
+
* `promptTokens` / `completionTokens` / `totalTokens`) plus legacy
|
|
167
|
+
* `tokensUsed` / `totalTokensUsed` rows from older handlers. Real counts —
|
|
168
|
+
* 0 when nothing has run in the period, never a fabricated number.
|
|
169
|
+
*/
|
|
175
170
|
export interface AIUsageSummary {
|
|
176
171
|
totalTokens: number;
|
|
177
172
|
promptTokens: number;
|
|
@@ -190,4 +185,5 @@ export interface AIConnectionTestResult {
|
|
|
190
185
|
message: string;
|
|
191
186
|
testedAt: string;
|
|
192
187
|
}
|
|
188
|
+
export {};
|
|
193
189
|
//# sourceMappingURL=types.d.ts.map
|