@aglyn/plugins-ai 1.0.0-beta.218 → 1.0.0-beta.220
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/README.md +2 -2
- package/package.json +18 -18
- package/src/index.d.ts +1 -0
- package/src/index.js.map +1 -1
- package/src/lib/activity/ai-activity-actions.js +8 -1
- package/src/lib/activity/ai-activity-actions.js.map +1 -1
- package/src/lib/billing/ai-month-meter.d.ts +51 -0
- package/src/lib/billing/ai-month-meter.js +89 -0
- package/src/lib/billing/ai-month-meter.js.map +1 -0
- package/src/lib/billing/ai-overage-close.d.ts +11 -33
- package/src/lib/billing/ai-overage-close.js +4 -3
- package/src/lib/billing/ai-overage-close.js.map +1 -1
- package/src/lib/billing/ai-overage-cutover.d.ts +28 -12
- package/src/lib/billing/ai-overage-cutover.js +1 -28
- package/src/lib/billing/ai-overage-cutover.js.map +1 -1
- package/src/lib/billing/ai-overage-ledger.js +1 -1
- package/src/lib/billing/ai-overage-ledger.js.map +1 -1
- package/src/lib/billing/ai-overage-standing.js +1 -1
- package/src/lib/billing/ai-overage-standing.js.map +1 -1
- package/src/lib/components/ai-allotment-editor.component.js +1 -1
- package/src/lib/components/ai-allotment-editor.component.js.map +1 -1
- package/src/lib/components/ai-assist-provider.component.js +5 -4
- package/src/lib/components/ai-assist-provider.component.js.map +1 -1
- package/src/lib/components/ai-automation-zones.d.ts +63 -0
- package/src/lib/components/ai-automation-zones.js +31 -0
- package/src/lib/components/ai-automation-zones.js.map +1 -0
- package/src/lib/components/ai-brief-dialog.component.js +3 -3
- package/src/lib/components/ai-brief-dialog.component.js.map +1 -1
- package/src/lib/components/ai-describe-automation.component.d.ts +1 -1
- package/src/lib/components/ai-describe-automation.component.js.map +1 -1
- package/src/lib/components/ai-describe-button.component.d.ts +6 -3
- package/src/lib/components/ai-describe-button.component.js +4 -1
- package/src/lib/components/ai-describe-button.component.js.map +1 -1
- package/src/lib/components/ai-experiment-cards.component.js +1 -1
- package/src/lib/components/ai-experiment-cards.component.js.map +1 -1
- package/src/lib/components/ai-explain-automation.component.d.ts +1 -1
- package/src/lib/components/ai-explain-automation.component.js.map +1 -1
- package/src/lib/components/ai-job-plan.component.d.ts +8 -0
- package/src/lib/components/ai-job-plan.component.js +21 -4
- package/src/lib/components/ai-job-plan.component.js.map +1 -1
- package/src/lib/components/ai-permissions-on-host.component.d.ts +12 -2
- package/src/lib/components/ai-permissions-on-host.component.js +12 -3
- package/src/lib/components/ai-permissions-on-host.component.js.map +1 -1
- package/src/lib/components/ai-product-copy-card.component.d.ts +1 -1
- package/src/lib/components/ai-product-copy-card.component.js.map +1 -1
- package/src/lib/components/ai-product-import-option.component.d.ts +1 -1
- package/src/lib/components/ai-product-import-option.component.js.map +1 -1
- package/src/lib/components/ai-product-zones.d.ts +124 -0
- package/src/lib/components/ai-product-zones.js +32 -0
- package/src/lib/components/ai-product-zones.js.map +1 -0
- package/src/lib/components/ai-products-hub-card.component.d.ts +1 -1
- package/src/lib/components/ai-products-hub-card.component.js.map +1 -1
- package/src/lib/components/ai-products-proposals.component.d.ts +1 -1
- package/src/lib/components/ai-products-proposals.component.js.map +1 -1
- package/src/lib/components/ai-seo-audit-card.component.js +35 -92
- package/src/lib/components/ai-seo-audit-card.component.js.map +1 -1
- package/src/lib/components/ai-seo-fields-card.component.js.map +1 -1
- package/src/lib/components/ai-theme-proposal-card.component.js.map +1 -1
- package/src/lib/components/assist-jobs-drawer.component.js +21 -15
- package/src/lib/components/assist-jobs-drawer.component.js.map +1 -1
- package/src/lib/components/assist-panel.component.js +2 -2
- package/src/lib/components/assist-panel.component.js.map +1 -1
- package/src/lib/components/assist-signals-page.component.js +2 -2
- package/src/lib/components/assist-signals-page.component.js.map +1 -1
- package/src/lib/components/billing-ai-allotments.component.js +1 -1
- package/src/lib/components/billing-ai-allotments.component.js.map +1 -1
- package/src/lib/components/billing-assist-overage-card.component.js +1 -1
- package/src/lib/components/billing-assist-overage-card.component.js.map +1 -1
- package/src/lib/constants.d.ts +5 -0
- package/src/lib/constants.js +4 -0
- package/src/lib/constants.js.map +1 -1
- package/src/lib/declarations.js +2 -0
- package/src/lib/declarations.js.map +1 -1
- package/src/lib/declarations.server.js +24 -10
- package/src/lib/declarations.server.js.map +1 -1
- package/src/lib/insights/ai-dataset-catalog.d.ts +49 -0
- package/src/lib/insights/ai-dataset-catalog.js +57 -0
- package/src/lib/insights/ai-dataset-catalog.js.map +1 -0
- package/src/lib/insights/ai-figure-readers.d.ts +6 -39
- package/src/lib/insights/ai-figure-readers.js +2 -391
- package/src/lib/insights/ai-figure-readers.js.map +1 -1
- package/src/lib/insights/ai-insight-digest.d.ts +10 -1
- package/src/lib/insights/ai-insight-digest.js +16 -4
- package/src/lib/insights/ai-insight-digest.js.map +1 -1
- package/src/lib/insights/ai-insight-readers.d.ts +1 -1
- package/src/lib/insights/ai-insight-readers.js.map +1 -1
- package/src/lib/jobs/ai-email-bindings.d.ts +28 -21
- package/src/lib/jobs/ai-email-bindings.js +75 -42
- package/src/lib/jobs/ai-email-bindings.js.map +1 -1
- package/src/lib/jobs/ai-job-campaign-step.d.ts +0 -2
- package/src/lib/jobs/ai-job-campaign-step.js +5 -10
- package/src/lib/jobs/ai-job-campaign-step.js.map +1 -1
- package/src/lib/jobs/ai-job-component-step.d.ts +6 -1
- package/src/lib/jobs/ai-job-component-step.js +32 -9
- package/src/lib/jobs/ai-job-component-step.js.map +1 -1
- package/src/lib/jobs/ai-job-draft-ids.js +11 -0
- package/src/lib/jobs/ai-job-draft-ids.js.map +1 -1
- package/src/lib/jobs/ai-job-drafts.js +1 -1
- package/src/lib/jobs/ai-job-drafts.js.map +1 -1
- package/src/lib/jobs/ai-job-email-step.d.ts +5 -3
- package/src/lib/jobs/ai-job-email-step.js +11 -4
- package/src/lib/jobs/ai-job-email-step.js.map +1 -1
- package/src/lib/jobs/ai-job-experiment-step.d.ts +1 -1
- package/src/lib/jobs/ai-job-experiment-step.js +1 -1
- package/src/lib/jobs/ai-job-experiment-step.js.map +1 -1
- package/src/lib/jobs/ai-job-form-step.d.ts +17 -1
- package/src/lib/jobs/ai-job-form-step.js +60 -8
- package/src/lib/jobs/ai-job-form-step.js.map +1 -1
- package/src/lib/jobs/ai-job-generation.d.ts +22 -1
- package/src/lib/jobs/ai-job-generation.js +63 -3
- package/src/lib/jobs/ai-job-generation.js.map +1 -1
- package/src/lib/jobs/ai-job-insight-step.js +1 -1
- package/src/lib/jobs/ai-job-insight-step.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-step.d.ts +15 -4
- package/src/lib/jobs/ai-job-layout-step.js +46 -13
- package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
- package/src/lib/jobs/ai-job-page-sections.d.ts +4 -2
- package/src/lib/jobs/ai-job-page-sections.js +7 -4
- package/src/lib/jobs/ai-job-page-sections.js.map +1 -1
- package/src/lib/jobs/ai-job-page-step.js +7 -2
- package/src/lib/jobs/ai-job-page-step.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-conformance.d.ts +5 -0
- package/src/lib/jobs/ai-job-plan-conformance.js +40 -1
- package/src/lib/jobs/ai-job-plan-conformance.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-creations.d.ts +80 -0
- package/src/lib/jobs/ai-job-plan-creations.js +199 -0
- package/src/lib/jobs/ai-job-plan-creations.js.map +1 -0
- package/src/lib/jobs/ai-job-plan-step.d.ts +11 -2
- package/src/lib/jobs/ai-job-plan-step.js +49 -23
- package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
- package/src/lib/jobs/ai-job-plugin-drafts.d.ts +14 -6
- package/src/lib/jobs/ai-job-plugin-drafts.js +12 -4
- package/src/lib/jobs/ai-job-plugin-drafts.js.map +1 -1
- package/src/lib/jobs/ai-job-products-step.d.ts +7 -0
- package/src/lib/jobs/ai-job-products-step.js +54 -32
- package/src/lib/jobs/ai-job-products-step.js.map +1 -1
- package/src/lib/jobs/ai-job-seo-step.d.ts +3 -24
- package/src/lib/jobs/ai-job-seo-step.js +35 -108
- package/src/lib/jobs/ai-job-seo-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-step.d.ts +2 -0
- package/src/lib/jobs/ai-job-site-step.js +6 -6
- package/src/lib/jobs/ai-job-site-step.js.map +1 -1
- package/src/lib/jobs/ai-job-template-step.d.ts +9 -6
- package/src/lib/jobs/ai-job-template-step.js +73 -15
- package/src/lib/jobs/ai-job-template-step.js.map +1 -1
- package/src/lib/jobs/ai-job-theme-step.d.ts +1 -1
- package/src/lib/jobs/ai-job-theme-step.js +1 -1
- package/src/lib/jobs/ai-job-theme-step.js.map +1 -1
- package/src/lib/jobs/ai-job-workflow-step.js +17 -9
- package/src/lib/jobs/ai-job-workflow-step.js.map +1 -1
- package/src/lib/jobs/ai-jobs.js +1 -1
- package/src/lib/jobs/ai-jobs.js.map +1 -1
- package/src/lib/jobs/ai-workflow-records.d.ts +20 -11
- package/src/lib/jobs/ai-workflow-records.js +80 -43
- package/src/lib/jobs/ai-workflow-records.js.map +1 -1
- package/src/lib/model/ai-allotments.d.ts +27 -6
- package/src/lib/model/ai-allotments.js +54 -14
- package/src/lib/model/ai-allotments.js.map +1 -1
- package/src/lib/model/ai-automation-outline.d.ts +21 -2
- package/src/lib/model/ai-automation-outline.js.map +1 -1
- package/src/lib/model/ai-build-plan.d.ts +57 -0
- package/src/lib/model/ai-build-plan.js +116 -7
- package/src/lib/model/ai-build-plan.js.map +1 -1
- package/src/lib/model/ai-job-creations.d.ts +55 -0
- package/src/lib/model/ai-job-creations.js +85 -0
- package/src/lib/model/ai-job-creations.js.map +1 -0
- package/src/lib/model/ai-page-job.d.ts +3 -2
- package/src/lib/model/ai-page-job.js +4 -3
- package/src/lib/model/ai-page-job.js.map +1 -1
- package/src/lib/model/ai-seo.d.ts +11 -51
- package/src/lib/model/ai-seo.js +0 -17
- package/src/lib/model/ai-seo.js.map +1 -1
- package/src/lib/model/ai-site-inventory.js +1 -1
- package/src/lib/model/ai-site-inventory.js.map +1 -1
- package/src/lib/model/ai-site-job.d.ts +4 -2
- package/src/lib/model/ai-site-job.js +8 -4
- package/src/lib/model/ai-site-job.js.map +1 -1
- package/src/lib/model/ai-template-subjects.d.ts +10 -0
- package/src/lib/model/ai-template-subjects.js +16 -2
- package/src/lib/model/ai-template-subjects.js.map +1 -1
- package/src/lib/model/ai-theme-proposal.d.ts +1 -1
- package/src/lib/model/ai-theme-proposal.js +1 -1
- package/src/lib/model/ai-theme-proposal.js.map +1 -1
- package/src/lib/model/ai-tokens.js +1 -1
- package/src/lib/model/ai-tokens.js.map +1 -1
- package/src/lib/model/ai-usage-by-user.js +1 -1
- package/src/lib/model/ai-usage-by-user.js.map +1 -1
- package/src/lib/model/ai-workflow-job.d.ts +1 -3
- package/src/lib/model/ai-workflow-job.js +1 -2
- package/src/lib/model/ai-workflow-job.js.map +1 -1
- package/src/lib/plan-entitlements.d.ts +149 -0
- package/src/lib/plan-entitlements.js +168 -0
- package/src/lib/plan-entitlements.js.map +1 -0
- package/src/lib/plugin.js +1 -1
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/providers/catalog.js +1 -1
- package/src/lib/providers/catalog.js.map +1 -1
- package/src/lib/providers/model-choice.js +1 -1
- package/src/lib/providers/model-choice.js.map +1 -1
- package/src/lib/runtime/ai-device-audit.d.ts +1 -1
- package/src/lib/runtime/ai-device-audit.js +1 -1
- package/src/lib/runtime/ai-device-audit.js.map +1 -1
- package/src/lib/runtime/ai-doctrine-validators.d.ts +25 -4
- package/src/lib/runtime/ai-doctrine-validators.js +178 -21
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-doctrine.js +5 -5
- package/src/lib/runtime/ai-doctrine.js.map +1 -1
- package/src/lib/runtime/ai-eval-live.js +30 -15
- package/src/lib/runtime/ai-eval-live.js.map +1 -1
- package/src/lib/runtime/ai-eval.d.ts +9 -2
- package/src/lib/runtime/ai-eval.js +29 -2
- package/src/lib/runtime/ai-eval.js.map +1 -1
- package/src/lib/runtime/ai-gate.js +1 -1
- package/src/lib/runtime/ai-gate.js.map +1 -1
- package/src/lib/runtime/ai-node-tree.js +3 -3
- package/src/lib/runtime/ai-node-tree.js.map +1 -1
- package/src/lib/runtime/ai-palette.d.ts +7 -1
- package/src/lib/runtime/ai-palette.generated.d.ts +1 -1
- package/src/lib/runtime/ai-palette.generated.js +16 -13
- package/src/lib/runtime/ai-palette.generated.js.map +1 -1
- package/src/lib/runtime/ai-palette.js +7 -1
- package/src/lib/runtime/ai-palette.js.map +1 -1
- package/src/lib/runtime/seo-audit.d.ts +13 -63
- package/src/lib/runtime/seo-audit.js +22 -207
- package/src/lib/runtime/seo-audit.js.map +1 -1
- package/src/lib/runtime/site-inventory.js +29 -23
- package/src/lib/runtime/site-inventory.js.map +1 -1
- package/src/lib/server/ai-admin-org.js +1 -1
- package/src/lib/server/ai-admin-org.js.map +1 -1
- package/src/lib/server/ai-admin-orgs-spend.js +1 -1
- package/src/lib/server/ai-admin-orgs-spend.js.map +1 -1
- package/src/lib/server/ai-admin-signals.js +1 -1
- package/src/lib/server/ai-admin-signals.js.map +1 -1
- package/src/lib/server/ai-allotments.js +1 -1
- package/src/lib/server/ai-allotments.js.map +1 -1
- package/src/lib/server/ai-assist-prompts.js +3 -3
- package/src/lib/server/ai-assist-prompts.js.map +1 -1
- package/src/lib/server/ai-assist.js +2 -2
- package/src/lib/server/ai-assist.js.map +1 -1
- package/src/lib/server/assist-chat.d.ts +1 -1
- package/src/lib/server/assist-chat.js +13 -12
- package/src/lib/server/assist-chat.js.map +1 -1
- package/src/lib/server/assist-view-context.js +23 -23
- package/src/lib/server/assist-view-context.js.map +1 -1
- package/src/lib/server/billing-credits.d.ts +5 -0
- package/src/lib/server/billing-credits.js +8 -2
- package/src/lib/server/billing-credits.js.map +1 -1
- package/src/lib/server/billing-overage.js +2 -2
- package/src/lib/server/billing-overage.js.map +1 -1
- package/src/lib/server.d.ts +1 -0
- package/src/lib/server.js.map +1 -1
- package/src/lib/testing/stand-in-automation-index.d.ts +20 -0
- package/src/lib/testing/stand-in-automation-index.js +102 -0
- package/src/lib/testing/stand-in-automation-index.js.map +1 -0
- package/src/lib/testing/stand-in-dataset-index.d.ts +20 -0
- package/src/lib/testing/stand-in-dataset-index.js +80 -0
- package/src/lib/testing/stand-in-dataset-index.js.map +1 -0
- package/src/lib/testing/stand-in-list-send-time.d.ts +4 -0
- package/src/lib/testing/stand-in-list-send-time.js +77 -0
- package/src/lib/testing/stand-in-list-send-time.js.map +1 -0
- package/src/lib/testing/stand-in-pipeline-index.d.ts +32 -0
- package/src/lib/testing/stand-in-pipeline-index.js +59 -0
- package/src/lib/testing/stand-in-pipeline-index.js.map +1 -0
- package/src/lib/testing/stand-in-product-index.d.ts +20 -0
- package/src/lib/testing/stand-in-product-index.js +105 -0
- package/src/lib/testing/stand-in-product-index.js.map +1 -0
- package/src/lib/tools/ai-component-tool.js +2 -2
- package/src/lib/tools/ai-component-tool.js.map +1 -1
- package/src/lib/tools/ai-duplicate-resource-tool.js +1 -1
- package/src/lib/tools/ai-duplicate-resource-tool.js.map +1 -1
- package/src/lib/tools/ai-seo-tool.d.ts +6 -18
- package/src/lib/tools/ai-seo-tool.js +7 -38
- package/src/lib/tools/ai-seo-tool.js.map +1 -1
- package/src/lib/tools/ai-workflow-tool.d.ts +1 -1
- package/src/lib/tools/ai-workflow-tool.js +1 -1
- package/src/lib/tools/ai-workflow-tool.js.map +1 -1
- package/src/lib/usage/ai-allotment-alerts.d.ts +10 -7
- package/src/lib/usage/ai-allotment-alerts.js +39 -15
- package/src/lib/usage/ai-allotment-alerts.js.map +1 -1
- package/src/lib/usage/ai-quota-checks.d.ts +37 -0
- package/src/lib/usage/ai-quota-checks.js +69 -0
- package/src/lib/usage/ai-quota-checks.js.map +1 -0
- package/src/lib/usage/ai-usage-alerts.js +22 -10
- package/src/lib/usage/ai-usage-alerts.js.map +1 -1
- package/src/lib/usage/ai-usage-by-user.js +1 -1
- package/src/lib/usage/ai-usage-by-user.js.map +1 -1
- package/src/lib/usage/assist-ceiling.d.ts +2 -2
- package/src/lib/usage/assist-ceiling.js +4 -4
- package/src/lib/usage/assist-ceiling.js.map +1 -1
- package/src/lib/usage/assist-credits.d.ts +494 -0
- package/src/lib/usage/assist-credits.js +524 -0
- package/src/lib/usage/assist-credits.js.map +1 -0
- package/src/lib/usage/assist-free-taste.d.ts +1 -1
- package/src/lib/usage/assist-free-taste.js +3 -2
- package/src/lib/usage/assist-free-taste.js.map +1 -1
- package/src/lib/usage/assist-signal-mining.js +1 -1
- package/src/lib/usage/assist-signal-mining.js.map +1 -1
- package/src/lib/usage/assist-spend-guards.d.ts +153 -0
- package/src/lib/usage/assist-spend-guards.js +182 -0
- package/src/lib/usage/assist-spend-guards.js.map +1 -0
- package/src/lib/usage/assist-usage.d.ts +1 -1
- package/src/lib/usage/assist-usage.js +1 -1
- package/src/lib/usage/assist-usage.js.map +1 -1
- package/src/lib/usage/staff-org-ai.js +1 -1
- package/src/lib/usage/staff-org-ai.js.map +1 -1
- package/src/lib/usage-axes.d.ts +49 -0
- package/src/lib/usage-axes.js +108 -0
- package/src/lib/usage-axes.js.map +1 -0
- package/src/lib/runtime/seo-page-facts.d.ts +0 -61
- package/src/lib/runtime/seo-page-facts.js +0 -150
- package/src/lib/runtime/seo-page-facts.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-template-step.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { hostCollectionKind } from '@aglyn/aglyn/app-utils/collection-kind'\nimport { normalizeScreenSlug } from '@aglyn/aglyn/app-utils/screen-route'\nimport type { AglynOrgBilling } from '@aglyn/aglyn/foundation/definitions/org-billing.types'\nimport { duplicateResource } from '@aglyn/tenant-data-admin/server/duplicate-resource'\nimport { CANVAS_ROOT_ELEMENT_ID } from '@aglyn/aglyn/foundation/constants/canvas'\nimport type { AiJob, AiJobOutput, AiJobPlan, AiJobReview } from '../model/ai-jobs.types'\nimport {\n AI_TEMPLATE_SUBJECT_DEFINITIONS,\n AI_TEMPLATE_SUBJECTS,\n aiBindingTokensIn,\n aiTemplateAddressTokens,\n parseAiTemplateJobInputs,\n type AiTemplateSubjectDefinition,\n} from '../model/ai-template-subjects'\nimport { AI_STEP_TIERS } from '../providers/catalog'\nimport { AI_ROUTING_TABLE, aiModelForStep } from '../providers/routing'\nimport {\n aiDoctrineTreeTool,\n runValidatedGeneration,\n type AiValidatedTree,\n} from '../runtime/ai-doctrine'\nimport {\n walkTree,\n type AiDoctrineNode,\n type AiDoctrineViolation,\n} from '../runtime/ai-doctrine-validators'\nimport type { AiLoadEstimate } from '../runtime/ai-palette'\nimport { AI_PALETTE } from '../runtime/ai-palette.generated'\nimport type { AiSystemBlock } from '../runtime/ai-runtime'\nimport { aiTemplateExamplesSystemBlock } from '../runtime/ai-template-examples'\nimport { readSiteInventory } from '../runtime/site-inventory'\nimport { registerAiJobAdmission, type AiJobAdmission } from './ai-job-admission'\nimport { aiJobDraftId } from './ai-job-draft-ids'\nimport {\n aiDraftAdmissionRefusal,\n aiDraftAllowanceRefusal,\n aiSiteSubdomain,\n readAiDraft,\n readAiTemplateDraftNodes,\n writeAiDraft,\n type AiDraftRecord,\n} from './ai-job-drafts'\nimport {\n aiConfirmedPlan,\n aiDoctrineReview,\n aiGenerationSpent,\n aiJobBriefLine,\n aiLimitReview,\n aiModelNodeIds,\n aiPlacedComponentIds,\n aiPlanCreation,\n aiPlanReferenceLines,\n aiPlanReuseViolations,\n aiUnspentOutcome,\n} from './ai-job-generation'\nimport { aiPlannedTemplateTokens, aiPlanTemplateTokenViolations } from './ai-job-plan-conformance'\nimport type { AiJobStepRunner } from './ai-job-text-step'\nimport { aiJobStepBudget } from './ai-job-budget'\nimport { registerAiJobStep } from './ai-jobs'\n\n/**\n * The template step (AGL-2909): the generation step of a `template` job, run\n * once a member confirmed the job's plan.\n *\n * It builds ONE page template — the page a site renders once per record: a\n * content collection's entry, a product, an author — through\n * `runValidatedGeneration('template', …)`. Everything that differs per\n * record is a binding token from the subject's vocabulary\n * (`model/ai-template-subjects.ts`), which is the list the besigner's insert\n * picker offers for that page; a link or media prop may hold one of those\n * tokens whole, which the palette validator admits only because this door\n * names them. The door adds one check of its own through `extend` (rule 8):\n * no token the page does not fill, an h1 bound to the subject's title, and no\n * block that fills itself only on another subject's page.\n *\n * The model is shown the platform's starter pages as examples, in a cached\n * block (`runtime/ai-template-examples.ts`), and writes against them.\n *\n * The template lands in the site's library as a new draft\n * (`ai-job-drafts.ts`), inert until a member uses it: bound to no\n * collection, set as no product or author page, served nowhere. A plan that\n * starts from a copy of a template the site has gets that copy instead.\n */\n\n/** The longest answer a template may run to; the tree is held to the template budget either way. */\nexport const AI_JOB_TEMPLATE_MAX_TOKENS = AI_ROUTING_TABLE['job.template'].maxTokens\n\n/**\n * The template step's time (AGL-3035, AGL-3036): its two inventory-lookup\n * rounds, its answer and its re-ask at the routing table's ceiling on the tier\n * `job.template` is served from, fitted to what a beat can give a step. It\n * runs on the job beat, never at an inline door, and asks for no more of the\n * ceiling than fits that time on the model it runs.\n */\nexport const AI_JOB_TEMPLATE_STEP_BUDGET = aiJobStepBudget({\n tier: AI_STEP_TIERS['job.template'],\n maxTokens: AI_JOB_TEMPLATE_MAX_TOKENS,\n})\n\n/** The least time one template step needs before it starts. */\nexport const AI_JOB_TEMPLATE_STEP_MINIMUM_MS = AI_JOB_TEMPLATE_STEP_BUDGET.minimumMs\n\n/** The template step's own instructions, cached after the doctrine and before the examples. */\nexport const AI_JOB_TEMPLATE_INSTRUCTIONS: readonly AiSystemBlock[] = [\n {\n text: [\n 'You build one page template: the page a website renders once for each record it keeps, such as each entry of a content collection, each product or each author. Answer with submit_template: the whole page as one flat node map.',\n 'Everything that differs from one record to the next is a binding token, never typed copy. The request names what the page is for, the tokens that page fills and the blocks that fill themselves there; bind only those tokens.',\n 'The page’s one h1 holds the title token the request names. Copy that reads the same on every record, such as a section heading, is written in the site’s voice.',\n 'A link or media prop holds exactly one token from the request’s link and media tokens, such as an Image whose src is {{entry.coverImage}}; its alt text may combine words and tokens.',\n 'The page renders inside the site’s layout, so it carries no header, navigation or footer of its own.',\n ].join('\\n'),\n },\n]\n\n/** A content collection an entry template is for, as the prompt and the draft name it. */\nexport interface AiTemplateCollection {\n id: string\n name: string\n slug: string\n}\n\n/** The content collection by that id on the site, or `null` when there is none. */\nexport async function readAiTemplateCollection(\n firestore: FirebaseFirestore.Firestore,\n hostId: string,\n collectionId: string,\n): Promise<AiTemplateCollection | null> {\n const snapshot = await firestore\n .collection('hosts')\n .doc(hostId)\n .collection('collections')\n .doc(collectionId)\n .get()\n if (!snapshot.exists) return null\n const data = (snapshot.data() ?? {}) as Record<string, unknown>\n if (hostCollectionKind(data) !== 'content') return null\n const text = (value: unknown) => (typeof value === 'string' ? value.trim() : '')\n const slug = text(data['slug'])\n return {\n id: collectionId,\n name: text(data['displayName']) || text(data['name']) || slug || collectionId,\n slug,\n }\n}\n\n/** The template's name: the plan's, else the collection's or the subject's. */\nexport function aiTemplateDraftName(\n definition: AiTemplateSubjectDefinition,\n collection: AiTemplateCollection | null,\n planned: string | null | undefined,\n): string {\n return planned || (collection ? `${collection.name} entry template` : definition.defaultName)\n}\n\n/** The address Use template offers for the page it creates. */\nexport function aiTemplateDraftSlug(\n definition: AiTemplateSubjectDefinition,\n collection: AiTemplateCollection | null,\n): string | null {\n return (\n normalizeScreenSlug(\n collection\n ? `${collection.slug || collection.id}-entry-template`\n : `${definition.subject}-page-template`,\n ) ?? null\n )\n}\n\n/** The template job as the generation's user turn. */\nexport function aiJobTemplatePrompt(\n job: Pick<AiJob, 'brief'>,\n definition: AiTemplateSubjectDefinition,\n collection: AiTemplateCollection | null,\n plan: AiJobPlan | null,\n name: string,\n): string {\n const of = collection\n ? ` of the \"${collection.name}\" collection${collection.slug ? ` (/${collection.slug})` : ''}`\n : ''\n return [\n `Template name: ${name}`,\n `The page is for ${definition.noun}${of}.`,\n `Title token: ${definition.titleToken}`,\n `Tokens this page fills: ${definition.tokens.map((entry) => `${entry.token} ${entry.label}`).join('; ')}`,\n `Link and media tokens: ${aiTemplateAddressTokens(definition).join(', ')}`,\n definition.blocks.length\n ? `Blocks that fill themselves on this page: ${definition.blocks.join(', ')}`\n : 'No block fills itself on this page: bind the tokens.',\n aiJobBriefLine(job),\n ...aiPlanReferenceLines(plan),\n ].join('\\n')\n}\n\n/** Which subject's page each self-filling block belongs to. */\nconst BLOCK_SUBJECTS = new Map<string, AiTemplateSubjectDefinition>(\n AI_TEMPLATE_SUBJECTS.flatMap((subject) =>\n AI_TEMPLATE_SUBJECT_DEFINITIONS[subject].blocks.map(\n (block) => [block, AI_TEMPLATE_SUBJECT_DEFINITIONS[subject]] as const,\n ),\n ),\n)\n\n/** Every string a prop value holds, however deeply. */\nfunction stringsIn(value: unknown, out: string[] = []): string[] {\n if (typeof value === 'string') out.push(value)\n else if (Array.isArray(value)) for (const inner of value) stringsIn(inner, out)\n else if (value && typeof value === 'object') {\n for (const inner of Object.values(value as Record<string, unknown>)) stringsIn(inner, out)\n }\n return out\n}\n\n/** The page's title: a Typography that renders as an h1. */\nfunction isPageTitle(node: AiDoctrineNode): boolean {\n if (node.componentId !== 'muiTypography') return false\n const element = typeof node.props?.['component'] === 'string' ? String(node.props['component']) : ''\n return (element || String(node.props?.['variant'] ?? '')) === 'h1'\n}\n\n/**\n * The template door's check on a tree the doctrine admitted (rule 8): every\n * token is one the page fills, the h1 binds the subject's title, and no block\n * that fills itself on another subject's page is placed — except inside a\n * Collection Entries card, where each fills from the card's own entry.\n */\nexport function aiTemplateBindingCheck(\n definition: AiTemplateSubjectDefinition,\n): (tree: AiValidatedTree) => AiDoctrineViolation[] {\n const allowed = new Set(definition.tokens.map((entry) => entry.token))\n return (tree) => {\n const nodes = tree.nodes as unknown as Record<string, AiDoctrineNode>\n const unknown = new Set<string>()\n const unknownAt: string[] = []\n const foreign: Array<{ id: string; componentId: string; owner: AiTemplateSubjectDefinition }> = []\n const titles: string[] = []\n let titleBound = false\n for (const { id, node, ancestors } of walkTree({ rootId: tree.rootId, nodes })) {\n const tokens = stringsIn(node.props).flatMap(aiBindingTokensIn)\n const strays = tokens.filter((token) => !allowed.has(token))\n if (strays.length) {\n for (const token of strays) unknown.add(token)\n unknownAt.push(id)\n }\n const owner = BLOCK_SUBJECTS.get(node.componentId)\n const inCard = ancestors.some((ancestor) => nodes[ancestor]?.componentId === 'collectionEntries')\n if (owner && owner.subject !== definition.subject && !inCard) {\n foreign.push({ id, componentId: node.componentId, owner })\n }\n if (isPageTitle(node)) {\n titles.push(id)\n if (tokens.includes(definition.titleToken)) titleBound = true\n }\n }\n const violations: AiDoctrineViolation[] = []\n if (unknown.size) {\n violations.push({\n rule: 8,\n code: 'unknown-binding',\n message: `This binds ${[...unknown].slice(0, 3).join(', ')}, which ${definition.noun}’s page does not fill. Bind only the tokens listed for this page.`,\n nodeIds: aiModelNodeIds(unknownAt, tree.sourceIds),\n })\n }\n // The doctrine already refuses a page with no h1 or with several.\n if (titles.length === 1 && !titleBound) {\n violations.push({\n rule: 8,\n code: 'typed-title',\n message: `The page title is typed, so every record would show the same one. Put ${definition.titleToken} in the h1.`,\n nodeIds: aiModelNodeIds(titles, tree.sourceIds),\n })\n }\n if (foreign.length) {\n const [first] = foreign\n violations.push({\n rule: 8,\n code: 'foreign-block',\n message: `The ${AI_PALETTE[first.componentId]?.displayName ?? first.componentId} block fills itself only on ${first.owner.noun}’s page, and this template is for ${definition.noun}. Remove it.`,\n nodeIds: aiModelNodeIds(\n foreign.map((entry) => entry.id),\n tree.sourceIds,\n ),\n })\n }\n return violations\n }\n}\n\nexport interface AiTemplateJobAdmissionDeps {\n readCollection?: typeof readAiTemplateCollection\n}\n\n/**\n * A template job is admitted with inputs that say what the page is for, for\n * a site of the job's own org that holds the collection it names, with a\n * template to spare.\n */\nexport function createAiTemplateJobAdmission(deps: AiTemplateJobAdmissionDeps = {}): AiJobAdmission {\n const readCollection = deps.readCollection ?? readAiTemplateCollection\n return async (context) => {\n const inputs = parseAiTemplateJobInputs(context.inputs)\n if (typeof inputs === 'string') return { status: 400, error: inputs }\n return aiDraftAdmissionRefusal(context.firestore, {\n orgId: context.orgId,\n hostId: context.hostId,\n kind: 'template',\n org: context.org,\n ownCheck: async (hostId) =>\n inputs.collectionId && !(await readCollection(context.firestore, hostId, inputs.collectionId))\n ? { status: 404, error: 'That content collection is not on this site' }\n : null,\n })\n }\n}\n\nexport interface AiJobTemplateStepDeps {\n /** The inventory reader; specs hand in a fake. */\n readInventory?: typeof readSiteInventory\n /** The platform's duplicate module, for a plan that starts from a copy. */\n duplicate?: typeof duplicateResource\n readCollection?: typeof readAiTemplateCollection\n /** The starter examples block; built from the starters once per process otherwise. */\n examples?: () => AiSystemBlock\n}\n\n/**\n * The binding tokens the confirmed plan gives the template, against the\n * template a COPY actually produced (AGL-3024); `null` when the copy keeps\n * the plan, or when the plan promised no token to keep.\n *\n * The same hole as `aiCopiedLayoutReview` and `aiCopiedPageReview`, on the\n * third kind that can start from a duplicate. The generate path already holds\n * a built template to `aiPlanTemplateTokenViolations`; the copy path reached\n * none of it, so a plan reading \"duplicate it and bind the author's photo\"\n * got the duplicate and reported Done.\n *\n * A copy that cannot be read back settles nothing, and a promise this step\n * cannot settle is reviewed by a person rather than reported as kept.\n */\nexport async function aiCopiedTemplateReview(\n firestore: FirebaseFirestore.Firestore,\n input: {\n hostId: string\n id: string\n name: string\n plan: AiJobPlan | null\n definition: AiTemplateSubjectDefinition\n },\n): Promise<AiJobReview | null> {\n const { tokens, unreadable } = aiPlannedTemplateTokens(input.plan, input.definition)\n if (!tokens.length && !unreadable.length) return null\n const stored = await readAiTemplateDraftNodes(firestore, {\n hostId: input.hostId,\n id: input.id,\n })\n const nodes = (stored ?? {}) as unknown as Record<string, AiDoctrineNode>\n if (!nodes[CANVAS_ROOT_ELEMENT_ID]) {\n return {\n reason: 'doctrine',\n message: `\"${input.name}\" was copied from the template the plan names, and this job could not read the copy back to check it shows what the plan gives it. Open the template and check it before you apply it.`,\n findings: [],\n }\n }\n const violations = aiPlanTemplateTokenViolations(input.plan, input.definition, {\n rootId: CANVAS_ROOT_ELEMENT_ID,\n nodes,\n })\n if (!violations.length) return null\n return aiDoctrineReview({\n message: `\"${input.name}\" is a copy of the template the plan names, and a copy is all it is: ${violations[0].message}`,\n violations,\n })\n}\n\nexport function createAiJobTemplateStep(deps: AiJobTemplateStepDeps = {}): AiJobStepRunner {\n const readInventory = deps.readInventory ?? readSiteInventory\n const duplicate = deps.duplicate ?? duplicateResource\n const readCollection = deps.readCollection ?? readAiTemplateCollection\n const examples = deps.examples ?? aiTemplateExamplesSystemBlock\n return async ({ job, now, signal, firestore, modelFor }) => {\n const hostId = job.hostId\n if (!hostId) throw new Error('a template job names no site, and its admission refuses one')\n const inputs = parseAiTemplateJobInputs(job.inputs)\n if (typeof inputs === 'string') {\n throw new Error(`a template job's inputs no longer read as they were admitted: ${inputs}`)\n }\n const definition = AI_TEMPLATE_SUBJECT_DEFINITIONS[inputs.subject]\n const output = (draft: AiDraftRecord, load?: AiLoadEstimate | null): AiJobOutput => ({\n resource: 'template',\n id: draft.id,\n versionId: null,\n hostId,\n hostSubdomain: draft.hostSubdomain,\n label: draft.name,\n ...(load ? { load } : {}),\n })\n // The switch's answer for this job, else the routing table's (AGL-2942).\n const model = modelFor?.('job.template') ?? aiModelForStep('job.template')\n\n // A run cut off after its draft was written reports that draft, found by the id the job recorded.\n const draftId = aiJobDraftId(job, 'template')\n const written = await readAiDraft(firestore, { kind: 'template', hostId, id: draftId })\n if (written) return aiUnspentOutcome(model, { outputs: [output(written)] })\n\n const [inventory, orgSnapshot, collection] = await Promise.all([\n readInventory(job.orgId, hostId, { firestore }),\n firestore.collection('orgs').doc(job.orgId).get(),\n inputs.collectionId\n ? readCollection(firestore, hostId, inputs.collectionId)\n : Promise.resolve(null),\n ])\n if (inputs.collectionId && !collection) {\n throw new Error(`collection ${inputs.collectionId} left site ${hostId} after its template job was admitted`)\n }\n const org = (orgSnapshot.data() ?? null) as Partial<AglynOrgBilling> | null\n const plan = aiConfirmedPlan(job)\n const creation = aiPlanCreation(plan, 'template')\n const name = aiTemplateDraftName(definition, collection, creation?.name)\n\n // The plan starts from a copy of a template the site has (rule 15): the\n // copy is the draft, and nothing is generated. A source gone since the\n // plan was made is built from the brief instead.\n if (\n creation?.duplicateOf &&\n inventory.templates.some((template) => template.id === creation.duplicateOf)\n ) {\n const copy = await duplicate('template', {\n orgId: job.orgId,\n hostId,\n sourceId: creation.duplicateOf,\n name,\n uid: job.createdBy,\n org: org as Record<string, unknown> | null,\n // The same claim the page step makes (AGL-3024): a copy core mints is\n // written under core's own id, never `draftId`, so a step that runs\n // again — a pass resumed, a run cut off after the copy — would mint a\n // second. The job's recorded id makes core replay the one it made.\n attemptKey: draftId,\n })\n if (copy.ok) {\n const hostSubdomain = await aiSiteSubdomain(firestore, hostId)\n // The copy is reported with what the plan promised of it read back\n // (AGL-3024): this branch generates nothing, so nothing else can.\n const review = await aiCopiedTemplateReview(firestore, {\n hostId,\n id: copy.id,\n name: copy.name,\n plan,\n definition,\n })\n if (review) return aiUnspentOutcome(model, { review })\n return aiUnspentOutcome(model, {\n outputs: [output({ id: copy.id, versionId: null, name: copy.name, hostSubdomain })],\n })\n }\n if (copy.ok === false && copy.status === 403) {\n return aiUnspentOutcome(model, { review: aiLimitReview(copy.error) })\n }\n }\n\n const allowance = await aiDraftAllowanceRefusal(firestore, { kind: 'template', hostId, org })\n if (allowance) return aiUnspentOutcome(model, { review: aiLimitReview(allowance) })\n\n const result = await runValidatedGeneration('template', {\n step: 'job.template',\n model,\n instructions: [...AI_JOB_TEMPLATE_INSTRUCTIONS, examples()],\n inventory,\n messages: [\n {\n role: 'user',\n content: aiJobTemplatePrompt(job, definition, collection, plan, name),\n },\n ],\n tool: aiDoctrineTreeTool('template'),\n maxTokens: AI_JOB_TEMPLATE_STEP_BUDGET.maxTokens(model),\n ...(AI_ROUTING_TABLE['job.template'].thinking ? { thinking: AI_ROUTING_TABLE['job.template'].thinking } : {}),\n ...(AI_ROUTING_TABLE['job.template'].effort ? { effort: AI_ROUTING_TABLE['job.template'].effort } : {}),\n context: { bindingTokens: aiTemplateAddressTokens(definition) },\n // Rule 8's binding check, and the confirmed plan kept in both halves: a\n // token the plan promised that the template does not bind (AGL-3143\n // §11), and a component the plan reuses that it does not place. The\n // layout and component doors hold their builds to that reuse; the\n // template door did not, so it could drop a promised component and\n // still answer `Done` (AGL-3024).\n extend: (tree) => [\n ...aiTemplateBindingCheck(definition)(tree),\n ...aiPlanTemplateTokenViolations(plan, definition, {\n rootId: tree.rootId,\n nodes: tree.nodes as unknown as Record<string, AiDoctrineNode>,\n }),\n ...aiPlanReuseViolations(inventory, plan, aiPlacedComponentIds(tree), 'template'),\n ],\n ...(signal ? { signal } : {}),\n })\n const spent = aiGenerationSpent(result)\n if (result.status === 'refused') return { ...spent, refused: true }\n if (result.status === 'needs_input') return { ...spent, review: aiDoctrineReview(result) }\n\n const draft = await writeAiDraft(firestore, {\n kind: 'template',\n hostId,\n id: draftId,\n uid: job.createdBy,\n org,\n name,\n nodes: result.value.nodes,\n slug: aiTemplateDraftSlug(definition, collection),\n now,\n })\n if (draft.ok === false) {\n if (draft.status === 404) throw new Error(`site ${hostId} vanished while its template was generated`)\n return { ...spent, review: aiLimitReview(draft.error) }\n }\n return { ...spent, outputs: [output(draft, result.value.load)] }\n }\n}\n\nexport const runAiJobTemplateStep = createAiJobTemplateStep()\n\n/** Registers the template step and the check a template job passes before it is created or resumed. */\nexport function registerAiTemplateJob(): void {\n registerAiJobStep('template', runAiJobTemplateStep, { minimumMs: AI_JOB_TEMPLATE_STEP_MINIMUM_MS })\n registerAiJobAdmission('template', createAiTemplateJobAdmission())\n}\n"],"names":["hostCollectionKind","normalizeScreenSlug","duplicateResource","CANVAS_ROOT_ELEMENT_ID","AI_TEMPLATE_SUBJECT_DEFINITIONS","AI_TEMPLATE_SUBJECTS","aiBindingTokensIn","aiTemplateAddressTokens","parseAiTemplateJobInputs","AI_STEP_TIERS","AI_ROUTING_TABLE","aiModelForStep","aiDoctrineTreeTool","runValidatedGeneration","walkTree","AI_PALETTE","aiTemplateExamplesSystemBlock","readSiteInventory","registerAiJobAdmission","aiJobDraftId","aiDraftAdmissionRefusal","aiDraftAllowanceRefusal","aiSiteSubdomain","readAiDraft","readAiTemplateDraftNodes","writeAiDraft","aiConfirmedPlan","aiDoctrineReview","aiGenerationSpent","aiJobBriefLine","aiLimitReview","aiModelNodeIds","aiPlacedComponentIds","aiPlanCreation","aiPlanReferenceLines","aiPlanReuseViolations","aiUnspentOutcome","aiPlannedTemplateTokens","aiPlanTemplateTokenViolations","aiJobStepBudget","registerAiJobStep","AI_JOB_TEMPLATE_MAX_TOKENS","maxTokens","AI_JOB_TEMPLATE_STEP_BUDGET","tier","AI_JOB_TEMPLATE_STEP_MINIMUM_MS","minimumMs","AI_JOB_TEMPLATE_INSTRUCTIONS","text","join","readAiTemplateCollection","firestore","hostId","collectionId","snapshot","collection","doc","get","exists","data","value","trim","slug","id","name","aiTemplateDraftName","definition","planned","defaultName","aiTemplateDraftSlug","subject","aiJobTemplatePrompt","job","plan","of","noun","titleToken","tokens","map","entry","token","label","blocks","length","BLOCK_SUBJECTS","Map","flatMap","block","stringsIn","out","push","Array","isArray","inner","Object","values","isPageTitle","node","componentId","element","props","String","aiTemplateBindingCheck","allowed","Set","tree","nodes","unknown","unknownAt","foreign","titles","titleBound","ancestors","rootId","strays","filter","has","add","owner","inCard","some","ancestor","includes","violations","size","rule","code","message","slice","nodeIds","sourceIds","first","displayName","createAiTemplateJobAdmission","deps","readCollection","context","inputs","status","error","orgId","kind","org","ownCheck","aiCopiedTemplateReview","input","unreadable","stored","reason","findings","createAiJobTemplateStep","readInventory","duplicate","examples","now","signal","modelFor","orgSnapshot","Error","output","draft","load","resource","versionId","hostSubdomain","model","draftId","written","outputs","inventory","Promise","all","resolve","creation","duplicateOf","templates","template","copy","sourceId","uid","createdBy","attemptKey","ok","review","allowance","result","step","instructions","messages","role","content","tool","thinking","effort","bindingTokens","extend","spent","refused","runAiJobTemplateStep","registerAiTemplateJob"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,kBAAkB,QAAQ,yCAAwC;AAC3E,SAASC,mBAAmB,QAAQ,sCAAqC;AAEzE,SAASC,iBAAiB,QAAQ,qDAAoD;AACtF,SAASC,sBAAsB,QAAQ,2CAA0C;AAEjF,SACEC,+BAA+B,EAC/BC,oBAAoB,EACpBC,iBAAiB,EACjBC,uBAAuB,EACvBC,wBAAwB,QAEnB,mCAA+B;AACtC,SAASC,aAAa,QAAQ,0BAAsB;AACpD,SAASC,gBAAgB,EAAEC,cAAc,QAAQ,0BAAsB;AACvE,SACEC,kBAAkB,EAClBC,sBAAsB,QAEjB,4BAAwB;AAC/B,SACEC,QAAQ,QAGH,uCAAmC;AAE1C,SAASC,UAAU,QAAQ,qCAAiC;AAE5D,SAASC,6BAA6B,QAAQ,qCAAiC;AAC/E,SAASC,iBAAiB,QAAQ,+BAA2B;AAC7D,SAASC,sBAAsB,QAA6B,wBAAoB;AAChF,SAASC,YAAY,QAAQ,wBAAoB;AACjD,SACEC,uBAAuB,EACvBC,uBAAuB,EACvBC,eAAe,EACfC,WAAW,EACXC,wBAAwB,EACxBC,YAAY,QAEP,qBAAiB;AACxB,SACEC,eAAe,EACfC,gBAAgB,EAChBC,iBAAiB,EACjBC,cAAc,EACdC,aAAa,EACbC,cAAc,EACdC,oBAAoB,EACpBC,cAAc,EACdC,oBAAoB,EACpBC,qBAAqB,EACrBC,gBAAgB,QACX,yBAAqB;AAC5B,SAASC,uBAAuB,EAAEC,6BAA6B,QAAQ,+BAA2B;AAElG,SAASC,eAAe,QAAQ,qBAAiB;AACjD,SAASC,iBAAiB,QAAQ,eAAW;AAE7C;;;;;;;;;;;;;;;;;;;;;;CAsBC,GAED,kGAAkG,GAClG,OAAO,MAAMC,6BAA6B/B,gBAAgB,CAAC,eAAe,CAACgC,SAAS,CAAA;AAEpF;;;;;;CAMC,GACD,OAAO,MAAMC,8BAA8BJ,gBAAgB;IACzDK,MAAMnC,aAAa,CAAC,eAAe;IACnCiC,WAAWD;AACb,GAAE;AAEF,6DAA6D,GAC7D,OAAO,MAAMI,kCAAkCF,4BAA4BG,SAAS,CAAA;AAEpF,6FAA6F,GAC7F,OAAO,MAAMC,+BAAyD;IACpE;QACEC,MAAM;YACJ;YACA;YACA;YACA;YACA;SACD,CAACC,IAAI,CAAC;IACT;CACD,CAAA;AASD,iFAAiF,GACjF,OAAO,eAAeC,yBACpBC,SAAsC,EACtCC,MAAc,EACdC,YAAoB;QASNC;IAPd,MAAMA,WAAW,MAAMH,UACpBI,UAAU,CAAC,SACXC,GAAG,CAACJ,QACJG,UAAU,CAAC,eACXC,GAAG,CAACH,cACJI,GAAG;IACN,IAAI,CAACH,SAASI,MAAM,EAAE,OAAO;IAC7B,MAAMC,QAAQL,iBAAAA,SAASK,IAAI,cAAbL,iBAAmB,CAAC;IAClC,IAAItD,mBAAmB2D,UAAU,WAAW,OAAO;IACnD,MAAMX,OAAO,CAACY,QAAoB,OAAOA,UAAU,WAAWA,MAAMC,IAAI,KAAK;IAC7E,MAAMC,OAAOd,KAAKW,IAAI,CAAC,OAAO;IAC9B,OAAO;QACLI,IAAIV;QACJW,MAAMhB,KAAKW,IAAI,CAAC,cAAc,KAAKX,KAAKW,IAAI,CAAC,OAAO,KAAKG,QAAQT;QACjES;IACF;AACF;AAEA,6EAA6E,GAC7E,OAAO,SAASG,oBACdC,UAAuC,EACvCX,UAAuC,EACvCY,OAAkC;IAElC,OAAOA,WAAYZ,CAAAA,aAAa,GAAGA,WAAWS,IAAI,CAAC,eAAe,CAAC,GAAGE,WAAWE,WAAW,AAAD;AAC7F;AAEA,6DAA6D,GAC7D,OAAO,SAASC,oBACdH,UAAuC,EACvCX,UAAuC;QAGrCtD;IADF,QACEA,uBAAAA,oBACEsD,aACI,GAAGA,WAAWO,IAAI,IAAIP,WAAWQ,EAAE,CAAC,eAAe,CAAC,GACpD,GAAGG,WAAWI,OAAO,CAAC,cAAc,CAAC,aAH3CrE,uBAIK;AAET;AAEA,oDAAoD,GACpD,OAAO,SAASsE,oBACdC,GAAyB,EACzBN,UAAuC,EACvCX,UAAuC,EACvCkB,IAAsB,EACtBT,IAAY;IAEZ,MAAMU,KAAKnB,aACP,CAAC,SAAS,EAAEA,WAAWS,IAAI,CAAC,YAAY,EAAET,WAAWO,IAAI,GAAG,CAAC,GAAG,EAAEP,WAAWO,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,GAC3F;IACJ,OAAO;QACL,CAAC,eAAe,EAAEE,MAAM;QACxB,CAAC,gBAAgB,EAAEE,WAAWS,IAAI,GAAGD,GAAG,CAAC,CAAC;QAC1C,CAAC,aAAa,EAAER,WAAWU,UAAU,EAAE;QACvC,CAAC,wBAAwB,EAAEV,WAAWW,MAAM,CAACC,GAAG,CAAC,CAACC,QAAU,GAAGA,MAAMC,KAAK,CAAC,CAAC,EAAED,MAAME,KAAK,EAAE,EAAEhC,IAAI,CAAC,OAAO;QACzG,CAAC,uBAAuB,EAAE1C,wBAAwB2D,YAAYjB,IAAI,CAAC,OAAO;QAC1EiB,WAAWgB,MAAM,CAACC,MAAM,GACpB,CAAC,0CAA0C,EAAEjB,WAAWgB,MAAM,CAACjC,IAAI,CAAC,OAAO,GAC3E;QACJpB,eAAe2C;WACZtC,qBAAqBuC;KACzB,CAACxB,IAAI,CAAC;AACT;AAEA,6DAA6D,GAC7D,MAAMmC,iBAAiB,IAAIC,IACzBhF,qBAAqBiF,OAAO,CAAC,CAAChB,UAC5BlE,+BAA+B,CAACkE,QAAQ,CAACY,MAAM,CAACJ,GAAG,CACjD,CAACS,QAAU;YAACA;YAAOnF,+BAA+B,CAACkE,QAAQ;SAAC;AAKlE,qDAAqD,GACrD,SAASkB,UAAU5B,KAAc,EAAE6B,MAAgB,EAAE;IACnD,IAAI,OAAO7B,UAAU,UAAU6B,IAAIC,IAAI,CAAC9B;SACnC,IAAI+B,MAAMC,OAAO,CAAChC,QAAQ,KAAK,MAAMiC,SAASjC,MAAO4B,UAAUK,OAAOJ;SACtE,IAAI7B,SAAS,OAAOA,UAAU,UAAU;QAC3C,KAAK,MAAMiC,SAASC,OAAOC,MAAM,CAACnC,OAAmC4B,UAAUK,OAAOJ;IACxF;IACA,OAAOA;AACT;AAEA,0DAA0D,GAC1D,SAASO,YAAYC,IAAoB;;QAEhBA,aACGA;IAF1B,IAAIA,KAAKC,WAAW,KAAK,iBAAiB,OAAO;IACjD,MAAMC,UAAU,SAAOF,cAAAA,KAAKG,KAAK,qBAAVH,WAAY,CAAC,YAAY,MAAK,WAAWI,OAAOJ,KAAKG,KAAK,CAAC,YAAY,IAAI;IAClG,OAAO,AAACD,CAAAA,WAAWE,gBAAOJ,eAAAA,KAAKG,KAAK,qBAAVH,YAAY,CAAC,UAAU,mBAAI,GAAE,MAAO;AAChE;AAEA;;;;;CAKC,GACD,OAAO,SAASK,uBACdpC,UAAuC;IAEvC,MAAMqC,UAAU,IAAIC,IAAItC,WAAWW,MAAM,CAACC,GAAG,CAAC,CAACC,QAAUA,MAAMC,KAAK;IACpE,OAAO,CAACyB;QACN,MAAMC,QAAQD,KAAKC,KAAK;QACxB,MAAMC,UAAU,IAAIH;QACpB,MAAMI,YAAsB,EAAE;QAC9B,MAAMC,UAA0F,EAAE;QAClG,MAAMC,SAAmB,EAAE;QAC3B,IAAIC,aAAa;QACjB,KAAK,MAAM,EAAEhD,EAAE,EAAEkC,IAAI,EAAEe,SAAS,EAAE,IAAIlG,SAAS;YAAEmG,QAAQR,KAAKQ,MAAM;YAAEP;QAAM,GAAI;YAC9E,MAAM7B,SAASW,UAAUS,KAAKG,KAAK,EAAEd,OAAO,CAAChF;YAC7C,MAAM4G,SAASrC,OAAOsC,MAAM,CAAC,CAACnC,QAAU,CAACuB,QAAQa,GAAG,CAACpC;YACrD,IAAIkC,OAAO/B,MAAM,EAAE;gBACjB,KAAK,MAAMH,SAASkC,OAAQP,QAAQU,GAAG,CAACrC;gBACxC4B,UAAUlB,IAAI,CAAC3B;YACjB;YACA,MAAMuD,QAAQlC,eAAe3B,GAAG,CAACwC,KAAKC,WAAW;YACjD,MAAMqB,SAASP,UAAUQ,IAAI,CAAC,CAACC;oBAAaf;uBAAAA,EAAAA,kBAAAA,KAAK,CAACe,SAAS,qBAAff,gBAAiBR,WAAW,MAAK;;YAC7E,IAAIoB,SAASA,MAAMhD,OAAO,KAAKJ,WAAWI,OAAO,IAAI,CAACiD,QAAQ;gBAC5DV,QAAQnB,IAAI,CAAC;oBAAE3B;oBAAImC,aAAaD,KAAKC,WAAW;oBAAEoB;gBAAM;YAC1D;YACA,IAAItB,YAAYC,OAAO;gBACrBa,OAAOpB,IAAI,CAAC3B;gBACZ,IAAIc,OAAO6C,QAAQ,CAACxD,WAAWU,UAAU,GAAGmC,aAAa;YAC3D;QACF;QACA,MAAMY,aAAoC,EAAE;QAC5C,IAAIhB,QAAQiB,IAAI,EAAE;YAChBD,WAAWjC,IAAI,CAAC;gBACdmC,MAAM;gBACNC,MAAM;gBACNC,SAAS,CAAC,WAAW,EAAE;uBAAIpB;iBAAQ,CAACqB,KAAK,CAAC,GAAG,GAAG/E,IAAI,CAAC,MAAM,QAAQ,EAAEiB,WAAWS,IAAI,CAAC,iEAAiE,CAAC;gBACvJsD,SAASlG,eAAe6E,WAAWH,KAAKyB,SAAS;YACnD;QACF;QACA,kEAAkE;QAClE,IAAIpB,OAAO3B,MAAM,KAAK,KAAK,CAAC4B,YAAY;YACtCY,WAAWjC,IAAI,CAAC;gBACdmC,MAAM;gBACNC,MAAM;gBACNC,SAAS,CAAC,sEAAsE,EAAE7D,WAAWU,UAAU,CAAC,WAAW,CAAC;gBACpHqD,SAASlG,eAAe+E,QAAQL,KAAKyB,SAAS;YAChD;QACF;QACA,IAAIrB,QAAQ1B,MAAM,EAAE;;gBAKApE;YAJlB,MAAM,CAACoH,MAAM,GAAGtB;YAChBc,WAAWjC,IAAI,CAAC;gBACdmC,MAAM;gBACNC,MAAM;gBACNC,SAAS,CAAC,IAAI,WAAEhH,gCAAAA,UAAU,CAACoH,MAAMjC,WAAW,CAAC,qBAA7BnF,8BAA+BqH,WAAW,mBAAID,MAAMjC,WAAW,CAAC,4BAA4B,EAAEiC,MAAMb,KAAK,CAAC3C,IAAI,CAAC,kCAAkC,EAAET,WAAWS,IAAI,CAAC,YAAY,CAAC;gBAChMsD,SAASlG,eACP8E,QAAQ/B,GAAG,CAAC,CAACC,QAAUA,MAAMhB,EAAE,GAC/B0C,KAAKyB,SAAS;YAElB;QACF;QACA,OAAOP;IACT;AACF;AAMA;;;;CAIC,GACD,OAAO,SAASU,6BAA6BC,OAAmC,CAAC,CAAC;QACzDA;IAAvB,MAAMC,kBAAiBD,uBAAAA,KAAKC,cAAc,YAAnBD,uBAAuBpF;IAC9C,OAAO,OAAOsF;QACZ,MAAMC,SAASjI,yBAAyBgI,QAAQC,MAAM;QACtD,IAAI,OAAOA,WAAW,UAAU,OAAO;YAAEC,QAAQ;YAAKC,OAAOF;QAAO;QACpE,OAAOrH,wBAAwBoH,QAAQrF,SAAS,EAAE;YAChDyF,OAAOJ,QAAQI,KAAK;YACpBxF,QAAQoF,QAAQpF,MAAM;YACtByF,MAAM;YACNC,KAAKN,QAAQM,GAAG;YAChBC,UAAU,OAAO3F,SACfqF,OAAOpF,YAAY,IAAI,CAAE,MAAMkF,eAAeC,QAAQrF,SAAS,EAAEC,QAAQqF,OAAOpF,YAAY,IACxF;oBAAEqF,QAAQ;oBAAKC,OAAO;gBAA8C,IACpE;QACR;IACF;AACF;AAYA;;;;;;;;;;;;;CAaC,GACD,OAAO,eAAeK,uBACpB7F,SAAsC,EACtC8F,KAMC;IAED,MAAM,EAAEpE,MAAM,EAAEqE,UAAU,EAAE,GAAG7G,wBAAwB4G,MAAMxE,IAAI,EAAEwE,MAAM/E,UAAU;IACnF,IAAI,CAACW,OAAOM,MAAM,IAAI,CAAC+D,WAAW/D,MAAM,EAAE,OAAO;IACjD,MAAMgE,SAAS,MAAM3H,yBAAyB2B,WAAW;QACvDC,QAAQ6F,MAAM7F,MAAM;QACpBW,IAAIkF,MAAMlF,EAAE;IACd;IACA,MAAM2C,QAASyC,iBAAAA,SAAU,CAAC;IAC1B,IAAI,CAACzC,KAAK,CAACvG,uBAAuB,EAAE;QAClC,OAAO;YACLiJ,QAAQ;YACRrB,SAAS,CAAC,CAAC,EAAEkB,MAAMjF,IAAI,CAAC,sLAAsL,CAAC;YAC/MqF,UAAU,EAAE;QACd;IACF;IACA,MAAM1B,aAAarF,8BAA8B2G,MAAMxE,IAAI,EAAEwE,MAAM/E,UAAU,EAAE;QAC7E+C,QAAQ9G;QACRuG;IACF;IACA,IAAI,CAACiB,WAAWxC,MAAM,EAAE,OAAO;IAC/B,OAAOxD,iBAAiB;QACtBoG,SAAS,CAAC,CAAC,EAAEkB,MAAMjF,IAAI,CAAC,qEAAqE,EAAE2D,UAAU,CAAC,EAAE,CAACI,OAAO,EAAE;QACtHJ;IACF;AACF;AAEA,OAAO,SAAS2B,wBAAwBhB,OAA8B,CAAC,CAAC;QAChDA,qBACJA,iBACKA,sBACNA;IAHjB,MAAMiB,iBAAgBjB,sBAAAA,KAAKiB,aAAa,YAAlBjB,sBAAsBrH;IAC5C,MAAMuI,aAAYlB,kBAAAA,KAAKkB,SAAS,YAAdlB,kBAAkBpI;IACpC,MAAMqI,kBAAiBD,uBAAAA,KAAKC,cAAc,YAAnBD,uBAAuBpF;IAC9C,MAAMuG,YAAWnB,iBAAAA,KAAKmB,QAAQ,YAAbnB,iBAAiBtH;IAClC,OAAO,OAAO,EAAEwD,GAAG,EAAEkF,GAAG,EAAEC,MAAM,EAAExG,SAAS,EAAEyG,QAAQ,EAAE;kBAmCxCC;QAlCb,MAAMzG,SAASoB,IAAIpB,MAAM;QACzB,IAAI,CAACA,QAAQ,MAAM,IAAI0G,MAAM;QAC7B,MAAMrB,SAASjI,yBAAyBgE,IAAIiE,MAAM;QAClD,IAAI,OAAOA,WAAW,UAAU;YAC9B,MAAM,IAAIqB,MAAM,CAAC,8DAA8D,EAAErB,QAAQ;QAC3F;QACA,MAAMvE,aAAa9D,+BAA+B,CAACqI,OAAOnE,OAAO,CAAC;QAClE,MAAMyF,SAAS,CAACC,OAAsBC,OAA+C;gBACnFC,UAAU;gBACVnG,IAAIiG,MAAMjG,EAAE;gBACZoG,WAAW;gBACX/G;gBACAgH,eAAeJ,MAAMI,aAAa;gBAClCnF,OAAO+E,MAAMhG,IAAI;eACbiG,OAAO;gBAAEA;YAAK,IAAI,CAAC;QAEzB,yEAAyE;QACzE,MAAMI,gBAAQT,4BAAAA,SAAW,kCAAmBjJ,eAAe;QAE3D,kGAAkG;QAClG,MAAM2J,UAAUnJ,aAAaqD,KAAK;QAClC,MAAM+F,UAAU,MAAMhJ,YAAY4B,WAAW;YAAE0F,MAAM;YAAYzF;YAAQW,IAAIuG;QAAQ;QACrF,IAAIC,SAAS,OAAOnI,iBAAiBiI,OAAO;YAAEG,SAAS;gBAACT,OAAOQ;aAAS;QAAC;QAEzE,MAAM,CAACE,WAAWZ,aAAatG,WAAW,GAAG,MAAMmH,QAAQC,GAAG,CAAC;YAC7DpB,cAAc/E,IAAIoE,KAAK,EAAExF,QAAQ;gBAAED;YAAU;YAC7CA,UAAUI,UAAU,CAAC,QAAQC,GAAG,CAACgB,IAAIoE,KAAK,EAAEnF,GAAG;YAC/CgF,OAAOpF,YAAY,GACfkF,eAAepF,WAAWC,QAAQqF,OAAOpF,YAAY,IACrDqH,QAAQE,OAAO,CAAC;SACrB;QACD,IAAInC,OAAOpF,YAAY,IAAI,CAACE,YAAY;YACtC,MAAM,IAAIuG,MAAM,CAAC,WAAW,EAAErB,OAAOpF,YAAY,CAAC,WAAW,EAAED,OAAO,oCAAoC,CAAC;QAC7G;QACA,MAAM0F,OAAOe,oBAAAA,YAAYlG,IAAI,cAAhBkG,oBAAsB;QACnC,MAAMpF,OAAO/C,gBAAgB8C;QAC7B,MAAMqG,WAAW5I,eAAewC,MAAM;QACtC,MAAMT,OAAOC,oBAAoBC,YAAYX,YAAYsH,4BAAAA,SAAU7G,IAAI;QAEvE,wEAAwE;QACxE,uEAAuE;QACvE,iDAAiD;QACjD,IACE6G,CAAAA,4BAAAA,SAAUC,WAAW,KACrBL,UAAUM,SAAS,CAACvD,IAAI,CAAC,CAACwD,WAAaA,SAASjH,EAAE,KAAK8G,SAASC,WAAW,GAC3E;YACA,MAAMG,OAAO,MAAMzB,UAAU,YAAY;gBACvCZ,OAAOpE,IAAIoE,KAAK;gBAChBxF;gBACA8H,UAAUL,SAASC,WAAW;gBAC9B9G;gBACAmH,KAAK3G,IAAI4G,SAAS;gBAClBtC,KAAKA;gBACL,sEAAsE;gBACtE,oEAAoE;gBACpE,sEAAsE;gBACtE,mEAAmE;gBACnEuC,YAAYf;YACd;YACA,IAAIW,KAAKK,EAAE,EAAE;gBACX,MAAMlB,gBAAgB,MAAM9I,gBAAgB6B,WAAWC;gBACvD,mEAAmE;gBACnE,kEAAkE;gBAClE,MAAMmI,SAAS,MAAMvC,uBAAuB7F,WAAW;oBACrDC;oBACAW,IAAIkH,KAAKlH,EAAE;oBACXC,MAAMiH,KAAKjH,IAAI;oBACfS;oBACAP;gBACF;gBACA,IAAIqH,QAAQ,OAAOnJ,iBAAiBiI,OAAO;oBAAEkB;gBAAO;gBACpD,OAAOnJ,iBAAiBiI,OAAO;oBAC7BG,SAAS;wBAACT,OAAO;4BAAEhG,IAAIkH,KAAKlH,EAAE;4BAAEoG,WAAW;4BAAMnG,MAAMiH,KAAKjH,IAAI;4BAAEoG;wBAAc;qBAAG;gBACrF;YACF;YACA,IAAIa,KAAKK,EAAE,KAAK,SAASL,KAAKvC,MAAM,KAAK,KAAK;gBAC5C,OAAOtG,iBAAiBiI,OAAO;oBAAEkB,QAAQzJ,cAAcmJ,KAAKtC,KAAK;gBAAE;YACrE;QACF;QAEA,MAAM6C,YAAY,MAAMnK,wBAAwB8B,WAAW;YAAE0F,MAAM;YAAYzF;YAAQ0F;QAAI;QAC3F,IAAI0C,WAAW,OAAOpJ,iBAAiBiI,OAAO;YAAEkB,QAAQzJ,cAAc0J;QAAW;QAEjF,MAAMC,SAAS,MAAM5K,uBAAuB,YAAY;YACtD6K,MAAM;YACNrB;YACAsB,cAAc;mBAAI5I;gBAA8B0G;aAAW;YAC3DgB;YACAmB,UAAU;gBACR;oBACEC,MAAM;oBACNC,SAASvH,oBAAoBC,KAAKN,YAAYX,YAAYkB,MAAMT;gBAClE;aACD;YACD+H,MAAMnL,mBAAmB;YACzB8B,WAAWC,4BAA4BD,SAAS,CAAC2H;WAC7C3J,gBAAgB,CAAC,eAAe,CAACsL,QAAQ,GAAG;YAAEA,UAAUtL,gBAAgB,CAAC,eAAe,CAACsL,QAAQ;QAAC,IAAI,CAAC,GACvGtL,gBAAgB,CAAC,eAAe,CAACuL,MAAM,GAAG;YAAEA,QAAQvL,gBAAgB,CAAC,eAAe,CAACuL,MAAM;QAAC,IAAI,CAAC;YACrGzD,SAAS;gBAAE0D,eAAe3L,wBAAwB2D;YAAY;YAC9D,wEAAwE;YACxE,oEAAoE;YACpE,oEAAoE;YACpE,kEAAkE;YAClE,mEAAmE;YACnE,kCAAkC;YAClCiI,QAAQ,CAAC1F,OAAS;uBACbH,uBAAuBpC,YAAYuC;uBACnCnE,8BAA8BmC,MAAMP,YAAY;wBACjD+C,QAAQR,KAAKQ,MAAM;wBACnBP,OAAOD,KAAKC,KAAK;oBACnB;uBACGvE,sBAAsBsI,WAAWhG,MAAMzC,qBAAqByE,OAAO;iBACvE;WACGkD,SAAS;YAAEA;QAAO,IAAI,CAAC;QAE7B,MAAMyC,QAAQxK,kBAAkB6J;QAChC,IAAIA,OAAO/C,MAAM,KAAK,WAAW,OAAO,aAAK0D;YAAOC,SAAS;;QAC7D,IAAIZ,OAAO/C,MAAM,KAAK,eAAe,OAAO,aAAK0D;YAAOb,QAAQ5J,iBAAiB8J;;QAEjF,MAAMzB,QAAQ,MAAMvI,aAAa0B,WAAW;YAC1C0F,MAAM;YACNzF;YACAW,IAAIuG;YACJa,KAAK3G,IAAI4G,SAAS;YAClBtC;YACA9E;YACA0C,OAAO+E,OAAO7H,KAAK,CAAC8C,KAAK;YACzB5C,MAAMO,oBAAoBH,YAAYX;YACtCmG;QACF;QACA,IAAIM,MAAMsB,EAAE,KAAK,OAAO;YACtB,IAAItB,MAAMtB,MAAM,KAAK,KAAK,MAAM,IAAIoB,MAAM,CAAC,KAAK,EAAE1G,OAAO,0CAA0C,CAAC;YACpG,OAAO,aAAKgJ;gBAAOb,QAAQzJ,cAAckI,MAAMrB,KAAK;;QACtD;QACA,OAAO,aAAKyD;YAAO5B,SAAS;gBAACT,OAAOC,OAAOyB,OAAO7H,KAAK,CAACqG,IAAI;aAAE;;IAChE;AACF;AAEA,OAAO,MAAMqC,uBAAuBhD,0BAAyB;AAE7D,qGAAqG,GACrG,OAAO,SAASiD;IACd/J,kBAAkB,YAAY8J,sBAAsB;QAAExJ,WAAWD;IAAgC;IACjG3B,uBAAuB,YAAYmH;AACrC"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-template-step.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { hostCollectionKind } from '@aglyn/aglyn/app-utils/collection-kind'\nimport { normalizeScreenSlug } from '@aglyn/aglyn/app-utils/screen-route'\nimport type { AglynOrgBilling } from '@aglyn/aglyn/foundation/definitions/org-billing.types'\nimport { duplicateResource } from '@aglyn/tenant-data-admin/server/duplicate-resource'\nimport { CANVAS_ROOT_ELEMENT_ID } from '@aglyn/aglyn/foundation/constants/canvas'\nimport type { AiJob, AiJobOutput, AiJobPlan, AiJobReview } from '../model/ai-jobs.types'\nimport {\n AI_TEMPLATE_SUBJECT_DEFINITIONS,\n AI_TEMPLATE_SUBJECTS,\n aiBindingTokensIn,\n aiTemplateAddressTokens,\n isAiAddressToken,\n isAiUnreadToken,\n parseAiTemplateJobInputs,\n type AiTemplateSubjectDefinition,\n} from '../model/ai-template-subjects'\nimport { AI_STEP_TIERS } from '../providers/catalog'\nimport { AI_ROUTING_TABLE, aiModelForStep } from '../providers/routing'\nimport {\n aiDoctrineTreeTool,\n runValidatedGeneration,\n type AiValidatedTree,\n} from '../runtime/ai-doctrine'\nimport {\n walkTree,\n type AiDoctrineNode,\n type AiDoctrineViolation,\n} from '../runtime/ai-doctrine-validators'\nimport type { AiLoadEstimate } from '../runtime/ai-palette'\nimport { AI_PALETTE } from '../runtime/ai-palette.generated'\nimport type { AiSystemBlock } from '../runtime/ai-runtime'\nimport { aiTemplateExamplesSystemBlock } from '../runtime/ai-template-examples'\nimport { readSiteInventory } from '../runtime/site-inventory'\nimport { registerAiJobAdmission, type AiJobAdmission } from './ai-job-admission'\nimport { aiJobDraftId } from './ai-job-draft-ids'\nimport {\n aiDraftAdmissionRefusal,\n aiDraftAllowanceRefusal,\n aiSiteSubdomain,\n readAiDraft,\n readAiTemplateDraftNodes,\n writeAiDraft,\n type AiDraftRecord,\n} from './ai-job-drafts'\nimport {\n aiConfirmedPlan,\n aiCopySourceLines,\n aiDoctrineReview,\n aiGenerationSpent,\n aiJobBriefLine,\n aiLimitReview,\n aiModelNodeIds,\n aiPlacedComponentIds,\n aiPlanCreation,\n aiPlanReferenceLines,\n aiPlanReuseViolations,\n aiUnspentOutcome,\n} from './ai-job-generation'\nimport { aiPlannedTemplateTokens, aiPlanTemplateTokenViolations } from './ai-job-plan-conformance'\nimport {\n aiBuildingPlanCreations,\n aiJobPlanCreationsRefusal,\n aiPlanCreationsRunMinimumMs,\n} from './ai-job-plan-creations'\nimport type { AiJobStepRunner } from './ai-job-text-step'\nimport { aiJobStepBudget } from './ai-job-budget'\nimport { registerAiJobStep } from './ai-jobs'\n\n/**\n * The template step (AGL-2909): the generation step of a `template` job, run\n * once a member confirmed the job's plan.\n *\n * It builds ONE page template — the page a site renders once per record: a\n * content collection's entry, a product, an author — through\n * `runValidatedGeneration('template', …)`. Everything that differs per\n * record is a binding token from the subject's vocabulary\n * (`model/ai-template-subjects.ts`), which is the list the besigner's insert\n * picker offers for that page; a link or media prop may hold one of those\n * tokens whole, which the palette validator admits only because this door\n * names them. The door adds one check of its own through `extend` (rule 8):\n * no token the page does not fill, an h1 bound to the subject's title, no\n * block that fills itself only on another subject's page, and no address,\n * slug or timestamp printed in its copy.\n *\n * The model is shown the platform's starter pages as examples, in a cached\n * block (`runtime/ai-template-examples.ts`), and writes against them.\n *\n * The template lands in the site's library as a new draft\n * (`ai-job-drafts.ts`), inert until a member uses it: bound to no\n * collection, set as no product or author page, served nowhere. A plan that\n * starts from a copy of a template the site has gets that copy instead.\n */\n\n/** The longest answer a template may run to; the tree is held to the template budget either way. */\nexport const AI_JOB_TEMPLATE_MAX_TOKENS = AI_ROUTING_TABLE['job.template'].maxTokens\n\n/**\n * The template step's time (AGL-3035, AGL-3036): its two inventory-lookup\n * rounds, its answer and its re-ask at the routing table's ceiling on the tier\n * `job.template` is served from, fitted to what a beat can give a step. It\n * runs on the job beat, never at an inline door, and asks for no more of the\n * ceiling than fits that time on the model it runs.\n */\nexport const AI_JOB_TEMPLATE_STEP_BUDGET = aiJobStepBudget({\n tier: AI_STEP_TIERS['job.template'],\n maxTokens: AI_JOB_TEMPLATE_MAX_TOKENS,\n})\n\n/** The least time one template step needs before it starts. */\nexport const AI_JOB_TEMPLATE_STEP_MINIMUM_MS = AI_JOB_TEMPLATE_STEP_BUDGET.minimumMs\n\n/** The template step's own instructions, cached after the doctrine and before the examples. */\nexport const AI_JOB_TEMPLATE_INSTRUCTIONS: readonly AiSystemBlock[] = [\n {\n text: [\n 'You build one page template: the page a website renders once for each record it keeps, such as each entry of a content collection, each product or each author. Answer with submit_template: the whole page as one flat node map.',\n 'Everything that differs from one record to the next is a binding token, never typed copy. The request names what the page is for, the tokens that page fills and the blocks that fill themselves there; bind only those tokens.',\n 'The page’s one h1 holds the title token the request names. Copy that reads the same on every record, such as a section heading, is written in the site’s voice.',\n 'A link or media prop holds exactly one token from the request’s link and media tokens, such as an Image whose src is {{entry.coverImage}}; its alt text may combine words and tokens. Copy never prints a link or media token, a slug or a timestamp.',\n 'A template carries no third-party player. It plays a video token only when the confirmed plan lists it, in a Video (video) whose src is that token and whose poster is the cover image token.',\n 'The page renders inside the site’s layout, so it carries no header, navigation or footer of its own.',\n ].join('\\n'),\n },\n]\n\n/** A content collection an entry template is for, as the prompt and the draft name it. */\nexport interface AiTemplateCollection {\n id: string\n name: string\n slug: string\n}\n\n/** The content collection by that id on the site, or `null` when there is none. */\nexport async function readAiTemplateCollection(\n firestore: FirebaseFirestore.Firestore,\n hostId: string,\n collectionId: string,\n): Promise<AiTemplateCollection | null> {\n const snapshot = await firestore\n .collection('hosts')\n .doc(hostId)\n .collection('collections')\n .doc(collectionId)\n .get()\n if (!snapshot.exists) return null\n const data = (snapshot.data() ?? {}) as Record<string, unknown>\n if (hostCollectionKind(data) !== 'content') return null\n const text = (value: unknown) => (typeof value === 'string' ? value.trim() : '')\n const slug = text(data['slug'])\n return {\n id: collectionId,\n name: text(data['displayName']) || text(data['name']) || slug || collectionId,\n slug,\n }\n}\n\n/** The template's name: the plan's, else the collection's or the subject's. */\nexport function aiTemplateDraftName(\n definition: AiTemplateSubjectDefinition,\n collection: AiTemplateCollection | null,\n planned: string | null | undefined,\n): string {\n return planned || (collection ? `${collection.name} entry template` : definition.defaultName)\n}\n\n/** The address Use template offers for the page it creates. */\nexport function aiTemplateDraftSlug(\n definition: AiTemplateSubjectDefinition,\n collection: AiTemplateCollection | null,\n): string | null {\n return (\n normalizeScreenSlug(\n collection\n ? `${collection.slug || collection.id}-entry-template`\n : `${definition.subject}-page-template`,\n ) ?? null\n )\n}\n\n/** The template job as the generation's user turn, with the template a short copy would have started from (AGL-3143 §14). */\nexport function aiJobTemplatePrompt(\n job: Pick<AiJob, 'brief'>,\n definition: AiTemplateSubjectDefinition,\n collection: AiTemplateCollection | null,\n plan: AiJobPlan | null,\n name: string,\n source: readonly string[] = [],\n): string {\n const of = collection\n ? ` of the \"${collection.name}\" collection${collection.slug ? ` (/${collection.slug})` : ''}`\n : ''\n return [\n `Template name: ${name}`,\n `The page is for ${definition.noun}${of}.`,\n `Title token: ${definition.titleToken}`,\n `Tokens this page fills: ${definition.tokens.map((entry) => `${entry.token} ${entry.label}`).join('; ')}`,\n `Link and media tokens: ${aiTemplateAddressTokens(definition).join(', ')}`,\n definition.blocks.length\n ? `Blocks that fill themselves on this page: ${definition.blocks.join(', ')}`\n : 'No block fills itself on this page: bind the tokens.',\n aiJobBriefLine(job),\n ...aiPlanReferenceLines(plan),\n ...source,\n ].join('\\n')\n}\n\n/** Which subject's page each self-filling block belongs to. */\nconst BLOCK_SUBJECTS = new Map<string, AiTemplateSubjectDefinition>(\n AI_TEMPLATE_SUBJECTS.flatMap((subject) =>\n AI_TEMPLATE_SUBJECT_DEFINITIONS[subject].blocks.map(\n (block) => [block, AI_TEMPLATE_SUBJECT_DEFINITIONS[subject]] as const,\n ),\n ),\n)\n\n/** Every string a prop value holds, however deeply. */\nfunction stringsIn(value: unknown, out: string[] = []): string[] {\n if (typeof value === 'string') out.push(value)\n else if (Array.isArray(value)) for (const inner of value) stringsIn(inner, out)\n else if (value && typeof value === 'object') {\n for (const inner of Object.values(value as Record<string, unknown>)) stringsIn(inner, out)\n }\n return out\n}\n\n/** The page's title: a Typography that renders as an h1. */\nfunction isPageTitle(node: AiDoctrineNode): boolean {\n if (node.componentId !== 'muiTypography') return false\n const element = typeof node.props?.['component'] === 'string' ? String(node.props['component']) : ''\n return (element || String(node.props?.['variant'] ?? '')) === 'h1'\n}\n\n/**\n * The tokens a prop prints for a reader that no reader reads: a link or\n * picture token, a slug or a timestamp, in copy (AGL-3143 §16). Copy is a\n * prop the palette gives the text role, read as words where it holds more\n * than one token or is the element's children. A block handed one token\n * whole reads it as a value, as Entry Meta's avatar takes the author's\n * portrait and a Video's publication date takes the timestamp, and a\n * markdown link's target is followed, not read.\n */\nfunction printedTokens(node: AiDoctrineNode): string[] {\n const roles = AI_PALETTE[node.componentId]?.propRoles ?? {}\n return Object.entries(node.props ?? {}).flatMap(([key, value]) => {\n if (typeof value !== 'string' || roles[key] !== 'text') return []\n const tokens = aiBindingTokensIn(value.replace(/\\]\\([^)]*\\)/g, ']'))\n const unread = tokens.filter((token) => isAiAddressToken(token) || isAiUnreadToken(token))\n const whole = tokens.length === 1 && value.trim() === tokens[0]\n return unread.length && (key === 'children' || !whole) ? unread : []\n })\n}\n\n/**\n * The template door's check on a tree the doctrine admitted (rule 8): every\n * token is one the page fills, the h1 binds the subject's title, no block\n * that fills itself on another subject's page is placed — except inside a\n * Collection Entries card, where each fills from the card's own entry — and\n * no copy prints an address, a slug or a timestamp.\n */\nexport function aiTemplateBindingCheck(\n definition: AiTemplateSubjectDefinition,\n): (tree: AiValidatedTree) => AiDoctrineViolation[] {\n const allowed = new Set(definition.tokens.map((entry) => entry.token))\n return (tree) => {\n const nodes = tree.nodes as unknown as Record<string, AiDoctrineNode>\n const unknown = new Set<string>()\n const unknownAt: string[] = []\n const foreign: Array<{ id: string; componentId: string; owner: AiTemplateSubjectDefinition }> = []\n const titles: string[] = []\n const printed = new Set<string>()\n const printedAt: string[] = []\n let titleBound = false\n for (const { id, node, ancestors } of walkTree({ rootId: tree.rootId, nodes })) {\n const tokens = stringsIn(node.props).flatMap(aiBindingTokensIn)\n const strays = tokens.filter((token) => !allowed.has(token))\n if (strays.length) {\n for (const token of strays) unknown.add(token)\n unknownAt.push(id)\n }\n const shown = printedTokens(node).filter((token) => allowed.has(token))\n if (shown.length) {\n for (const token of shown) printed.add(token)\n printedAt.push(id)\n }\n const owner = BLOCK_SUBJECTS.get(node.componentId)\n const inCard = ancestors.some((ancestor) => nodes[ancestor]?.componentId === 'collectionEntries')\n if (owner && owner.subject !== definition.subject && !inCard) {\n foreign.push({ id, componentId: node.componentId, owner })\n }\n if (isPageTitle(node)) {\n titles.push(id)\n if (tokens.includes(definition.titleToken)) titleBound = true\n }\n }\n const violations: AiDoctrineViolation[] = []\n if (unknown.size) {\n violations.push({\n rule: 8,\n code: 'unknown-binding',\n message: `This binds ${[...unknown].slice(0, 3).join(', ')}, which ${definition.noun}’s page does not fill. Bind only the tokens listed for this page.`,\n nodeIds: aiModelNodeIds(unknownAt, tree.sourceIds),\n })\n }\n if (printed.size) {\n violations.push({\n rule: 8,\n code: 'printed-token',\n message: `This prints ${[...printed].slice(0, 3).join(', ')} in the page's copy, where a reader sees an address, a slug or a timestamp. A link or picture token goes whole in a link or media prop, and a slug or a timestamp stays off the page.`,\n nodeIds: aiModelNodeIds(printedAt, tree.sourceIds),\n })\n }\n // The doctrine already refuses a page with no h1 or with several.\n if (titles.length === 1 && !titleBound) {\n violations.push({\n rule: 8,\n code: 'typed-title',\n message: `The page title is typed, so every record would show the same one. Put ${definition.titleToken} in the h1.`,\n nodeIds: aiModelNodeIds(titles, tree.sourceIds),\n })\n }\n if (foreign.length) {\n const [first] = foreign\n violations.push({\n rule: 8,\n code: 'foreign-block',\n message: `The ${AI_PALETTE[first.componentId]?.displayName ?? first.componentId} block fills itself only on ${first.owner.noun}’s page, and this template is for ${definition.noun}. Remove it.`,\n nodeIds: aiModelNodeIds(\n foreign.map((entry) => entry.id),\n tree.sourceIds,\n ),\n })\n }\n return violations\n }\n}\n\nexport interface AiTemplateJobAdmissionDeps {\n readCollection?: typeof readAiTemplateCollection\n}\n\n/**\n * A template job is admitted with inputs that say what the page is for, for\n * a site of the job's own org that holds the collection it names, with a\n * template to spare.\n */\nexport function createAiTemplateJobAdmission(deps: AiTemplateJobAdmissionDeps = {}): AiJobAdmission {\n const readCollection = deps.readCollection ?? readAiTemplateCollection\n return async (context) => {\n const inputs = parseAiTemplateJobInputs(context.inputs)\n if (typeof inputs === 'string') return { status: 400, error: inputs }\n return aiDraftAdmissionRefusal(context.firestore, {\n orgId: context.orgId,\n hostId: context.hostId,\n kind: 'template',\n org: context.org,\n ownCheck: async (hostId) =>\n inputs.collectionId && !(await readCollection(context.firestore, hostId, inputs.collectionId))\n ? { status: 404, error: 'That content collection is not on this site' }\n : aiJobPlanCreationsRefusal('template', context, hostId),\n })\n }\n}\n\nexport interface AiJobTemplateStepDeps {\n /** The inventory reader; specs hand in a fake. */\n readInventory?: typeof readSiteInventory\n /** The platform's duplicate module, for a plan that starts from a copy. */\n duplicate?: typeof duplicateResource\n readCollection?: typeof readAiTemplateCollection\n /** The starter examples block; built from the starters once per process otherwise. */\n examples?: () => AiSystemBlock\n}\n\n/**\n * The binding tokens the confirmed plan gives the template, against the\n * template a COPY actually produced (AGL-3024); `null` when the copy keeps\n * the plan, or when the plan promised no token to keep.\n *\n * The same hole as `aiCopiedLayoutReview` and `aiCopiedPageReview`, on the\n * third kind that can start from a duplicate. The generate path already holds\n * a built template to `aiPlanTemplateTokenViolations`; the copy path reached\n * none of it, so a plan reading \"duplicate it and bind the author's photo\"\n * got the duplicate and reported Done.\n *\n * A copy that cannot be read back settles nothing, and a promise this step\n * cannot settle is reviewed by a person rather than reported as kept.\n */\nexport async function aiCopiedTemplateReview(\n firestore: FirebaseFirestore.Firestore,\n input: {\n hostId: string\n id: string\n name: string\n plan: AiJobPlan | null\n definition: AiTemplateSubjectDefinition\n },\n): Promise<AiJobReview | null> {\n const { tokens, unreadable } = aiPlannedTemplateTokens(input.plan, input.definition)\n if (!tokens.length && !unreadable.length) return null\n const stored = await readAiTemplateDraftNodes(firestore, {\n hostId: input.hostId,\n id: input.id,\n })\n const nodes = (stored ?? {}) as unknown as Record<string, AiDoctrineNode>\n if (!nodes[CANVAS_ROOT_ELEMENT_ID]) {\n return {\n reason: 'doctrine',\n message: `\"${input.name}\" was copied from the template the plan names, and this job could not read the copy back to check it shows what the plan gives it. Open the template and check it before you apply it.`,\n findings: [],\n }\n }\n const violations = aiPlanTemplateTokenViolations(input.plan, input.definition, {\n rootId: CANVAS_ROOT_ELEMENT_ID,\n nodes,\n })\n if (!violations.length) return null\n return aiDoctrineReview({\n message: `\"${input.name}\" is a copy of the template the plan names, and a copy is all it is: ${violations[0].message}`,\n violations,\n })\n}\n\nexport function createAiJobTemplateStep(deps: AiJobTemplateStepDeps = {}): AiJobStepRunner {\n const readInventory = deps.readInventory ?? readSiteInventory\n const duplicate = deps.duplicate ?? duplicateResource\n const readCollection = deps.readCollection ?? readAiTemplateCollection\n const examples = deps.examples ?? aiTemplateExamplesSystemBlock\n return async ({ job, now, signal, firestore, modelFor }) => {\n const hostId = job.hostId\n if (!hostId) throw new Error('a template job names no site, and its admission refuses one')\n const inputs = parseAiTemplateJobInputs(job.inputs)\n if (typeof inputs === 'string') {\n throw new Error(`a template job's inputs no longer read as they were admitted: ${inputs}`)\n }\n const definition = AI_TEMPLATE_SUBJECT_DEFINITIONS[inputs.subject]\n const output = (draft: AiDraftRecord, load?: AiLoadEstimate | null): AiJobOutput => ({\n resource: 'template',\n id: draft.id,\n versionId: null,\n hostId,\n hostSubdomain: draft.hostSubdomain,\n label: draft.name,\n ...(load ? { load } : {}),\n })\n // The switch's answer for this job, else the routing table's (AGL-2942).\n const model = modelFor?.('job.template') ?? aiModelForStep('job.template')\n\n // A run cut off after its draft was written reports that draft, found by the id the job recorded.\n const draftId = aiJobDraftId(job, 'template')\n const written = await readAiDraft(firestore, { kind: 'template', hostId, id: draftId })\n if (written) return aiUnspentOutcome(model, { outputs: [output(written)] })\n\n const [inventory, orgSnapshot, collection] = await Promise.all([\n readInventory(job.orgId, hostId, { firestore }),\n firestore.collection('orgs').doc(job.orgId).get(),\n inputs.collectionId\n ? readCollection(firestore, hostId, inputs.collectionId)\n : Promise.resolve(null),\n ])\n if (inputs.collectionId && !collection) {\n throw new Error(`collection ${inputs.collectionId} left site ${hostId} after its template job was admitted`)\n }\n const org = (orgSnapshot.data() ?? null) as Partial<AglynOrgBilling> | null\n const plan = aiConfirmedPlan(job)\n const creation = aiPlanCreation(plan, 'template')\n const name = aiTemplateDraftName(definition, collection, creation?.name)\n\n // The plan starts from a copy of a template the site has (rule 15): the\n // copy is the draft, and nothing is generated. A source gone since the\n // plan was made is built from the brief instead, and one that cannot keep\n // the plan's tokens is built FROM (AGL-3143 §14): copying it could only end\n // in the review below. A source this step cannot read still copies.\n const source = creation?.duplicateOf\n ? inventory.templates.find((template) => template.id === creation.duplicateOf)\n : undefined\n const sourceReview = source\n ? await aiCopiedTemplateReview(firestore, { hostId, id: source.id, name: source.name, plan, definition })\n : null\n const sourceShort = Boolean(sourceReview?.findings.length)\n const sourceLines =\n source && sourceShort\n ? aiCopySourceLines(\n 'template',\n source.name,\n (await readAiTemplateDraftNodes(firestore, { hostId, id: source.id })) as Record<string, unknown> | null,\n )\n : []\n if (creation?.duplicateOf && source && !sourceShort) {\n const copy = await duplicate('template', {\n orgId: job.orgId,\n hostId,\n sourceId: creation.duplicateOf,\n name,\n uid: job.createdBy,\n org: org as Record<string, unknown> | null,\n // The same claim the page step makes (AGL-3024): a copy core mints is\n // written under core's own id, never `draftId`, so a step that runs\n // again — a pass resumed, a run cut off after the copy — would mint a\n // second. The job's recorded id makes core replay the one it made.\n attemptKey: draftId,\n })\n if (copy.ok) {\n const hostSubdomain = await aiSiteSubdomain(firestore, hostId)\n // The copy is reported with what the plan promised of it read back\n // (AGL-3024): this branch generates nothing, so nothing else can.\n const review = await aiCopiedTemplateReview(firestore, {\n hostId,\n id: copy.id,\n name: copy.name,\n plan,\n definition,\n })\n if (review) return aiUnspentOutcome(model, { review })\n return aiUnspentOutcome(model, {\n outputs: [output({ id: copy.id, versionId: null, name: copy.name, hostSubdomain })],\n })\n }\n if (copy.ok === false && copy.status === 403) {\n return aiUnspentOutcome(model, { review: aiLimitReview(copy.error) })\n }\n }\n\n const allowance = await aiDraftAllowanceRefusal(firestore, { kind: 'template', hostId, org })\n if (allowance) return aiUnspentOutcome(model, { review: aiLimitReview(allowance) })\n\n const result = await runValidatedGeneration('template', {\n step: 'job.template',\n model,\n instructions: [...AI_JOB_TEMPLATE_INSTRUCTIONS, examples()],\n inventory,\n messages: [\n {\n role: 'user',\n content: aiJobTemplatePrompt(job, definition, collection, plan, name, sourceLines),\n },\n ],\n tool: aiDoctrineTreeTool('template'),\n maxTokens: AI_JOB_TEMPLATE_STEP_BUDGET.maxTokens(model),\n ...(AI_ROUTING_TABLE['job.template'].thinking ? { thinking: AI_ROUTING_TABLE['job.template'].thinking } : {}),\n ...(AI_ROUTING_TABLE['job.template'].effort ? { effort: AI_ROUTING_TABLE['job.template'].effort } : {}),\n context: { bindingTokens: aiTemplateAddressTokens(definition) },\n // Rule 8's binding check, and the confirmed plan kept in both halves: a\n // token the plan promised that the template does not bind (AGL-3143\n // §11), and a component the plan reuses that it does not place. The\n // layout and component doors hold their builds to that reuse; the\n // template door did not, so it could drop a promised component and\n // still answer `Done` (AGL-3024).\n extend: (tree) => [\n ...aiTemplateBindingCheck(definition)(tree),\n ...aiPlanTemplateTokenViolations(plan, definition, {\n rootId: tree.rootId,\n nodes: tree.nodes as unknown as Record<string, AiDoctrineNode>,\n }),\n ...aiPlanReuseViolations(inventory, plan, aiPlacedComponentIds(tree), 'template'),\n ],\n ...(signal ? { signal } : {}),\n })\n const spent = aiGenerationSpent(result)\n if (result.status === 'refused') return { ...spent, refused: true }\n if (result.status === 'needs_input') return { ...spent, review: aiDoctrineReview(result) }\n\n const draft = await writeAiDraft(firestore, {\n kind: 'template',\n hostId,\n id: draftId,\n uid: job.createdBy,\n org,\n name,\n nodes: result.value.nodes,\n slug: aiTemplateDraftSlug(definition, collection),\n now,\n })\n if (draft.ok === false) {\n if (draft.status === 404) throw new Error(`site ${hostId} vanished while its template was generated`)\n return { ...spent, review: aiLimitReview(draft.error) }\n }\n return { ...spent, outputs: [output(draft, result.value.load)] }\n }\n}\n\n/** The template step, building the components and forms its plan creates before the template (AGL-3143 §15). */\nexport const runAiJobTemplateStep = aiBuildingPlanCreations('template', 'job.template', createAiJobTemplateStep())\n\n/** Registers the template step and the check a template job passes before it is created or resumed. */\nexport function registerAiTemplateJob(): void {\n registerAiJobStep('template', runAiJobTemplateStep, {\n minimumMs: AI_JOB_TEMPLATE_STEP_MINIMUM_MS,\n minimumMsFor: aiPlanCreationsRunMinimumMs('template'),\n })\n registerAiJobAdmission('template', createAiTemplateJobAdmission())\n}\n"],"names":["hostCollectionKind","normalizeScreenSlug","duplicateResource","CANVAS_ROOT_ELEMENT_ID","AI_TEMPLATE_SUBJECT_DEFINITIONS","AI_TEMPLATE_SUBJECTS","aiBindingTokensIn","aiTemplateAddressTokens","isAiAddressToken","isAiUnreadToken","parseAiTemplateJobInputs","AI_STEP_TIERS","AI_ROUTING_TABLE","aiModelForStep","aiDoctrineTreeTool","runValidatedGeneration","walkTree","AI_PALETTE","aiTemplateExamplesSystemBlock","readSiteInventory","registerAiJobAdmission","aiJobDraftId","aiDraftAdmissionRefusal","aiDraftAllowanceRefusal","aiSiteSubdomain","readAiDraft","readAiTemplateDraftNodes","writeAiDraft","aiConfirmedPlan","aiCopySourceLines","aiDoctrineReview","aiGenerationSpent","aiJobBriefLine","aiLimitReview","aiModelNodeIds","aiPlacedComponentIds","aiPlanCreation","aiPlanReferenceLines","aiPlanReuseViolations","aiUnspentOutcome","aiPlannedTemplateTokens","aiPlanTemplateTokenViolations","aiBuildingPlanCreations","aiJobPlanCreationsRefusal","aiPlanCreationsRunMinimumMs","aiJobStepBudget","registerAiJobStep","AI_JOB_TEMPLATE_MAX_TOKENS","maxTokens","AI_JOB_TEMPLATE_STEP_BUDGET","tier","AI_JOB_TEMPLATE_STEP_MINIMUM_MS","minimumMs","AI_JOB_TEMPLATE_INSTRUCTIONS","text","join","readAiTemplateCollection","firestore","hostId","collectionId","snapshot","collection","doc","get","exists","data","value","trim","slug","id","name","aiTemplateDraftName","definition","planned","defaultName","aiTemplateDraftSlug","subject","aiJobTemplatePrompt","job","plan","source","of","noun","titleToken","tokens","map","entry","token","label","blocks","length","BLOCK_SUBJECTS","Map","flatMap","block","stringsIn","out","push","Array","isArray","inner","Object","values","isPageTitle","node","componentId","element","props","String","printedTokens","roles","propRoles","entries","key","replace","unread","filter","whole","aiTemplateBindingCheck","allowed","Set","tree","nodes","unknown","unknownAt","foreign","titles","printed","printedAt","titleBound","ancestors","rootId","strays","has","add","shown","owner","inCard","some","ancestor","includes","violations","size","rule","code","message","slice","nodeIds","sourceIds","first","displayName","createAiTemplateJobAdmission","deps","readCollection","context","inputs","status","error","orgId","kind","org","ownCheck","aiCopiedTemplateReview","input","unreadable","stored","reason","findings","createAiJobTemplateStep","readInventory","duplicate","examples","now","signal","modelFor","orgSnapshot","Error","output","draft","load","resource","versionId","hostSubdomain","model","draftId","written","outputs","inventory","Promise","all","resolve","creation","duplicateOf","templates","find","template","undefined","sourceReview","sourceShort","Boolean","sourceLines","copy","sourceId","uid","createdBy","attemptKey","ok","review","allowance","result","step","instructions","messages","role","content","tool","thinking","effort","bindingTokens","extend","spent","refused","runAiJobTemplateStep","registerAiTemplateJob","minimumMsFor"],"mappings":"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"}
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* See the License for the specific language governing permissions and
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/
|
|
17
|
-
import { type HostThemeSource } from '@aglyn/aglyn/app-utils/
|
|
17
|
+
import { type HostThemeSource } from '@aglyn/aglyn/app-utils/site-theme';
|
|
18
18
|
import type { HostTheme } from '@aglyn/shared-data-types';
|
|
19
19
|
import type { AiJob } from '../model/ai-jobs.types';
|
|
20
20
|
import { type AiThemeProposalMode } from '../model/ai-theme-proposal';
|
|
@@ -14,7 +14,7 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
|
14
14
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
15
|
* See the License for the specific language governing permissions and
|
|
16
16
|
* limitations under the License.
|
|
17
|
-
*/ import { hostThemeSource, resolveSiteTheme } from "@aglyn/aglyn/app-utils/
|
|
17
|
+
*/ import { hostThemeSource, resolveSiteTheme } from "@aglyn/aglyn/app-utils/site-theme";
|
|
18
18
|
import { INHERITED_BORDER_RADIUS, INHERITED_FONT_FAMILY, INHERITED_SPACING, INHERITED_TOOLBAR_HEIGHTS, inheritedThemeColor } from "@aglyn/shared-ui-theme/util/theme-editor-defaults";
|
|
19
19
|
import { readDarkScheme, readFontFamily, readThemeColor, readToolbarHeight, SYSTEM_FONT_VALUE, THEME_COLOR_FIELDS, THEME_EDITOR_MEDIA_QUERIES } from "@aglyn/shared-ui-theme/util/theme-editor-fields";
|
|
20
20
|
import { buildAiThemeProposal } from "../model/ai-theme-proposal.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-theme-step.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n hostThemeSource,\n resolveSiteTheme,\n type HostThemeSource,\n type ThemeHostDocument,\n} from '@aglyn/aglyn/app-utils/marketplace-theme'\nimport type { HostTheme, HostThemeScheme } from '@aglyn/shared-data-types'\nimport {\n INHERITED_BORDER_RADIUS,\n INHERITED_FONT_FAMILY,\n INHERITED_SPACING,\n INHERITED_TOOLBAR_HEIGHTS,\n inheritedThemeColor,\n} from '@aglyn/shared-ui-theme/util/theme-editor-defaults'\nimport {\n readDarkScheme,\n readFontFamily,\n readThemeColor,\n readToolbarHeight,\n SYSTEM_FONT_VALUE,\n THEME_COLOR_FIELDS,\n THEME_EDITOR_MEDIA_QUERIES,\n} from '@aglyn/shared-ui-theme/util/theme-editor-fields'\nimport type { AiJob, AiJobOutput } from '../model/ai-jobs.types'\nimport {\n buildAiThemeProposal,\n type AiThemeProposal,\n type AiThemeProposalMode,\n} from '../model/ai-theme-proposal'\nimport { AI_ROUTING_TABLE, aiModelForStep } from '../providers/routing'\nimport {\n runValidatedGeneration,\n type AiCustomGenerationInput,\n type AiGenerationCheck,\n} from '../runtime/ai-doctrine'\nimport type { AiSystemBlock, AiUsage } from '../runtime/ai-runtime'\nimport {\n AI_THEME_TOOL_NAME,\n aiThemeTool,\n parseAiThemeToolInput,\n type AiThemeToolParse,\n} from '../tools/ai-theme-tool'\nimport {\n AI_JOB_BRIEF_MAX_CHARS,\n type AiJobStepOutcome,\n type AiJobStepRunner,\n} from './ai-job-text-step'\nimport { AI_JOB_THEME_STEP_BUDGET } from './ai-job-theme-budget'\nimport {\n AI_THEME_BRAND_SOURCE_WORDS,\n gatherAiThemeBrandInputs,\n type AiThemeBrandColor,\n} from './ai-theme-brand-inputs'\n\n/**\n * The `theme` step (AGL-2938): a brief and a site's current theme in, a theme\n * proposal out — every control the theme editor exposes, and nothing else.\n *\n * The step writes nothing to the site. A site's theme is fields on its host\n * document, and writing those is the save the Theme section's editor\n * performs; the step's output is the proposal, which a person puts in the\n * editor, previews against the current theme, and saves or discards.\n *\n * The request is shaped for the cache: the rules and the tool are static and\n * sit in the cached prefix, and everything about this site — its theme, its\n * brand colors, the brief — rides in the user turn. The model answers through\n * one strict tool derived from the editor's own catalog, and the answer is\n * held to that catalog and to the doctrine (a targeted diff, dark values,\n * contrast) before it becomes a proposal.\n */\n\n/**\n * The routing table's answer for `job.theme`: the model the theme step runs on\n * when the machine hands it no `modelFor`.\n */\nexport function aiJobThemeModel(): string {\n return aiModelForStep('job.theme')\n}\n\n/**\n * The output budget, from the routing table. The largest answer the tool\n * accepts — every color in both schemes, the full run of component leaves,\n * the longest summary — is about 2,500 tokens of JSON\n * (`ai-job-theme-step.spec.ts` measures it), and the rest is room for the\n * model to think before it answers.\n */\nexport const AI_JOB_THEME_MAX_TOKENS = AI_ROUTING_TABLE['job.theme'].maxTokens\n\n/**\n * The theme's answer ceiling on the model a job runs (AGL-3035): the most\n * whose worst case — the answer, its re-ask and the brand reads — fits the\n * least time the step registers (`ai-job-theme-budget.ts`), and never more\n * than the routing table's.\n */\nexport function aiJobThemeMaxTokens(model: string): number {\n return AI_JOB_THEME_STEP_BUDGET.maxTokens(model)\n}\n\n/** How many of a site's own component override leaves the prompt lists. */\nexport const AI_JOB_THEME_INVENTORY_LEAVES = 40\n\n/** The customer-safe sentences a failed theme step leaves on the job. */\nexport const AI_JOB_THEME_NO_SITE_COPY =\n 'This theme job names a site this workspace does not have.'\nexport const AI_JOB_THEME_NO_ANSWER_COPY =\n 'The AI could not produce a theme proposal from this brief. Try describing the change differently.'\n\n/**\n * The theme step's own instructions, byte-identical on every request so they\n * cache. The acceptable-use rules are not in them: they belong to the block\n * every generator shares, which the generation call puts ahead of these.\n */\nexport const AI_JOB_THEME_INSTRUCTIONS: AiSystemBlock[] = [\n {\n text:\n 'You change the theme of a website built with a site builder. You are given the ' +\n \"site's current theme — every control its theme editor has, with the value the site \" +\n 'set or the default it inherits — any brand colors that belong to the site, and a ' +\n 'brief from the person who owns it.\\n\\n' +\n `Answer by calling ${AI_THEME_TOOL_NAME} exactly once. A reply in prose cannot be used.\\n\\n` +\n 'Rules:\\n' +\n '- In modify mode, name only the controls the brief is about. Every control you leave ' +\n 'out keeps its current value. \"Warmer\" is about color, not about fonts or corners.\\n' +\n '- In create mode, design a complete theme: the primary, secondary and tertiary ' +\n 'accents, the page background and paper, the text colors, and their dark values.\\n' +\n \"- Colors are hex values on the theme's own tokens. A theme is where the building rules \" +\n 'send every color, so this is the one answer that writes a color as a value. A component ' +\n 'style never carries a literal color; color belongs to the palette.\\n' +\n '- Give every color you change a dark value beside its light value unless the dark ' +\n 'scheme is off. A dark value keeps the hue and reads on a dark background.\\n' +\n '- Keep text readable: body text needs 4.5:1 against the background and the paper, and ' +\n 'the primary color 3:1 against the background.\\n' +\n '- A hex color written in the brief is used exactly as written. When the brief asks to ' +\n 'match the brand, build on the brand colors you are given.\\n' +\n '- Fonts come from the list the tool offers, and numbers stay inside the ranges it ' +\n 'states.\\n' +\n '- Write the summary in the language of the brief.',\n cacheBreakpoint: true,\n },\n]\n\nconst ZERO_USAGE: AiUsage = {\n inputTokens: 0,\n outputTokens: 0,\n cacheReadTokens: 0,\n cacheWriteTokens: 0,\n}\n\n/** `create` when the job asked for a whole design; `modify` otherwise. */\nexport function aiJobThemeMode(inputs: AiJob['inputs'] | undefined): AiThemeProposalMode {\n return inputs?.['mode'] === 'create' ? 'create' : 'modify'\n}\n\nconst SOURCE_WORDS: Record<HostThemeSource, string> = {\n installed:\n \"an installed marketplace theme; a change is stored as the site's own edits on top of it\",\n preset: \"a built-in theme; a change is stored as the site's own edits on top of it\",\n custom: \"one of the site's own saved themes; a change is stored as edits on top of it\",\n default: \"the platform's default theme; a change stores only the values it sets\",\n}\n\n/** One value as the inventory shows it: the site's own, or the default it inherits. */\nconst shown = (own: string | number | null | undefined, inherited: string | number | undefined) =>\n own === null || own === undefined ? `${inherited ?? 'unset'} (default)` : `${own} (set)`\n\n/** The site's own component overrides, one bounded line a leaf. */\nfunction overrideLeaves(theme: HostTheme | undefined): string[] {\n const leaves: string[] = []\n const media = Object.fromEntries(\n Object.entries(THEME_EDITOR_MEDIA_QUERIES).map(([name, query]) => [query, name]),\n )\n const text = (value: unknown) => JSON.stringify(value)?.slice(0, 60) ?? ''\n for (const [component, override] of Object.entries(theme?.components ?? {})) {\n for (const [prop, value] of Object.entries(override?.defaultProps ?? {})) {\n leaves.push(`${component} default ${prop} = ${text(value)}`)\n }\n // `sx` leaves are marked so the model writes them back to `sx`: the same\n // number is a different length in each (AGL-3403).\n for (const [target, slots] of [\n ['', override?.styleOverrides],\n [' sx', override?.sx],\n ] as const) {\n for (const [slot, styles] of Object.entries(slots ?? {})) {\n if (!styles || typeof styles !== 'object') continue\n for (const [property, value] of Object.entries(styles as Record<string, unknown>)) {\n if (value && typeof value === 'object') {\n for (const [nested, nestedValue] of Object.entries(value as Record<string, unknown>)) {\n leaves.push(\n `${component}${target} ${slot} ${nested} = ${text(nestedValue)} (${media[property] ?? property})`,\n )\n }\n } else {\n leaves.push(`${component}${target} ${slot} ${property} = ${text(value)}`)\n }\n }\n }\n }\n if (override?.variants?.length) {\n leaves.push(`${component} ${override.variants.length} props-matched variant style(s)`)\n }\n }\n return leaves\n}\n\n/**\n * The site's theme as the model reads it: compact, one control a line, each\n * value marked as the site's own or the default it inherits, so \"make the\n * background warmer\" starts from the color the visitor actually sees.\n */\nexport function aiJobThemeInventory(theme: HostTheme | undefined, source: HostThemeSource): string {\n const lines = [`Source: ${SOURCE_WORDS[source]}.`, `Dark scheme: ${readDarkScheme(theme)}.`]\n lines.push('Colors, light | dark:')\n for (const { token } of THEME_COLOR_FIELDS) {\n const value = (scheme: HostThemeScheme) =>\n shown(readThemeColor(theme, scheme, token), inheritedThemeColor(scheme, token))\n lines.push(`- ${token}: ${value('light')} | ${value('dark')}`)\n }\n const font = readFontFamily(theme)\n lines.push(`Font family: ${shown(font === SYSTEM_FONT_VALUE ? null : font, INHERITED_FONT_FAMILY)}`)\n lines.push(`Border radius: ${shown(theme?.shape?.borderRadius, INHERITED_BORDER_RADIUS)}`)\n lines.push(`Spacing unit: ${shown(theme?.spacing, INHERITED_SPACING)}`)\n lines.push(\n `Nav height: mobile ${shown(theme && readToolbarHeight(theme, 'xs'), INHERITED_TOOLBAR_HEIGHTS.xs)}, ` +\n `desktop ${shown(theme && readToolbarHeight(theme, 'sm'), INHERITED_TOOLBAR_HEIGHTS.sm)}`,\n )\n const leaves = overrideLeaves(theme)\n if (!leaves.length) {\n lines.push('Component overrides: none.')\n } else {\n lines.push('Component overrides:')\n for (const leaf of leaves.slice(0, AI_JOB_THEME_INVENTORY_LEAVES)) lines.push(`- ${leaf}`)\n if (leaves.length > AI_JOB_THEME_INVENTORY_LEAVES) {\n lines.push(`- and ${leaves.length - AI_JOB_THEME_INVENTORY_LEAVES} more`)\n }\n }\n return lines.join('\\n')\n}\n\n/** The user turn: the mode, the site's theme, its brand colors, and the brief. */\nexport function aiJobThemePrompt(input: {\n brief: string\n mode: AiThemeProposalMode\n theme: HostTheme | undefined\n source: HostThemeSource\n brand: readonly AiThemeBrandColor[]\n}): string {\n const sections = [\n input.mode === 'create'\n ? 'Mode: create. Design a complete theme over every control.'\n : 'Mode: modify. Change only what the brief is about.',\n `Current theme:\\n${aiJobThemeInventory(input.theme, input.source)}`,\n ]\n if (input.brand.length) {\n sections.push(\n `Brand colors:\\n${input.brand\n .map((color) => `- ${color.hex} (${AI_THEME_BRAND_SOURCE_WORDS[color.source]})`)\n .join('\\n')}`,\n )\n }\n sections.push(`Brief: ${input.brief.slice(0, AI_JOB_BRIEF_MAX_CHARS)}`)\n return sections.join('\\n\\n')\n}\n\n/**\n * The theme tool's answer held to the editor — the check the doctrine's loop\n * runs on each answer (AGL-2935), which also holds rule 13 on it. Usable when\n * anything in it survives; refused, naming what was refused, when nothing\n * does. A call that proposes no change and refuses nothing is a real answer —\n * the theme already does what the brief asks.\n */\nexport const aiJobThemeCheck: AiGenerationCheck<AiThemeToolParse> = (answer) => {\n const parse = parseAiThemeToolInput(answer)\n const nothingUsable =\n !parse.changes.length && !parse.components.length && !parse.resetComponents\n if (!nothingUsable || !parse.dropped.length) return { value: parse, violations: [] }\n return {\n value: null,\n violations: parse.dropped.map((message) => ({ rule: null, code: 'theme-control', message })),\n }\n}\n\n/** What a theme generation is asked from, once the site and its brand have been read. */\nexport interface AiJobThemeRequest {\n brief: string\n mode: AiThemeProposalMode\n theme: HostTheme | undefined\n source: HostThemeSource\n brand: readonly AiThemeBrandColor[]\n model: string\n signal?: AbortSignal\n}\n\n/**\n * The theme step's generation as the doctrine's loop runs it: its cached\n * block, which carries the acceptable-use rules, then this step's\n * instructions; the site's theme in the user turn rather than a site\n * inventory; the strict tool, the ceiling and the check. The eval harness\n * records a theme answer through this same call.\n */\nexport function aiJobThemeGeneration(\n request: AiJobThemeRequest,\n): AiCustomGenerationInput<AiThemeToolParse> {\n return {\n step: 'job.theme',\n model: request.model,\n instructions: AI_JOB_THEME_INSTRUCTIONS,\n messages: [{ role: 'user', content: aiJobThemePrompt(request) }],\n tool: aiThemeTool(),\n maxTokens: aiJobThemeMaxTokens(request.model),\n ...(AI_ROUTING_TABLE['job.theme'].thinking ? { thinking: AI_ROUTING_TABLE['job.theme'].thinking } : {}),\n ...(AI_ROUTING_TABLE['job.theme'].effort ? { effort: AI_ROUTING_TABLE['job.theme'].effort } : {}),\n ...(request.signal ? { signal: request.signal } : {}),\n check: aiJobThemeCheck,\n }\n}\n\n/** The site a theme job changes, read and checked against the job's org. */\nasync function loadThemeSite(\n firestore: FirebaseFirestore.Firestore,\n job: AiJob,\n): Promise<\n | { host: ThemeHostDocument & Record<string, unknown>; theme: HostTheme | undefined; source: HostThemeSource }\n | { failure: string }\n> {\n if (!job.hostId) return { failure: AI_JOB_THEME_NO_SITE_COPY }\n const snapshot = await firestore.collection('hosts').doc(job.hostId).get()\n const host = snapshot.exists\n ? (snapshot.data() as ThemeHostDocument & Record<string, unknown>)\n : null\n // The route checks the caller's membership of the org the job is metered\n // against, not that the site it named belongs to that org. A site of some\n // other org is refused here, before anything about it reaches a prompt.\n if (!host || host['orgId'] !== job.orgId) return { failure: AI_JOB_THEME_NO_SITE_COPY }\n return { host, theme: resolveSiteTheme(host), source: hostThemeSource(host) }\n}\n\n/** How many edits a proposal makes, for its label. */\nfunction proposalSize(proposal: AiThemeProposal): number {\n return proposal.changes.length + proposal.components.length + (proposal.resetComponents ? 1 : 0)\n}\n\nexport const runAiJobThemeStep: AiJobStepRunner = async ({\n job,\n signal,\n firestore,\n org,\n modelFor,\n}) => {\n if (!firestore) {\n // The machine always hands one in; a runner called without it is a\n // wiring fault, not a customer path.\n throw new Error('the theme step reads the site it changes and was given no Firestore')\n }\n // The model switch's answer for this job (AGL-2942): the creator's pick\n // where the plan, the org restriction and the allotment allowlists allow\n // it, and Auto held to those same lists otherwise. Only a runner called\n // without a resolver asks the routing table directly.\n const model = modelFor?.('job.theme') ?? aiJobThemeModel()\n const site = await loadThemeSite(firestore, job)\n if ('failure' in site) {\n return {\n outputs: [],\n usage: ZERO_USAGE,\n estCostUsd: 0,\n model,\n stopReason: null,\n failure: site.failure,\n }\n }\n const mode = aiJobThemeMode(job.inputs)\n const brand = await gatherAiThemeBrandInputs({\n firestore,\n org: org ?? null,\n hostId: job.hostId as string,\n host: site.host,\n brief: job.brief,\n signal,\n })\n // A second refused answer ends in this step's own sentence, below.\n const generation = await runValidatedGeneration(\n 'theme',\n aiJobThemeGeneration({\n brief: job.brief,\n mode,\n theme: site.theme,\n source: site.source,\n brand: brand.colors,\n model,\n ...(signal ? { signal } : {}),\n }),\n )\n const spent: Pick<AiJobStepOutcome, 'usage' | 'estCostUsd' | 'model' | 'stopReason' | 'effort'> = {\n usage: generation.usage,\n estCostUsd: generation.estCostUsd,\n model: generation.model,\n stopReason: generation.stopReason,\n ...(generation.effort ? { effort: generation.effort } : {}),\n }\n if (generation.status === 'refused') return { outputs: [], ...spent, refused: true }\n if (generation.status === 'needs_input') {\n return { outputs: [], ...spent, failure: AI_JOB_THEME_NO_ANSWER_COPY }\n }\n\n const answer = generation.value\n const proposal = buildAiThemeProposal({\n base: site.theme,\n source: site.source,\n mode,\n brief: job.brief,\n summary: answer.summary,\n changes: answer.changes,\n components: answer.components,\n resetComponents: answer.resetComponents,\n dropped: answer.dropped,\n notes: [...brand.notes, ...answer.notes],\n })\n const size = proposalSize(proposal)\n const output: AiJobOutput = {\n resource: 'theme',\n // A proposal has no document of its own; the id names what it is within\n // the job, as a text output's `draft` does.\n id: 'proposal',\n hostId: job.hostId ?? null,\n hostSubdomain: typeof site.host['subdomain'] === 'string' ? site.host['subdomain'] : null,\n label: `Theme proposal · ${size} ${size === 1 ? 'change' : 'changes'}`,\n // Through JSON so no `undefined` reaches the document write.\n proposal: JSON.parse(JSON.stringify(proposal)) as Record<string, unknown>,\n }\n return { outputs: [output], ...spent }\n}\n"],"names":["hostThemeSource","resolveSiteTheme","INHERITED_BORDER_RADIUS","INHERITED_FONT_FAMILY","INHERITED_SPACING","INHERITED_TOOLBAR_HEIGHTS","inheritedThemeColor","readDarkScheme","readFontFamily","readThemeColor","readToolbarHeight","SYSTEM_FONT_VALUE","THEME_COLOR_FIELDS","THEME_EDITOR_MEDIA_QUERIES","buildAiThemeProposal","AI_ROUTING_TABLE","aiModelForStep","runValidatedGeneration","AI_THEME_TOOL_NAME","aiThemeTool","parseAiThemeToolInput","AI_JOB_BRIEF_MAX_CHARS","AI_JOB_THEME_STEP_BUDGET","AI_THEME_BRAND_SOURCE_WORDS","gatherAiThemeBrandInputs","aiJobThemeModel","AI_JOB_THEME_MAX_TOKENS","maxTokens","aiJobThemeMaxTokens","model","AI_JOB_THEME_INVENTORY_LEAVES","AI_JOB_THEME_NO_SITE_COPY","AI_JOB_THEME_NO_ANSWER_COPY","AI_JOB_THEME_INSTRUCTIONS","text","cacheBreakpoint","ZERO_USAGE","inputTokens","outputTokens","cacheReadTokens","cacheWriteTokens","aiJobThemeMode","inputs","SOURCE_WORDS","installed","preset","custom","default","shown","own","inherited","undefined","overrideLeaves","theme","leaves","media","Object","fromEntries","entries","map","name","query","value","JSON","stringify","slice","component","override","components","prop","defaultProps","push","target","slots","styleOverrides","sx","slot","styles","property","nested","nestedValue","variants","length","aiJobThemeInventory","source","lines","token","scheme","font","shape","borderRadius","spacing","xs","sm","leaf","join","aiJobThemePrompt","input","sections","mode","brand","color","hex","brief","aiJobThemeCheck","answer","parse","nothingUsable","changes","resetComponents","dropped","violations","message","rule","code","aiJobThemeGeneration","request","step","instructions","messages","role","content","tool","thinking","effort","signal","check","loadThemeSite","firestore","job","hostId","failure","snapshot","collection","doc","get","host","exists","data","orgId","proposalSize","proposal","runAiJobThemeStep","org","modelFor","Error","site","outputs","usage","estCostUsd","stopReason","generation","colors","spent","status","refused","base","summary","notes","size","output","resource","id","hostSubdomain","label"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-theme-step.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n hostThemeSource,\n resolveSiteTheme,\n type HostThemeSource,\n type ThemeHostDocument,\n} from '@aglyn/aglyn/app-utils/site-theme'\nimport type { HostTheme, HostThemeScheme } from '@aglyn/shared-data-types'\nimport {\n INHERITED_BORDER_RADIUS,\n INHERITED_FONT_FAMILY,\n INHERITED_SPACING,\n INHERITED_TOOLBAR_HEIGHTS,\n inheritedThemeColor,\n} from '@aglyn/shared-ui-theme/util/theme-editor-defaults'\nimport {\n readDarkScheme,\n readFontFamily,\n readThemeColor,\n readToolbarHeight,\n SYSTEM_FONT_VALUE,\n THEME_COLOR_FIELDS,\n THEME_EDITOR_MEDIA_QUERIES,\n} from '@aglyn/shared-ui-theme/util/theme-editor-fields'\nimport type { AiJob, AiJobOutput } from '../model/ai-jobs.types'\nimport {\n buildAiThemeProposal,\n type AiThemeProposal,\n type AiThemeProposalMode,\n} from '../model/ai-theme-proposal'\nimport { AI_ROUTING_TABLE, aiModelForStep } from '../providers/routing'\nimport {\n runValidatedGeneration,\n type AiCustomGenerationInput,\n type AiGenerationCheck,\n} from '../runtime/ai-doctrine'\nimport type { AiSystemBlock, AiUsage } from '../runtime/ai-runtime'\nimport {\n AI_THEME_TOOL_NAME,\n aiThemeTool,\n parseAiThemeToolInput,\n type AiThemeToolParse,\n} from '../tools/ai-theme-tool'\nimport {\n AI_JOB_BRIEF_MAX_CHARS,\n type AiJobStepOutcome,\n type AiJobStepRunner,\n} from './ai-job-text-step'\nimport { AI_JOB_THEME_STEP_BUDGET } from './ai-job-theme-budget'\nimport {\n AI_THEME_BRAND_SOURCE_WORDS,\n gatherAiThemeBrandInputs,\n type AiThemeBrandColor,\n} from './ai-theme-brand-inputs'\n\n/**\n * The `theme` step (AGL-2938): a brief and a site's current theme in, a theme\n * proposal out — every control the theme editor exposes, and nothing else.\n *\n * The step writes nothing to the site. A site's theme is fields on its host\n * document, and writing those is the save the Theme section's editor\n * performs; the step's output is the proposal, which a person puts in the\n * editor, previews against the current theme, and saves or discards.\n *\n * The request is shaped for the cache: the rules and the tool are static and\n * sit in the cached prefix, and everything about this site — its theme, its\n * brand colors, the brief — rides in the user turn. The model answers through\n * one strict tool derived from the editor's own catalog, and the answer is\n * held to that catalog and to the doctrine (a targeted diff, dark values,\n * contrast) before it becomes a proposal.\n */\n\n/**\n * The routing table's answer for `job.theme`: the model the theme step runs on\n * when the machine hands it no `modelFor`.\n */\nexport function aiJobThemeModel(): string {\n return aiModelForStep('job.theme')\n}\n\n/**\n * The output budget, from the routing table. The largest answer the tool\n * accepts — every color in both schemes, the full run of component leaves,\n * the longest summary — is about 2,500 tokens of JSON\n * (`ai-job-theme-step.spec.ts` measures it), and the rest is room for the\n * model to think before it answers.\n */\nexport const AI_JOB_THEME_MAX_TOKENS = AI_ROUTING_TABLE['job.theme'].maxTokens\n\n/**\n * The theme's answer ceiling on the model a job runs (AGL-3035): the most\n * whose worst case — the answer, its re-ask and the brand reads — fits the\n * least time the step registers (`ai-job-theme-budget.ts`), and never more\n * than the routing table's.\n */\nexport function aiJobThemeMaxTokens(model: string): number {\n return AI_JOB_THEME_STEP_BUDGET.maxTokens(model)\n}\n\n/** How many of a site's own component override leaves the prompt lists. */\nexport const AI_JOB_THEME_INVENTORY_LEAVES = 40\n\n/** The customer-safe sentences a failed theme step leaves on the job. */\nexport const AI_JOB_THEME_NO_SITE_COPY =\n 'This theme job names a site this workspace does not have.'\nexport const AI_JOB_THEME_NO_ANSWER_COPY =\n 'The AI could not produce a theme proposal from this brief. Try describing the change differently.'\n\n/**\n * The theme step's own instructions, byte-identical on every request so they\n * cache. The acceptable-use rules are not in them: they belong to the block\n * every generator shares, which the generation call puts ahead of these.\n */\nexport const AI_JOB_THEME_INSTRUCTIONS: AiSystemBlock[] = [\n {\n text:\n 'You change the theme of a website built with a site builder. You are given the ' +\n \"site's current theme — every control its theme editor has, with the value the site \" +\n 'set or the default it inherits — any brand colors that belong to the site, and a ' +\n 'brief from the person who owns it.\\n\\n' +\n `Answer by calling ${AI_THEME_TOOL_NAME} exactly once. A reply in prose cannot be used.\\n\\n` +\n 'Rules:\\n' +\n '- In modify mode, name only the controls the brief is about. Every control you leave ' +\n 'out keeps its current value. \"Warmer\" is about color, not about fonts or corners.\\n' +\n '- In create mode, design a complete theme: the primary, secondary and tertiary ' +\n 'accents, the page background and paper, the text colors, and their dark values.\\n' +\n \"- Colors are hex values on the theme's own tokens. A theme is where the building rules \" +\n 'send every color, so this is the one answer that writes a color as a value. A component ' +\n 'style never carries a literal color; color belongs to the palette.\\n' +\n '- Give every color you change a dark value beside its light value unless the dark ' +\n 'scheme is off. A dark value keeps the hue and reads on a dark background.\\n' +\n '- Keep text readable: body text needs 4.5:1 against the background and the paper, and ' +\n 'the primary color 3:1 against the background.\\n' +\n '- A hex color written in the brief is used exactly as written. When the brief asks to ' +\n 'match the brand, build on the brand colors you are given.\\n' +\n '- Fonts come from the list the tool offers, and numbers stay inside the ranges it ' +\n 'states.\\n' +\n '- Write the summary in the language of the brief.',\n cacheBreakpoint: true,\n },\n]\n\nconst ZERO_USAGE: AiUsage = {\n inputTokens: 0,\n outputTokens: 0,\n cacheReadTokens: 0,\n cacheWriteTokens: 0,\n}\n\n/** `create` when the job asked for a whole design; `modify` otherwise. */\nexport function aiJobThemeMode(inputs: AiJob['inputs'] | undefined): AiThemeProposalMode {\n return inputs?.['mode'] === 'create' ? 'create' : 'modify'\n}\n\nconst SOURCE_WORDS: Record<HostThemeSource, string> = {\n installed:\n \"an installed marketplace theme; a change is stored as the site's own edits on top of it\",\n preset: \"a built-in theme; a change is stored as the site's own edits on top of it\",\n custom: \"one of the site's own saved themes; a change is stored as edits on top of it\",\n default: \"the platform's default theme; a change stores only the values it sets\",\n}\n\n/** One value as the inventory shows it: the site's own, or the default it inherits. */\nconst shown = (own: string | number | null | undefined, inherited: string | number | undefined) =>\n own === null || own === undefined ? `${inherited ?? 'unset'} (default)` : `${own} (set)`\n\n/** The site's own component overrides, one bounded line a leaf. */\nfunction overrideLeaves(theme: HostTheme | undefined): string[] {\n const leaves: string[] = []\n const media = Object.fromEntries(\n Object.entries(THEME_EDITOR_MEDIA_QUERIES).map(([name, query]) => [query, name]),\n )\n const text = (value: unknown) => JSON.stringify(value)?.slice(0, 60) ?? ''\n for (const [component, override] of Object.entries(theme?.components ?? {})) {\n for (const [prop, value] of Object.entries(override?.defaultProps ?? {})) {\n leaves.push(`${component} default ${prop} = ${text(value)}`)\n }\n // `sx` leaves are marked so the model writes them back to `sx`: the same\n // number is a different length in each (AGL-3403).\n for (const [target, slots] of [\n ['', override?.styleOverrides],\n [' sx', override?.sx],\n ] as const) {\n for (const [slot, styles] of Object.entries(slots ?? {})) {\n if (!styles || typeof styles !== 'object') continue\n for (const [property, value] of Object.entries(styles as Record<string, unknown>)) {\n if (value && typeof value === 'object') {\n for (const [nested, nestedValue] of Object.entries(value as Record<string, unknown>)) {\n leaves.push(\n `${component}${target} ${slot} ${nested} = ${text(nestedValue)} (${media[property] ?? property})`,\n )\n }\n } else {\n leaves.push(`${component}${target} ${slot} ${property} = ${text(value)}`)\n }\n }\n }\n }\n if (override?.variants?.length) {\n leaves.push(`${component} ${override.variants.length} props-matched variant style(s)`)\n }\n }\n return leaves\n}\n\n/**\n * The site's theme as the model reads it: compact, one control a line, each\n * value marked as the site's own or the default it inherits, so \"make the\n * background warmer\" starts from the color the visitor actually sees.\n */\nexport function aiJobThemeInventory(theme: HostTheme | undefined, source: HostThemeSource): string {\n const lines = [`Source: ${SOURCE_WORDS[source]}.`, `Dark scheme: ${readDarkScheme(theme)}.`]\n lines.push('Colors, light | dark:')\n for (const { token } of THEME_COLOR_FIELDS) {\n const value = (scheme: HostThemeScheme) =>\n shown(readThemeColor(theme, scheme, token), inheritedThemeColor(scheme, token))\n lines.push(`- ${token}: ${value('light')} | ${value('dark')}`)\n }\n const font = readFontFamily(theme)\n lines.push(`Font family: ${shown(font === SYSTEM_FONT_VALUE ? null : font, INHERITED_FONT_FAMILY)}`)\n lines.push(`Border radius: ${shown(theme?.shape?.borderRadius, INHERITED_BORDER_RADIUS)}`)\n lines.push(`Spacing unit: ${shown(theme?.spacing, INHERITED_SPACING)}`)\n lines.push(\n `Nav height: mobile ${shown(theme && readToolbarHeight(theme, 'xs'), INHERITED_TOOLBAR_HEIGHTS.xs)}, ` +\n `desktop ${shown(theme && readToolbarHeight(theme, 'sm'), INHERITED_TOOLBAR_HEIGHTS.sm)}`,\n )\n const leaves = overrideLeaves(theme)\n if (!leaves.length) {\n lines.push('Component overrides: none.')\n } else {\n lines.push('Component overrides:')\n for (const leaf of leaves.slice(0, AI_JOB_THEME_INVENTORY_LEAVES)) lines.push(`- ${leaf}`)\n if (leaves.length > AI_JOB_THEME_INVENTORY_LEAVES) {\n lines.push(`- and ${leaves.length - AI_JOB_THEME_INVENTORY_LEAVES} more`)\n }\n }\n return lines.join('\\n')\n}\n\n/** The user turn: the mode, the site's theme, its brand colors, and the brief. */\nexport function aiJobThemePrompt(input: {\n brief: string\n mode: AiThemeProposalMode\n theme: HostTheme | undefined\n source: HostThemeSource\n brand: readonly AiThemeBrandColor[]\n}): string {\n const sections = [\n input.mode === 'create'\n ? 'Mode: create. Design a complete theme over every control.'\n : 'Mode: modify. Change only what the brief is about.',\n `Current theme:\\n${aiJobThemeInventory(input.theme, input.source)}`,\n ]\n if (input.brand.length) {\n sections.push(\n `Brand colors:\\n${input.brand\n .map((color) => `- ${color.hex} (${AI_THEME_BRAND_SOURCE_WORDS[color.source]})`)\n .join('\\n')}`,\n )\n }\n sections.push(`Brief: ${input.brief.slice(0, AI_JOB_BRIEF_MAX_CHARS)}`)\n return sections.join('\\n\\n')\n}\n\n/**\n * The theme tool's answer held to the editor — the check the doctrine's loop\n * runs on each answer (AGL-2935), which also holds rule 13 on it. Usable when\n * anything in it survives; refused, naming what was refused, when nothing\n * does. A call that proposes no change and refuses nothing is a real answer —\n * the theme already does what the brief asks.\n */\nexport const aiJobThemeCheck: AiGenerationCheck<AiThemeToolParse> = (answer) => {\n const parse = parseAiThemeToolInput(answer)\n const nothingUsable =\n !parse.changes.length && !parse.components.length && !parse.resetComponents\n if (!nothingUsable || !parse.dropped.length) return { value: parse, violations: [] }\n return {\n value: null,\n violations: parse.dropped.map((message) => ({ rule: null, code: 'theme-control', message })),\n }\n}\n\n/** What a theme generation is asked from, once the site and its brand have been read. */\nexport interface AiJobThemeRequest {\n brief: string\n mode: AiThemeProposalMode\n theme: HostTheme | undefined\n source: HostThemeSource\n brand: readonly AiThemeBrandColor[]\n model: string\n signal?: AbortSignal\n}\n\n/**\n * The theme step's generation as the doctrine's loop runs it: its cached\n * block, which carries the acceptable-use rules, then this step's\n * instructions; the site's theme in the user turn rather than a site\n * inventory; the strict tool, the ceiling and the check. The eval harness\n * records a theme answer through this same call.\n */\nexport function aiJobThemeGeneration(\n request: AiJobThemeRequest,\n): AiCustomGenerationInput<AiThemeToolParse> {\n return {\n step: 'job.theme',\n model: request.model,\n instructions: AI_JOB_THEME_INSTRUCTIONS,\n messages: [{ role: 'user', content: aiJobThemePrompt(request) }],\n tool: aiThemeTool(),\n maxTokens: aiJobThemeMaxTokens(request.model),\n ...(AI_ROUTING_TABLE['job.theme'].thinking ? { thinking: AI_ROUTING_TABLE['job.theme'].thinking } : {}),\n ...(AI_ROUTING_TABLE['job.theme'].effort ? { effort: AI_ROUTING_TABLE['job.theme'].effort } : {}),\n ...(request.signal ? { signal: request.signal } : {}),\n check: aiJobThemeCheck,\n }\n}\n\n/** The site a theme job changes, read and checked against the job's org. */\nasync function loadThemeSite(\n firestore: FirebaseFirestore.Firestore,\n job: AiJob,\n): Promise<\n | { host: ThemeHostDocument & Record<string, unknown>; theme: HostTheme | undefined; source: HostThemeSource }\n | { failure: string }\n> {\n if (!job.hostId) return { failure: AI_JOB_THEME_NO_SITE_COPY }\n const snapshot = await firestore.collection('hosts').doc(job.hostId).get()\n const host = snapshot.exists\n ? (snapshot.data() as ThemeHostDocument & Record<string, unknown>)\n : null\n // The route checks the caller's membership of the org the job is metered\n // against, not that the site it named belongs to that org. A site of some\n // other org is refused here, before anything about it reaches a prompt.\n if (!host || host['orgId'] !== job.orgId) return { failure: AI_JOB_THEME_NO_SITE_COPY }\n return { host, theme: resolveSiteTheme(host), source: hostThemeSource(host) }\n}\n\n/** How many edits a proposal makes, for its label. */\nfunction proposalSize(proposal: AiThemeProposal): number {\n return proposal.changes.length + proposal.components.length + (proposal.resetComponents ? 1 : 0)\n}\n\nexport const runAiJobThemeStep: AiJobStepRunner = async ({\n job,\n signal,\n firestore,\n org,\n modelFor,\n}) => {\n if (!firestore) {\n // The machine always hands one in; a runner called without it is a\n // wiring fault, not a customer path.\n throw new Error('the theme step reads the site it changes and was given no Firestore')\n }\n // The model switch's answer for this job (AGL-2942): the creator's pick\n // where the plan, the org restriction and the allotment allowlists allow\n // it, and Auto held to those same lists otherwise. Only a runner called\n // without a resolver asks the routing table directly.\n const model = modelFor?.('job.theme') ?? aiJobThemeModel()\n const site = await loadThemeSite(firestore, job)\n if ('failure' in site) {\n return {\n outputs: [],\n usage: ZERO_USAGE,\n estCostUsd: 0,\n model,\n stopReason: null,\n failure: site.failure,\n }\n }\n const mode = aiJobThemeMode(job.inputs)\n const brand = await gatherAiThemeBrandInputs({\n firestore,\n org: org ?? null,\n hostId: job.hostId as string,\n host: site.host,\n brief: job.brief,\n signal,\n })\n // A second refused answer ends in this step's own sentence, below.\n const generation = await runValidatedGeneration(\n 'theme',\n aiJobThemeGeneration({\n brief: job.brief,\n mode,\n theme: site.theme,\n source: site.source,\n brand: brand.colors,\n model,\n ...(signal ? { signal } : {}),\n }),\n )\n const spent: Pick<AiJobStepOutcome, 'usage' | 'estCostUsd' | 'model' | 'stopReason' | 'effort'> = {\n usage: generation.usage,\n estCostUsd: generation.estCostUsd,\n model: generation.model,\n stopReason: generation.stopReason,\n ...(generation.effort ? { effort: generation.effort } : {}),\n }\n if (generation.status === 'refused') return { outputs: [], ...spent, refused: true }\n if (generation.status === 'needs_input') {\n return { outputs: [], ...spent, failure: AI_JOB_THEME_NO_ANSWER_COPY }\n }\n\n const answer = generation.value\n const proposal = buildAiThemeProposal({\n base: site.theme,\n source: site.source,\n mode,\n brief: job.brief,\n summary: answer.summary,\n changes: answer.changes,\n components: answer.components,\n resetComponents: answer.resetComponents,\n dropped: answer.dropped,\n notes: [...brand.notes, ...answer.notes],\n })\n const size = proposalSize(proposal)\n const output: AiJobOutput = {\n resource: 'theme',\n // A proposal has no document of its own; the id names what it is within\n // the job, as a text output's `draft` does.\n id: 'proposal',\n hostId: job.hostId ?? null,\n hostSubdomain: typeof site.host['subdomain'] === 'string' ? site.host['subdomain'] : null,\n label: `Theme proposal · ${size} ${size === 1 ? 'change' : 'changes'}`,\n // Through JSON so no `undefined` reaches the document write.\n proposal: JSON.parse(JSON.stringify(proposal)) as Record<string, unknown>,\n }\n return { outputs: [output], ...spent }\n}\n"],"names":["hostThemeSource","resolveSiteTheme","INHERITED_BORDER_RADIUS","INHERITED_FONT_FAMILY","INHERITED_SPACING","INHERITED_TOOLBAR_HEIGHTS","inheritedThemeColor","readDarkScheme","readFontFamily","readThemeColor","readToolbarHeight","SYSTEM_FONT_VALUE","THEME_COLOR_FIELDS","THEME_EDITOR_MEDIA_QUERIES","buildAiThemeProposal","AI_ROUTING_TABLE","aiModelForStep","runValidatedGeneration","AI_THEME_TOOL_NAME","aiThemeTool","parseAiThemeToolInput","AI_JOB_BRIEF_MAX_CHARS","AI_JOB_THEME_STEP_BUDGET","AI_THEME_BRAND_SOURCE_WORDS","gatherAiThemeBrandInputs","aiJobThemeModel","AI_JOB_THEME_MAX_TOKENS","maxTokens","aiJobThemeMaxTokens","model","AI_JOB_THEME_INVENTORY_LEAVES","AI_JOB_THEME_NO_SITE_COPY","AI_JOB_THEME_NO_ANSWER_COPY","AI_JOB_THEME_INSTRUCTIONS","text","cacheBreakpoint","ZERO_USAGE","inputTokens","outputTokens","cacheReadTokens","cacheWriteTokens","aiJobThemeMode","inputs","SOURCE_WORDS","installed","preset","custom","default","shown","own","inherited","undefined","overrideLeaves","theme","leaves","media","Object","fromEntries","entries","map","name","query","value","JSON","stringify","slice","component","override","components","prop","defaultProps","push","target","slots","styleOverrides","sx","slot","styles","property","nested","nestedValue","variants","length","aiJobThemeInventory","source","lines","token","scheme","font","shape","borderRadius","spacing","xs","sm","leaf","join","aiJobThemePrompt","input","sections","mode","brand","color","hex","brief","aiJobThemeCheck","answer","parse","nothingUsable","changes","resetComponents","dropped","violations","message","rule","code","aiJobThemeGeneration","request","step","instructions","messages","role","content","tool","thinking","effort","signal","check","loadThemeSite","firestore","job","hostId","failure","snapshot","collection","doc","get","host","exists","data","orgId","proposalSize","proposal","runAiJobThemeStep","org","modelFor","Error","site","outputs","usage","estCostUsd","stopReason","generation","colors","spent","status","refused","base","summary","notes","size","output","resource","id","hostSubdomain","label"],"mappings":"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"}
|
|
@@ -16,13 +16,13 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
|
16
16
|
* limitations under the License.
|
|
17
17
|
*/ import { ACTION_MAX_CONDITIONS, ACTION_MAX_STEPS, CONTACT_TAG_MAX_LENGTH, FLOW_TIMED_OUT_FIELD, FLOW_WAIT_MAX_MINUTES, HOST_ACTION_STEP_LABELS } from "@aglyn/aglyn/app-utils/actions";
|
|
18
18
|
import { CONTACT_LIFECYCLE_STAGES, CRM_TASK_MAX_DUE_DAYS } from "@aglyn/aglyn/app-utils/crm";
|
|
19
|
-
import { HOST_EVENT_PAYLOAD_KEYS } from "@aglyn/aglyn/app-utils/
|
|
19
|
+
import { HOST_EVENT_PAYLOAD_KEYS } from "@aglyn/aglyn/app-utils/host-events";
|
|
20
20
|
import { isHostPluginEnabled } from "@aglyn/aglyn/plugin-manager/enabled-plugins";
|
|
21
21
|
import { filterEnabledPluginsByReleaseFlags } from "@aglyn/tenant-data-admin/server/release-flags";
|
|
22
22
|
import { resolveOrgIdForHost } from "@aglyn/tenant-data-admin/server/organizations";
|
|
23
23
|
import { aiAutomationDraft, aiAutomationDraftNote } from "../model/ai-automation-draft.js";
|
|
24
24
|
import { aiActionOutline, aiActionRecordKinds, aiRunOutline, aiWorkflowExplanationText, aiWorkflowOutline } from "../model/ai-automation-outline.js";
|
|
25
|
-
import { AI_AUTOMATION_NEED_LABELS, AI_AUTOMATION_RESOURCE, AI_AUTOMATION_STEP_NEEDS, AI_AUTOMATION_STEP_TYPES, AI_AUTOMATION_TRIGGER_NEEDS, AI_AUTOMATION_TRIGGERS, AI_AUTOMATION_UNSUPPORTED_COPY, AI_WORKFLOW_GONE_COPY, AI_WORKFLOW_NO_DRAFT_COPY, AI_WORKFLOW_NO_EXPLANATION_COPY, AI_WORKFLOW_NO_SITE_COPY, AI_WORKFLOW_RUN_GONE_COPY, AI_WORKFLOW_RUN_NOT_FAILED_COPY, AI_WORKFLOW_SAVE_FAILURE_COPY, AI_WORKFLOW_UNAVAILABLE_COPY,
|
|
25
|
+
import { AI_AUTOMATION_NEED_LABELS, AI_AUTOMATION_RESOURCE, AI_AUTOMATION_STEP_NEEDS, AI_AUTOMATION_STEP_TYPES, AI_AUTOMATION_TRIGGER_NEEDS, AI_AUTOMATION_TRIGGERS, AI_AUTOMATION_UNSUPPORTED_COPY, AI_WORKFLOW_GONE_COPY, AI_WORKFLOW_NO_DRAFT_COPY, AI_WORKFLOW_NO_EXPLANATION_COPY, AI_WORKFLOW_NO_SITE_COPY, AI_WORKFLOW_RUN_GONE_COPY, AI_WORKFLOW_RUN_NOT_FAILED_COPY, AI_WORKFLOW_SAVE_FAILURE_COPY, AI_WORKFLOW_UNAVAILABLE_COPY, aiAutomationCapabilities, aiAutomationTriggerLabel, parseAiWorkflowJobInputs } from "../model/ai-workflow-job.js";
|
|
26
26
|
import { AI_STEP_TIERS } from "../providers/catalog.js";
|
|
27
27
|
import { AI_ROUTING_TABLE, aiModelForStep } from "../providers/routing.js";
|
|
28
28
|
import { runValidatedGeneration } from "../runtime/ai-doctrine.js";
|
|
@@ -31,7 +31,7 @@ import { registerAiJobAdmission } from "./ai-job-admission.js";
|
|
|
31
31
|
import { aiJobStepBudget } from "./ai-job-budget.js";
|
|
32
32
|
import { aiJobDraftId } from "./ai-job-draft-ids.js";
|
|
33
33
|
import { aiDoctrineReview, aiGenerationSpent, aiLimitReview, aiUnspentOutcome } from "./ai-job-generation.js";
|
|
34
|
-
import { aiPluginDraftAdmissionRefusal, aiPluginDraftUnavailable, aiPluginDraftWriter } from "./ai-job-plugin-drafts.js";
|
|
34
|
+
import { aiPluginDraftAdmissionRefusal, aiPluginDraftOwner, aiPluginDraftUnavailable, aiPluginDraftWriter } from "./ai-job-plugin-drafts.js";
|
|
35
35
|
import { AI_JOB_BRIEF_MAX_CHARS } from "./ai-job-text-step.js";
|
|
36
36
|
import { registerAiJobStep } from "./ai-jobs.js";
|
|
37
37
|
import { readAiAutomationRecords, readAiWorkflowFunctions, readAiWorkflowRun, readAiWorkflowTarget } from "./ai-workflow-records.js";
|
|
@@ -361,7 +361,7 @@ export function createAiJobWorkflowStep(deps = {}) {
|
|
|
361
361
|
]
|
|
362
362
|
});
|
|
363
363
|
}
|
|
364
|
-
const target = await readTarget(
|
|
364
|
+
const target = await readTarget({
|
|
365
365
|
hostId,
|
|
366
366
|
type: inputs.targetType,
|
|
367
367
|
id: inputs.targetId
|
|
@@ -435,17 +435,26 @@ function draftOutput(id, name, place, note) {
|
|
|
435
435
|
}
|
|
436
436
|
export const runAiJobWorkflowStep = createAiJobWorkflowStep();
|
|
437
437
|
// ── Admission ─────────────────────────────────────────────────────────────
|
|
438
|
-
/**
|
|
438
|
+
/**
|
|
439
|
+
* Whether the plugin that writes automations runs on this site: switched on,
|
|
440
|
+
* and past its release flag. The owner is whichever plugin registered the
|
|
441
|
+
* automation writer (AGL-3080); none registered is the same refusal.
|
|
442
|
+
*/ async function workflowsPluginRefusal(context, hostId) {
|
|
439
443
|
var _data;
|
|
444
|
+
const owner = aiPluginDraftOwner(AI_AUTOMATION_RESOURCE);
|
|
445
|
+
if (!owner) return {
|
|
446
|
+
status: 403,
|
|
447
|
+
error: aiPluginDraftUnavailable('Automation')
|
|
448
|
+
};
|
|
440
449
|
const host = (_data = (await context.firestore.collection('hosts').doc(hostId).get()).data()) != null ? _data : null;
|
|
441
450
|
const org = context.org;
|
|
442
451
|
const released = await filterEnabledPluginsByReleaseFlags([
|
|
443
|
-
|
|
452
|
+
owner
|
|
444
453
|
], {
|
|
445
454
|
orgId: context.orgId,
|
|
446
455
|
authorization: null
|
|
447
456
|
});
|
|
448
|
-
return released.includes(
|
|
457
|
+
return released.includes(owner) && isHostPluginEnabled(org, host, owner) ? null : {
|
|
449
458
|
status: 403,
|
|
450
459
|
error: aiPluginDraftUnavailable('Automation')
|
|
451
460
|
};
|
|
@@ -471,7 +480,6 @@ export const runAiJobWorkflowStep = createAiJobWorkflowStep();
|
|
|
471
480
|
drafts: [
|
|
472
481
|
{
|
|
473
482
|
resource: AI_AUTOMATION_RESOURCE,
|
|
474
|
-
pluginId: AI_WORKFLOWS_PLUGIN_ID,
|
|
475
483
|
label: 'Automation'
|
|
476
484
|
}
|
|
477
485
|
]
|
|
@@ -491,7 +499,7 @@ export const runAiJobWorkflowStep = createAiJobWorkflowStep();
|
|
|
491
499
|
};
|
|
492
500
|
const plugin = await workflowsPluginRefusal(context, hostId);
|
|
493
501
|
if (plugin) return plugin;
|
|
494
|
-
const target = await readTarget(
|
|
502
|
+
const target = await readTarget({
|
|
495
503
|
hostId,
|
|
496
504
|
type: inputs.targetType,
|
|
497
505
|
id: inputs.targetId
|