@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,1257 @@
|
|
|
1
|
+
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright 2026 Aglyn LLC
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/ import { assistCreditsFromUsd } from "@aglyn/aglyn/app-utils/assist-credits";
|
|
18
|
+
import { LAYOUT_SLOT_COMPONENT_ID } from "@aglyn/aglyn/app-utils/compose-layout-nodes";
|
|
19
|
+
import { stampDocumentLandmark } from "@aglyn/aglyn/app-utils/document-landmark";
|
|
20
|
+
import { decodeStoredNodes } from "@aglyn/aglyn/app-utils/stored-nodes";
|
|
21
|
+
import { CANVAS_ROOT_ELEMENT_ID } from "@aglyn/aglyn/foundation/constants/canvas";
|
|
22
|
+
import { createAiJobComponentStep } from "../jobs/ai-job-component-step.js";
|
|
23
|
+
import { aiJobDraftId, aiMintJobDraftIds } from "../jobs/ai-job-draft-ids.js";
|
|
24
|
+
import { createAiJobFormStep } from "../jobs/ai-job-form-step.js";
|
|
25
|
+
import { createAiJobLayoutStep } from "../jobs/ai-job-layout-step.js";
|
|
26
|
+
import { aiPageJobUnits, createAiJobPageStep } from "../jobs/ai-job-page-step.js";
|
|
27
|
+
import { AI_JOB_PLAN_SCOPES, createAiJobPlanStep } from "../jobs/ai-job-plan-step.js";
|
|
28
|
+
import { createAiJobProductsStep } from "../jobs/ai-job-products-step.js";
|
|
29
|
+
import { aiSitePendingUnits } from "../jobs/ai-job-site-step.js";
|
|
30
|
+
import { runAiJobTextStep } from "../jobs/ai-job-text-step.js";
|
|
31
|
+
import { aiJobThemeCheck, aiJobThemeGeneration, aiJobThemeMode } from "../jobs/ai-job-theme-step.js";
|
|
32
|
+
import { aiJobWorkflowDraftGeneration, aiJobWorkflowExplainGeneration, aiJobWorkflowModel } from "../jobs/ai-job-workflow-step.js";
|
|
33
|
+
import { readAiProductImage } from "../jobs/ai-product-image.js";
|
|
34
|
+
import { emptyAiAutomationRecords } from "../model/ai-automation-draft.js";
|
|
35
|
+
import { aiActionOutline, aiActionRecordKinds, aiRunOutline } from "../model/ai-automation-outline.js";
|
|
36
|
+
import { aiPlanCapabilitiesForJob, aiPlanCapabilityLines } from "../model/ai-plan-capabilities.js";
|
|
37
|
+
import { aiProductsJobInputs, aiProductsProposalOf } from "../model/ai-products.js";
|
|
38
|
+
import { aiDefaultModelFor } from "../providers/catalog.js";
|
|
39
|
+
import { aiModelForStep, resolveAiProvider } from "../providers/routing.js";
|
|
40
|
+
import { aiSiteInventoryBlock, runValidatedGeneration } from "./ai-doctrine.js";
|
|
41
|
+
import { AI_EVAL_AUTOMATION_CAPABILITIES, AI_EVAL_SITE_ID, AI_EVAL_TREE_OUTPUT } from "./ai-eval.js";
|
|
42
|
+
import { aiEvalMemoryFirestore, aiEvalMemoryInventory } from "./ai-eval-memory-firestore.js";
|
|
43
|
+
import { aiCategoriesPrompt, aiProductCopyPrompt } from "./ai-products-generation.js";
|
|
44
|
+
import { aiModelReadsImages } from "./ai-runtime.js";
|
|
45
|
+
/**
|
|
46
|
+
* THE EVAL HARNESS, LIVE (AGL-2937): recording new answers from the model.
|
|
47
|
+
*
|
|
48
|
+
* Offline scoring reads fixtures and costs nothing. A live run asks the
|
|
49
|
+
* provider for real answers to the golden briefs, grades each with a
|
|
50
|
+
* stronger model, and hands back recordings the offline harness then scores
|
|
51
|
+
* like any fixture. It spends real money, so it runs only when
|
|
52
|
+
* `AI_EVAL_LIVE=1` names it, and it is never part of CI or a deployment.
|
|
53
|
+
*
|
|
54
|
+
* A recording is only worth having if it measures what production sends. So
|
|
55
|
+
* each recorder answers a brief through the door that answers that kind in
|
|
56
|
+
* production — the plan step's runner, the text step's runner, the products
|
|
57
|
+
* step's runner, the theme and workflow steps' own generation calls — with
|
|
58
|
+
* the case's site handed in where the door would read one. Nothing here
|
|
59
|
+
* writes a prompt of its own for a kind.
|
|
60
|
+
*
|
|
61
|
+
* A kind with no recorder is skipped before any request, saying where
|
|
62
|
+
* production answers it (`AI_EVAL_UNRECORDED_DOORS`): a request route no job
|
|
63
|
+
* runs (the copy assistant's section, element and blog modes, the chat door),
|
|
64
|
+
* or a job step no recorder drives yet. A planned kind's document records the
|
|
65
|
+
* plan alone until its generator is recorded. A door that gains a recorder
|
|
66
|
+
* registers it with `registerAiEvalRecorder`.
|
|
67
|
+
*
|
|
68
|
+
* An automation brief is recorded through the workflow step's own generation
|
|
69
|
+
* call (AGL-3074) — a draft from the workspace's reach and the site's records,
|
|
70
|
+
* an explanation from the outline of the saved automation — because the
|
|
71
|
+
* step's runner keeps the tool's input nowhere: it writes the draft it makes
|
|
72
|
+
* of it, or the text it reads out of it, and the harness scores the input. A
|
|
73
|
+
* product brief is recorded through the products step's runner, against the
|
|
74
|
+
* case's store in memory, its photo read from the case's fixtures through the
|
|
75
|
+
* step's own resize-and-strip path, and its answer read back off the proposal
|
|
76
|
+
* the runner makes.
|
|
77
|
+
*
|
|
78
|
+
* A page brief is recorded END TO END (AGL-3030): the plan step's runner,
|
|
79
|
+
* the plan confirmed as a member confirms it, and every pass of the page
|
|
80
|
+
* step's runner against a site kept in memory — the layout, forms and
|
|
81
|
+
* components its plan creates built first by their own steps (AGL-3031) —
|
|
82
|
+
* each exchange's spend kept as the machine meters it. That is what proves
|
|
83
|
+
* what one page costs, on the workspace the case describes. Beside the page's
|
|
84
|
+
* tree it keeps the draft's screen — its address, its listing and the layout
|
|
85
|
+
* it renders inside — which the page's grader is shown with the plan
|
|
86
|
+
* (AGL-3073).
|
|
87
|
+
*/ /** The environment variable a live run must be named by. */ export const AI_EVAL_LIVE_ENV = 'AI_EVAL_LIVE';
|
|
88
|
+
export const AI_EVAL_LIVE_REFUSAL = `A live eval run asks the AI provider for real answers and spends real money. It runs only when ${AI_EVAL_LIVE_ENV}=1 is set, from a machine that holds a provider key, and never in CI or on a deployment.`;
|
|
89
|
+
export class AiEvalLiveRefusedError extends Error {
|
|
90
|
+
constructor(){
|
|
91
|
+
super(AI_EVAL_LIVE_REFUSAL);
|
|
92
|
+
this.name = 'AiEvalLiveRefusedError';
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/** Whether the environment names a live run. */ export function aiEvalLiveAllowed(env) {
|
|
96
|
+
return env[AI_EVAL_LIVE_ENV] === '1';
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* The variable a live run names the briefs it records by: case ids, comma
|
|
100
|
+
* separated. Unset records every brief a recorder covers.
|
|
101
|
+
*/ export const AI_EVAL_CASES_ENV = 'AI_EVAL_CASES';
|
|
102
|
+
/** The briefs a run records: the ones `AI_EVAL_CASES` names, else every one. */ export function aiEvalCasesNamed(cases, env) {
|
|
103
|
+
var _env_AI_EVAL_CASES_ENV;
|
|
104
|
+
const named = ((_env_AI_EVAL_CASES_ENV = env[AI_EVAL_CASES_ENV]) != null ? _env_AI_EVAL_CASES_ENV : '').split(',').map((id)=>id.trim()).filter(Boolean);
|
|
105
|
+
if (!named.length) return [
|
|
106
|
+
...cases
|
|
107
|
+
];
|
|
108
|
+
const unknown = named.filter((id)=>!cases.some((evalCase)=>evalCase.id === id));
|
|
109
|
+
if (unknown.length) throw new Error(`${AI_EVAL_CASES_ENV} names no golden brief: ${unknown.join(', ')}`);
|
|
110
|
+
return cases.filter((evalCase)=>named.includes(evalCase.id));
|
|
111
|
+
}
|
|
112
|
+
const recorders = new Map();
|
|
113
|
+
/** Idempotent per kind; last registration wins. */ export function registerAiEvalRecorder(kind, recorder) {
|
|
114
|
+
recorders.set(kind, recorder);
|
|
115
|
+
}
|
|
116
|
+
export function aiEvalRecorderFor(kind) {
|
|
117
|
+
var _recorders_get;
|
|
118
|
+
return (_recorders_get = recorders.get(kind)) != null ? _recorders_get : null;
|
|
119
|
+
}
|
|
120
|
+
const EVAL_ORG = 'eval-org';
|
|
121
|
+
/** A job for a golden brief, in the shape a step runner reads: its drafts named as a created job's are. */ function evalJob(evalCase, kind) {
|
|
122
|
+
var _ref;
|
|
123
|
+
var _evalCase_inventory;
|
|
124
|
+
const now = new Date(0);
|
|
125
|
+
const draftIds = aiMintJobDraftIds(kind);
|
|
126
|
+
return {
|
|
127
|
+
$id: `eval-${evalCase.id}`,
|
|
128
|
+
orgId: EVAL_ORG,
|
|
129
|
+
hostId: (_ref = (_evalCase_inventory = evalCase.inventory) == null ? void 0 : _evalCase_inventory.hostId) != null ? _ref : null,
|
|
130
|
+
kind,
|
|
131
|
+
status: 'running',
|
|
132
|
+
brief: evalCase.brief,
|
|
133
|
+
inputs: {},
|
|
134
|
+
steps: draftIds ? [
|
|
135
|
+
{
|
|
136
|
+
name: 'generate',
|
|
137
|
+
status: 'running',
|
|
138
|
+
creditsSpent: 0,
|
|
139
|
+
draftIds
|
|
140
|
+
}
|
|
141
|
+
] : [],
|
|
142
|
+
outputs: [],
|
|
143
|
+
creditsReserved: 0,
|
|
144
|
+
creditsSpent: 0,
|
|
145
|
+
createdBy: 'eval',
|
|
146
|
+
createdAt: now,
|
|
147
|
+
updatedAt: now,
|
|
148
|
+
expiresAt: now
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* The model a step runner is handed: the one the run names, else the routing
|
|
153
|
+
* table's. A runner reaches its provider through the runtime's registry.
|
|
154
|
+
*/ function modelFor(options) {
|
|
155
|
+
return (step)=>{
|
|
156
|
+
var _options_model;
|
|
157
|
+
return (_options_model = options.model) != null ? _options_model : aiModelForStep(step);
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
/** Runs the doctrine's call with the provider a live run chose, when it chose one. */ function withProvider(input, options) {
|
|
161
|
+
return options.provider ? _extends({}, input, {
|
|
162
|
+
provider: options.provider
|
|
163
|
+
}) : input;
|
|
164
|
+
}
|
|
165
|
+
/** The case's site, as a reader the step runners are handed. */ function inventoryOf(evalCase) {
|
|
166
|
+
return async ()=>{
|
|
167
|
+
if (!evalCase.inventory) throw new Error(`${evalCase.id} has no inventory to build from`);
|
|
168
|
+
return evalCase.inventory;
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* The plan step's runner for a brief, on the case's site and the case's
|
|
173
|
+
* workspace. No plan reuse in a recording (AGL-2937): the point of a live run
|
|
174
|
+
* is to measure what the model answers for this brief, and a reused plan
|
|
175
|
+
* would record someone else's answer as this case's. No admission either: a
|
|
176
|
+
* recording writes to no workspace a door would count against.
|
|
177
|
+
*/ function evalPlanStep(evalCase) {
|
|
178
|
+
return createAiJobPlanStep({
|
|
179
|
+
readInventory: inventoryOf(evalCase),
|
|
180
|
+
findPlansByKey: null,
|
|
181
|
+
readCapabilities: async ()=>{
|
|
182
|
+
var _evalCase_capabilities;
|
|
183
|
+
return (_evalCase_capabilities = evalCase.capabilities) != null ? _evalCase_capabilities : null;
|
|
184
|
+
},
|
|
185
|
+
admissionRefusal: async ()=>null
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
/** A plan outcome's plan, as a candidate records it. */ function planOf(outcome) {
|
|
189
|
+
return outcome.plan ? {
|
|
190
|
+
reuse: outcome.plan.reuse,
|
|
191
|
+
create: outcome.plan.create,
|
|
192
|
+
screens: outcome.plan.screens
|
|
193
|
+
} : null;
|
|
194
|
+
}
|
|
195
|
+
/** A planned kind's brief, recorded through the plan step: the plan alone. */ const recordPlan = async (evalCase, options)=>{
|
|
196
|
+
var _options_now, _outcome_effort;
|
|
197
|
+
var _outcome_review;
|
|
198
|
+
const kind = evalCase.kind;
|
|
199
|
+
const outcome = await evalPlanStep(evalCase)({
|
|
200
|
+
job: evalJob(evalCase, kind),
|
|
201
|
+
stepIndex: 0,
|
|
202
|
+
now: (_options_now = options.now) != null ? _options_now : new Date(),
|
|
203
|
+
firestore: {},
|
|
204
|
+
modelFor: modelFor(options)
|
|
205
|
+
});
|
|
206
|
+
return {
|
|
207
|
+
source: 'recorded',
|
|
208
|
+
scope: 'plan',
|
|
209
|
+
step: 'job.plan',
|
|
210
|
+
model: outcome.model,
|
|
211
|
+
effort: (_outcome_effort = outcome.effort) != null ? _outcome_effort : null,
|
|
212
|
+
plan: planOf(outcome),
|
|
213
|
+
answer: null,
|
|
214
|
+
usage: outcome.usage,
|
|
215
|
+
note: ((_outcome_review = outcome.review) == null ? void 0 : _outcome_review.reason) === 'doctrine' ? `needs review: ${outcome.review.message}` : undefined
|
|
216
|
+
};
|
|
217
|
+
};
|
|
218
|
+
/** The most passes a recorded page may take: the plan limit's sections and the listing, with room. */ const AI_EVAL_PAGE_MAX_PASSES = 64;
|
|
219
|
+
/** One exchange's spend, as the machine meters a step. */ function recordedStep(step, outcome) {
|
|
220
|
+
return {
|
|
221
|
+
step,
|
|
222
|
+
model: outcome.model,
|
|
223
|
+
usage: outcome.usage,
|
|
224
|
+
estCostUsd: outcome.estCostUsd,
|
|
225
|
+
credits: assistCreditsFromUsd(outcome.estCostUsd)
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
/** Several exchanges' tokens, added. */ function totalUsage(steps) {
|
|
229
|
+
return steps.reduce((sum, step)=>({
|
|
230
|
+
inputTokens: sum.inputTokens + step.usage.inputTokens,
|
|
231
|
+
outputTokens: sum.outputTokens + step.usage.outputTokens,
|
|
232
|
+
cacheReadTokens: sum.cacheReadTokens + step.usage.cacheReadTokens,
|
|
233
|
+
cacheWriteTokens: sum.cacheWriteTokens + step.usage.cacheWriteTokens
|
|
234
|
+
}), {
|
|
235
|
+
inputTokens: 0,
|
|
236
|
+
outputTokens: 0,
|
|
237
|
+
cacheReadTokens: 0,
|
|
238
|
+
cacheWriteTokens: 0
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* The workspace a case describes, as the org document the page step reads:
|
|
243
|
+
* a workspace that keeps no reusable components is the Free plan's shape,
|
|
244
|
+
* and any other is a plan that keeps them.
|
|
245
|
+
*/ function evalOrgDocument(evalCase) {
|
|
246
|
+
var _evalCase_capabilities;
|
|
247
|
+
return ((_evalCase_capabilities = evalCase.capabilities) == null ? void 0 : _evalCase_capabilities.reusableComponents) === false ? {
|
|
248
|
+
plan: 'free'
|
|
249
|
+
} : {
|
|
250
|
+
plan: 'business',
|
|
251
|
+
billingStatus: 'active'
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* A page brief, recorded end to end (AGL-3030): the plan, confirmed, then the
|
|
256
|
+
* page step's passes until it reports the draft or stops for a person. What
|
|
257
|
+
* it answers is the page as the draft holds it, and what it spent is every
|
|
258
|
+
* exchange, each metered as the machine meters a step. A plan that stops for
|
|
259
|
+
* review records the plan alone, as a plan answer.
|
|
260
|
+
*/ const recordPage = async (evalCase, options)=>{
|
|
261
|
+
var _options_now, _ref, _ref1;
|
|
262
|
+
var _evalCase_inventory, _planned_review, _steps_find;
|
|
263
|
+
const now = (_options_now = options.now) != null ? _options_now : new Date();
|
|
264
|
+
const job = evalJob(evalCase, 'page');
|
|
265
|
+
const hostId = job.hostId;
|
|
266
|
+
const org = evalOrgDocument(evalCase);
|
|
267
|
+
// The page step names the site in the listing request by its host
|
|
268
|
+
// document, so the site is named only as the case names it (AGL-3077).
|
|
269
|
+
const site = aiEvalMemoryFirestore({
|
|
270
|
+
[`orgs/${EVAL_ORG}`]: org,
|
|
271
|
+
[`hosts/${hostId}`]: _extends({}, evalCase.siteName ? {
|
|
272
|
+
displayName: evalCase.siteName
|
|
273
|
+
} : {}, {
|
|
274
|
+
screens: Object.fromEntries(((_ref = (_evalCase_inventory = evalCase.inventory) == null ? void 0 : _evalCase_inventory.screens) != null ? _ref : []).map((row)=>[
|
|
275
|
+
row.id,
|
|
276
|
+
row.slug
|
|
277
|
+
]))
|
|
278
|
+
})
|
|
279
|
+
});
|
|
280
|
+
const context = {
|
|
281
|
+
stepIndex: 0,
|
|
282
|
+
now,
|
|
283
|
+
firestore: site.firestore,
|
|
284
|
+
modelFor: modelFor(options),
|
|
285
|
+
org
|
|
286
|
+
};
|
|
287
|
+
const planned = await evalPlanStep(evalCase)(_extends({}, context, {
|
|
288
|
+
job
|
|
289
|
+
}));
|
|
290
|
+
const steps = [
|
|
291
|
+
recordedStep('job.plan', planned)
|
|
292
|
+
];
|
|
293
|
+
const planOnly = (note)=>{
|
|
294
|
+
var _planned_effort;
|
|
295
|
+
return _extends({
|
|
296
|
+
source: 'recorded',
|
|
297
|
+
scope: 'plan',
|
|
298
|
+
step: 'job.plan',
|
|
299
|
+
model: planned.model,
|
|
300
|
+
effort: (_planned_effort = planned.effort) != null ? _planned_effort : null,
|
|
301
|
+
plan: planOf(planned),
|
|
302
|
+
answer: null,
|
|
303
|
+
usage: planned.usage
|
|
304
|
+
}, note ? {
|
|
305
|
+
note
|
|
306
|
+
} : {});
|
|
307
|
+
};
|
|
308
|
+
if (!planned.plan || ((_planned_review = planned.review) == null ? void 0 : _planned_review.reason) !== 'plan') {
|
|
309
|
+
return planOnly(planned.failure ? `refused before confirm: ${planned.failure}` : planned.review ? `needs review: ${planned.review.message}` : undefined);
|
|
310
|
+
}
|
|
311
|
+
const confirmed = _extends({}, job, {
|
|
312
|
+
plan: _extends({}, planned.plan, {
|
|
313
|
+
status: 'confirmed',
|
|
314
|
+
confirmedAt: now,
|
|
315
|
+
confirmedBy: 'eval'
|
|
316
|
+
})
|
|
317
|
+
});
|
|
318
|
+
// The site as the steps read it: the case's rows, and what the job has
|
|
319
|
+
// built there so far. A case's site holds no documents to copy, so a plan
|
|
320
|
+
// that starts from a copy is built from its brief, as a step builds one
|
|
321
|
+
// whose source is gone.
|
|
322
|
+
const readers = {
|
|
323
|
+
readInventory: async ()=>{
|
|
324
|
+
if (!evalCase.inventory) throw new Error(`${evalCase.id} has no inventory to build from`);
|
|
325
|
+
return aiEvalMemoryInventory(evalCase.inventory, site.docs);
|
|
326
|
+
},
|
|
327
|
+
duplicate: async ()=>({
|
|
328
|
+
ok: false,
|
|
329
|
+
status: 404,
|
|
330
|
+
error: 'A recording copies nothing'
|
|
331
|
+
})
|
|
332
|
+
};
|
|
333
|
+
const creations = {
|
|
334
|
+
layout: createAiJobLayoutStep(readers),
|
|
335
|
+
form: createAiJobFormStep(readers),
|
|
336
|
+
component: createAiJobComponentStep(readers)
|
|
337
|
+
};
|
|
338
|
+
const generate = createAiJobPageStep(_extends({}, readers, {
|
|
339
|
+
runnerFor: (kind)=>kind in creations ? creations[kind] : null
|
|
340
|
+
}));
|
|
341
|
+
const units = aiPageJobUnits(confirmed.plan);
|
|
342
|
+
let outputs = [];
|
|
343
|
+
let stopped;
|
|
344
|
+
for(let pass = 0; pass < AI_EVAL_PAGE_MAX_PASSES; pass += 1){
|
|
345
|
+
const [unit] = aiSitePendingUnits(units, outputs);
|
|
346
|
+
const outcome = await generate(_extends({}, context, {
|
|
347
|
+
stepIndex: 1,
|
|
348
|
+
job: _extends({}, confirmed, {
|
|
349
|
+
outputs
|
|
350
|
+
})
|
|
351
|
+
}));
|
|
352
|
+
// A creation's pass is its own step's; the pass that reports the draft
|
|
353
|
+
// asks for the listing, on the SEO step's model.
|
|
354
|
+
const kind = unit ? `job.${unit.jobKind}` : outcome.outputs.some((output)=>output.resource === 'screen') ? 'job.seo' : 'job.page';
|
|
355
|
+
steps.push(recordedStep(kind, outcome));
|
|
356
|
+
outputs = [
|
|
357
|
+
...outputs,
|
|
358
|
+
...outcome.outputs
|
|
359
|
+
];
|
|
360
|
+
if (outcome.refused || outcome.failure || outcome.review) {
|
|
361
|
+
var _outcome_failure;
|
|
362
|
+
var _outcome_review;
|
|
363
|
+
stopped = outcome.refused ? 'refused' : (_outcome_failure = outcome.failure) != null ? _outcome_failure : (_outcome_review = outcome.review) == null ? void 0 : _outcome_review.message;
|
|
364
|
+
break;
|
|
365
|
+
}
|
|
366
|
+
if (!outcome.continue) break;
|
|
367
|
+
}
|
|
368
|
+
const screenPath = `hosts/${hostId}/screens/${aiJobDraftId(confirmed, 'screen')}`;
|
|
369
|
+
const nodes = storedTree(site.docs, screenPath);
|
|
370
|
+
const built = outputs.some((output)=>output.resource === 'screen');
|
|
371
|
+
const screen = built && nodes ? recordedScreen(evalCase, site.docs, screenPath, outputs) : undefined;
|
|
372
|
+
return _extends({
|
|
373
|
+
source: 'recorded',
|
|
374
|
+
scope: built && nodes ? 'full' : 'plan',
|
|
375
|
+
step: 'job.page',
|
|
376
|
+
model: (_ref1 = (_steps_find = steps.find((entry)=>entry.step === 'job.page')) == null ? void 0 : _steps_find.model) != null ? _ref1 : planned.model,
|
|
377
|
+
effort: null,
|
|
378
|
+
plan: planOf(planned),
|
|
379
|
+
answer: built && nodes ? {
|
|
380
|
+
tree: {
|
|
381
|
+
rootId: CANVAS_ROOT_ELEMENT_ID,
|
|
382
|
+
nodes
|
|
383
|
+
}
|
|
384
|
+
} : null,
|
|
385
|
+
usage: totalUsage(steps),
|
|
386
|
+
steps
|
|
387
|
+
}, screen ? {
|
|
388
|
+
screen
|
|
389
|
+
} : {}, stopped ? {
|
|
390
|
+
note: `stopped: ${stopped}`
|
|
391
|
+
} : {});
|
|
392
|
+
};
|
|
393
|
+
/** A string a stored document holds, trimmed; `null` for none. */ function storedText(value) {
|
|
394
|
+
return typeof value === 'string' && value.trim() ? value.trim() : null;
|
|
395
|
+
}
|
|
396
|
+
/** The node map a versioned draft's first version holds, decoded; `null` when there is none. */ function storedTree(docs, draftPath) {
|
|
397
|
+
const draft = docs.get(draftPath);
|
|
398
|
+
const version = draft ? docs.get(`${draftPath}/versions/${String(draft['versionId'])}`) : undefined;
|
|
399
|
+
return version ? decodeStoredNodes(version['nodes']) : null;
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* The draft screen a page job wrote, as its grader reads it beside the tree
|
|
403
|
+
* (AGL-3073): the address and listing the draft holds, whether its output
|
|
404
|
+
* proposes a navigation entry, and the layout its version renders inside —
|
|
405
|
+
* one this job built, with its tree, or one the case's site lists by name.
|
|
406
|
+
*/ function recordedScreen(evalCase, docs, screenPath, outputs) {
|
|
407
|
+
var _screen_seo, _screen_slug, _ref, _storedText;
|
|
408
|
+
var _evalCase_inventory;
|
|
409
|
+
const screen = docs.get(screenPath);
|
|
410
|
+
const version = screen ? docs.get(`${screenPath}/versions/${String(screen['versionId'])}`) : undefined;
|
|
411
|
+
if (!screen || !version) return undefined;
|
|
412
|
+
const seo = (_screen_seo = screen['seo']) != null ? _screen_seo : {};
|
|
413
|
+
const layoutId = storedText(version['layoutId']);
|
|
414
|
+
const layoutPath = `${screenPath.split('/').slice(0, 2).join('/')}/layouts/${layoutId}`;
|
|
415
|
+
const built = layoutId ? docs.get(layoutPath) : undefined;
|
|
416
|
+
const listed = layoutId ? (_evalCase_inventory = evalCase.inventory) == null ? void 0 : _evalCase_inventory.layouts.find((row)=>row.id === layoutId) : undefined;
|
|
417
|
+
const nodes = built ? storedTree(docs, layoutPath) : null;
|
|
418
|
+
return {
|
|
419
|
+
slug: String((_screen_slug = screen['slug']) != null ? _screen_slug : ''),
|
|
420
|
+
seoTitle: storedText(seo['title']),
|
|
421
|
+
seoDescription: storedText(seo['description']),
|
|
422
|
+
nav: outputs.some((output)=>{
|
|
423
|
+
var _output_proposal;
|
|
424
|
+
return output.resource === 'screen' && ((_output_proposal = output.proposal) == null ? void 0 : _output_proposal['navigation']) != null;
|
|
425
|
+
}),
|
|
426
|
+
layout: layoutId && (built || listed) ? {
|
|
427
|
+
id: layoutId,
|
|
428
|
+
name: (_ref = (_storedText = storedText(built == null ? void 0 : built['displayName'])) != null ? _storedText : listed == null ? void 0 : listed.name) != null ? _ref : layoutId,
|
|
429
|
+
built: Boolean(built),
|
|
430
|
+
tree: nodes ? {
|
|
431
|
+
rootId: CANVAS_ROOT_ELEMENT_ID,
|
|
432
|
+
nodes
|
|
433
|
+
} : null
|
|
434
|
+
} : null
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
/** A text brief, recorded through the text step. */ const recordText = async (evalCase, options)=>{
|
|
438
|
+
var _options_now, _outcome_effort, _ref;
|
|
439
|
+
var _outcome_outputs_;
|
|
440
|
+
const outcome = await runAiJobTextStep({
|
|
441
|
+
job: evalJob(evalCase, 'text'),
|
|
442
|
+
stepIndex: 0,
|
|
443
|
+
now: (_options_now = options.now) != null ? _options_now : new Date(),
|
|
444
|
+
firestore: {},
|
|
445
|
+
modelFor: modelFor(options)
|
|
446
|
+
});
|
|
447
|
+
return {
|
|
448
|
+
source: 'recorded',
|
|
449
|
+
scope: 'full',
|
|
450
|
+
step: 'job.text',
|
|
451
|
+
model: outcome.model,
|
|
452
|
+
effort: (_outcome_effort = outcome.effort) != null ? _outcome_effort : null,
|
|
453
|
+
plan: null,
|
|
454
|
+
answer: (_ref = (_outcome_outputs_ = outcome.outputs[0]) == null ? void 0 : _outcome_outputs_.text) != null ? _ref : null,
|
|
455
|
+
usage: outcome.usage
|
|
456
|
+
};
|
|
457
|
+
};
|
|
458
|
+
/**
|
|
459
|
+
* A theme brief, recorded through the theme step's own generation call. The
|
|
460
|
+
* tool's raw input is kept, because that is what the harness scores; the
|
|
461
|
+
* site's theme comes from the case rather than a host document.
|
|
462
|
+
*/ const recordTheme = async (evalCase, options)=>{
|
|
463
|
+
var _options_model, _evalCase_themeSource;
|
|
464
|
+
const model = (_options_model = options.model) != null ? _options_model : aiModelForStep('job.theme');
|
|
465
|
+
let raw = null;
|
|
466
|
+
const generation = aiJobThemeGeneration({
|
|
467
|
+
brief: evalCase.brief,
|
|
468
|
+
mode: aiJobThemeMode(undefined),
|
|
469
|
+
theme: evalCase.siteTheme,
|
|
470
|
+
source: (_evalCase_themeSource = evalCase.themeSource) != null ? _evalCase_themeSource : 'default',
|
|
471
|
+
brand: [],
|
|
472
|
+
model
|
|
473
|
+
});
|
|
474
|
+
const result = await runValidatedGeneration('theme', withProvider(_extends({}, generation, {
|
|
475
|
+
check: (answer)=>{
|
|
476
|
+
raw = answer;
|
|
477
|
+
return aiJobThemeCheck(answer);
|
|
478
|
+
}
|
|
479
|
+
}), options));
|
|
480
|
+
return {
|
|
481
|
+
source: 'recorded',
|
|
482
|
+
scope: 'full',
|
|
483
|
+
step: 'job.theme',
|
|
484
|
+
model: result.model,
|
|
485
|
+
effort: result.effort,
|
|
486
|
+
plan: null,
|
|
487
|
+
answer: result.status === 'refused' ? null : raw,
|
|
488
|
+
usage: result.usage
|
|
489
|
+
};
|
|
490
|
+
};
|
|
491
|
+
/**
|
|
492
|
+
* The workflow step's generation for an automation brief (AGL-3074), from
|
|
493
|
+
* the case as the step's runner builds it from a site: a draft from what the
|
|
494
|
+
* workspace can run and the site's forms and datasets; or, for a brief about
|
|
495
|
+
* a saved action, an explanation from the action's outline, told whether the
|
|
496
|
+
* records its steps name exist as the runner reads only those, with a failed
|
|
497
|
+
* run's outline where the case gives one.
|
|
498
|
+
*/ export function aiEvalWorkflowGeneration(evalCase, model) {
|
|
499
|
+
var _evalCase_automationRecords;
|
|
500
|
+
const records = (_evalCase_automationRecords = evalCase.automationRecords) != null ? _evalCase_automationRecords : emptyAiAutomationRecords();
|
|
501
|
+
const automation = evalCase.automation;
|
|
502
|
+
if (!automation) {
|
|
503
|
+
var _evalCase_automationCapabilities;
|
|
504
|
+
return aiJobWorkflowDraftGeneration({
|
|
505
|
+
brief: evalCase.brief,
|
|
506
|
+
capabilities: (_evalCase_automationCapabilities = evalCase.automationCapabilities) != null ? _evalCase_automationCapabilities : AI_EVAL_AUTOMATION_CAPABILITIES,
|
|
507
|
+
records,
|
|
508
|
+
model
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
const named = aiActionRecordKinds(automation.action);
|
|
512
|
+
const read = Object.fromEntries(Object.entries(records).map(([kind, rows])=>[
|
|
513
|
+
kind,
|
|
514
|
+
named.includes(kind) ? rows : []
|
|
515
|
+
]));
|
|
516
|
+
return aiJobWorkflowExplainGeneration({
|
|
517
|
+
brief: evalCase.brief,
|
|
518
|
+
mode: automation.run ? 'diagnose' : 'explain',
|
|
519
|
+
outline: aiActionOutline(automation.action, read),
|
|
520
|
+
run: automation.run ? aiRunOutline(automation.run) : null,
|
|
521
|
+
model
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* An automation brief, recorded through the workflow step's own generation
|
|
526
|
+
* call (AGL-3074). The tool's raw input is kept, because that is what the
|
|
527
|
+
* harness scores: the step's runner turns a draft into the action it writes,
|
|
528
|
+
* and an explanation into the text it reports, and keeps the input nowhere.
|
|
529
|
+
*/ const recordWorkflow = async (evalCase, options)=>{
|
|
530
|
+
var _options_model;
|
|
531
|
+
const generation = aiEvalWorkflowGeneration(evalCase, (_options_model = options.model) != null ? _options_model : aiJobWorkflowModel());
|
|
532
|
+
let raw = null;
|
|
533
|
+
const result = await runValidatedGeneration('workflow', withProvider(_extends({}, generation, {
|
|
534
|
+
check: (answer)=>{
|
|
535
|
+
raw = answer;
|
|
536
|
+
return generation.check(answer);
|
|
537
|
+
}
|
|
538
|
+
}), options));
|
|
539
|
+
return _extends({
|
|
540
|
+
source: 'recorded',
|
|
541
|
+
scope: 'full',
|
|
542
|
+
step: 'job.workflow',
|
|
543
|
+
model: result.model,
|
|
544
|
+
effort: result.effort,
|
|
545
|
+
plan: null,
|
|
546
|
+
answer: result.status === 'refused' ? null : raw,
|
|
547
|
+
usage: result.usage,
|
|
548
|
+
steps: [
|
|
549
|
+
recordedStep('job.workflow', result)
|
|
550
|
+
]
|
|
551
|
+
}, result.status === 'needs_input' ? _extends({
|
|
552
|
+
note: `needs review: ${result.message}`
|
|
553
|
+
}, result.rawOutput ? {
|
|
554
|
+
rawOutput: result.rawOutput
|
|
555
|
+
} : {}) : {});
|
|
556
|
+
};
|
|
557
|
+
/** The site a case's job runs on: its inventory's, or the harness's own. */ function aiEvalSiteId(evalCase) {
|
|
558
|
+
var _ref;
|
|
559
|
+
var _evalCase_inventory;
|
|
560
|
+
return (_ref = (_evalCase_inventory = evalCase.inventory) == null ? void 0 : _evalCase_inventory.hostId) != null ? _ref : AI_EVAL_SITE_ID;
|
|
561
|
+
}
|
|
562
|
+
/** A products brief's job inputs, as the editor or a products card hands them over. */ function aiEvalProductsInputs(evalCase) {
|
|
563
|
+
if (evalCase.kind === 'catalog') return aiProductsJobInputs({
|
|
564
|
+
target: 'catalog'
|
|
565
|
+
});
|
|
566
|
+
if (evalCase.kind === 'categories') return aiProductsJobInputs({
|
|
567
|
+
target: 'categories'
|
|
568
|
+
});
|
|
569
|
+
if (!evalCase.product) throw new Error(`${evalCase.id} names no product to write copy for`);
|
|
570
|
+
return aiProductsJobInputs({
|
|
571
|
+
target: 'product',
|
|
572
|
+
product: evalCase.product.facts
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
/** A picture's content type, from the extension of the fixture that holds it. */ function fixtureContentType(file) {
|
|
576
|
+
const extension = file.slice(file.lastIndexOf('.') + 1).toLowerCase();
|
|
577
|
+
return `image/${extension === 'jpg' ? 'jpeg' : extension}`;
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* The storage read of a case's media library (AGL-3074): the fixture the case
|
|
581
|
+
* names for an asset of its own site's library, and nothing for any other. It
|
|
582
|
+
* stands in for the download alone, so the products step's own path still
|
|
583
|
+
* locates the photo, decodes it, turns it upright, resizes it and encodes the
|
|
584
|
+
* JPEG the model is sent.
|
|
585
|
+
*/ function aiEvalMediaSeams(evalCase, options) {
|
|
586
|
+
return {
|
|
587
|
+
readBytes: async (_firestore, location, hostId)=>{
|
|
588
|
+
var _evalCase_media;
|
|
589
|
+
const own = !location.scope.isOrg && location.scope.scopeId === hostId;
|
|
590
|
+
const file = own ? (_evalCase_media = evalCase.media) == null ? void 0 : _evalCase_media[location.mediaId] : undefined;
|
|
591
|
+
if (!file || !options.readFixture) return null;
|
|
592
|
+
return {
|
|
593
|
+
buffer: await options.readFixture(file),
|
|
594
|
+
contentType: fixtureContentType(file)
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* A product case's photo, as the products step sends it (AGL-3074): read
|
|
601
|
+
* through `ai-product-image.ts` from the case's fixtures. `null` for a case
|
|
602
|
+
* whose product has none, or whose photo cannot be read.
|
|
603
|
+
*/ export async function aiEvalCasePhoto(evalCase, options) {
|
|
604
|
+
var _evalCase_product;
|
|
605
|
+
const value = (_evalCase_product = evalCase.product) == null ? void 0 : _evalCase_product.facts.imageUrl;
|
|
606
|
+
if (!value) return null;
|
|
607
|
+
const read = await readAiProductImage({}, {
|
|
608
|
+
value,
|
|
609
|
+
hostId: aiEvalSiteId(evalCase),
|
|
610
|
+
orgId: EVAL_ORG
|
|
611
|
+
}, aiEvalMediaSeams(evalCase, options));
|
|
612
|
+
return read.status === 'read' ? read.image : null;
|
|
613
|
+
}
|
|
614
|
+
/**
|
|
615
|
+
* A products proposal read back as the tool input the harness scores
|
|
616
|
+
* (AGL-3074): a product's copy as its check kept it, a catalog's products,
|
|
617
|
+
* and categories with each discount in the tool's own terms — a percentage or
|
|
618
|
+
* dollars, a minimum order in dollars, and an empty code for a discount that
|
|
619
|
+
* applies on its own.
|
|
620
|
+
*/ export function aiEvalProductsAnswer(proposal) {
|
|
621
|
+
if (proposal.kind === 'copy') return proposal.values ? _extends({}, proposal.values) : null;
|
|
622
|
+
if (proposal.kind === 'catalog') return {
|
|
623
|
+
products: proposal.products
|
|
624
|
+
};
|
|
625
|
+
return {
|
|
626
|
+
categories: proposal.categories,
|
|
627
|
+
discounts: proposal.discounts.map((discount)=>{
|
|
628
|
+
var _discount_code, _discount_valuePct, _discount_valueCents, _discount_minSubtotalCents;
|
|
629
|
+
return {
|
|
630
|
+
name: discount.name,
|
|
631
|
+
code: (_discount_code = discount.code) != null ? _discount_code : '',
|
|
632
|
+
kind: discount.kind,
|
|
633
|
+
value: discount.kind === 'percent' ? (_discount_valuePct = discount.valuePct) != null ? _discount_valuePct : 0 : discount.kind === 'fixed' ? ((_discount_valueCents = discount.valueCents) != null ? _discount_valueCents : 0) / 100 : 0,
|
|
634
|
+
minimumOrderUsd: ((_discount_minSubtotalCents = discount.minSubtotalCents) != null ? _discount_minSubtotalCents : 0) / 100,
|
|
635
|
+
why: discount.why
|
|
636
|
+
};
|
|
637
|
+
})
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* A products brief — one product's copy, a store's first catalog, or its
|
|
642
|
+
* categories and discounts — recorded through the products step's runner
|
|
643
|
+
* (AGL-3074), against the case's store kept in memory: its name, its
|
|
644
|
+
* categories, and the photo its media names. What it answers is read back off
|
|
645
|
+
* the proposal the runner makes.
|
|
646
|
+
*/ const recordProducts = async (evalCase, options)=>{
|
|
647
|
+
var _ref, _evalCase_existingCategoryNames, _options_now, _outcome_effort;
|
|
648
|
+
var _evalCase_product;
|
|
649
|
+
const hostId = aiEvalSiteId(evalCase);
|
|
650
|
+
const categories = evalCase.kind === 'product' ? (_ref = (_evalCase_product = evalCase.product) == null ? void 0 : _evalCase_product.categories) != null ? _ref : [] : ((_evalCase_existingCategoryNames = evalCase.existingCategoryNames) != null ? _evalCase_existingCategoryNames : []).map((name, index)=>({
|
|
651
|
+
id: `category-${index + 1}`,
|
|
652
|
+
name
|
|
653
|
+
}));
|
|
654
|
+
const store = aiEvalMemoryFirestore(_extends({
|
|
655
|
+
[`hosts/${hostId}`]: _extends({
|
|
656
|
+
orgId: EVAL_ORG
|
|
657
|
+
}, evalCase.siteName ? {
|
|
658
|
+
displayName: evalCase.siteName
|
|
659
|
+
} : {})
|
|
660
|
+
}, Object.fromEntries(categories.map((category)=>[
|
|
661
|
+
`hosts/${hostId}/productCategories/${category.id}`,
|
|
662
|
+
{
|
|
663
|
+
name: category.name
|
|
664
|
+
}
|
|
665
|
+
]))));
|
|
666
|
+
const outcome = await createAiJobProductsStep({
|
|
667
|
+
image: aiEvalMediaSeams(evalCase, options)
|
|
668
|
+
})({
|
|
669
|
+
job: _extends({}, evalJob(evalCase, 'products'), {
|
|
670
|
+
hostId,
|
|
671
|
+
inputs: aiEvalProductsInputs(evalCase)
|
|
672
|
+
}),
|
|
673
|
+
stepIndex: 0,
|
|
674
|
+
now: (_options_now = options.now) != null ? _options_now : new Date(),
|
|
675
|
+
firestore: store.firestore,
|
|
676
|
+
modelFor: modelFor(options)
|
|
677
|
+
});
|
|
678
|
+
const proposal = aiProductsProposalOf(outcome.outputs[0]);
|
|
679
|
+
const stopped = outcome.refused ? 'refused' : outcome.failure;
|
|
680
|
+
return _extends({
|
|
681
|
+
source: 'recorded',
|
|
682
|
+
scope: 'full',
|
|
683
|
+
step: 'job.products',
|
|
684
|
+
model: outcome.model,
|
|
685
|
+
effort: (_outcome_effort = outcome.effort) != null ? _outcome_effort : null,
|
|
686
|
+
plan: null,
|
|
687
|
+
answer: proposal ? aiEvalProductsAnswer(proposal) : null,
|
|
688
|
+
usage: outcome.usage,
|
|
689
|
+
steps: [
|
|
690
|
+
recordedStep('job.products', outcome)
|
|
691
|
+
]
|
|
692
|
+
}, (proposal == null ? void 0 : proposal.kind) === 'copy' ? {
|
|
693
|
+
photo: proposal.photo
|
|
694
|
+
} : {}, stopped ? {
|
|
695
|
+
note: `stopped: ${stopped}`
|
|
696
|
+
} : {});
|
|
697
|
+
};
|
|
698
|
+
/**
|
|
699
|
+
* Every kind no recorder covers, and the door that answers it in production
|
|
700
|
+
* (AGL-3074), so a skipped brief says why truthfully. Modules are named from
|
|
701
|
+
* the plugin's `src/lib`.
|
|
702
|
+
*/ export const AI_EVAL_UNRECORDED_DOORS = {
|
|
703
|
+
section: {
|
|
704
|
+
route: 'the copy assistant’s section mode',
|
|
705
|
+
file: 'server/ai-assist.ts'
|
|
706
|
+
},
|
|
707
|
+
element: {
|
|
708
|
+
route: 'the copy assistant’s element mode',
|
|
709
|
+
file: 'server/ai-assist.ts'
|
|
710
|
+
},
|
|
711
|
+
blog: {
|
|
712
|
+
route: 'the copy assistant’s blog mode',
|
|
713
|
+
file: 'server/ai-assist.ts'
|
|
714
|
+
},
|
|
715
|
+
chat: {
|
|
716
|
+
route: 'the console assistant',
|
|
717
|
+
file: 'server/assist-chat.ts'
|
|
718
|
+
},
|
|
719
|
+
email: {
|
|
720
|
+
step: 'email',
|
|
721
|
+
file: 'jobs/ai-job-email-step.ts'
|
|
722
|
+
},
|
|
723
|
+
seo: {
|
|
724
|
+
step: 'seo',
|
|
725
|
+
file: 'jobs/ai-job-seo-step.ts'
|
|
726
|
+
},
|
|
727
|
+
insight: {
|
|
728
|
+
step: 'insight',
|
|
729
|
+
file: 'jobs/ai-job-insight-step.ts'
|
|
730
|
+
},
|
|
731
|
+
crm: {
|
|
732
|
+
step: 'crm',
|
|
733
|
+
file: 'jobs/ai-job-crm-step.ts'
|
|
734
|
+
},
|
|
735
|
+
experiment: {
|
|
736
|
+
step: 'experiment',
|
|
737
|
+
file: 'jobs/ai-job-experiment-step.ts'
|
|
738
|
+
}
|
|
739
|
+
};
|
|
740
|
+
/** Why a brief of this kind is skipped: where its door is, and that no recorder drives it. */ export function aiEvalSkipReason(kind) {
|
|
741
|
+
const door = AI_EVAL_UNRECORDED_DOORS[kind];
|
|
742
|
+
if (!door) return 'no recorder: none is registered for this kind';
|
|
743
|
+
return 'route' in door ? `no recorder: the door that answers this kind is a request route (${door.route}, ${door.file}), not a job step` : `no recorder yet: this kind is answered by the ${door.step} job step (${door.file}), which no recorder drives`;
|
|
744
|
+
}
|
|
745
|
+
for (const kind of Object.keys(AI_EVAL_TREE_OUTPUT)){
|
|
746
|
+
// A section rewrite is the copy assistant's, a request route: no plan step.
|
|
747
|
+
if (kind !== 'section' && kind !== 'email') registerAiEvalRecorder(kind, recordPlan);
|
|
748
|
+
}
|
|
749
|
+
registerAiEvalRecorder('page', recordPage);
|
|
750
|
+
registerAiEvalRecorder('text', recordText);
|
|
751
|
+
registerAiEvalRecorder('theme', recordTheme);
|
|
752
|
+
registerAiEvalRecorder('workflow', recordWorkflow);
|
|
753
|
+
for (const kind of [
|
|
754
|
+
'product',
|
|
755
|
+
'catalog',
|
|
756
|
+
'categories'
|
|
757
|
+
])registerAiEvalRecorder(kind, recordProducts);
|
|
758
|
+
// ── The grader ────────────────────────────────────────────────────────────
|
|
759
|
+
export const AI_EVAL_RUBRIC_TOOL = {
|
|
760
|
+
name: 'grade_output',
|
|
761
|
+
description: 'Grade the output against the brief it was built from.',
|
|
762
|
+
strict: true,
|
|
763
|
+
inputSchema: {
|
|
764
|
+
type: 'object',
|
|
765
|
+
additionalProperties: false,
|
|
766
|
+
required: [
|
|
767
|
+
'structure',
|
|
768
|
+
'copy',
|
|
769
|
+
'reuse',
|
|
770
|
+
'notes'
|
|
771
|
+
],
|
|
772
|
+
properties: {
|
|
773
|
+
structure: {
|
|
774
|
+
type: 'integer',
|
|
775
|
+
description: 'From 1 (fails) to 5 (excellent): organized the way the brief needs, with nothing missing or extra.'
|
|
776
|
+
},
|
|
777
|
+
copy: {
|
|
778
|
+
type: 'integer',
|
|
779
|
+
description: 'From 1 to 5: the words fit the brief, the site and its audience, with no filler.'
|
|
780
|
+
},
|
|
781
|
+
reuse: {
|
|
782
|
+
anyOf: [
|
|
783
|
+
{
|
|
784
|
+
type: 'integer'
|
|
785
|
+
},
|
|
786
|
+
{
|
|
787
|
+
type: 'null'
|
|
788
|
+
}
|
|
789
|
+
],
|
|
790
|
+
description: 'From 1 to 5: reuses what the site inventory lists before creating, and creates only what nothing listed can do. Null when the output places nothing.'
|
|
791
|
+
},
|
|
792
|
+
notes: {
|
|
793
|
+
type: 'string',
|
|
794
|
+
description: 'One or two sentences on the lowest grade.'
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
};
|
|
799
|
+
export const AI_EVAL_GRADER_INSTRUCTIONS = [
|
|
800
|
+
{
|
|
801
|
+
text: 'You grade an output an AI produced for a website builder, against the brief it was built from and the site it was built for. ' + 'Grade three things from 1 to 5: structure, copy and reuse, as the grade_output tool describes them. ' + 'A 3 is usable as it is; a 5 is what a careful professional would have made. Judge only what you are shown, and answer through grade_output.'
|
|
802
|
+
}
|
|
803
|
+
];
|
|
804
|
+
/**
|
|
805
|
+
* What a build plan is, for the grader that grades one (AGL-3022).
|
|
806
|
+
*
|
|
807
|
+
* A plan is the proposal a member confirms, not the document the generation
|
|
808
|
+
* step writes afterwards, and `AI_BUILD_PLAN_TOOL` gives it nowhere to put a
|
|
809
|
+
* node tree, a heading order, a landmark or a line of body copy. Graded
|
|
810
|
+
* against a finished page it loses marks for every one of those, on structure
|
|
811
|
+
* and on copy alike, and the grade then measures the schema rather than the
|
|
812
|
+
* answer. So the grader is told the shape it is reading, and that the only
|
|
813
|
+
* words a plan writes are the ones `aiPlanCopy` collects.
|
|
814
|
+
*
|
|
815
|
+
* Two facts it cannot read off the schema are stated as well. Only a page or
|
|
816
|
+
* site job plans screens: the plan step tells a component, layout, template,
|
|
817
|
+
* form or email job to plan none, so an empty `screens` on one of those is the
|
|
818
|
+
* answer working, and a grader that does not know it marks the plan down for
|
|
819
|
+
* doing what it was told. And a search is an element, never a form: a form
|
|
820
|
+
* collects submissions, and a grader that does not know the platform has
|
|
821
|
+
* search elements rewards a plan for creating a form to search with.
|
|
822
|
+
*/ export const AI_EVAL_PLAN_GRADER_NOTE = 'A build plan is not the finished output: it is the proposal a member confirms before anything is generated. ' + 'It holds only what it reuses by inventory id, what it creates and why, and its screens — each with a title, ' + 'slug, layout, template, nav flag, search title and description, and sections named with what they place and ' + 'how many items they hold. Only a page or site job plans screens: the plan of a component, layout, template, ' + 'form or email job has an empty screens list by design, and is never marked down for it. ' + 'A plan has no node tree, no heading order, no landmarks, no theme tokens, no field ' + 'labels or validation, and no body copy; all of those belong to the generation step that runs once the plan is ' + 'confirmed. Grade structure on whether the plan proposes the right screens, sections, fields and creations for ' + 'the brief. A search box is a Search Box element, or a Collection Search element over a collection\'s entries, ' + 'and never a new form, because a form collects submissions: a plan that creates a form to search with has ' + 'proposed the wrong creation. Grade copy on the only words a plan writes: screen titles, search titles and ' + 'descriptions, and the names and rationales of what it creates. Do not mark a plan down for anything a plan ' + 'cannot hold.';
|
|
823
|
+
/**
|
|
824
|
+
* What the grader of a case with capabilities is told beside them (AGL-3040).
|
|
825
|
+
*
|
|
826
|
+
* A case that describes its workspace had its plan told what that workspace
|
|
827
|
+
* may create, and a workspace that keeps no reusable components or saved
|
|
828
|
+
* forms builds the only way it can: a list's repeated items drawn in one
|
|
829
|
+
* section, the form carried by the page. A grader told none of that grades
|
|
830
|
+
* the doctrine whole, and marks reuse down for exactly the build the workspace
|
|
831
|
+
* required, as it marked down the first live recording of the Free brief.
|
|
832
|
+
*/ export const AI_EVAL_CAPABILITIES_GRADER_NOTE = 'Grade reuse against what this workspace may create, as listed above: never mark an output down for not creating what the workspace may not make.';
|
|
833
|
+
/**
|
|
834
|
+
* Beside that, where the workspace keeps no reusable components or saved
|
|
835
|
+
* forms, which the capability lines have already said.
|
|
836
|
+
*/ export const AI_EVAL_INLINE_GRADER_NOTE = "On this workspace, a list's repeated items drawn in one section and a form drawn on the page, as a Form element holding its Form Fields, are the correct build and never a missed reuse.";
|
|
837
|
+
/**
|
|
838
|
+
* What a section's `uses` can hold on such a workspace (AGL-3143).
|
|
839
|
+
*
|
|
840
|
+
* A section's `uses` names what the section PLACES, and the only things it
|
|
841
|
+
* can name are an inventory id and a `new:<name>` creation. A workspace that
|
|
842
|
+
* keeps no reusable components or saved forms has neither to name, and the
|
|
843
|
+
* capability lines above have already refused the job the creations — so
|
|
844
|
+
* every inline section leaves `uses` empty because it is the only answer it
|
|
845
|
+
* can give. A grader that does not know it reads the empty list as a section
|
|
846
|
+
* that places nothing and marks the plan down for being correct, which drags
|
|
847
|
+
* the floors down for a reason that has nothing to do with the answer.
|
|
848
|
+
*/ export const AI_EVAL_INLINE_USES_GRADER_NOTE = "A section's `uses` names what it places, and can name only an inventory id or a creation. This workspace has neither to place and may create neither, so an empty `uses` on a section drawn inline is the only answer there is: never mark a plan or a section down for it.";
|
|
849
|
+
/**
|
|
850
|
+
* The workspace a case describes, as its grader reads it: the capability
|
|
851
|
+
* lines the plan step was sent, narrowed to what the job builds the way the
|
|
852
|
+
* step narrows them, and the notes on grading against them. `null` for a
|
|
853
|
+
* case that describes no workspace, whose output is held to the doctrine
|
|
854
|
+
* whole.
|
|
855
|
+
*/ export function aiEvalGraderCapabilities(evalCase) {
|
|
856
|
+
var _AI_JOB_PLAN_SCOPES_evalCase_kind;
|
|
857
|
+
if (!evalCase.capabilities) return null;
|
|
858
|
+
const scope = (_AI_JOB_PLAN_SCOPES_evalCase_kind = AI_JOB_PLAN_SCOPES[evalCase.kind]) != null ? _AI_JOB_PLAN_SCOPES_evalCase_kind : null;
|
|
859
|
+
const capabilities = aiPlanCapabilitiesForJob(evalCase.capabilities, scope);
|
|
860
|
+
return [
|
|
861
|
+
...aiPlanCapabilityLines(capabilities),
|
|
862
|
+
AI_EVAL_CAPABILITIES_GRADER_NOTE,
|
|
863
|
+
...capabilities.reusableComponents ? [] : [
|
|
864
|
+
AI_EVAL_INLINE_GRADER_NOTE,
|
|
865
|
+
AI_EVAL_INLINE_USES_GRADER_NOTE
|
|
866
|
+
]
|
|
867
|
+
].join('\n');
|
|
868
|
+
}
|
|
869
|
+
/**
|
|
870
|
+
* What the grader of a built page is told beside it (AGL-3073).
|
|
871
|
+
*
|
|
872
|
+
* A page's tree holds only what the page adds. The site's header, navigation
|
|
873
|
+
* and footer are its layout's; the one main landmark is placed when the page
|
|
874
|
+
* is composed (`stampDocumentLandmark`), on the layout's slot where the page
|
|
875
|
+
* renders, or on the page's root when it renders inside none; and its address
|
|
876
|
+
* and listing are fields of its screen. A grader shown the tree alone marks a
|
|
877
|
+
* page down for a layout, a landmark and a listing the page has.
|
|
878
|
+
*/ export const AI_EVAL_PAGE_GRADER_NOTE = 'A page is built inside its layout and published as a screen, so its tree holds only what the page adds: ' + "the site's header, navigation and footer are the layout's; the page's one main landmark is placed when it is " + "published, on the layout's slot where the page renders, or on the page's root when it has no layout; and its " + 'address, search title and description are fields of its screen. Grade the page as built with all of them, as ' + 'listed above, and never mark it down for a layout, a landmark, an address or a listing its tree does not repeat.';
|
|
879
|
+
/** The most elements a layout's outline lists; the rest are counted. */ export const AI_EVAL_LAYOUT_OUTLINE_MAX_LINES = 40;
|
|
880
|
+
/** The most characters of an element's own text an outline quotes. */ const AI_EVAL_OUTLINE_TEXT_MAX_CHARS = 48;
|
|
881
|
+
const isLoose = (value)=>typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
882
|
+
const looseList = (value)=>Array.isArray(value) ? value.filter(isLoose) : [];
|
|
883
|
+
const looseText = (value)=>typeof value === 'string' ? value.replace(/\s+/g, ' ').trim() : '';
|
|
884
|
+
/** An element of an outline: what it renders as, its component, and its own text, quoted short. */ function outlineLine(node) {
|
|
885
|
+
var _node_componentId;
|
|
886
|
+
const props = isLoose(node['props']) ? node['props'] : {};
|
|
887
|
+
const element = looseText(props['component']) || looseText(props['element']);
|
|
888
|
+
const text = looseText(props['children']);
|
|
889
|
+
const quoted = text ? ` "${text.length > AI_EVAL_OUTLINE_TEXT_MAX_CHARS ? `${text.slice(0, AI_EVAL_OUTLINE_TEXT_MAX_CHARS)}…` : text}"` : '';
|
|
890
|
+
const slot = node['componentId'] === LAYOUT_SLOT_COMPONENT_ID ? ' ← the page renders here' : '';
|
|
891
|
+
return `${element ? `${element} · ` : ''}${String((_node_componentId = node['componentId']) != null ? _node_componentId : '?')}${quoted}${slot}`;
|
|
892
|
+
}
|
|
893
|
+
/**
|
|
894
|
+
* A layout as its page's grader reads it (AGL-3073): an element a line,
|
|
895
|
+
* indented by depth, named by the element it renders as and its component,
|
|
896
|
+
* with its own text quoted short. The main landmark is placed where a
|
|
897
|
+
* published page places it, by the function that places it there.
|
|
898
|
+
*/ export function aiEvalLayoutOutline(tree) {
|
|
899
|
+
const nodes = stampDocumentLandmark(tree.nodes);
|
|
900
|
+
const lines = [];
|
|
901
|
+
const seen = new Set();
|
|
902
|
+
let unlisted = 0;
|
|
903
|
+
const visit = (id, depth)=>{
|
|
904
|
+
const node = nodes[id];
|
|
905
|
+
if (!isLoose(node) || seen.has(id)) return;
|
|
906
|
+
seen.add(id);
|
|
907
|
+
if (lines.length < AI_EVAL_LAYOUT_OUTLINE_MAX_LINES) lines.push(`${' '.repeat(depth)}${outlineLine(node)}`);
|
|
908
|
+
else unlisted += 1;
|
|
909
|
+
for (const child of Array.isArray(node['nodes']) ? node['nodes'] : []){
|
|
910
|
+
if (typeof child === 'string') visit(child, depth + 1);
|
|
911
|
+
}
|
|
912
|
+
};
|
|
913
|
+
visit(tree.rootId, 0);
|
|
914
|
+
return [
|
|
915
|
+
...lines,
|
|
916
|
+
...unlisted ? [
|
|
917
|
+
`… and ${unlisted} more elements`
|
|
918
|
+
] : []
|
|
919
|
+
].join('\n');
|
|
920
|
+
}
|
|
921
|
+
/**
|
|
922
|
+
* The plan a built answer came from, as its grader reads it (AGL-3073): what
|
|
923
|
+
* it reuses and creates, and each screen's sections in order, with what each
|
|
924
|
+
* places and how many items it shows. `null` for an answer with no plan.
|
|
925
|
+
*/ export function aiEvalPlanOutline(plan) {
|
|
926
|
+
if (!isLoose(plan)) return null;
|
|
927
|
+
const lines = [
|
|
928
|
+
'The plan it was built from, as the member confirmed it:'
|
|
929
|
+
];
|
|
930
|
+
for (const entry of looseList(plan['reuse'])){
|
|
931
|
+
lines.push(`- reuses the ${looseText(entry['kind'])} ${looseText(entry['id'])}: ${looseText(entry['purpose'])}`);
|
|
932
|
+
}
|
|
933
|
+
for (const entry of looseList(plan['create'])){
|
|
934
|
+
const fields = Array.isArray(entry['fields']) ? entry['fields'].map(looseText).filter(Boolean) : [];
|
|
935
|
+
lines.push(`- creates the ${looseText(entry['kind'])} "${looseText(entry['name'])}": ${looseText(entry['why'])}${fields.length ? ` Holds: ${fields.join('; ')}.` : ''}`);
|
|
936
|
+
}
|
|
937
|
+
for (const screen of looseList(plan['screens'])){
|
|
938
|
+
lines.push(`- the screen "${looseText(screen['title'])}", its sections in order:`);
|
|
939
|
+
looseList(screen['sections']).forEach((section, index)=>{
|
|
940
|
+
const uses = Array.isArray(section['uses']) ? section['uses'].map(looseText).filter(Boolean) : [];
|
|
941
|
+
const items = Number(section['items']);
|
|
942
|
+
lines.push(` ${index + 1}. ${looseText(section['name'])}${uses.length ? `, placing ${uses.join(', ')}` : ''}${items > 0 ? `, ${items} ${items === 1 ? 'item' : 'items'}` : ''}`);
|
|
943
|
+
});
|
|
944
|
+
}
|
|
945
|
+
return lines.length > 1 ? lines.join('\n') : null;
|
|
946
|
+
}
|
|
947
|
+
/** A screen's address as a visitor reads it. */ function addressOf(slug) {
|
|
948
|
+
return slug.startsWith('/') ? slug : `/${slug}`;
|
|
949
|
+
}
|
|
950
|
+
/** A listing value quoted, or said to be missing. */ function listingValue(value) {
|
|
951
|
+
return value ? `"${value}"` : 'none';
|
|
952
|
+
}
|
|
953
|
+
/**
|
|
954
|
+
* A built page's screen and layout, as its grader reads them (AGL-3073). A
|
|
955
|
+
* recording that kept the draft's screen gives its stored address and listing
|
|
956
|
+
* and the layout it renders inside, outlined from its tree. One made before
|
|
957
|
+
* that was kept gives its plan's screen instead, which is what the page step
|
|
958
|
+
* builds from, and says so.
|
|
959
|
+
*/ export function aiEvalBuiltPage(answer) {
|
|
960
|
+
var _answer_steps;
|
|
961
|
+
const recorded = answer.screen;
|
|
962
|
+
const plan = isLoose(answer.plan) ? answer.plan : null;
|
|
963
|
+
const planned = looseList(plan == null ? void 0 : plan['screens'])[0];
|
|
964
|
+
if (!recorded && !planned) return null;
|
|
965
|
+
if (recorded) {
|
|
966
|
+
const layout = recorded.layout;
|
|
967
|
+
return [
|
|
968
|
+
'The page as built:',
|
|
969
|
+
`- address: ${addressOf(recorded.slug)}`,
|
|
970
|
+
`- search title: ${listingValue(recorded.seoTitle)}`,
|
|
971
|
+
`- search description: ${listingValue(recorded.seoDescription)}`,
|
|
972
|
+
`- navigation: ${recorded.nav ? 'an entry for the page is proposed' : 'no entry is proposed'}`,
|
|
973
|
+
!layout ? "- layout: none, so the page's root is its main landmark" : layout.tree ? `- layout: "${layout.name}", ${layout.built ? 'built by this job before the page' : 'which the site already had'}. Its outline:\n${aiEvalLayoutOutline(layout.tree).split('\n').map((line)=>` ${line}`).join('\n')}` : `- layout: "${layout.name}", which the site already has; the page renders in its slot`
|
|
974
|
+
].join('\n');
|
|
975
|
+
}
|
|
976
|
+
const reference = looseText(planned['layout']);
|
|
977
|
+
const created = reference.startsWith('new:') ? looseList(plan == null ? void 0 : plan['create']).find((entry)=>entry['kind'] === 'layout' && looseText(entry['name']) === reference.slice('new:'.length)) : undefined;
|
|
978
|
+
const builtByJob = ((_answer_steps = answer.steps) != null ? _answer_steps : []).some((step)=>step.step === 'job.layout');
|
|
979
|
+
// What the layout holds is on the plan's own line above; it is not repeated.
|
|
980
|
+
const layout = !reference ? "- layout: none, so the page's root is its main landmark" : created ? `- layout: "${looseText(created['name'])}", which the plan creates${builtByJob ? ' and this job built before the page' : ''}. A layout this job builds holds one Layout Slot, where the page renders, and the slot is the page's main landmark; the recording kept no tree of it` : `- layout: ${reference}, which the site already has; the page renders in its slot`;
|
|
981
|
+
return [
|
|
982
|
+
"The page as its plan built it (the recording kept no record of the draft's screen):",
|
|
983
|
+
`- address: ${addressOf(looseText(planned['slug']))}`,
|
|
984
|
+
`- search title, as planned: ${listingValue(looseText(planned['seoTitle']))}`,
|
|
985
|
+
`- search description, as planned: ${listingValue(looseText(planned['seoDescription']))}`,
|
|
986
|
+
`- navigation: ${planned['nav'] === true ? 'an entry for the page is proposed' : 'no entry is proposed'}`,
|
|
987
|
+
layout
|
|
988
|
+
].join('\n');
|
|
989
|
+
}
|
|
990
|
+
/** Keys a stored node keeps for the store: its own id, its parent, its node type and its plugin. */ function graderNode(id, node) {
|
|
991
|
+
if (!isLoose(node)) return node;
|
|
992
|
+
const kept = {};
|
|
993
|
+
for (const [key, value] of Object.entries(node)){
|
|
994
|
+
if (key === 'parentId' || key === 'pluginId') continue;
|
|
995
|
+
if (key === '$id' && value === id) continue;
|
|
996
|
+
if (key === 'type' && value === 'node') continue;
|
|
997
|
+
kept[key] = value;
|
|
998
|
+
}
|
|
999
|
+
return kept;
|
|
1000
|
+
}
|
|
1001
|
+
/**
|
|
1002
|
+
* An answer's output as its grader reads it: copy as written, and anything
|
|
1003
|
+
* else as JSON, a tree's nodes without the keys the store keeps for itself
|
|
1004
|
+
* (AGL-3073). Those are about a third of a recorded page, and a grade reads
|
|
1005
|
+
* none of them: a node's id is its key and its parent is the node that lists
|
|
1006
|
+
* it.
|
|
1007
|
+
*/ export function aiEvalGraderOutput(output) {
|
|
1008
|
+
if (typeof output === 'string') return output;
|
|
1009
|
+
const tree = isLoose(output) && isLoose(output['tree']) ? output['tree'] : null;
|
|
1010
|
+
if (!isLoose(output) || !tree || !isLoose(tree['nodes'])) return JSON.stringify(output);
|
|
1011
|
+
const nodes = Object.fromEntries(Object.entries(tree['nodes']).map(([id, node])=>[
|
|
1012
|
+
id,
|
|
1013
|
+
graderNode(id, node)
|
|
1014
|
+
]));
|
|
1015
|
+
return JSON.stringify(_extends({}, output, {
|
|
1016
|
+
tree: _extends({}, tree, {
|
|
1017
|
+
nodes
|
|
1018
|
+
})
|
|
1019
|
+
}));
|
|
1020
|
+
}
|
|
1021
|
+
/**
|
|
1022
|
+
* What the grader of an answer written from more than its brief is shown of
|
|
1023
|
+
* the request (AGL-3074): the user turn the kind's step sends, built from the
|
|
1024
|
+
* case by the step's own prompt builder — a product and the categories it may
|
|
1025
|
+
* go in, the categories a store already has, or what an automation draft or
|
|
1026
|
+
* explanation was asked from. A grader that is not shown them grades copy
|
|
1027
|
+
* against a product it never saw, and an explanation against an automation it
|
|
1028
|
+
* never read. `null` for a kind whose request is its brief and its site.
|
|
1029
|
+
*/ export function aiEvalGraderRequest(evalCase, answer) {
|
|
1030
|
+
if (evalCase.kind === 'product') {
|
|
1031
|
+
var _evalCase_siteName;
|
|
1032
|
+
return evalCase.product ? aiProductCopyPrompt({
|
|
1033
|
+
store: (_evalCase_siteName = evalCase.siteName) != null ? _evalCase_siteName : '',
|
|
1034
|
+
product: evalCase.product.facts,
|
|
1035
|
+
categories: evalCase.product.categories,
|
|
1036
|
+
photo: answer.photo === 'read'
|
|
1037
|
+
}) : null;
|
|
1038
|
+
}
|
|
1039
|
+
if (evalCase.kind === 'categories') {
|
|
1040
|
+
var _evalCase_siteName1, _evalCase_existingCategoryNames;
|
|
1041
|
+
return aiCategoriesPrompt({
|
|
1042
|
+
store: (_evalCase_siteName1 = evalCase.siteName) != null ? _evalCase_siteName1 : '',
|
|
1043
|
+
brief: evalCase.brief,
|
|
1044
|
+
existingCategoryNames: (_evalCase_existingCategoryNames = evalCase.existingCategoryNames) != null ? _evalCase_existingCategoryNames : []
|
|
1045
|
+
});
|
|
1046
|
+
}
|
|
1047
|
+
if (evalCase.kind === 'workflow') {
|
|
1048
|
+
var _answer_model;
|
|
1049
|
+
const [turn] = aiEvalWorkflowGeneration(evalCase, (_answer_model = answer.model) != null ? _answer_model : aiJobWorkflowModel()).messages;
|
|
1050
|
+
return typeof (turn == null ? void 0 : turn.content) === 'string' ? turn.content : null;
|
|
1051
|
+
}
|
|
1052
|
+
return null;
|
|
1053
|
+
}
|
|
1054
|
+
/** What the grader of product copy written with its photo in view is told beside the photo (AGL-3074). */ export const AI_EVAL_PHOTO_GRADER_NOTE = "The product's photo is attached, as the job sent it. Copy may state what the photo plainly shows and is never marked down for it; it is marked down for what neither the photo nor the product's words show.";
|
|
1055
|
+
/** What the grader is told when the photo the copy was written with cannot be shown to it. */ export const AI_EVAL_PHOTO_UNSHOWN_GRADER_NOTE = "The copy was written with the product's photo in view, and this grader is not shown it: never mark copy down for a detail only the photo could show.";
|
|
1056
|
+
/**
|
|
1057
|
+
* The grader's user turn: the brief, its site, the workspace where the case
|
|
1058
|
+
* describes one, the request beyond the brief, the plan a built answer came
|
|
1059
|
+
* from, the screen and layout a built page was built as, and the answer.
|
|
1060
|
+
* `shown.photo` says the product's photo rides beside this turn.
|
|
1061
|
+
*/ export function aiEvalGraderPrompt(evalCase, answer, shown = {}) {
|
|
1062
|
+
const planScope = answer.scope === 'plan';
|
|
1063
|
+
const output = planScope ? answer.plan : answer.answer;
|
|
1064
|
+
const workspace = aiEvalGraderCapabilities(evalCase);
|
|
1065
|
+
const request = planScope ? null : aiEvalGraderRequest(evalCase, answer);
|
|
1066
|
+
const plan = planScope ? null : aiEvalPlanOutline(answer.plan);
|
|
1067
|
+
const page = !planScope && evalCase.kind === 'page' ? aiEvalBuiltPage(answer) : null;
|
|
1068
|
+
return [
|
|
1069
|
+
`Output kind: ${evalCase.kind}${planScope ? ' (its build plan)' : ''}`,
|
|
1070
|
+
...planScope ? [
|
|
1071
|
+
AI_EVAL_PLAN_GRADER_NOTE
|
|
1072
|
+
] : [],
|
|
1073
|
+
`Brief: ${evalCase.brief}`,
|
|
1074
|
+
aiSiteInventoryBlock(evalCase.inventory),
|
|
1075
|
+
...workspace ? [
|
|
1076
|
+
workspace
|
|
1077
|
+
] : [],
|
|
1078
|
+
...request ? [
|
|
1079
|
+
`The request the answer was written from:\n${request}`
|
|
1080
|
+
] : [],
|
|
1081
|
+
...answer.photo === 'read' ? [
|
|
1082
|
+
shown.photo ? AI_EVAL_PHOTO_GRADER_NOTE : AI_EVAL_PHOTO_UNSHOWN_GRADER_NOTE
|
|
1083
|
+
] : [],
|
|
1084
|
+
...plan ? [
|
|
1085
|
+
plan
|
|
1086
|
+
] : [],
|
|
1087
|
+
...page ? [
|
|
1088
|
+
page,
|
|
1089
|
+
AI_EVAL_PAGE_GRADER_NOTE
|
|
1090
|
+
] : [],
|
|
1091
|
+
`Output:\n${aiEvalGraderOutput(output)}`
|
|
1092
|
+
].join('\n\n');
|
|
1093
|
+
}
|
|
1094
|
+
/** A grade tool call read as a rubric; `null` when a grade is missing. */ export function readAiEvalGrade(answer, grader) {
|
|
1095
|
+
const grade = (value)=>{
|
|
1096
|
+
const parsed = Number(value);
|
|
1097
|
+
return Number.isInteger(parsed) && parsed >= 1 && parsed <= 5 ? parsed : null;
|
|
1098
|
+
};
|
|
1099
|
+
const structure = grade(answer['structure']);
|
|
1100
|
+
const copy = grade(answer['copy']);
|
|
1101
|
+
if (structure === null || copy === null) return null;
|
|
1102
|
+
return _extends({
|
|
1103
|
+
structure,
|
|
1104
|
+
copy,
|
|
1105
|
+
reuse: answer['reuse'] === null ? null : grade(answer['reuse']),
|
|
1106
|
+
grader
|
|
1107
|
+
}, typeof answer['notes'] === 'string' && answer['notes'] ? {
|
|
1108
|
+
notes: answer['notes']
|
|
1109
|
+
} : {});
|
|
1110
|
+
}
|
|
1111
|
+
/** Grade one recorded answer with a stronger model, through the doctrine's loop. */ export async function gradeAiEvalCandidate(evalCase, answer, options) {
|
|
1112
|
+
var _options_provider, _options_graderModel;
|
|
1113
|
+
const provider = (_options_provider = options.provider) != null ? _options_provider : resolveAiProvider();
|
|
1114
|
+
const model = (_options_graderModel = options.graderModel) != null ? _options_graderModel : provider ? aiDefaultModelFor(provider.id, 'deep') : undefined;
|
|
1115
|
+
if (!model) throw new Error('no grader model: the provider serves no deep tier and none was named');
|
|
1116
|
+
// Copy written with the product's photo in view is graded with the same
|
|
1117
|
+
// picture, where the grader's model reads one (AGL-3074).
|
|
1118
|
+
const photo = answer.photo === 'read' && provider && aiModelReadsImages(provider, model) ? await aiEvalCasePhoto(evalCase, options) : null;
|
|
1119
|
+
const prompt = aiEvalGraderPrompt(evalCase, answer, {
|
|
1120
|
+
photo: Boolean(photo)
|
|
1121
|
+
});
|
|
1122
|
+
const result = await runValidatedGeneration('eval-grade', withProvider({
|
|
1123
|
+
model,
|
|
1124
|
+
instructions: AI_EVAL_GRADER_INSTRUCTIONS,
|
|
1125
|
+
messages: [
|
|
1126
|
+
{
|
|
1127
|
+
role: 'user',
|
|
1128
|
+
content: photo ? [
|
|
1129
|
+
photo,
|
|
1130
|
+
{
|
|
1131
|
+
type: 'text',
|
|
1132
|
+
text: prompt
|
|
1133
|
+
}
|
|
1134
|
+
] : prompt
|
|
1135
|
+
}
|
|
1136
|
+
],
|
|
1137
|
+
tool: AI_EVAL_RUBRIC_TOOL,
|
|
1138
|
+
maxTokens: 2000,
|
|
1139
|
+
thinking: 'adaptive',
|
|
1140
|
+
check: (raw)=>{
|
|
1141
|
+
const rubric = readAiEvalGrade(raw, model);
|
|
1142
|
+
return rubric ? {
|
|
1143
|
+
value: rubric,
|
|
1144
|
+
violations: []
|
|
1145
|
+
} : {
|
|
1146
|
+
value: null,
|
|
1147
|
+
violations: [
|
|
1148
|
+
{
|
|
1149
|
+
rule: null,
|
|
1150
|
+
code: 'grade-missing',
|
|
1151
|
+
message: 'Every grade is a whole number from 1 to 5.'
|
|
1152
|
+
}
|
|
1153
|
+
]
|
|
1154
|
+
};
|
|
1155
|
+
}
|
|
1156
|
+
}, options));
|
|
1157
|
+
if (result.status !== 'ok') {
|
|
1158
|
+
return {
|
|
1159
|
+
structure: 1,
|
|
1160
|
+
copy: 1,
|
|
1161
|
+
reuse: null,
|
|
1162
|
+
grader: model,
|
|
1163
|
+
notes: `the grader returned ${result.status}`
|
|
1164
|
+
};
|
|
1165
|
+
}
|
|
1166
|
+
return result.value;
|
|
1167
|
+
}
|
|
1168
|
+
/** The vendor's request id off a thrown `UpstreamServiceError`, when it carried one. */ function requestIdOfThrown(error) {
|
|
1169
|
+
const id = error == null ? void 0 : error.requestId;
|
|
1170
|
+
return typeof id === 'string' && id ? id : null;
|
|
1171
|
+
}
|
|
1172
|
+
function messageOfThrown(error) {
|
|
1173
|
+
return error instanceof Error ? `${error.name}: ${error.message}` : String(error);
|
|
1174
|
+
}
|
|
1175
|
+
/**
|
|
1176
|
+
* Record and grade an answer for every brief a recorder covers. Refused
|
|
1177
|
+
* before anything else unless `AI_EVAL_LIVE=1` names the run, and before any
|
|
1178
|
+
* request when a brief it would record names media the run cannot read.
|
|
1179
|
+
*
|
|
1180
|
+
* ONE BRIEF'S FAILURE IS NOT THE RUN'S (AGL-3143). A run is owner-approved
|
|
1181
|
+
* spend, and a provider that refuses one brief — a tool it will not compile,
|
|
1182
|
+
* a rate limit — used to end the whole run with the answers already paid for
|
|
1183
|
+
* still in memory and never written. Each brief is run on its own instead:
|
|
1184
|
+
* one that throws is kept as a failure naming what was thrown, the vendor's
|
|
1185
|
+
* request id and which half of its run threw, and the briefs behind it are
|
|
1186
|
+
* still recorded. The caller writes what came back and decides what a
|
|
1187
|
+
* non-empty `failed` means for its exit status.
|
|
1188
|
+
*
|
|
1189
|
+
* A brief whose GRADE threw is in both halves: recorded, with its grade
|
|
1190
|
+
* marked missing, and failed, naming what the grader threw.
|
|
1191
|
+
*/ export async function recordAiEvalLive(cases, options) {
|
|
1192
|
+
if (!aiEvalLiveAllowed(options.env)) throw new AiEvalLiveRefusedError();
|
|
1193
|
+
const unreadable = cases.filter((evalCase)=>evalCase.media && aiEvalRecorderFor(evalCase.kind) && !options.readFixture);
|
|
1194
|
+
if (unreadable.length) {
|
|
1195
|
+
throw new Error(`${unreadable.map((evalCase)=>evalCase.id).join(', ')} name media, and this run was given no readFixture to read them with`);
|
|
1196
|
+
}
|
|
1197
|
+
const report = {
|
|
1198
|
+
recorded: [],
|
|
1199
|
+
skipped: [],
|
|
1200
|
+
failed: []
|
|
1201
|
+
};
|
|
1202
|
+
for (const evalCase of cases){
|
|
1203
|
+
const recorder = aiEvalRecorderFor(evalCase.kind);
|
|
1204
|
+
if (!recorder) {
|
|
1205
|
+
report.skipped.push({
|
|
1206
|
+
caseId: evalCase.id,
|
|
1207
|
+
kind: evalCase.kind,
|
|
1208
|
+
why: aiEvalSkipReason(evalCase.kind)
|
|
1209
|
+
});
|
|
1210
|
+
continue;
|
|
1211
|
+
}
|
|
1212
|
+
const failure = (step, error)=>{
|
|
1213
|
+
report.failed.push({
|
|
1214
|
+
caseId: evalCase.id,
|
|
1215
|
+
kind: evalCase.kind,
|
|
1216
|
+
step,
|
|
1217
|
+
error: messageOfThrown(error),
|
|
1218
|
+
requestId: requestIdOfThrown(error)
|
|
1219
|
+
});
|
|
1220
|
+
};
|
|
1221
|
+
let answer;
|
|
1222
|
+
try {
|
|
1223
|
+
answer = await recorder(evalCase, options);
|
|
1224
|
+
} catch (error) {
|
|
1225
|
+
failure('record', error);
|
|
1226
|
+
continue;
|
|
1227
|
+
}
|
|
1228
|
+
// A GRADER THAT THROWS DOES NOT TAKE THE ANSWER WITH IT (AGL-3143). The
|
|
1229
|
+
// brief is already paid for by here, and grading is a second request that
|
|
1230
|
+
// can fail on its own — a rate limit, a tool the provider will not
|
|
1231
|
+
// compile. The answer is recorded with its grade marked missing, and the
|
|
1232
|
+
// throw is still reported, so the run exits non-zero without the next
|
|
1233
|
+
// one having to buy this answer again.
|
|
1234
|
+
try {
|
|
1235
|
+
const rubric = await gradeAiEvalCandidate(evalCase, answer, options);
|
|
1236
|
+
report.recorded.push({
|
|
1237
|
+
caseId: evalCase.id,
|
|
1238
|
+
kind: evalCase.kind,
|
|
1239
|
+
candidate: _extends({}, answer, {
|
|
1240
|
+
rubric
|
|
1241
|
+
})
|
|
1242
|
+
});
|
|
1243
|
+
} catch (error) {
|
|
1244
|
+
report.recorded.push({
|
|
1245
|
+
caseId: evalCase.id,
|
|
1246
|
+
kind: evalCase.kind,
|
|
1247
|
+
candidate: _extends({}, answer, {
|
|
1248
|
+
rubric: null
|
|
1249
|
+
})
|
|
1250
|
+
});
|
|
1251
|
+
failure('grade', error);
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
return report;
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
//# sourceMappingURL=ai-eval-live.js.map
|