@aglyn/plugins-ai 1.0.0-beta.143
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/LICENSE +201 -0
- package/README.md +25 -0
- package/package.json +60 -0
- package/src/index.d.ts +21 -0
- package/src/index.js +22 -0
- package/src/index.js.map +1 -0
- package/src/lib/activity/ai-activity-actions.d.ts +106 -0
- package/src/lib/activity/ai-activity-actions.js +209 -0
- package/src/lib/activity/ai-activity-actions.js.map +1 -0
- package/src/lib/activity/ai-activity.d.ts +219 -0
- package/src/lib/activity/ai-activity.js +251 -0
- package/src/lib/activity/ai-activity.js.map +1 -0
- package/src/lib/billing/ai-overage-charge.d.ts +102 -0
- package/src/lib/billing/ai-overage-charge.js +258 -0
- package/src/lib/billing/ai-overage-charge.js.map +1 -0
- package/src/lib/billing/ai-overage-close.d.ts +62 -0
- package/src/lib/billing/ai-overage-close.js +129 -0
- package/src/lib/billing/ai-overage-close.js.map +1 -0
- package/src/lib/billing/ai-overage-cutover.d.ts +63 -0
- package/src/lib/billing/ai-overage-cutover.js +114 -0
- package/src/lib/billing/ai-overage-cutover.js.map +1 -0
- package/src/lib/billing/ai-overage-events.d.ts +77 -0
- package/src/lib/billing/ai-overage-events.js +174 -0
- package/src/lib/billing/ai-overage-events.js.map +1 -0
- package/src/lib/billing/ai-overage-gate.d.ts +147 -0
- package/src/lib/billing/ai-overage-gate.js +177 -0
- package/src/lib/billing/ai-overage-gate.js.map +1 -0
- package/src/lib/billing/ai-overage-ledger.d.ts +135 -0
- package/src/lib/billing/ai-overage-ledger.js +278 -0
- package/src/lib/billing/ai-overage-ledger.js.map +1 -0
- package/src/lib/billing/ai-overage-standing-writes.d.ts +110 -0
- package/src/lib/billing/ai-overage-standing-writes.js +205 -0
- package/src/lib/billing/ai-overage-standing-writes.js.map +1 -0
- package/src/lib/billing/ai-overage-standing.d.ts +168 -0
- package/src/lib/billing/ai-overage-standing.js +215 -0
- package/src/lib/billing/ai-overage-standing.js.map +1 -0
- package/src/lib/billing/ai-overage-trigger.d.ts +30 -0
- package/src/lib/billing/ai-overage-trigger.js +76 -0
- package/src/lib/billing/ai-overage-trigger.js.map +1 -0
- package/src/lib/components/ai-allotment-editor.component.d.ts +40 -0
- package/src/lib/components/ai-allotment-editor.component.js +213 -0
- package/src/lib/components/ai-allotment-editor.component.js.map +1 -0
- package/src/lib/components/ai-assist-actions-context.d.ts +13 -0
- package/src/lib/components/ai-assist-actions-context.js +21 -0
- package/src/lib/components/ai-assist-actions-context.js.map +1 -0
- package/src/lib/components/ai-assist-provider.component.d.ts +51 -0
- package/src/lib/components/ai-assist-provider.component.js +519 -0
- package/src/lib/components/ai-assist-provider.component.js.map +1 -0
- package/src/lib/components/ai-brief-dialog.component.d.ts +56 -0
- package/src/lib/components/ai-brief-dialog.component.js +319 -0
- package/src/lib/components/ai-brief-dialog.component.js.map +1 -0
- package/src/lib/components/ai-collaborator-permissions-column.component.d.ts +32 -0
- package/src/lib/components/ai-collaborator-permissions-column.component.js +132 -0
- package/src/lib/components/ai-collaborator-permissions-column.component.js.map +1 -0
- package/src/lib/components/ai-credits-card.component.d.ts +34 -0
- package/src/lib/components/ai-credits-card.component.js +131 -0
- package/src/lib/components/ai-credits-card.component.js.map +1 -0
- package/src/lib/components/ai-credits-columns.component.d.ts +56 -0
- package/src/lib/components/ai-credits-columns.component.js +118 -0
- package/src/lib/components/ai-credits-columns.component.js.map +1 -0
- package/src/lib/components/ai-crm-email-draft.component.d.ts +17 -0
- package/src/lib/components/ai-crm-email-draft.component.js +159 -0
- package/src/lib/components/ai-crm-email-draft.component.js.map +1 -0
- package/src/lib/components/ai-crm-import-mapping.component.d.ts +20 -0
- package/src/lib/components/ai-crm-import-mapping.component.js +167 -0
- package/src/lib/components/ai-crm-import-mapping.component.js.map +1 -0
- package/src/lib/components/ai-crm-record-card.component.d.ts +27 -0
- package/src/lib/components/ai-crm-record-card.component.js +263 -0
- package/src/lib/components/ai-crm-record-card.component.js.map +1 -0
- package/src/lib/components/ai-describe-automation.component.d.ts +31 -0
- package/src/lib/components/ai-describe-automation.component.js +226 -0
- package/src/lib/components/ai-describe-automation.component.js.map +1 -0
- package/src/lib/components/ai-describe-button.component.d.ts +39 -0
- package/src/lib/components/ai-describe-button.component.js +87 -0
- package/src/lib/components/ai-describe-button.component.js.map +1 -0
- package/src/lib/components/ai-describe-page.component.d.ts +26 -0
- package/src/lib/components/ai-describe-page.component.js +18 -0
- package/src/lib/components/ai-describe-page.component.js.map +1 -0
- package/src/lib/components/ai-experiment-cards.component.d.ts +75 -0
- package/src/lib/components/ai-experiment-cards.component.js +573 -0
- package/src/lib/components/ai-experiment-cards.component.js.map +1 -0
- package/src/lib/components/ai-explain-automation.component.d.ts +31 -0
- package/src/lib/components/ai-explain-automation.component.js +265 -0
- package/src/lib/components/ai-explain-automation.component.js.map +1 -0
- package/src/lib/components/ai-insight-dialog.component.d.ts +37 -0
- package/src/lib/components/ai-insight-dialog.component.js +441 -0
- package/src/lib/components/ai-insight-dialog.component.js.map +1 -0
- package/src/lib/components/ai-job-plan.component.d.ts +46 -0
- package/src/lib/components/ai-job-plan.component.js +192 -0
- package/src/lib/components/ai-job-plan.component.js.map +1 -0
- package/src/lib/components/ai-model-selector.component.d.ts +21 -0
- package/src/lib/components/ai-model-selector.component.js +109 -0
- package/src/lib/components/ai-model-selector.component.js.map +1 -0
- package/src/lib/components/ai-page-brief-dialog.component.d.ts +29 -0
- package/src/lib/components/ai-page-brief-dialog.component.js +27 -0
- package/src/lib/components/ai-page-brief-dialog.component.js.map +1 -0
- package/src/lib/components/ai-permissions-on-host.component.d.ts +55 -0
- package/src/lib/components/ai-permissions-on-host.component.js +91 -0
- package/src/lib/components/ai-permissions-on-host.component.js.map +1 -0
- package/src/lib/components/ai-product-copy-card.component.d.ts +25 -0
- package/src/lib/components/ai-product-copy-card.component.js +328 -0
- package/src/lib/components/ai-product-copy-card.component.js.map +1 -0
- package/src/lib/components/ai-product-import-option.component.d.ts +14 -0
- package/src/lib/components/ai-product-import-option.component.js +58 -0
- package/src/lib/components/ai-product-import-option.component.js.map +1 -0
- package/src/lib/components/ai-products-hub-card.component.d.ts +15 -0
- package/src/lib/components/ai-products-hub-card.component.js +418 -0
- package/src/lib/components/ai-products-hub-card.component.js.map +1 -0
- package/src/lib/components/ai-products-proposals.component.d.ts +19 -0
- package/src/lib/components/ai-products-proposals.component.js +547 -0
- package/src/lib/components/ai-products-proposals.component.js.map +1 -0
- package/src/lib/components/ai-save-as-component.component.d.ts +38 -0
- package/src/lib/components/ai-save-as-component.component.js +231 -0
- package/src/lib/components/ai-save-as-component.component.js.map +1 -0
- package/src/lib/components/ai-seo-audit-card.component.d.ts +9 -0
- package/src/lib/components/ai-seo-audit-card.component.js +620 -0
- package/src/lib/components/ai-seo-audit-card.component.js.map +1 -0
- package/src/lib/components/ai-seo-fields-card.component.d.ts +10 -0
- package/src/lib/components/ai-seo-fields-card.component.js +453 -0
- package/src/lib/components/ai-seo-fields-card.component.js.map +1 -0
- package/src/lib/components/ai-site-batch-card.component.d.ts +32 -0
- package/src/lib/components/ai-site-batch-card.component.js +531 -0
- package/src/lib/components/ai-site-batch-card.component.js.map +1 -0
- package/src/lib/components/ai-site-seo-start-card.component.d.ts +26 -0
- package/src/lib/components/ai-site-seo-start-card.component.js +178 -0
- package/src/lib/components/ai-site-seo-start-card.component.js.map +1 -0
- package/src/lib/components/ai-site-start-card.component.d.ts +19 -0
- package/src/lib/components/ai-site-start-card.component.js +348 -0
- package/src/lib/components/ai-site-start-card.component.js.map +1 -0
- package/src/lib/components/ai-template-collection-field.component.d.ts +32 -0
- package/src/lib/components/ai-template-collection-field.component.js +63 -0
- package/src/lib/components/ai-template-collection-field.component.js.map +1 -0
- package/src/lib/components/ai-theme-proposal-card.component.d.ts +6 -0
- package/src/lib/components/ai-theme-proposal-card.component.js +619 -0
- package/src/lib/components/ai-theme-proposal-card.component.js.map +1 -0
- package/src/lib/components/ai-usage-strip.component.d.ts +27 -0
- package/src/lib/components/ai-usage-strip.component.js +145 -0
- package/src/lib/components/ai-usage-strip.component.js.map +1 -0
- package/src/lib/components/assist-edit-canvas.d.ts +169 -0
- package/src/lib/components/assist-edit-canvas.js +636 -0
- package/src/lib/components/assist-edit-canvas.js.map +1 -0
- package/src/lib/components/assist-edit-card.component.d.ts +16 -0
- package/src/lib/components/assist-edit-card.component.js +181 -0
- package/src/lib/components/assist-edit-card.component.js.map +1 -0
- package/src/lib/components/assist-jobs-drawer.component.d.ts +72 -0
- package/src/lib/components/assist-jobs-drawer.component.js +626 -0
- package/src/lib/components/assist-jobs-drawer.component.js.map +1 -0
- package/src/lib/components/assist-panel.component.d.ts +136 -0
- package/src/lib/components/assist-panel.component.js +1099 -0
- package/src/lib/components/assist-panel.component.js.map +1 -0
- package/src/lib/components/assist-signals-page.component.d.ts +3 -0
- package/src/lib/components/assist-signals-page.component.js +1017 -0
- package/src/lib/components/assist-signals-page.component.js.map +1 -0
- package/src/lib/components/besigner-ai-controls.component.d.ts +29 -0
- package/src/lib/components/besigner-ai-controls.component.js +81 -0
- package/src/lib/components/besigner-ai-controls.component.js.map +1 -0
- package/src/lib/components/billing-ai-allotments.component.d.ts +36 -0
- package/src/lib/components/billing-ai-allotments.component.js +486 -0
- package/src/lib/components/billing-ai-allotments.component.js.map +1 -0
- package/src/lib/components/billing-ai-top-users.component.d.ts +44 -0
- package/src/lib/components/billing-ai-top-users.component.js +304 -0
- package/src/lib/components/billing-ai-top-users.component.js.map +1 -0
- package/src/lib/components/billing-assist-overage-card.component.d.ts +69 -0
- package/src/lib/components/billing-assist-overage-card.component.js +437 -0
- package/src/lib/components/billing-assist-overage-card.component.js.map +1 -0
- package/src/lib/components/host-ai-allotments.component.d.ts +40 -0
- package/src/lib/components/host-ai-allotments.component.js +239 -0
- package/src/lib/components/host-ai-allotments.component.js.map +1 -0
- package/src/lib/components/member-ai-allotment-card.component.d.ts +28 -0
- package/src/lib/components/member-ai-allotment-card.component.js +172 -0
- package/src/lib/components/member-ai-allotment-card.component.js.map +1 -0
- package/src/lib/components/member-ai-usage-card.component.d.ts +28 -0
- package/src/lib/components/member-ai-usage-card.component.js +240 -0
- package/src/lib/components/member-ai-usage-card.component.js.map +1 -0
- package/src/lib/components/staff-org-ai-card.component.d.ts +17 -0
- package/src/lib/components/staff-org-ai-card.component.js +739 -0
- package/src/lib/components/staff-org-ai-card.component.js.map +1 -0
- package/src/lib/components/staff-org-usage-ai-columns.component.d.ts +41 -0
- package/src/lib/components/staff-org-usage-ai-columns.component.js +65 -0
- package/src/lib/components/staff-org-usage-ai-columns.component.js.map +1 -0
- package/src/lib/components/staff-orgs-ai-spend-column.component.d.ts +28 -0
- package/src/lib/components/staff-orgs-ai-spend-column.component.js +89 -0
- package/src/lib/components/staff-orgs-ai-spend-column.component.js.map +1 -0
- package/src/lib/components/staff-user-ai-usage-card.component.d.ts +27 -0
- package/src/lib/components/staff-user-ai-usage-card.component.js +183 -0
- package/src/lib/components/staff-user-ai-usage-card.component.js.map +1 -0
- package/src/lib/components/use-ai-allotments.d.ts +49 -0
- package/src/lib/components/use-ai-allotments.js +171 -0
- package/src/lib/components/use-ai-allotments.js.map +1 -0
- package/src/lib/components/use-ai-crm-answer.d.ts +50 -0
- package/src/lib/components/use-ai-crm-answer.js +220 -0
- package/src/lib/components/use-ai-crm-answer.js.map +1 -0
- package/src/lib/components/use-ai-job-run.d.ts +51 -0
- package/src/lib/components/use-ai-job-run.js +176 -0
- package/src/lib/components/use-ai-job-run.js.map +1 -0
- package/src/lib/components/use-ai-model-choice.d.ts +47 -0
- package/src/lib/components/use-ai-model-choice.js +150 -0
- package/src/lib/components/use-ai-model-choice.js.map +1 -0
- package/src/lib/components/use-ai-products-jobs.d.ts +36 -0
- package/src/lib/components/use-ai-products-jobs.js +190 -0
- package/src/lib/components/use-ai-products-jobs.js.map +1 -0
- package/src/lib/components/use-ai-usage-meter.d.ts +11 -0
- package/src/lib/components/use-ai-usage-meter.js +96 -0
- package/src/lib/components/use-ai-usage-meter.js.map +1 -0
- package/src/lib/components/use-org-ai-usage.d.ts +42 -0
- package/src/lib/components/use-org-ai-usage.js +181 -0
- package/src/lib/components/use-org-ai-usage.js.map +1 -0
- package/src/lib/components/use-staff-orgs-ai-spend.d.ts +18 -0
- package/src/lib/components/use-staff-orgs-ai-spend.js +127 -0
- package/src/lib/components/use-staff-orgs-ai-spend.js.map +1 -0
- package/src/lib/constants.d.ts +24 -0
- package/src/lib/constants.js +23 -0
- package/src/lib/constants.js.map +1 -0
- package/src/lib/declarations.d.ts +48 -0
- package/src/lib/declarations.js +129 -0
- package/src/lib/declarations.js.map +1 -0
- package/src/lib/declarations.server.d.ts +35 -0
- package/src/lib/declarations.server.js +138 -0
- package/src/lib/declarations.server.js.map +1 -0
- package/src/lib/insights/ai-figure-readers.d.ts +90 -0
- package/src/lib/insights/ai-figure-readers.js +863 -0
- package/src/lib/insights/ai-figure-readers.js.map +1 -0
- package/src/lib/insights/ai-insight-digest.d.ts +165 -0
- package/src/lib/insights/ai-insight-digest.js +363 -0
- package/src/lib/insights/ai-insight-digest.js.map +1 -0
- package/src/lib/insights/ai-insight-readers.d.ts +71 -0
- package/src/lib/insights/ai-insight-readers.js +141 -0
- package/src/lib/insights/ai-insight-readers.js.map +1 -0
- package/src/lib/jobs/ai-crm-access.d.ts +61 -0
- package/src/lib/jobs/ai-crm-access.js +94 -0
- package/src/lib/jobs/ai-crm-access.js.map +1 -0
- package/src/lib/jobs/ai-email-bindings.d.ts +97 -0
- package/src/lib/jobs/ai-email-bindings.js +147 -0
- package/src/lib/jobs/ai-email-bindings.js.map +1 -0
- package/src/lib/jobs/ai-job-admission.d.ts +82 -0
- package/src/lib/jobs/ai-job-admission.js +45 -0
- package/src/lib/jobs/ai-job-admission.js.map +1 -0
- package/src/lib/jobs/ai-job-budget.d.ts +167 -0
- package/src/lib/jobs/ai-job-budget.js +161 -0
- package/src/lib/jobs/ai-job-budget.js.map +1 -0
- package/src/lib/jobs/ai-job-campaign-step.d.ts +108 -0
- package/src/lib/jobs/ai-job-campaign-step.js +340 -0
- package/src/lib/jobs/ai-job-campaign-step.js.map +1 -0
- package/src/lib/jobs/ai-job-component-checks.d.ts +74 -0
- package/src/lib/jobs/ai-job-component-checks.js +442 -0
- package/src/lib/jobs/ai-job-component-checks.js.map +1 -0
- package/src/lib/jobs/ai-job-component-step.d.ts +75 -0
- package/src/lib/jobs/ai-job-component-step.js +262 -0
- package/src/lib/jobs/ai-job-component-step.js.map +1 -0
- package/src/lib/jobs/ai-job-crm-step.d.ts +192 -0
- package/src/lib/jobs/ai-job-crm-step.js +734 -0
- package/src/lib/jobs/ai-job-crm-step.js.map +1 -0
- package/src/lib/jobs/ai-job-draft-ids.d.ts +62 -0
- package/src/lib/jobs/ai-job-draft-ids.js +132 -0
- package/src/lib/jobs/ai-job-draft-ids.js.map +1 -0
- package/src/lib/jobs/ai-job-drafts.d.ts +304 -0
- package/src/lib/jobs/ai-job-drafts.js +557 -0
- package/src/lib/jobs/ai-job-drafts.js.map +1 -0
- package/src/lib/jobs/ai-job-email-step.d.ts +195 -0
- package/src/lib/jobs/ai-job-email-step.js +547 -0
- package/src/lib/jobs/ai-job-email-step.js.map +1 -0
- package/src/lib/jobs/ai-job-experiment-step.d.ts +105 -0
- package/src/lib/jobs/ai-job-experiment-step.js +445 -0
- package/src/lib/jobs/ai-job-experiment-step.js.map +1 -0
- package/src/lib/jobs/ai-job-form-step.d.ts +250 -0
- package/src/lib/jobs/ai-job-form-step.js +634 -0
- package/src/lib/jobs/ai-job-form-step.js.map +1 -0
- package/src/lib/jobs/ai-job-generation.d.ts +114 -0
- package/src/lib/jobs/ai-job-generation.js +384 -0
- package/src/lib/jobs/ai-job-generation.js.map +1 -0
- package/src/lib/jobs/ai-job-insight-budget.d.ts +41 -0
- package/src/lib/jobs/ai-job-insight-budget.js +46 -0
- package/src/lib/jobs/ai-job-insight-budget.js.map +1 -0
- package/src/lib/jobs/ai-job-insight-step.d.ts +58 -0
- package/src/lib/jobs/ai-job-insight-step.js +350 -0
- package/src/lib/jobs/ai-job-insight-step.js.map +1 -0
- package/src/lib/jobs/ai-job-layout-step.d.ts +103 -0
- package/src/lib/jobs/ai-job-layout-step.js +340 -0
- package/src/lib/jobs/ai-job-layout-step.js.map +1 -0
- package/src/lib/jobs/ai-job-page-budget.d.ts +57 -0
- package/src/lib/jobs/ai-job-page-budget.js +60 -0
- package/src/lib/jobs/ai-job-page-budget.js.map +1 -0
- package/src/lib/jobs/ai-job-page-sections.d.ts +150 -0
- package/src/lib/jobs/ai-job-page-sections.js +450 -0
- package/src/lib/jobs/ai-job-page-sections.js.map +1 -0
- package/src/lib/jobs/ai-job-page-step.d.ts +198 -0
- package/src/lib/jobs/ai-job-page-step.js +667 -0
- package/src/lib/jobs/ai-job-page-step.js.map +1 -0
- package/src/lib/jobs/ai-job-plan-conformance.d.ts +107 -0
- package/src/lib/jobs/ai-job-plan-conformance.js +164 -0
- package/src/lib/jobs/ai-job-plan-conformance.js.map +1 -0
- package/src/lib/jobs/ai-job-plan-step.d.ts +196 -0
- package/src/lib/jobs/ai-job-plan-step.js +458 -0
- package/src/lib/jobs/ai-job-plan-step.js.map +1 -0
- package/src/lib/jobs/ai-job-plugin-drafts.d.ts +76 -0
- package/src/lib/jobs/ai-job-plugin-drafts.js +91 -0
- package/src/lib/jobs/ai-job-plugin-drafts.js.map +1 -0
- package/src/lib/jobs/ai-job-products-step.d.ts +106 -0
- package/src/lib/jobs/ai-job-products-step.js +436 -0
- package/src/lib/jobs/ai-job-products-step.js.map +1 -0
- package/src/lib/jobs/ai-job-seo-budget.d.ts +52 -0
- package/src/lib/jobs/ai-job-seo-budget.js +63 -0
- package/src/lib/jobs/ai-job-seo-budget.js.map +1 -0
- package/src/lib/jobs/ai-job-seo-step.d.ts +145 -0
- package/src/lib/jobs/ai-job-seo-step.js +809 -0
- package/src/lib/jobs/ai-job-seo-step.js.map +1 -0
- package/src/lib/jobs/ai-job-site-step.d.ts +252 -0
- package/src/lib/jobs/ai-job-site-step.js +538 -0
- package/src/lib/jobs/ai-job-site-step.js.map +1 -0
- package/src/lib/jobs/ai-job-template-step.d.ts +105 -0
- package/src/lib/jobs/ai-job-template-step.js +391 -0
- package/src/lib/jobs/ai-job-template-step.js.map +1 -0
- package/src/lib/jobs/ai-job-text-step.d.ts +125 -0
- package/src/lib/jobs/ai-job-text-step.js +128 -0
- package/src/lib/jobs/ai-job-text-step.js.map +1 -0
- package/src/lib/jobs/ai-job-theme-budget.d.ts +38 -0
- package/src/lib/jobs/ai-job-theme-budget.js +43 -0
- package/src/lib/jobs/ai-job-theme-budget.js.map +1 -0
- package/src/lib/jobs/ai-job-theme-step.d.ts +115 -0
- package/src/lib/jobs/ai-job-theme-step.js +337 -0
- package/src/lib/jobs/ai-job-theme-step.js.map +1 -0
- package/src/lib/jobs/ai-job-workflow-step.d.ts +139 -0
- package/src/lib/jobs/ai-job-workflow-step.js +530 -0
- package/src/lib/jobs/ai-job-workflow-step.js.map +1 -0
- package/src/lib/jobs/ai-jobs-beat.d.ts +78 -0
- package/src/lib/jobs/ai-jobs-beat.js +93 -0
- package/src/lib/jobs/ai-jobs-beat.js.map +1 -0
- package/src/lib/jobs/ai-jobs-pause.d.ts +38 -0
- package/src/lib/jobs/ai-jobs-pause.js +45 -0
- package/src/lib/jobs/ai-jobs-pause.js.map +1 -0
- package/src/lib/jobs/ai-jobs.d.ts +558 -0
- package/src/lib/jobs/ai-jobs.js +1410 -0
- package/src/lib/jobs/ai-jobs.js.map +1 -0
- package/src/lib/jobs/ai-media-asset-server.d.ts +34 -0
- package/src/lib/jobs/ai-media-asset-server.js +34 -0
- package/src/lib/jobs/ai-media-asset-server.js.map +1 -0
- package/src/lib/jobs/ai-media-asset.d.ts +59 -0
- package/src/lib/jobs/ai-media-asset.js +86 -0
- package/src/lib/jobs/ai-media-asset.js.map +1 -0
- package/src/lib/jobs/ai-product-image.d.ts +78 -0
- package/src/lib/jobs/ai-product-image.js +113 -0
- package/src/lib/jobs/ai-product-image.js.map +1 -0
- package/src/lib/jobs/ai-theme-brand-inputs.d.ts +125 -0
- package/src/lib/jobs/ai-theme-brand-inputs.js +378 -0
- package/src/lib/jobs/ai-theme-brand-inputs.js.map +1 -0
- package/src/lib/jobs/ai-theme-brand-server.d.ts +33 -0
- package/src/lib/jobs/ai-theme-brand-server.js +33 -0
- package/src/lib/jobs/ai-theme-brand-server.js.map +1 -0
- package/src/lib/jobs/ai-workflow-records.d.ts +84 -0
- package/src/lib/jobs/ai-workflow-records.js +158 -0
- package/src/lib/jobs/ai-workflow-records.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-free-page-built-recording.d.ts +31 -0
- package/src/lib/jobs/fixtures/ai-free-page-built-recording.js +367 -0
- package/src/lib/jobs/fixtures/ai-free-page-built-recording.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-free-page-recording.d.ts +69 -0
- package/src/lib/jobs/fixtures/ai-free-page-recording.js +442 -0
- package/src/lib/jobs/fixtures/ai-free-page-recording.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-page-briefs.d.ts +146 -0
- package/src/lib/jobs/fixtures/ai-page-briefs.js +1850 -0
- package/src/lib/jobs/fixtures/ai-page-briefs.js.map +1 -0
- package/src/lib/model/ai-allotments.d.ts +208 -0
- package/src/lib/model/ai-allotments.js +269 -0
- package/src/lib/model/ai-allotments.js.map +1 -0
- package/src/lib/model/ai-automation-draft.d.ts +74 -0
- package/src/lib/model/ai-automation-draft.js +310 -0
- package/src/lib/model/ai-automation-draft.js.map +1 -0
- package/src/lib/model/ai-automation-outline.d.ts +50 -0
- package/src/lib/model/ai-automation-outline.js +262 -0
- package/src/lib/model/ai-automation-outline.js.map +1 -0
- package/src/lib/model/ai-build-plan.d.ts +197 -0
- package/src/lib/model/ai-build-plan.js +438 -0
- package/src/lib/model/ai-build-plan.js.map +1 -0
- package/src/lib/model/ai-crm.d.ts +237 -0
- package/src/lib/model/ai-crm.js +225 -0
- package/src/lib/model/ai-crm.js.map +1 -0
- package/src/lib/model/ai-experiment-proposal.d.ts +68 -0
- package/src/lib/model/ai-experiment-proposal.js +146 -0
- package/src/lib/model/ai-experiment-proposal.js.map +1 -0
- package/src/lib/model/ai-experiment.d.ts +309 -0
- package/src/lib/model/ai-experiment.js +395 -0
- package/src/lib/model/ai-experiment.js.map +1 -0
- package/src/lib/model/ai-insight.d.ts +178 -0
- package/src/lib/model/ai-insight.js +201 -0
- package/src/lib/model/ai-insight.js.map +1 -0
- package/src/lib/model/ai-jobs.types.d.ts +412 -0
- package/src/lib/model/ai-jobs.types.js +42 -0
- package/src/lib/model/ai-jobs.types.js.map +1 -0
- package/src/lib/model/ai-page-job.d.ts +92 -0
- package/src/lib/model/ai-page-job.js +159 -0
- package/src/lib/model/ai-page-job.js.map +1 -0
- package/src/lib/model/ai-permissions.d.ts +49 -0
- package/src/lib/model/ai-permissions.js +97 -0
- package/src/lib/model/ai-permissions.js.map +1 -0
- package/src/lib/model/ai-plan-capabilities.d.ts +139 -0
- package/src/lib/model/ai-plan-capabilities.js +149 -0
- package/src/lib/model/ai-plan-capabilities.js.map +1 -0
- package/src/lib/model/ai-products.d.ts +251 -0
- package/src/lib/model/ai-products.js +253 -0
- package/src/lib/model/ai-products.js.map +1 -0
- package/src/lib/model/ai-seo.d.ts +208 -0
- package/src/lib/model/ai-seo.js +150 -0
- package/src/lib/model/ai-seo.js.map +1 -0
- package/src/lib/model/ai-site-inventory.d.ts +164 -0
- package/src/lib/model/ai-site-inventory.js +145 -0
- package/src/lib/model/ai-site-inventory.js.map +1 -0
- package/src/lib/model/ai-site-job.d.ts +247 -0
- package/src/lib/model/ai-site-job.js +314 -0
- package/src/lib/model/ai-site-job.js.map +1 -0
- package/src/lib/model/ai-site-start-seo.d.ts +103 -0
- package/src/lib/model/ai-site-start-seo.js +154 -0
- package/src/lib/model/ai-site-start-seo.js.map +1 -0
- package/src/lib/model/ai-site-start.d.ts +122 -0
- package/src/lib/model/ai-site-start.js +159 -0
- package/src/lib/model/ai-site-start.js.map +1 -0
- package/src/lib/model/ai-site-switch.d.ts +56 -0
- package/src/lib/model/ai-site-switch.js +73 -0
- package/src/lib/model/ai-site-switch.js.map +1 -0
- package/src/lib/model/ai-storefront-claims.d.ts +70 -0
- package/src/lib/model/ai-storefront-claims.js +130 -0
- package/src/lib/model/ai-storefront-claims.js.map +1 -0
- package/src/lib/model/ai-template-subjects.d.ts +82 -0
- package/src/lib/model/ai-template-subjects.js +136 -0
- package/src/lib/model/ai-template-subjects.js.map +1 -0
- package/src/lib/model/ai-theme-proposal.d.ts +210 -0
- package/src/lib/model/ai-theme-proposal.js +506 -0
- package/src/lib/model/ai-theme-proposal.js.map +1 -0
- package/src/lib/model/ai-tokens.d.ts +96 -0
- package/src/lib/model/ai-tokens.js +117 -0
- package/src/lib/model/ai-tokens.js.map +1 -0
- package/src/lib/model/ai-usage-by-user.d.ts +120 -0
- package/src/lib/model/ai-usage-by-user.js +139 -0
- package/src/lib/model/ai-usage-by-user.js.map +1 -0
- package/src/lib/model/ai-workflow-job.d.ts +111 -0
- package/src/lib/model/ai-workflow-job.js +183 -0
- package/src/lib/model/ai-workflow-job.js.map +1 -0
- package/src/lib/model/assist-edit.d.ts +261 -0
- package/src/lib/model/assist-edit.js +175 -0
- package/src/lib/model/assist-edit.js.map +1 -0
- package/src/lib/plugin-config.d.ts +33 -0
- package/src/lib/plugin-config.js +118 -0
- package/src/lib/plugin-config.js.map +1 -0
- package/src/lib/plugin.d.ts +30 -0
- package/src/lib/plugin.js +525 -0
- package/src/lib/plugin.js.map +1 -0
- package/src/lib/providers/anthropic.d.ts +84 -0
- package/src/lib/providers/anthropic.js +432 -0
- package/src/lib/providers/anthropic.js.map +1 -0
- package/src/lib/providers/catalog.d.ts +197 -0
- package/src/lib/providers/catalog.js +312 -0
- package/src/lib/providers/catalog.js.map +1 -0
- package/src/lib/providers/contract.d.ts +464 -0
- package/src/lib/providers/contract.js +274 -0
- package/src/lib/providers/contract.js.map +1 -0
- package/src/lib/providers/model-choice.d.ts +119 -0
- package/src/lib/providers/model-choice.js +416 -0
- package/src/lib/providers/model-choice.js.map +1 -0
- package/src/lib/providers/openai-compatible.d.ts +58 -0
- package/src/lib/providers/openai-compatible.js +361 -0
- package/src/lib/providers/openai-compatible.js.map +1 -0
- package/src/lib/providers/registry.d.ts +49 -0
- package/src/lib/providers/registry.js +71 -0
- package/src/lib/providers/registry.js.map +1 -0
- package/src/lib/providers/routing.d.ts +98 -0
- package/src/lib/providers/routing.js +450 -0
- package/src/lib/providers/routing.js.map +1 -0
- package/src/lib/runtime/ai-abuse-guards.d.ts +113 -0
- package/src/lib/runtime/ai-abuse-guards.js +148 -0
- package/src/lib/runtime/ai-abuse-guards.js.map +1 -0
- package/src/lib/runtime/ai-component-bindings.d.ts +67 -0
- package/src/lib/runtime/ai-component-bindings.js +106 -0
- package/src/lib/runtime/ai-component-bindings.js.map +1 -0
- package/src/lib/runtime/ai-device-audit.d.ts +109 -0
- package/src/lib/runtime/ai-device-audit.js +97 -0
- package/src/lib/runtime/ai-device-audit.js.map +1 -0
- package/src/lib/runtime/ai-doctrine-validators.d.ts +720 -0
- package/src/lib/runtime/ai-doctrine-validators.js +3133 -0
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -0
- package/src/lib/runtime/ai-doctrine.d.ts +372 -0
- package/src/lib/runtime/ai-doctrine.js +944 -0
- package/src/lib/runtime/ai-doctrine.js.map +1 -0
- package/src/lib/runtime/ai-eval-live.d.ts +321 -0
- package/src/lib/runtime/ai-eval-live.js +1257 -0
- package/src/lib/runtime/ai-eval-live.js.map +1 -0
- package/src/lib/runtime/ai-eval-memory-firestore.d.ts +51 -0
- package/src/lib/runtime/ai-eval-memory-firestore.js +183 -0
- package/src/lib/runtime/ai-eval-memory-firestore.js.map +1 -0
- package/src/lib/runtime/ai-eval.d.ts +441 -0
- package/src/lib/runtime/ai-eval.js +1151 -0
- package/src/lib/runtime/ai-eval.js.map +1 -0
- package/src/lib/runtime/ai-gate.d.ts +130 -0
- package/src/lib/runtime/ai-gate.js +272 -0
- package/src/lib/runtime/ai-gate.js.map +1 -0
- package/src/lib/runtime/ai-generation-bounds.d.ts +24 -0
- package/src/lib/runtime/ai-generation-bounds.js +23 -0
- package/src/lib/runtime/ai-generation-bounds.js.map +1 -0
- package/src/lib/runtime/ai-insight-check.d.ts +76 -0
- package/src/lib/runtime/ai-insight-check.js +281 -0
- package/src/lib/runtime/ai-insight-check.js.map +1 -0
- package/src/lib/runtime/ai-node-tree.d.ts +181 -0
- package/src/lib/runtime/ai-node-tree.js +931 -0
- package/src/lib/runtime/ai-node-tree.js.map +1 -0
- package/src/lib/runtime/ai-page-links.d.ts +79 -0
- package/src/lib/runtime/ai-page-links.js +135 -0
- package/src/lib/runtime/ai-page-links.js.map +1 -0
- package/src/lib/runtime/ai-palette.d.ts +252 -0
- package/src/lib/runtime/ai-palette.generated.d.ts +34 -0
- package/src/lib/runtime/ai-palette.generated.js +5263 -0
- package/src/lib/runtime/ai-palette.generated.js.map +1 -0
- package/src/lib/runtime/ai-palette.js +187 -0
- package/src/lib/runtime/ai-palette.js.map +1 -0
- package/src/lib/runtime/ai-products-generation.d.ts +107 -0
- package/src/lib/runtime/ai-products-generation.js +250 -0
- package/src/lib/runtime/ai-products-generation.js.map +1 -0
- package/src/lib/runtime/ai-repeated-items.d.ts +95 -0
- package/src/lib/runtime/ai-repeated-items.js +397 -0
- package/src/lib/runtime/ai-repeated-items.js.map +1 -0
- package/src/lib/runtime/ai-runtime.d.ts +148 -0
- package/src/lib/runtime/ai-runtime.js +237 -0
- package/src/lib/runtime/ai-runtime.js.map +1 -0
- package/src/lib/runtime/ai-template-examples.d.ts +71 -0
- package/src/lib/runtime/ai-template-examples.js +135 -0
- package/src/lib/runtime/ai-template-examples.js.map +1 -0
- package/src/lib/runtime/assist-answer-cache.d.ts +80 -0
- package/src/lib/runtime/assist-answer-cache.js +184 -0
- package/src/lib/runtime/assist-answer-cache.js.map +1 -0
- package/src/lib/runtime/seo-audit.d.ts +93 -0
- package/src/lib/runtime/seo-audit.js +260 -0
- package/src/lib/runtime/seo-audit.js.map +1 -0
- package/src/lib/runtime/seo-content-fixes.d.ts +41 -0
- package/src/lib/runtime/seo-content-fixes.js +168 -0
- package/src/lib/runtime/seo-content-fixes.js.map +1 -0
- package/src/lib/runtime/seo-fields.d.ts +152 -0
- package/src/lib/runtime/seo-fields.js +215 -0
- package/src/lib/runtime/seo-fields.js.map +1 -0
- package/src/lib/runtime/seo-page-facts.d.ts +61 -0
- package/src/lib/runtime/seo-page-facts.js +150 -0
- package/src/lib/runtime/seo-page-facts.js.map +1 -0
- package/src/lib/runtime/site-inventory.d.ts +63 -0
- package/src/lib/runtime/site-inventory.js +230 -0
- package/src/lib/runtime/site-inventory.js.map +1 -0
- package/src/lib/server/ai-admin-org.d.ts +33 -0
- package/src/lib/server/ai-admin-org.js +381 -0
- package/src/lib/server/ai-admin-org.js.map +1 -0
- package/src/lib/server/ai-admin-orgs-spend.d.ts +40 -0
- package/src/lib/server/ai-admin-orgs-spend.js +129 -0
- package/src/lib/server/ai-admin-orgs-spend.js.map +1 -0
- package/src/lib/server/ai-admin-overage.d.ts +19 -0
- package/src/lib/server/ai-admin-overage.js +246 -0
- package/src/lib/server/ai-admin-overage.js.map +1 -0
- package/src/lib/server/ai-admin-signals.d.ts +34 -0
- package/src/lib/server/ai-admin-signals.js +330 -0
- package/src/lib/server/ai-admin-signals.js.map +1 -0
- package/src/lib/server/ai-admin-user.d.ts +27 -0
- package/src/lib/server/ai-admin-user.js +128 -0
- package/src/lib/server/ai-admin-user.js.map +1 -0
- package/src/lib/server/ai-allotments.d.ts +27 -0
- package/src/lib/server/ai-allotments.js +544 -0
- package/src/lib/server/ai-allotments.js.map +1 -0
- package/src/lib/server/ai-assist-prompts.d.ts +102 -0
- package/src/lib/server/ai-assist-prompts.js +207 -0
- package/src/lib/server/ai-assist-prompts.js.map +1 -0
- package/src/lib/server/ai-assist.d.ts +119 -0
- package/src/lib/server/ai-assist.js +517 -0
- package/src/lib/server/ai-assist.js.map +1 -0
- package/src/lib/server/ai-crm-answer.d.ts +30 -0
- package/src/lib/server/ai-crm-answer.js +98 -0
- package/src/lib/server/ai-crm-answer.js.map +1 -0
- package/src/lib/server/ai-generate-component.d.ts +111 -0
- package/src/lib/server/ai-generate-component.js +643 -0
- package/src/lib/server/ai-generate-component.js.map +1 -0
- package/src/lib/server/ai-host-permissions.d.ts +46 -0
- package/src/lib/server/ai-host-permissions.js +204 -0
- package/src/lib/server/ai-host-permissions.js.map +1 -0
- package/src/lib/server/ai-insight-answer.d.ts +30 -0
- package/src/lib/server/ai-insight-answer.js +83 -0
- package/src/lib/server/ai-insight-answer.js.map +1 -0
- package/src/lib/server/ai-insight-digest-route.d.ts +30 -0
- package/src/lib/server/ai-insight-digest-route.js +156 -0
- package/src/lib/server/ai-insight-digest-route.js.map +1 -0
- package/src/lib/server/ai-jobs-batch.d.ts +48 -0
- package/src/lib/server/ai-jobs-batch.js +306 -0
- package/src/lib/server/ai-jobs-batch.js.map +1 -0
- package/src/lib/server/ai-jobs-beat-route.d.ts +36 -0
- package/src/lib/server/ai-jobs-beat-route.js +86 -0
- package/src/lib/server/ai-jobs-beat-route.js.map +1 -0
- package/src/lib/server/ai-jobs-cancel.d.ts +30 -0
- package/src/lib/server/ai-jobs-cancel.js +74 -0
- package/src/lib/server/ai-jobs-cancel.js.map +1 -0
- package/src/lib/server/ai-jobs-events-route.d.ts +31 -0
- package/src/lib/server/ai-jobs-events-route.js +51 -0
- package/src/lib/server/ai-jobs-events-route.js.map +1 -0
- package/src/lib/server/ai-jobs-events.d.ts +55 -0
- package/src/lib/server/ai-jobs-events.js +144 -0
- package/src/lib/server/ai-jobs-events.js.map +1 -0
- package/src/lib/server/ai-jobs-gate.d.ts +47 -0
- package/src/lib/server/ai-jobs-gate.js +106 -0
- package/src/lib/server/ai-jobs-gate.js.map +1 -0
- package/src/lib/server/ai-jobs-resume.d.ts +23 -0
- package/src/lib/server/ai-jobs-resume.js +209 -0
- package/src/lib/server/ai-jobs-resume.js.map +1 -0
- package/src/lib/server/ai-jobs-route.d.ts +32 -0
- package/src/lib/server/ai-jobs-route.js +307 -0
- package/src/lib/server/ai-jobs-route.js.map +1 -0
- package/src/lib/server/ai-models.d.ts +18 -0
- package/src/lib/server/ai-models.js +134 -0
- package/src/lib/server/ai-models.js.map +1 -0
- package/src/lib/server/ai-seo-apply.d.ts +53 -0
- package/src/lib/server/ai-seo-apply.js +294 -0
- package/src/lib/server/ai-seo-apply.js.map +1 -0
- package/src/lib/server/ai-usage.d.ts +18 -0
- package/src/lib/server/ai-usage.js +220 -0
- package/src/lib/server/ai-usage.js.map +1 -0
- package/src/lib/server/assist-chat.d.ts +234 -0
- package/src/lib/server/assist-chat.js +1446 -0
- package/src/lib/server/assist-chat.js.map +1 -0
- package/src/lib/server/assist-edit-applied.d.ts +22 -0
- package/src/lib/server/assist-edit-applied.js +239 -0
- package/src/lib/server/assist-edit-applied.js.map +1 -0
- package/src/lib/server/assist-edit.d.ts +124 -0
- package/src/lib/server/assist-edit.js +1000 -0
- package/src/lib/server/assist-edit.js.map +1 -0
- package/src/lib/server/assist-feedback.d.ts +31 -0
- package/src/lib/server/assist-feedback.js +115 -0
- package/src/lib/server/assist-feedback.js.map +1 -0
- package/src/lib/server/assist-view-context.d.ts +343 -0
- package/src/lib/server/assist-view-context.js +690 -0
- package/src/lib/server/assist-view-context.js.map +1 -0
- package/src/lib/server/billing-credits.d.ts +59 -0
- package/src/lib/server/billing-credits.js +146 -0
- package/src/lib/server/billing-credits.js.map +1 -0
- package/src/lib/server/billing-overage.d.ts +19 -0
- package/src/lib/server/billing-overage.js +337 -0
- package/src/lib/server/billing-overage.js.map +1 -0
- package/src/lib/server.d.ts +32 -0
- package/src/lib/server.js +265 -0
- package/src/lib/server.js.map +1 -0
- package/src/lib/subprocessors.d.ts +18 -0
- package/src/lib/subprocessors.js +52 -0
- package/src/lib/subprocessors.js.map +1 -0
- package/src/lib/tools/ai-component-tool.d.ts +99 -0
- package/src/lib/tools/ai-component-tool.js +369 -0
- package/src/lib/tools/ai-component-tool.js.map +1 -0
- package/src/lib/tools/ai-crm-tool.d.ts +106 -0
- package/src/lib/tools/ai-crm-tool.js +488 -0
- package/src/lib/tools/ai-crm-tool.js.map +1 -0
- package/src/lib/tools/ai-duplicate-resource-tool.d.ts +71 -0
- package/src/lib/tools/ai-duplicate-resource-tool.js +164 -0
- package/src/lib/tools/ai-duplicate-resource-tool.js.map +1 -0
- package/src/lib/tools/ai-experiment-tool.d.ts +40 -0
- package/src/lib/tools/ai-experiment-tool.js +155 -0
- package/src/lib/tools/ai-experiment-tool.js.map +1 -0
- package/src/lib/tools/ai-insight-tool.d.ts +46 -0
- package/src/lib/tools/ai-insight-tool.js +200 -0
- package/src/lib/tools/ai-insight-tool.js.map +1 -0
- package/src/lib/tools/ai-inventory-lookup-tool.d.ts +86 -0
- package/src/lib/tools/ai-inventory-lookup-tool.js +164 -0
- package/src/lib/tools/ai-inventory-lookup-tool.js.map +1 -0
- package/src/lib/tools/ai-products-tool.d.ts +65 -0
- package/src/lib/tools/ai-products-tool.js +644 -0
- package/src/lib/tools/ai-products-tool.js.map +1 -0
- package/src/lib/tools/ai-seo-tool.d.ts +162 -0
- package/src/lib/tools/ai-seo-tool.js +546 -0
- package/src/lib/tools/ai-seo-tool.js.map +1 -0
- package/src/lib/tools/ai-theme-tool.d.ts +101 -0
- package/src/lib/tools/ai-theme-tool.js +562 -0
- package/src/lib/tools/ai-theme-tool.js.map +1 -0
- package/src/lib/tools/ai-workflow-tool.d.ts +166 -0
- package/src/lib/tools/ai-workflow-tool.js +860 -0
- package/src/lib/tools/ai-workflow-tool.js.map +1 -0
- package/src/lib/usage/ai-allotment-alerts.d.ts +72 -0
- package/src/lib/usage/ai-allotment-alerts.js +157 -0
- package/src/lib/usage/ai-allotment-alerts.js.map +1 -0
- package/src/lib/usage/ai-allotments.d.ts +125 -0
- package/src/lib/usage/ai-allotments.js +239 -0
- package/src/lib/usage/ai-allotments.js.map +1 -0
- package/src/lib/usage/ai-model-sample.d.ts +36 -0
- package/src/lib/usage/ai-model-sample.js +68 -0
- package/src/lib/usage/ai-model-sample.js.map +1 -0
- package/src/lib/usage/ai-usage-alerts.d.ts +55 -0
- package/src/lib/usage/ai-usage-alerts.js +114 -0
- package/src/lib/usage/ai-usage-alerts.js.map +1 -0
- package/src/lib/usage/ai-usage-by-user.d.ts +161 -0
- package/src/lib/usage/ai-usage-by-user.js +211 -0
- package/src/lib/usage/ai-usage-by-user.js.map +1 -0
- package/src/lib/usage/ai-usage-eraser.d.ts +29 -0
- package/src/lib/usage/ai-usage-eraser.js +40 -0
- package/src/lib/usage/ai-usage-eraser.js.map +1 -0
- package/src/lib/usage/ai-usage-meter.d.ts +39 -0
- package/src/lib/usage/ai-usage-meter.js +85 -0
- package/src/lib/usage/ai-usage-meter.js.map +1 -0
- package/src/lib/usage/ai-usage-wire.d.ts +184 -0
- package/src/lib/usage/ai-usage-wire.js +98 -0
- package/src/lib/usage/ai-usage-wire.js.map +1 -0
- package/src/lib/usage/assist-ceiling.d.ts +87 -0
- package/src/lib/usage/assist-ceiling.js +110 -0
- package/src/lib/usage/assist-ceiling.js.map +1 -0
- package/src/lib/usage/assist-free-taste.d.ts +225 -0
- package/src/lib/usage/assist-free-taste.js +308 -0
- package/src/lib/usage/assist-free-taste.js.map +1 -0
- package/src/lib/usage/assist-refusals.d.ts +69 -0
- package/src/lib/usage/assist-refusals.js +79 -0
- package/src/lib/usage/assist-refusals.js.map +1 -0
- package/src/lib/usage/assist-signal-mining.d.ts +375 -0
- package/src/lib/usage/assist-signal-mining.js +344 -0
- package/src/lib/usage/assist-signal-mining.js.map +1 -0
- package/src/lib/usage/assist-usage.d.ts +571 -0
- package/src/lib/usage/assist-usage.js +978 -0
- package/src/lib/usage/assist-usage.js.map +1 -0
- package/src/lib/usage/staff-org-ai.d.ts +242 -0
- package/src/lib/usage/staff-org-ai.js +173 -0
- package/src/lib/usage/staff-org-ai.js.map +1 -0
- package/src/lib/usage/staff-org-usage-ai.d.ts +83 -0
- package/src/lib/usage/staff-org-usage-ai.js +59 -0
- package/src/lib/usage/staff-org-usage-ai.js.map +1 -0
- package/src/lib/usage/staff-orgs-ai-spend.d.ts +83 -0
- package/src/lib/usage/staff-orgs-ai-spend.js +114 -0
- package/src/lib/usage/staff-orgs-ai-spend.js.map +1 -0
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
17
|
+
export const AI_EXPERIMENT_TASKS = [
|
|
18
|
+
'variants',
|
|
19
|
+
'explain'
|
|
20
|
+
];
|
|
21
|
+
/** The task a job's inputs name, or `null` when they name none this step runs. */ export function aiExperimentTask(inputs) {
|
|
22
|
+
const value = inputs == null ? void 0 : inputs['task'];
|
|
23
|
+
return typeof value === 'string' && AI_EXPERIMENT_TASKS.includes(value) ? value : null;
|
|
24
|
+
}
|
|
25
|
+
export const AI_EXPERIMENT_TARGETS = [
|
|
26
|
+
'screen',
|
|
27
|
+
'section',
|
|
28
|
+
'email'
|
|
29
|
+
];
|
|
30
|
+
/** The target a job's inputs name, or `null` when they name none. */ export function aiExperimentTarget(inputs) {
|
|
31
|
+
const value = inputs == null ? void 0 : inputs['target'];
|
|
32
|
+
return typeof value === 'string' && AI_EXPERIMENT_TARGETS.includes(value) ? value : null;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* How many variants a proposal may hold, the cap the A/B testing card stores
|
|
36
|
+
* and the page runner buckets against. Proposing a fifth would be proposing
|
|
37
|
+
* something nothing can save.
|
|
38
|
+
*/ export const AI_EXPERIMENT_MAX_VARIANTS = 4;
|
|
39
|
+
/** The fewest a test can be: a control and one challenger. */ export const AI_EXPERIMENT_MIN_VARIANTS = 2;
|
|
40
|
+
/** How long a variant's name may run, as the card's own field holds one. */ export const AI_EXPERIMENT_NAME_MAX_CHARS = 60;
|
|
41
|
+
/** How long one line of a variant's copy may run. */ export const AI_EXPERIMENT_LINE_MAX_CHARS = 200;
|
|
42
|
+
/** How long a variant's body may run. */ export const AI_EXPERIMENT_BODY_MAX_CHARS = 1200;
|
|
43
|
+
/** How long the sentence saying what a variant changes may run. */ export const AI_EXPERIMENT_RATIONALE_MAX_CHARS = 200;
|
|
44
|
+
/** How much of what is under test is sent. Past this it is a document, not a subject. */ export const AI_EXPERIMENT_SUBJECT_MAX_CHARS = 4000;
|
|
45
|
+
// ── The result ────────────────────────────────────────────────────────────
|
|
46
|
+
/**
|
|
47
|
+
* The reader marketing registers for its A/B test results, and the columns it
|
|
48
|
+
* publishes. Named here rather than imported because the owner is another
|
|
49
|
+
* plugin: a table that does not carry these columns is one this step cannot
|
|
50
|
+
* read, and it says so instead of guessing.
|
|
51
|
+
*/ export const AI_EXPERIMENT_FIGURE_READER = 'marketing.experiments';
|
|
52
|
+
export const AI_EXPERIMENT_FIGURE_COLUMNS = {
|
|
53
|
+
test: 'test',
|
|
54
|
+
variant: 'variant',
|
|
55
|
+
exposures: 'shown',
|
|
56
|
+
conversions: 'conversions',
|
|
57
|
+
rate: 'rate',
|
|
58
|
+
lift: 'lift',
|
|
59
|
+
confidence: 'confidence'
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* The exposures each arm needs before a comparison is read at all. Below this
|
|
63
|
+
* a test is early rather than inconclusive, which is a different thing to tell
|
|
64
|
+
* someone: one says wait, the other says stop.
|
|
65
|
+
*/ export const AI_EXPERIMENT_MIN_EXPOSURES = 200;
|
|
66
|
+
/**
|
|
67
|
+
* The successes and failures each arm needs, at the pooled rate, for the
|
|
68
|
+
* normal approximation behind a two-proportion test to be worth reading. The
|
|
69
|
+
* textbook condition, and the reason a test with plenty of traffic and almost
|
|
70
|
+
* no conversions is still too early to call.
|
|
71
|
+
*/ export const AI_EXPERIMENT_MIN_EXPECTED = 5;
|
|
72
|
+
/**
|
|
73
|
+
* The confidence a single comparison is called at: the same 95% the A/B
|
|
74
|
+
* testing card's auto-winner offers, so the explanation and the card agree
|
|
75
|
+
* about what "confident" means when there is one challenger.
|
|
76
|
+
*/ export const AI_EXPERIMENT_CONFIDENCE = 0.95;
|
|
77
|
+
/**
|
|
78
|
+
* The confidence ONE comparison must clear when a test has `challengers` of
|
|
79
|
+
* them (Šidák): testing three challengers at 95% each would call a winner by
|
|
80
|
+
* chance about one time in seven, so the bar rises with the number of chances
|
|
81
|
+
* taken. With one challenger this is exactly `AI_EXPERIMENT_CONFIDENCE`.
|
|
82
|
+
*/ export function aiExperimentConfidenceThreshold(challengers) {
|
|
83
|
+
const count = Math.max(1, Math.floor(challengers));
|
|
84
|
+
return AI_EXPERIMENT_CONFIDENCE ** (1 / count);
|
|
85
|
+
}
|
|
86
|
+
/** The verdicts that name an arm, and so let an explanation name one. */ export const AI_EXPERIMENT_DECIDED_VERDICTS = [
|
|
87
|
+
'winner',
|
|
88
|
+
'control'
|
|
89
|
+
];
|
|
90
|
+
/** Whether a verdict lets an explanation name a winner. */ export function aiExperimentVerdictNamesWinner(verdict) {
|
|
91
|
+
return AI_EXPERIMENT_DECIDED_VERDICTS.includes(verdict);
|
|
92
|
+
}
|
|
93
|
+
const num = (value)=>typeof value === 'number' && Number.isFinite(value) ? value : 0;
|
|
94
|
+
const maybe = (value)=>typeof value === 'number' && Number.isFinite(value) ? value : null;
|
|
95
|
+
const text = (value)=>typeof value === 'string' ? value.trim() : '';
|
|
96
|
+
/** Whether a table is the A/B test results, by the columns it publishes. */ export function isAiExperimentTable(table) {
|
|
97
|
+
var _ref;
|
|
98
|
+
const keys = new Set(((_ref = table == null ? void 0 : table.columns) != null ? _ref : []).map((column)=>column.key));
|
|
99
|
+
return Object.values(AI_EXPERIMENT_FIGURE_COLUMNS).every((key)=>keys.has(key));
|
|
100
|
+
}
|
|
101
|
+
/** The tests a results table holds, by the name it labels each with. */ export function aiExperimentTestNames(table) {
|
|
102
|
+
var _ref;
|
|
103
|
+
if (!isAiExperimentTable(table)) return [];
|
|
104
|
+
const names = [];
|
|
105
|
+
for (const row of (_ref = table == null ? void 0 : table.rows) != null ? _ref : []){
|
|
106
|
+
const name = text(row[AI_EXPERIMENT_FIGURE_COLUMNS.test]);
|
|
107
|
+
if (name && !names.includes(name)) names.push(name);
|
|
108
|
+
}
|
|
109
|
+
return names;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* One test's arms, in the order the table gave them — the control first, as
|
|
113
|
+
* the reader's own note says it publishes them. An empty list where the table
|
|
114
|
+
* is not the results, or holds no such test.
|
|
115
|
+
*/ export function readAiExperimentArms(table, test) {
|
|
116
|
+
var _ref;
|
|
117
|
+
if (!isAiExperimentTable(table)) return [];
|
|
118
|
+
const wanted = test.trim();
|
|
119
|
+
const arms = [];
|
|
120
|
+
for (const row of (_ref = table == null ? void 0 : table.rows) != null ? _ref : []){
|
|
121
|
+
if (text(row[AI_EXPERIMENT_FIGURE_COLUMNS.test]) !== wanted) continue;
|
|
122
|
+
const id = text(row[AI_EXPERIMENT_FIGURE_COLUMNS.variant]);
|
|
123
|
+
if (!id) continue;
|
|
124
|
+
arms.push({
|
|
125
|
+
id,
|
|
126
|
+
exposures: Math.max(0, num(row[AI_EXPERIMENT_FIGURE_COLUMNS.exposures])),
|
|
127
|
+
conversions: Math.max(0, num(row[AI_EXPERIMENT_FIGURE_COLUMNS.conversions])),
|
|
128
|
+
rate: maybe(row[AI_EXPERIMENT_FIGURE_COLUMNS.rate]),
|
|
129
|
+
lift: maybe(row[AI_EXPERIMENT_FIGURE_COLUMNS.lift]),
|
|
130
|
+
confidence: maybe(row[AI_EXPERIMENT_FIGURE_COLUMNS.confidence])
|
|
131
|
+
});
|
|
132
|
+
if (arms.length === AI_EXPERIMENT_MAX_VARIANTS) break;
|
|
133
|
+
}
|
|
134
|
+
return arms;
|
|
135
|
+
}
|
|
136
|
+
/** What an arm is told to wait for, in words with no figure in them. */ export const AI_EXPERIMENT_REASON_EXPOSURES = 'at least one variant has not been shown to enough visitors yet';
|
|
137
|
+
export const AI_EXPERIMENT_REASON_CONVERSIONS = 'too little has converted yet for the comparison to mean anything';
|
|
138
|
+
export const AI_EXPERIMENT_REASON_SPREAD = 'no variant is far enough from the first one for the difference to be more than chance';
|
|
139
|
+
export const AI_EXPERIMENT_REASON_UNREADABLE = 'these figures do not describe a test with a first variant and something to compare against it';
|
|
140
|
+
/**
|
|
141
|
+
* What a test's figures support (AGL-2914), decided before a model is asked
|
|
142
|
+
* anything.
|
|
143
|
+
*
|
|
144
|
+
* The first arm is the control, as the results reader publishes it. A verdict
|
|
145
|
+
* is reached in this order, and the first answer wins:
|
|
146
|
+
*
|
|
147
|
+
* 1. fewer than two arms — `unreadable`;
|
|
148
|
+
* 2. an arm under `AI_EXPERIMENT_MIN_EXPOSURES`, or one whose expected
|
|
149
|
+
* successes or failures at the pooled rate are under
|
|
150
|
+
* `AI_EXPERIMENT_MIN_EXPECTED` — `too-early`. The second is what stops a
|
|
151
|
+
* test with a hundred thousand visitors and four conversions reading as a
|
|
152
|
+
* decided one;
|
|
153
|
+
* 3. a challenger whose confidence clears the multiplicity-corrected
|
|
154
|
+
* threshold — `winner`, the most confident of them;
|
|
155
|
+
* 4. every challenger below `1 - threshold`, which is the same statement made
|
|
156
|
+
* about the control — `control`;
|
|
157
|
+
* 5. anything else — `inconclusive`.
|
|
158
|
+
*
|
|
159
|
+
* A challenger the owner could not compare (`confidence` is `null`) can never
|
|
160
|
+
* win, and leaves the control undecided too: not knowing is not evidence.
|
|
161
|
+
*/ export function readAiExperimentResult(test, arms) {
|
|
162
|
+
const kept = arms.slice(0, AI_EXPERIMENT_MAX_VARIANTS);
|
|
163
|
+
const challengers = Math.max(1, kept.length - 1);
|
|
164
|
+
const threshold = aiExperimentConfidenceThreshold(challengers) * 100;
|
|
165
|
+
const reading = (verdict, winnerId, reasons)=>({
|
|
166
|
+
verdict,
|
|
167
|
+
test: test.trim(),
|
|
168
|
+
arms: kept,
|
|
169
|
+
winnerId,
|
|
170
|
+
threshold: Math.round(threshold * 100) / 100,
|
|
171
|
+
reasons
|
|
172
|
+
});
|
|
173
|
+
if (kept.length < AI_EXPERIMENT_MIN_VARIANTS) {
|
|
174
|
+
return reading('unreadable', null, [
|
|
175
|
+
AI_EXPERIMENT_REASON_UNREADABLE
|
|
176
|
+
]);
|
|
177
|
+
}
|
|
178
|
+
if (kept.some((arm)=>arm.exposures < AI_EXPERIMENT_MIN_EXPOSURES)) {
|
|
179
|
+
return reading('too-early', null, [
|
|
180
|
+
AI_EXPERIMENT_REASON_EXPOSURES
|
|
181
|
+
]);
|
|
182
|
+
}
|
|
183
|
+
const exposures = kept.reduce((sum, arm)=>sum + arm.exposures, 0);
|
|
184
|
+
const conversions = kept.reduce((sum, arm)=>sum + arm.conversions, 0);
|
|
185
|
+
const pooled = exposures > 0 ? conversions / exposures : 0;
|
|
186
|
+
const approximates = kept.every((arm)=>arm.exposures * pooled >= AI_EXPERIMENT_MIN_EXPECTED && arm.exposures * (1 - pooled) >= AI_EXPERIMENT_MIN_EXPECTED);
|
|
187
|
+
if (!approximates) {
|
|
188
|
+
return reading('too-early', null, [
|
|
189
|
+
AI_EXPERIMENT_REASON_CONVERSIONS
|
|
190
|
+
]);
|
|
191
|
+
}
|
|
192
|
+
const rest = kept.slice(1);
|
|
193
|
+
let best = null;
|
|
194
|
+
let allBehind = true;
|
|
195
|
+
for (const arm of rest){
|
|
196
|
+
var _best_confidence;
|
|
197
|
+
if (arm.confidence === null) {
|
|
198
|
+
allBehind = false;
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
if (arm.confidence >= threshold && (!best || arm.confidence > ((_best_confidence = best.confidence) != null ? _best_confidence : 0))) {
|
|
202
|
+
best = arm;
|
|
203
|
+
}
|
|
204
|
+
if (arm.confidence > 100 - threshold) allBehind = false;
|
|
205
|
+
}
|
|
206
|
+
if (best) return reading('winner', best.id, []);
|
|
207
|
+
if (allBehind) return reading('control', kept[0].id, []);
|
|
208
|
+
return reading('inconclusive', null, [
|
|
209
|
+
AI_EXPERIMENT_REASON_SPREAD
|
|
210
|
+
]);
|
|
211
|
+
}
|
|
212
|
+
// ── The explanation ───────────────────────────────────────────────────────
|
|
213
|
+
/** How long the sentence stating what happened may run. */ export const AI_EXPERIMENT_HEADLINE_MAX_CHARS = 280;
|
|
214
|
+
/** How many sentences of detail an explanation may carry. */ export const AI_EXPERIMENT_MAX_POINTS = 4;
|
|
215
|
+
/** How long one of them may run. */ export const AI_EXPERIMENT_POINT_MAX_CHARS = 280;
|
|
216
|
+
/** How long the sentence saying what to do next may run. */ export const AI_EXPERIMENT_NEXT_MAX_CHARS = 280;
|
|
217
|
+
/**
|
|
218
|
+
* Words an explanation may not use where the reading found no winner. A
|
|
219
|
+
* verdict of `too-early` or `inconclusive` describes an experiment that has
|
|
220
|
+
* not answered, and a sentence like "B is the clear winner" contradicts it
|
|
221
|
+
* however the rest of the paragraph is hedged.
|
|
222
|
+
*/ const VERDICT_WORDS = /\b(?:winner|winning|won|wins|beat(?:s|en)?|out ?perform(?:s|ed|ing)?|clear(?:ly)? (?:ahead|better|best)|significant(?:ly)?|conclusive(?:ly)?|proven?|declare)\b/i;
|
|
223
|
+
/**
|
|
224
|
+
* Telling someone to ship one variant is naming a winner whatever words it
|
|
225
|
+
* avoids, so a recommendation is refused on an undecided result too. This is
|
|
226
|
+
* the second belt; the first is that `winnerId` is the reading's and the
|
|
227
|
+
* answer cannot set it.
|
|
228
|
+
*/ const RECOMMENDS = /\b(?:roll(?:ing)? (?:it |them |this |that )?out|ship(?:ping)?|go with|switch(?:ing)? to|adopt|pick|choose|keep) \b/i;
|
|
229
|
+
/**
|
|
230
|
+
* What to do next where the result decided nothing. Code's sentence rather
|
|
231
|
+
* than the model's: "what next" is where an explanation that may not name a
|
|
232
|
+
* winner would name one anyway, and the honest answer is the same every time.
|
|
233
|
+
*/ export const AI_EXPERIMENT_NEXT_TOO_EARLY = 'Leave the test running until every variant has been shown to enough visitors to compare.';
|
|
234
|
+
export const AI_EXPERIMENT_NEXT_INCONCLUSIVE = 'These variants are too close to separate. Try a bigger change rather than a longer test.';
|
|
235
|
+
export const AI_EXPERIMENT_NEXT_UNREADABLE = 'Open the test in A/B testing and check it has a first variant and at least one to compare against it.';
|
|
236
|
+
/** The next step an undecided verdict carries; empty for a decided one, which the answer writes. */ export function aiExperimentNextStep(verdict) {
|
|
237
|
+
if (verdict === 'too-early') return AI_EXPERIMENT_NEXT_TOO_EARLY;
|
|
238
|
+
if (verdict === 'inconclusive') return AI_EXPERIMENT_NEXT_INCONCLUSIVE;
|
|
239
|
+
if (verdict === 'unreadable') return AI_EXPERIMENT_NEXT_UNREADABLE;
|
|
240
|
+
return '';
|
|
241
|
+
}
|
|
242
|
+
const CLAIM_UNSUPPORTED = 'claims-a-winner';
|
|
243
|
+
const WINNER_UNKNOWN = 'names-an-arm-the-figures-do-not';
|
|
244
|
+
const WINNER_UNSUPPORTED = 'names-a-winner-the-reading-did-not';
|
|
245
|
+
const trim = (value, max)=>(typeof value === 'string' ? value : '').trim().slice(0, max);
|
|
246
|
+
/**
|
|
247
|
+
* The model's explanation held to the reading (AGL-2914).
|
|
248
|
+
*
|
|
249
|
+
* The reading decided the verdict; this decides what may be said about it.
|
|
250
|
+
* Where the reading found no winner, the named arm is removed and any sentence
|
|
251
|
+
* that claims one is dropped — not softened, dropped, because a hedge in front
|
|
252
|
+
* of a claim still reads as the claim. Where it found one, the answer may only
|
|
253
|
+
* name THAT arm; another is removed as well.
|
|
254
|
+
*
|
|
255
|
+
* An explanation left with no headline is no explanation, and the step fails
|
|
256
|
+
* rather than showing an empty card.
|
|
257
|
+
*/ export function checkAiExperimentExplanation(answer, reading) {
|
|
258
|
+
if (!answer) return null;
|
|
259
|
+
const decided = aiExperimentVerdictNamesWinner(reading.verdict);
|
|
260
|
+
const findings = [];
|
|
261
|
+
const named = trim(answer.winner, AI_EXPERIMENT_NAME_MAX_CHARS);
|
|
262
|
+
let winnerId = null;
|
|
263
|
+
if (named) {
|
|
264
|
+
const arm = reading.arms.find((entry)=>entry.id.toLowerCase() === named.toLowerCase());
|
|
265
|
+
if (!arm) findings.push(`${WINNER_UNKNOWN}:${named}`);
|
|
266
|
+
else if (!decided || arm.id !== reading.winnerId) {
|
|
267
|
+
findings.push(`${WINNER_UNSUPPORTED}:${arm.id}`);
|
|
268
|
+
} else winnerId = arm.id;
|
|
269
|
+
}
|
|
270
|
+
// The reading's own answer, never the model's: a decided verdict names its
|
|
271
|
+
// arm even where the answer forgot to.
|
|
272
|
+
if (decided && !winnerId) winnerId = reading.winnerId;
|
|
273
|
+
const allowed = (sentence)=>decided || !(VERDICT_WORDS.test(sentence) || RECOMMENDS.test(sentence));
|
|
274
|
+
const headline = trim(answer.headline, AI_EXPERIMENT_HEADLINE_MAX_CHARS);
|
|
275
|
+
if (!headline || !allowed(headline)) {
|
|
276
|
+
if (headline) findings.push(`${CLAIM_UNSUPPORTED}:headline`);
|
|
277
|
+
// An explanation whose one statement of what happened cannot be shown is
|
|
278
|
+
// no explanation; the step fails rather than showing an empty card.
|
|
279
|
+
return null;
|
|
280
|
+
}
|
|
281
|
+
const points = [];
|
|
282
|
+
for (const raw of Array.isArray(answer.points) ? answer.points : []){
|
|
283
|
+
if (points.length === AI_EXPERIMENT_MAX_POINTS) break;
|
|
284
|
+
const point = trim(raw, AI_EXPERIMENT_POINT_MAX_CHARS);
|
|
285
|
+
if (!point) continue;
|
|
286
|
+
if (!allowed(point)) {
|
|
287
|
+
findings.push(`${CLAIM_UNSUPPORTED}:point`);
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
points.push(point);
|
|
291
|
+
}
|
|
292
|
+
// On an undecided result the next step is the verdict's own sentence, so the
|
|
293
|
+
// one field that invites a recommendation cannot carry a smuggled winner.
|
|
294
|
+
const answered = trim(answer.next, AI_EXPERIMENT_NEXT_MAX_CHARS);
|
|
295
|
+
const next = decided ? answered : aiExperimentNextStep(reading.verdict);
|
|
296
|
+
if (!decided && answered) findings.push(`${CLAIM_UNSUPPORTED}:next`);
|
|
297
|
+
return {
|
|
298
|
+
verdict: reading.verdict,
|
|
299
|
+
test: reading.test,
|
|
300
|
+
headline,
|
|
301
|
+
points,
|
|
302
|
+
winnerId,
|
|
303
|
+
next,
|
|
304
|
+
findings
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
// ── The variants ──────────────────────────────────────────────────────────
|
|
308
|
+
const MARKUP = /<\/?[a-z][^>]*>/i;
|
|
309
|
+
const VARIANT_COPY_MISSING = 'variant-has-no-copy';
|
|
310
|
+
const VARIANT_MARKUP = 'variant-carries-markup';
|
|
311
|
+
const VARIANT_DUPLICATE = 'variant-repeats-another';
|
|
312
|
+
const VARIANT_OFF_TARGET = 'variant-fills-another-targets-fields';
|
|
313
|
+
/**
|
|
314
|
+
* The fields a target's variant fills. An email varies its subject line, its
|
|
315
|
+
* preheader and its body; a screen or a section varies the copy on it. A
|
|
316
|
+
* variant that fills the other target's fields is answering a different
|
|
317
|
+
* question, and those fields are dropped.
|
|
318
|
+
*/ export function aiExperimentVariantFields(target) {
|
|
319
|
+
return target === 'email' ? [
|
|
320
|
+
'subject',
|
|
321
|
+
'preheader',
|
|
322
|
+
'body'
|
|
323
|
+
] : [
|
|
324
|
+
'headline',
|
|
325
|
+
'body'
|
|
326
|
+
];
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* A variants answer, held to what the A/B testing card can store (AGL-2914):
|
|
330
|
+
* between two and four variants, each with copy for the target under test,
|
|
331
|
+
* none repeating another's copy, and no markup anywhere — the card's fields
|
|
332
|
+
* are plain text and the besigner writes the elements.
|
|
333
|
+
*
|
|
334
|
+
* Returns the proposal with what could not be kept removed, or `null` when
|
|
335
|
+
* nothing usable is left.
|
|
336
|
+
*/ export function checkAiExperimentVariants(answer, target) {
|
|
337
|
+
if (!answer) return null;
|
|
338
|
+
const fields = aiExperimentVariantFields(target);
|
|
339
|
+
const others = [
|
|
340
|
+
'headline',
|
|
341
|
+
'body',
|
|
342
|
+
'subject',
|
|
343
|
+
'preheader'
|
|
344
|
+
].filter((field)=>!fields.includes(field));
|
|
345
|
+
const findings = [];
|
|
346
|
+
const variants = [];
|
|
347
|
+
const seen = new Set();
|
|
348
|
+
for (const raw of Array.isArray(answer.variants) ? answer.variants : []){
|
|
349
|
+
if (variants.length === AI_EXPERIMENT_MAX_VARIANTS) break;
|
|
350
|
+
if (!raw) continue;
|
|
351
|
+
const variant = {
|
|
352
|
+
name: trim(raw.name, AI_EXPERIMENT_NAME_MAX_CHARS),
|
|
353
|
+
headline: '',
|
|
354
|
+
body: '',
|
|
355
|
+
subject: '',
|
|
356
|
+
preheader: '',
|
|
357
|
+
rationale: trim(raw.rationale, AI_EXPERIMENT_RATIONALE_MAX_CHARS)
|
|
358
|
+
};
|
|
359
|
+
if (others.some((field)=>typeof raw[field] === 'string' && raw[field].trim())) {
|
|
360
|
+
findings.push(VARIANT_OFF_TARGET);
|
|
361
|
+
}
|
|
362
|
+
for (const field of fields){
|
|
363
|
+
variant[field] = trim(raw[field], field === 'body' ? AI_EXPERIMENT_BODY_MAX_CHARS : AI_EXPERIMENT_LINE_MAX_CHARS);
|
|
364
|
+
}
|
|
365
|
+
const copy = fields.map((field)=>variant[field]).filter(Boolean);
|
|
366
|
+
if (!copy.length) {
|
|
367
|
+
findings.push(VARIANT_COPY_MISSING);
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
if (copy.some((value)=>MARKUP.test(value))) {
|
|
371
|
+
findings.push(VARIANT_MARKUP);
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
const key = JSON.stringify(copy).toLowerCase();
|
|
375
|
+
if (seen.has(key)) {
|
|
376
|
+
findings.push(VARIANT_DUPLICATE);
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
379
|
+
seen.add(key);
|
|
380
|
+
variants.push(_extends({}, variant, {
|
|
381
|
+
name: variant.name || `Variant ${variants.length + 1}`
|
|
382
|
+
}));
|
|
383
|
+
}
|
|
384
|
+
// One variant is not a test: the card refuses it and so does this.
|
|
385
|
+
if (variants.length < AI_EXPERIMENT_MIN_VARIANTS) return null;
|
|
386
|
+
return {
|
|
387
|
+
proposal: {
|
|
388
|
+
goal: trim(answer.goal, AI_EXPERIMENT_RATIONALE_MAX_CHARS),
|
|
389
|
+
variants
|
|
390
|
+
},
|
|
391
|
+
findings
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
//# sourceMappingURL=ai-experiment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-experiment.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { PluginFigureTable } from '@aglyn/aglyn/plugin-manager/plugin-figures'\n\n/**\n * A/B tests by AI (AGL-2914): what an `experiment` job proposes, and what it\n * is allowed to say about a result.\n *\n * ## The test is not ours\n *\n * A/B testing is the marketing plugin's: it models the experiment document,\n * buckets visitors, counts exposures and conversions, compares a challenger\n * with the control and decides an auto-winner. None of that is restated here,\n * and none of it is imported — a plugin never imports another plugin. This\n * module is the AI layer over it, and it reaches the test through the two\n * generic seams core already publishes: the figure reader marketing registers\n * for its results, and the job inputs a console surface hands the door.\n *\n * ## Variants are proposed, never written\n *\n * A variant of a screen or a section is a screen VERSION, and a variant of an\n * email is that email's own copy: both belong to editors a person already\n * uses, and the experiment document belongs to marketing. So a variants\n * answer is a proposal, like a theme's or a listing's — words a person puts\n * into the A/B testing card, or does not. Nothing here writes an experiment,\n * starts one, or moves traffic.\n *\n * ## The verdict is code's, and the words are the model's\n *\n * `readAiExperimentResult` decides what a result supports BEFORE a model is\n * asked anything, from the arms' own counts and the confidence marketing\n * published beside them. The model is then asked to explain that verdict, and\n * `checkAiExperimentExplanation` holds it to it: an explanation may name a\n * winner only where the reading found one, and a winner it names anyway is\n * dropped rather than shown. A confident explanation of noise is worse than\n * no explanation, so the one thing the model is never trusted with is whether\n * there is something to explain.\n */\n\n/** What an `experiment` job was asked for. */\nexport type AiExperimentTask = 'variants' | 'explain'\n\nexport const AI_EXPERIMENT_TASKS: readonly AiExperimentTask[] = [\n 'variants',\n 'explain',\n]\n\n/** The task a job's inputs name, or `null` when they name none this step runs. */\nexport function aiExperimentTask(\n inputs: Readonly<Record<string, unknown>> | null | undefined,\n): AiExperimentTask | null {\n const value = inputs?.['task']\n return typeof value === 'string' &&\n (AI_EXPERIMENT_TASKS as readonly string[]).includes(value)\n ? (value as AiExperimentTask)\n : null\n}\n\n/**\n * What the experiment varies, in the words the A/B testing card uses. The job\n * carries it as an input because the card that opens the job knows it; it is\n * never read out of marketing's own document.\n */\nexport type AiExperimentTarget = 'screen' | 'section' | 'email'\n\nexport const AI_EXPERIMENT_TARGETS: readonly AiExperimentTarget[] = [\n 'screen',\n 'section',\n 'email',\n]\n\n/** The target a job's inputs name, or `null` when they name none. */\nexport function aiExperimentTarget(\n inputs: Readonly<Record<string, unknown>> | null | undefined,\n): AiExperimentTarget | null {\n const value = inputs?.['target']\n return typeof value === 'string' &&\n (AI_EXPERIMENT_TARGETS as readonly string[]).includes(value)\n ? (value as AiExperimentTarget)\n : null\n}\n\n/**\n * How many variants a proposal may hold, the cap the A/B testing card stores\n * and the page runner buckets against. Proposing a fifth would be proposing\n * something nothing can save.\n */\nexport const AI_EXPERIMENT_MAX_VARIANTS = 4\n\n/** The fewest a test can be: a control and one challenger. */\nexport const AI_EXPERIMENT_MIN_VARIANTS = 2\n\n/** How long a variant's name may run, as the card's own field holds one. */\nexport const AI_EXPERIMENT_NAME_MAX_CHARS = 60\n\n/** How long one line of a variant's copy may run. */\nexport const AI_EXPERIMENT_LINE_MAX_CHARS = 200\n\n/** How long a variant's body may run. */\nexport const AI_EXPERIMENT_BODY_MAX_CHARS = 1_200\n\n/** How long the sentence saying what a variant changes may run. */\nexport const AI_EXPERIMENT_RATIONALE_MAX_CHARS = 200\n\n/** How much of what is under test is sent. Past this it is a document, not a subject. */\nexport const AI_EXPERIMENT_SUBJECT_MAX_CHARS = 4_000\n\n/**\n * One variant a proposal holds. `headline` and `body` are a screen's or a\n * section's copy; `subject` and `preheader` are an email's. A variant carries\n * the fields its target has and leaves the rest empty, so what a person pastes\n * into the card is what that card has a place for.\n */\nexport interface AiExperimentVariantProposal {\n name: string\n headline: string\n body: string\n subject: string\n preheader: string\n /** One sentence: what this variant changes and what it is testing. */\n rationale: string\n}\n\n/** A variants answer as the step keeps it. */\nexport interface AiExperimentVariantsProposal {\n /** What the variants are trying to move, in the person's own terms. */\n goal: string\n variants: AiExperimentVariantProposal[]\n}\n\n// ── The result ────────────────────────────────────────────────────────────\n\n/**\n * The reader marketing registers for its A/B test results, and the columns it\n * publishes. Named here rather than imported because the owner is another\n * plugin: a table that does not carry these columns is one this step cannot\n * read, and it says so instead of guessing.\n */\nexport const AI_EXPERIMENT_FIGURE_READER = 'marketing.experiments'\n\nexport const AI_EXPERIMENT_FIGURE_COLUMNS = {\n test: 'test',\n variant: 'variant',\n exposures: 'shown',\n conversions: 'conversions',\n rate: 'rate',\n lift: 'lift',\n confidence: 'confidence',\n} as const\n\n/**\n * One arm of a test, as the figures report it. `lift` and `confidence` are the\n * owner's own comparison against the control, as percentages, and are `null`\n * on the control row and wherever the owner could not compare.\n */\nexport interface AiExperimentArm {\n id: string\n /** The exposures the reader counted: visitors shown this arm. */\n exposures: number\n conversions: number\n /** Conversions over exposures, as a percentage; `null` with no exposures. */\n rate: number | null\n /** Relative change against the control, as a percentage; `null` on the control. */\n lift: number | null\n /** The owner's confidence that this arm beats the control, as a percentage. */\n confidence: number | null\n}\n\n/**\n * The exposures each arm needs before a comparison is read at all. Below this\n * a test is early rather than inconclusive, which is a different thing to tell\n * someone: one says wait, the other says stop.\n */\nexport const AI_EXPERIMENT_MIN_EXPOSURES = 200\n\n/**\n * The successes and failures each arm needs, at the pooled rate, for the\n * normal approximation behind a two-proportion test to be worth reading. The\n * textbook condition, and the reason a test with plenty of traffic and almost\n * no conversions is still too early to call.\n */\nexport const AI_EXPERIMENT_MIN_EXPECTED = 5\n\n/**\n * The confidence a single comparison is called at: the same 95% the A/B\n * testing card's auto-winner offers, so the explanation and the card agree\n * about what \"confident\" means when there is one challenger.\n */\nexport const AI_EXPERIMENT_CONFIDENCE = 0.95\n\n/**\n * The confidence ONE comparison must clear when a test has `challengers` of\n * them (Šidák): testing three challengers at 95% each would call a winner by\n * chance about one time in seven, so the bar rises with the number of chances\n * taken. With one challenger this is exactly `AI_EXPERIMENT_CONFIDENCE`.\n */\nexport function aiExperimentConfidenceThreshold(challengers: number): number {\n const count = Math.max(1, Math.floor(challengers))\n return AI_EXPERIMENT_CONFIDENCE ** (1 / count)\n}\n\n/**\n * What a result supports:\n *\n * - `unreadable` — the figures do not describe a test with a control and a\n * challenger, so there is nothing to read;\n * - `too-early` — an arm is under the exposure floor, or so little has\n * converted that the comparison behind the confidence is not worth reading;\n * - `inconclusive` — enough has happened, and no arm is far enough from the\n * control for the difference to be more than chance;\n * - `winner` — a challenger is ahead by more than chance explains;\n * - `control` — every challenger is behind by more than chance explains.\n */\nexport type AiExperimentVerdict =\n | 'unreadable'\n | 'too-early'\n | 'inconclusive'\n | 'winner'\n | 'control'\n\n/** The verdicts that name an arm, and so let an explanation name one. */\nexport const AI_EXPERIMENT_DECIDED_VERDICTS: readonly AiExperimentVerdict[] = [\n 'winner',\n 'control',\n]\n\n/** Whether a verdict lets an explanation name a winner. */\nexport function aiExperimentVerdictNamesWinner(\n verdict: AiExperimentVerdict,\n): boolean {\n return AI_EXPERIMENT_DECIDED_VERDICTS.includes(verdict)\n}\n\nexport interface AiExperimentReading {\n verdict: AiExperimentVerdict\n /** The test's name as the figures label it. */\n test: string\n /** The control first, then its challengers in the order the figures gave them. */\n arms: AiExperimentArm[]\n /** The arm the verdict names, on `winner` and `control`; `null` otherwise. */\n winnerId: string | null\n /** The confidence one comparison had to clear, as a percentage. */\n threshold: number\n /**\n * Why the verdict is not a winner, in the words the explanation is asked to\n * use; empty where it is one. Never a number — the figures carry those.\n */\n reasons: string[]\n}\n\nconst num = (value: unknown): number =>\n typeof value === 'number' && Number.isFinite(value) ? value : 0\n\nconst maybe = (value: unknown): number | null =>\n typeof value === 'number' && Number.isFinite(value) ? value : null\n\nconst text = (value: unknown): string =>\n typeof value === 'string' ? value.trim() : ''\n\n/** Whether a table is the A/B test results, by the columns it publishes. */\nexport function isAiExperimentTable(\n table: Pick<PluginFigureTable, 'columns'> | null | undefined,\n): boolean {\n const keys = new Set((table?.columns ?? []).map((column) => column.key))\n return Object.values(AI_EXPERIMENT_FIGURE_COLUMNS).every((key) =>\n keys.has(key),\n )\n}\n\n/** The tests a results table holds, by the name it labels each with. */\nexport function aiExperimentTestNames(\n table: Pick<PluginFigureTable, 'columns' | 'rows'> | null | undefined,\n): string[] {\n if (!isAiExperimentTable(table)) return []\n const names: string[] = []\n for (const row of table?.rows ?? []) {\n const name = text(row[AI_EXPERIMENT_FIGURE_COLUMNS.test])\n if (name && !names.includes(name)) names.push(name)\n }\n return names\n}\n\n/**\n * One test's arms, in the order the table gave them — the control first, as\n * the reader's own note says it publishes them. An empty list where the table\n * is not the results, or holds no such test.\n */\nexport function readAiExperimentArms(\n table: Pick<PluginFigureTable, 'columns' | 'rows'> | null | undefined,\n test: string,\n): AiExperimentArm[] {\n if (!isAiExperimentTable(table)) return []\n const wanted = test.trim()\n const arms: AiExperimentArm[] = []\n for (const row of table?.rows ?? []) {\n if (text(row[AI_EXPERIMENT_FIGURE_COLUMNS.test]) !== wanted) continue\n const id = text(row[AI_EXPERIMENT_FIGURE_COLUMNS.variant])\n if (!id) continue\n arms.push({\n id,\n exposures: Math.max(0, num(row[AI_EXPERIMENT_FIGURE_COLUMNS.exposures])),\n conversions: Math.max(\n 0,\n num(row[AI_EXPERIMENT_FIGURE_COLUMNS.conversions]),\n ),\n rate: maybe(row[AI_EXPERIMENT_FIGURE_COLUMNS.rate]),\n lift: maybe(row[AI_EXPERIMENT_FIGURE_COLUMNS.lift]),\n confidence: maybe(row[AI_EXPERIMENT_FIGURE_COLUMNS.confidence]),\n })\n if (arms.length === AI_EXPERIMENT_MAX_VARIANTS) break\n }\n return arms\n}\n\n/** What an arm is told to wait for, in words with no figure in them. */\nexport const AI_EXPERIMENT_REASON_EXPOSURES =\n 'at least one variant has not been shown to enough visitors yet'\nexport const AI_EXPERIMENT_REASON_CONVERSIONS =\n 'too little has converted yet for the comparison to mean anything'\nexport const AI_EXPERIMENT_REASON_SPREAD =\n 'no variant is far enough from the first one for the difference to be more than chance'\nexport const AI_EXPERIMENT_REASON_UNREADABLE =\n 'these figures do not describe a test with a first variant and something to compare against it'\n\n/**\n * What a test's figures support (AGL-2914), decided before a model is asked\n * anything.\n *\n * The first arm is the control, as the results reader publishes it. A verdict\n * is reached in this order, and the first answer wins:\n *\n * 1. fewer than two arms — `unreadable`;\n * 2. an arm under `AI_EXPERIMENT_MIN_EXPOSURES`, or one whose expected\n * successes or failures at the pooled rate are under\n * `AI_EXPERIMENT_MIN_EXPECTED` — `too-early`. The second is what stops a\n * test with a hundred thousand visitors and four conversions reading as a\n * decided one;\n * 3. a challenger whose confidence clears the multiplicity-corrected\n * threshold — `winner`, the most confident of them;\n * 4. every challenger below `1 - threshold`, which is the same statement made\n * about the control — `control`;\n * 5. anything else — `inconclusive`.\n *\n * A challenger the owner could not compare (`confidence` is `null`) can never\n * win, and leaves the control undecided too: not knowing is not evidence.\n */\nexport function readAiExperimentResult(\n test: string,\n arms: readonly AiExperimentArm[],\n): AiExperimentReading {\n const kept = arms.slice(0, AI_EXPERIMENT_MAX_VARIANTS)\n const challengers = Math.max(1, kept.length - 1)\n const threshold = aiExperimentConfidenceThreshold(challengers) * 100\n const reading = (\n verdict: AiExperimentVerdict,\n winnerId: string | null,\n reasons: string[],\n ): AiExperimentReading => ({\n verdict,\n test: test.trim(),\n arms: kept,\n winnerId,\n threshold: Math.round(threshold * 100) / 100,\n reasons,\n })\n\n if (kept.length < AI_EXPERIMENT_MIN_VARIANTS) {\n return reading('unreadable', null, [AI_EXPERIMENT_REASON_UNREADABLE])\n }\n if (kept.some((arm) => arm.exposures < AI_EXPERIMENT_MIN_EXPOSURES)) {\n return reading('too-early', null, [AI_EXPERIMENT_REASON_EXPOSURES])\n }\n const exposures = kept.reduce((sum, arm) => sum + arm.exposures, 0)\n const conversions = kept.reduce((sum, arm) => sum + arm.conversions, 0)\n const pooled = exposures > 0 ? conversions / exposures : 0\n const approximates = kept.every(\n (arm) =>\n arm.exposures * pooled >= AI_EXPERIMENT_MIN_EXPECTED &&\n arm.exposures * (1 - pooled) >= AI_EXPERIMENT_MIN_EXPECTED,\n )\n if (!approximates) {\n return reading('too-early', null, [AI_EXPERIMENT_REASON_CONVERSIONS])\n }\n\n const rest = kept.slice(1)\n let best: AiExperimentArm | null = null\n let allBehind = true\n for (const arm of rest) {\n if (arm.confidence === null) {\n allBehind = false\n continue\n }\n if (\n arm.confidence >= threshold &&\n (!best || arm.confidence > (best.confidence ?? 0))\n ) {\n best = arm\n }\n if (arm.confidence > 100 - threshold) allBehind = false\n }\n if (best) return reading('winner', best.id, [])\n if (allBehind) return reading('control', kept[0].id, [])\n return reading('inconclusive', null, [AI_EXPERIMENT_REASON_SPREAD])\n}\n\n// ── The explanation ───────────────────────────────────────────────────────\n\n/** How long the sentence stating what happened may run. */\nexport const AI_EXPERIMENT_HEADLINE_MAX_CHARS = 280\n\n/** How many sentences of detail an explanation may carry. */\nexport const AI_EXPERIMENT_MAX_POINTS = 4\n\n/** How long one of them may run. */\nexport const AI_EXPERIMENT_POINT_MAX_CHARS = 280\n\n/** How long the sentence saying what to do next may run. */\nexport const AI_EXPERIMENT_NEXT_MAX_CHARS = 280\n\n/** An explanation as the model answers it. */\nexport interface AiExperimentExplanationAnswer {\n headline: string\n points: string[]\n /** The variant the answer says won; empty when it names none. */\n winner: string\n next: string\n}\n\n/** An explanation as the step keeps it, after the reading has had its say. */\nexport interface AiExperimentExplanation {\n verdict: AiExperimentVerdict\n test: string\n headline: string\n points: string[]\n /**\n * The arm the explanation names, which is the arm the READING named: an\n * answer that named another, or named one where the reading named none, has\n * had it removed.\n */\n winnerId: string | null\n next: string\n /** What was dropped from the answer, for the log; never shown to a customer. */\n findings: string[]\n}\n\n/**\n * Words an explanation may not use where the reading found no winner. A\n * verdict of `too-early` or `inconclusive` describes an experiment that has\n * not answered, and a sentence like \"B is the clear winner\" contradicts it\n * however the rest of the paragraph is hedged.\n */\nconst VERDICT_WORDS =\n /\\b(?:winner|winning|won|wins|beat(?:s|en)?|out ?perform(?:s|ed|ing)?|clear(?:ly)? (?:ahead|better|best)|significant(?:ly)?|conclusive(?:ly)?|proven?|declare)\\b/i\n\n/**\n * Telling someone to ship one variant is naming a winner whatever words it\n * avoids, so a recommendation is refused on an undecided result too. This is\n * the second belt; the first is that `winnerId` is the reading's and the\n * answer cannot set it.\n */\nconst RECOMMENDS =\n /\\b(?:roll(?:ing)? (?:it |them |this |that )?out|ship(?:ping)?|go with|switch(?:ing)? to|adopt|pick|choose|keep) \\b/i\n\n/**\n * What to do next where the result decided nothing. Code's sentence rather\n * than the model's: \"what next\" is where an explanation that may not name a\n * winner would name one anyway, and the honest answer is the same every time.\n */\nexport const AI_EXPERIMENT_NEXT_TOO_EARLY =\n 'Leave the test running until every variant has been shown to enough visitors to compare.'\nexport const AI_EXPERIMENT_NEXT_INCONCLUSIVE =\n 'These variants are too close to separate. Try a bigger change rather than a longer test.'\nexport const AI_EXPERIMENT_NEXT_UNREADABLE =\n 'Open the test in A/B testing and check it has a first variant and at least one to compare against it.'\n\n/** The next step an undecided verdict carries; empty for a decided one, which the answer writes. */\nexport function aiExperimentNextStep(verdict: AiExperimentVerdict): string {\n if (verdict === 'too-early') return AI_EXPERIMENT_NEXT_TOO_EARLY\n if (verdict === 'inconclusive') return AI_EXPERIMENT_NEXT_INCONCLUSIVE\n if (verdict === 'unreadable') return AI_EXPERIMENT_NEXT_UNREADABLE\n return ''\n}\n\nconst CLAIM_UNSUPPORTED = 'claims-a-winner'\nconst WINNER_UNKNOWN = 'names-an-arm-the-figures-do-not'\nconst WINNER_UNSUPPORTED = 'names-a-winner-the-reading-did-not'\n\nconst trim = (value: unknown, max: number): string =>\n (typeof value === 'string' ? value : '').trim().slice(0, max)\n\n/**\n * The model's explanation held to the reading (AGL-2914).\n *\n * The reading decided the verdict; this decides what may be said about it.\n * Where the reading found no winner, the named arm is removed and any sentence\n * that claims one is dropped — not softened, dropped, because a hedge in front\n * of a claim still reads as the claim. Where it found one, the answer may only\n * name THAT arm; another is removed as well.\n *\n * An explanation left with no headline is no explanation, and the step fails\n * rather than showing an empty card.\n */\nexport function checkAiExperimentExplanation(\n answer: AiExperimentExplanationAnswer | null,\n reading: AiExperimentReading,\n): AiExperimentExplanation | null {\n if (!answer) return null\n const decided = aiExperimentVerdictNamesWinner(reading.verdict)\n const findings: string[] = []\n\n const named = trim(answer.winner, AI_EXPERIMENT_NAME_MAX_CHARS)\n let winnerId: string | null = null\n if (named) {\n const arm = reading.arms.find(\n (entry) => entry.id.toLowerCase() === named.toLowerCase(),\n )\n if (!arm) findings.push(`${WINNER_UNKNOWN}:${named}`)\n else if (!decided || arm.id !== reading.winnerId) {\n findings.push(`${WINNER_UNSUPPORTED}:${arm.id}`)\n } else winnerId = arm.id\n }\n // The reading's own answer, never the model's: a decided verdict names its\n // arm even where the answer forgot to.\n if (decided && !winnerId) winnerId = reading.winnerId\n\n const allowed = (sentence: string): boolean =>\n decided || !(VERDICT_WORDS.test(sentence) || RECOMMENDS.test(sentence))\n\n const headline = trim(answer.headline, AI_EXPERIMENT_HEADLINE_MAX_CHARS)\n if (!headline || !allowed(headline)) {\n if (headline) findings.push(`${CLAIM_UNSUPPORTED}:headline`)\n // An explanation whose one statement of what happened cannot be shown is\n // no explanation; the step fails rather than showing an empty card.\n return null\n }\n const points: string[] = []\n for (const raw of Array.isArray(answer.points) ? answer.points : []) {\n if (points.length === AI_EXPERIMENT_MAX_POINTS) break\n const point = trim(raw, AI_EXPERIMENT_POINT_MAX_CHARS)\n if (!point) continue\n if (!allowed(point)) {\n findings.push(`${CLAIM_UNSUPPORTED}:point`)\n continue\n }\n points.push(point)\n }\n // On an undecided result the next step is the verdict's own sentence, so the\n // one field that invites a recommendation cannot carry a smuggled winner.\n const answered = trim(answer.next, AI_EXPERIMENT_NEXT_MAX_CHARS)\n const next = decided ? answered : aiExperimentNextStep(reading.verdict)\n if (!decided && answered) findings.push(`${CLAIM_UNSUPPORTED}:next`)\n return {\n verdict: reading.verdict,\n test: reading.test,\n headline,\n points,\n winnerId,\n next,\n findings,\n }\n}\n\n// ── The variants ──────────────────────────────────────────────────────────\n\nconst MARKUP = /<\\/?[a-z][^>]*>/i\n\nconst VARIANT_COPY_MISSING = 'variant-has-no-copy'\nconst VARIANT_MARKUP = 'variant-carries-markup'\nconst VARIANT_DUPLICATE = 'variant-repeats-another'\nconst VARIANT_OFF_TARGET = 'variant-fills-another-targets-fields'\n\n/**\n * The fields a target's variant fills. An email varies its subject line, its\n * preheader and its body; a screen or a section varies the copy on it. A\n * variant that fills the other target's fields is answering a different\n * question, and those fields are dropped.\n */\nexport function aiExperimentVariantFields(\n target: AiExperimentTarget,\n): Array<keyof AiExperimentVariantProposal> {\n return target === 'email'\n ? ['subject', 'preheader', 'body']\n : ['headline', 'body']\n}\n\n/**\n * A variants answer, held to what the A/B testing card can store (AGL-2914):\n * between two and four variants, each with copy for the target under test,\n * none repeating another's copy, and no markup anywhere — the card's fields\n * are plain text and the besigner writes the elements.\n *\n * Returns the proposal with what could not be kept removed, or `null` when\n * nothing usable is left.\n */\nexport function checkAiExperimentVariants(\n answer: AiExperimentVariantsProposal | null,\n target: AiExperimentTarget,\n): { proposal: AiExperimentVariantsProposal; findings: string[] } | null {\n if (!answer) return null\n const fields = aiExperimentVariantFields(target)\n const others = (\n ['headline', 'body', 'subject', 'preheader'] as Array<\n keyof AiExperimentVariantProposal\n >\n ).filter((field) => !fields.includes(field))\n const findings: string[] = []\n const variants: AiExperimentVariantProposal[] = []\n const seen = new Set<string>()\n\n for (const raw of Array.isArray(answer.variants) ? answer.variants : []) {\n if (variants.length === AI_EXPERIMENT_MAX_VARIANTS) break\n if (!raw) continue\n const variant: AiExperimentVariantProposal = {\n name: trim(raw.name, AI_EXPERIMENT_NAME_MAX_CHARS),\n headline: '',\n body: '',\n subject: '',\n preheader: '',\n rationale: trim(raw.rationale, AI_EXPERIMENT_RATIONALE_MAX_CHARS),\n }\n if (others.some((field) => typeof raw[field] === 'string' && raw[field].trim())) {\n findings.push(VARIANT_OFF_TARGET)\n }\n for (const field of fields) {\n variant[field] = trim(\n raw[field],\n field === 'body'\n ? AI_EXPERIMENT_BODY_MAX_CHARS\n : AI_EXPERIMENT_LINE_MAX_CHARS,\n )\n }\n const copy = fields.map((field) => variant[field]).filter(Boolean)\n if (!copy.length) {\n findings.push(VARIANT_COPY_MISSING)\n continue\n }\n if (copy.some((value) => MARKUP.test(value))) {\n findings.push(VARIANT_MARKUP)\n continue\n }\n const key = JSON.stringify(copy).toLowerCase()\n if (seen.has(key)) {\n findings.push(VARIANT_DUPLICATE)\n continue\n }\n seen.add(key)\n variants.push({\n ...variant,\n name: variant.name || `Variant ${variants.length + 1}`,\n })\n }\n // One variant is not a test: the card refuses it and so does this.\n if (variants.length < AI_EXPERIMENT_MIN_VARIANTS) return null\n return {\n proposal: {\n goal: trim(answer.goal, AI_EXPERIMENT_RATIONALE_MAX_CHARS),\n variants,\n },\n findings,\n }\n}\n"],"names":["AI_EXPERIMENT_TASKS","aiExperimentTask","inputs","value","includes","AI_EXPERIMENT_TARGETS","aiExperimentTarget","AI_EXPERIMENT_MAX_VARIANTS","AI_EXPERIMENT_MIN_VARIANTS","AI_EXPERIMENT_NAME_MAX_CHARS","AI_EXPERIMENT_LINE_MAX_CHARS","AI_EXPERIMENT_BODY_MAX_CHARS","AI_EXPERIMENT_RATIONALE_MAX_CHARS","AI_EXPERIMENT_SUBJECT_MAX_CHARS","AI_EXPERIMENT_FIGURE_READER","AI_EXPERIMENT_FIGURE_COLUMNS","test","variant","exposures","conversions","rate","lift","confidence","AI_EXPERIMENT_MIN_EXPOSURES","AI_EXPERIMENT_MIN_EXPECTED","AI_EXPERIMENT_CONFIDENCE","aiExperimentConfidenceThreshold","challengers","count","Math","max","floor","AI_EXPERIMENT_DECIDED_VERDICTS","aiExperimentVerdictNamesWinner","verdict","num","Number","isFinite","maybe","text","trim","isAiExperimentTable","table","keys","Set","columns","map","column","key","Object","values","every","has","aiExperimentTestNames","names","row","rows","name","push","readAiExperimentArms","wanted","arms","id","length","AI_EXPERIMENT_REASON_EXPOSURES","AI_EXPERIMENT_REASON_CONVERSIONS","AI_EXPERIMENT_REASON_SPREAD","AI_EXPERIMENT_REASON_UNREADABLE","readAiExperimentResult","kept","slice","threshold","reading","winnerId","reasons","round","some","arm","reduce","sum","pooled","approximates","rest","best","allBehind","AI_EXPERIMENT_HEADLINE_MAX_CHARS","AI_EXPERIMENT_MAX_POINTS","AI_EXPERIMENT_POINT_MAX_CHARS","AI_EXPERIMENT_NEXT_MAX_CHARS","VERDICT_WORDS","RECOMMENDS","AI_EXPERIMENT_NEXT_TOO_EARLY","AI_EXPERIMENT_NEXT_INCONCLUSIVE","AI_EXPERIMENT_NEXT_UNREADABLE","aiExperimentNextStep","CLAIM_UNSUPPORTED","WINNER_UNKNOWN","WINNER_UNSUPPORTED","checkAiExperimentExplanation","answer","decided","findings","named","winner","find","entry","toLowerCase","allowed","sentence","headline","points","raw","Array","isArray","point","answered","next","MARKUP","VARIANT_COPY_MISSING","VARIANT_MARKUP","VARIANT_DUPLICATE","VARIANT_OFF_TARGET","aiExperimentVariantFields","target","checkAiExperimentVariants","fields","others","filter","field","variants","seen","body","subject","preheader","rationale","copy","Boolean","JSON","stringify","add","proposal","goal"],"mappings":"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"}
|