@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/model/ai-automation-outline.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 CLIENT_ACTION_STEP_TYPES,\n HOST_ACTION_STEP_LABELS,\n type HostAction,\n type HostActionStep,\n type HostActionStepType,\n type HostActionTriggerCondition,\n normalizeTriggerConditions,\n} from '@aglyn/aglyn/app-utils/actions'\nimport { automationPlaceholderIn } from '@aglyn/aglyn/app-utils/automation-placeholders'\nimport { CONTACT_LIFECYCLE_STAGE_LABELS, type ContactLifecycleStage } from '@aglyn/aglyn/app-utils/crm'\nimport type { HostWorkflow } from '@aglyn/aglyn/app-utils/workflows'\nimport { aiAutomationTriggerLabel } from './ai-workflow-job'\nimport type { AiAutomationNamedRecord, AiAutomationRecords } from './ai-automation-draft'\nimport type { AiWorkflowExplanation } from '../tools/ai-workflow-tool'\n\n/**\n * What an explanation is asked from (AGL-2919): a saved automation, and one of\n * its failed runs, as plain lines a model reads — never the stored documents.\n *\n * WHAT IS LEFT OUT. An email address anywhere — a condition's value, an\n * email's text, a run's error naming the contact it could not find — becomes\n * `[email address]`. A teammate named by account id or address is \"a named\n * teammate\". A selector, a script or HTML an on-page step holds is not shown\n * at all; the step is named by its label. A run's event payload, which holds\n * what a visitor submitted, is never read. What is shown is how the automation\n * is set up, and what the run history recorded about the run.\n *\n * WHAT IS ADDED. Whether each list, campaign, workflow, webhook or dataset a\n * step names still exists on the site, looked up in code, because \"that list\n * was deleted\" is the answer to the most common failure and a model cannot\n * find it out.\n */\n\n/** An email address, wherever it appears in a line. */\nconst EMAIL_ADDRESS = /[^\\s@<>()[\\]\"',;:]+@[^\\s@<>()[\\]\"',;:]+\\.[A-Za-z]{2,}/g\n\n/** How much of an email's text an outline quotes. */\nexport const AI_OUTLINE_BODY_MAX_CHARS = 300\n/** How much of any other value an outline quotes. */\nexport const AI_OUTLINE_VALUE_MAX_CHARS = 120\n/** The most errors of one run an outline lists. */\nexport const AI_OUTLINE_RUN_ERRORS_MAX = 10\n\n/** A line with every email address replaced by what it is. */\nexport function aiRedactPersonal(text: string): string {\n return text.replace(EMAIL_ADDRESS, '[email address]')\n}\n\nfunction quoted(value: unknown, max = AI_OUTLINE_VALUE_MAX_CHARS): string {\n const text = aiRedactPersonal(String(value ?? '').replace(/\\s+/g, ' ').trim())\n return `\"${text.length > max ? `${text.slice(0, max)}…` : text}\"`\n}\n\nfunction conditionLine(condition: HostActionTriggerCondition): string {\n const field = String(condition.field ?? '')\n if (condition.op === 'notEmpty') return `${field} is not empty`\n const value = String(condition.value ?? '')\n const stage = CONTACT_LIFECYCLE_STAGE_LABELS[value as ContactLifecycleStage]\n const placeholder = automationPlaceholderIn(value) ? ' (a placeholder nobody has filled in)' : ''\n return `${field} ${condition.op} ${stage ? `\"${stage}\"` : quoted(value)}${placeholder}`\n}\n\n/** The kind of site record each step that names one names. */\nexport const AI_OUTLINE_RECORD_KINDS: Readonly<\n Partial<Record<HostActionStepType, 'lists' | 'campaigns' | 'workflows' | 'webhooks' | 'datasets'>>\n> = {\n enrollList: 'lists',\n assignCampaign: 'campaigns',\n runWorkflow: 'workflows',\n webhookPost: 'webhooks',\n datasetAppend: 'datasets',\n updateDataset: 'datasets',\n}\n\n/** The kinds of site record an action's steps name, each once. */\nexport function aiActionRecordKinds(action: Pick<HostAction, 'steps'>): Array<keyof AiAutomationRecords> {\n const kinds = (action.steps ?? []).map((step) => AI_OUTLINE_RECORD_KINDS[step?.type as HostActionStepType])\n return [...new Set(kinds.filter((kind): kind is NonNullable<typeof kind> => Boolean(kind)))]\n}\n\n/** Whether the site holds a record by the id or the name a step stores. */\nfunction standing(\n id: unknown,\n name: unknown,\n records: readonly AiAutomationNamedRecord[] | null,\n): string {\n if (automationPlaceholderIn(name) && !String(id ?? '').trim()) {\n return ' — a placeholder nobody has filled in'\n }\n if (!records) return ''\n const found = records.some(\n (record) =>\n (typeof id === 'string' && id && record.id === id) ||\n (typeof name === 'string' && name && record.name.trim() === name.trim()),\n )\n return found ? ' — the site has it' : ' — the site has none by that name'\n}\n\nfunction stepLine(\n step: HostActionStep,\n records: AiAutomationRecords | null,\n): string {\n const label = HOST_ACTION_STEP_LABELS[step.type as HostActionStepType] ?? step.type\n const detail = (() => {\n switch (step.type) {\n case 'sendEmail':\n return `subject ${quoted(step.subject)}, text ${quoted(step.body, AI_OUTLINE_BODY_MAX_CHARS)}, to the event's ${step.toField || 'email'} field`\n case 'notifyAdmins':\n return quoted(step.title)\n case 'enrollList':\n return `${quoted(step.listName || step.listId)}${standing(step.listId, step.listName, records?.lists ?? null)}`\n case 'assignCampaign':\n return `${quoted(step.campaignName || step.campaignId)}${standing(step.campaignId, step.campaignName, records?.campaigns ?? null)}`\n case 'runWorkflow':\n return `${quoted(step.workflowName || step.workflowId)}${standing(step.workflowId, step.workflowName, records?.workflows ?? null)}`\n case 'webhookPost':\n return `${quoted(step.webhookName || step.webhookId)}${standing(step.webhookId, step.webhookName, records?.webhooks ?? null)}`\n case 'datasetAppend':\n case 'updateDataset':\n return `${quoted(step.datasetName || step.datasetId)}${standing(step.datasetId, step.datasetName, records?.datasets ?? null)}`\n case 'siteAlert':\n return quoted(step.message)\n case 'wait':\n return durationOf(step.delayMinutes)\n case 'waitForEvent':\n return `${aiAutomationTriggerLabel(step.eventName)}, giving up after ${durationOf(step.timeoutMinutes)}`\n case 'exitFlow':\n return ''\n case 'setContactStage':\n return `\"${CONTACT_LIFECYCLE_STAGE_LABELS[step.lifecycleStage] ?? step.lifecycleStage}\"`\n case 'addContactTag':\n return quoted(step.tag)\n case 'assignContactOwner':\n return step.roundRobin ? 'by round robin' : 'a named teammate'\n case 'createCrmTask':\n return `${quoted(step.title)}, a ${step.kind}, due in ${step.dueInDays} ${step.dueInDays === 1 ? 'day' : 'days'}${\n step.assigneeUid || step.assigneeEmail ? ', for a named teammate' : ''\n }`\n case 'logCrmActivity':\n return `a ${step.kind}: ${quoted(step.body)}`\n case 'customEvent':\n return quoted(step.eventName)\n default:\n return CLIENT_ACTION_STEP_TYPES.has(step.type) ? 'runs in the visitor’s browser on the page' : ''\n }\n })()\n const guard = (step.when?.conditions ?? []).filter(Boolean)\n const only = guard.length\n ? ` Only if ${guard.map(conditionLine).join(step.when?.combinator === 'or' ? ' or ' : ' and ')}.`\n : ''\n return `${label}${detail ? `: ${detail}` : ''}.${only}`\n}\n\n/** Minutes as a person says them. */\nfunction durationOf(minutes: unknown): string {\n const value = Number(minutes)\n if (!Number.isFinite(value) || value <= 0) return 'no time'\n const units: Array<[number, string]> = [\n [60 * 24 * 7, 'week'],\n [60 * 24, 'day'],\n [60, 'hour'],\n [1, 'minute'],\n ]\n for (const [size, unit] of units) {\n if (value % size === 0) {\n const count = value / size\n return `${count} ${unit}${count === 1 ? '' : 's'}`\n }\n }\n return `${value} minutes`\n}\n\n/** A saved action as an outline. */\nexport function aiActionOutline(\n action: HostAction,\n records: AiAutomationRecords | null,\n): string {\n const lines = [\n `Automation: ${quoted(action.name)} — an action, switched ${action.enabled === false ? 'off' : 'on'}.`,\n `Starts when: ${aiAutomationTriggerLabel(String(action.trigger?.event ?? ''))}.`,\n ]\n const conditions = normalizeTriggerConditions(action.trigger)\n if (conditions.length) {\n lines.push(\n `Only when ${action.trigger?.combinator === 'or' ? 'any' : 'all'} of these hold: ${conditions\n .map(conditionLine)\n .join('; ')}.`,\n )\n }\n if (action.trigger?.filter?.trim()) {\n lines.push(`And only when this expression is true: ${quoted(action.trigger.filter)}.`)\n }\n lines.push('Steps, in order:')\n ;(action.steps ?? []).forEach((step, index) => {\n lines.push(`${index + 1}. ${stepLine(step, records)}`)\n })\n if (!(action.steps ?? []).length) lines.push('(none)')\n return lines.join('\\n')\n}\n\n/** A saved workflow — a pipeline of function calls — as an outline. */\nexport function aiWorkflowOutline(\n workflow: HostWorkflow,\n functions: readonly AiAutomationNamedRecord[] | null,\n): string {\n const lines = [`Workflow: ${quoted(workflow.name)} — a workflow, a list of function calls.`]\n if (workflow.trigger?.event) {\n const filter = workflow.trigger.filter?.trim()\n lines.push(\n `Starts when: ${aiAutomationTriggerLabel(workflow.trigger.event)}${\n filter ? `, only when this expression is true: ${quoted(filter)}` : ''\n }.`,\n )\n } else {\n lines.push('Starts when: it is called — by an automation, an inbound webhook or a computed variable.')\n }\n lines.push('Steps, in order:')\n ;(workflow.steps ?? []).forEach((step, index) => {\n const name = step.functionName || step.functionId || ''\n const exists = standing(step.functionId, step.functionName, functions)\n const args = (step.args ?? []).filter((arg) => String(arg ?? '').trim())\n lines.push(\n `${index + 1}. Runs the function ${quoted(name)}${exists}${\n args.length ? `, given ${args.map((arg) => quoted(arg)).join(', ')}` : ''\n }; its result is kept as ${quoted(step.resultName || `step${index + 1}`)}.`,\n )\n })\n if (!(workflow.steps ?? []).length) lines.push('(none)')\n if (workflow.returnValue?.trim()) lines.push(`Returns: ${quoted(workflow.returnValue)}.`)\n return lines.join('\\n')\n}\n\n/** What the run history recorded about one run, as far as an outline reads it. */\nexport interface AiRunRecord {\n result?: unknown\n trigger?: unknown\n summary?: unknown\n action?: unknown\n createdAt?: unknown\n}\n\nfunction whenOf(value: unknown): string | null {\n const toDate = (value as { toDate?: () => Date } | null)?.toDate\n const date =\n typeof toDate === 'function'\n ? toDate.call(value)\n : value instanceof Date\n ? value\n : null\n return date && Number.isFinite(date.getTime()) ? `${date.toISOString().slice(0, 16).replace('T', ' ')} UTC` : null\n}\n\n/** The errors a run recorded: the prose after \"with errors:\", or a workflow's failure. */\nexport function aiRunErrors(run: AiRunRecord): string[] {\n const action = String(run.action ?? '')\n const listed = action.match(/with errors:\\s*(.+)$/)?.[1]\n if (listed) {\n return listed\n .split(/;\\s+/)\n .map((error) => error.trim())\n .filter(Boolean)\n .slice(0, AI_OUTLINE_RUN_ERRORS_MAX)\n }\n const failed = action.match(/failed on [^:]+:\\s*(.+)$/)?.[1] ?? (run.result === 'failed' ? String(run.summary ?? '') : '')\n return failed.trim() ? [failed.trim()] : []\n}\n\n/** A failed run as an outline: when, on what, what it did, and what went wrong. */\nexport function aiRunOutline(run: AiRunRecord): string {\n const when = whenOf(run.createdAt)\n const lines = [\n `The run that failed: on ${aiAutomationTriggerLabel(String(run.trigger ?? ''))}${when ? `, ${when}` : ''}.`,\n ]\n const summary = String(run.summary ?? '').trim()\n const errors = aiRunErrors(run)\n if (summary && !errors.includes(summary)) lines.push(`What it did: ${aiRedactPersonal(summary)}.`)\n lines.push('What went wrong:')\n lines.push(...(errors.length ? errors.map((error) => `- ${aiRedactPersonal(error)}`) : ['- (the run recorded no error)']))\n return lines.join('\\n')\n}\n\n/** An explanation as the text a person reads. */\nexport function aiWorkflowExplanationText(\n explanation: AiWorkflowExplanation,\n mode: 'explain' | 'diagnose',\n): string {\n const parts = [explanation.summary]\n if (explanation.points.length) {\n parts.push(\n [\n mode === 'diagnose' ? 'What happened:' : 'What it does:',\n ...explanation.points.map((point, index) => `${index + 1}. ${point}`),\n ].join('\\n'),\n )\n }\n if (explanation.suggestions.length) {\n parts.push(\n [\n mode === 'diagnose' ? 'How to fix it:' : 'Worth checking:',\n ...explanation.suggestions.map((suggestion) => `- ${suggestion}`),\n ].join('\\n'),\n )\n }\n return parts.join('\\n\\n')\n}\n"],"names":["CLIENT_ACTION_STEP_TYPES","HOST_ACTION_STEP_LABELS","normalizeTriggerConditions","automationPlaceholderIn","CONTACT_LIFECYCLE_STAGE_LABELS","aiAutomationTriggerLabel","EMAIL_ADDRESS","AI_OUTLINE_BODY_MAX_CHARS","AI_OUTLINE_VALUE_MAX_CHARS","AI_OUTLINE_RUN_ERRORS_MAX","aiRedactPersonal","text","replace","quoted","value","max","String","trim","length","slice","conditionLine","condition","field","op","stage","placeholder","AI_OUTLINE_RECORD_KINDS","enrollList","assignCampaign","runWorkflow","webhookPost","datasetAppend","updateDataset","aiActionRecordKinds","action","kinds","steps","map","step","type","Set","filter","kind","Boolean","standing","id","name","records","found","some","record","stepLine","label","detail","subject","body","toField","title","listName","listId","lists","campaignName","campaignId","campaigns","workflowName","workflowId","workflows","webhookName","webhookId","webhooks","datasetName","datasetId","datasets","message","durationOf","delayMinutes","eventName","timeoutMinutes","lifecycleStage","tag","roundRobin","dueInDays","assigneeUid","assigneeEmail","has","guard","when","conditions","only","join","combinator","minutes","Number","isFinite","units","size","unit","count","aiActionOutline","lines","enabled","trigger","event","push","forEach","index","aiWorkflowOutline","workflow","functions","functionName","functionId","exists","args","arg","resultName","returnValue","whenOf","toDate","date","call","Date","getTime","toISOString","aiRunErrors","run","listed","match","split","error","failed","result","summary","aiRunOutline","createdAt","errors","includes","aiWorkflowExplanationText","explanation","mode","parts","points","point","suggestions","suggestion"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-automation-outline.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 CLIENT_ACTION_STEP_TYPES,\n HOST_ACTION_STEP_LABELS,\n type HostAction,\n type HostActionStep,\n type HostActionStepType,\n type HostActionTriggerCondition,\n normalizeTriggerConditions,\n} from '@aglyn/aglyn/app-utils/actions'\nimport { automationPlaceholderIn } from '@aglyn/aglyn/app-utils/automation-placeholders'\nimport { CONTACT_LIFECYCLE_STAGE_LABELS, type ContactLifecycleStage } from '@aglyn/aglyn/app-utils/crm'\nimport { aiAutomationTriggerLabel } from './ai-workflow-job'\nimport type { AiAutomationNamedRecord, AiAutomationRecords } from './ai-automation-draft'\nimport type { AiWorkflowExplanation } from '../tools/ai-workflow-tool'\n\n/**\n * What an explanation is asked from (AGL-2919): a saved automation, and one of\n * its failed runs, as plain lines a model reads — never the stored documents.\n *\n * WHAT IS LEFT OUT. An email address anywhere — a condition's value, an\n * email's text, a run's error naming the contact it could not find — becomes\n * `[email address]`. A teammate named by account id or address is \"a named\n * teammate\". A selector, a script or HTML an on-page step holds is not shown\n * at all; the step is named by its label. A run's event payload, which holds\n * what a visitor submitted, is never read. What is shown is how the automation\n * is set up, and what the run history recorded about the run.\n *\n * WHAT IS ADDED. Whether each list, campaign, workflow, webhook or dataset a\n * step names still exists on the site, looked up in code, because \"that list\n * was deleted\" is the answer to the most common failure and a model cannot\n * find it out.\n */\n\n/** An email address, wherever it appears in a line. */\nconst EMAIL_ADDRESS = /[^\\s@<>()[\\]\"',;:]+@[^\\s@<>()[\\]\"',;:]+\\.[A-Za-z]{2,}/g\n\n/** How much of an email's text an outline quotes. */\nexport const AI_OUTLINE_BODY_MAX_CHARS = 300\n/** How much of any other value an outline quotes. */\nexport const AI_OUTLINE_VALUE_MAX_CHARS = 120\n/** The most errors of one run an outline lists. */\nexport const AI_OUTLINE_RUN_ERRORS_MAX = 10\n\n/** A line with every email address replaced by what it is. */\nexport function aiRedactPersonal(text: string): string {\n return text.replace(EMAIL_ADDRESS, '[email address]')\n}\n\nfunction quoted(value: unknown, max = AI_OUTLINE_VALUE_MAX_CHARS): string {\n const text = aiRedactPersonal(String(value ?? '').replace(/\\s+/g, ' ').trim())\n return `\"${text.length > max ? `${text.slice(0, max)}…` : text}\"`\n}\n\nfunction conditionLine(condition: HostActionTriggerCondition): string {\n const field = String(condition.field ?? '')\n if (condition.op === 'notEmpty') return `${field} is not empty`\n const value = String(condition.value ?? '')\n const stage = CONTACT_LIFECYCLE_STAGE_LABELS[value as ContactLifecycleStage]\n const placeholder = automationPlaceholderIn(value) ? ' (a placeholder nobody has filled in)' : ''\n return `${field} ${condition.op} ${stage ? `\"${stage}\"` : quoted(value)}${placeholder}`\n}\n\n/** The kind of site record each step that names one names. */\nexport const AI_OUTLINE_RECORD_KINDS: Readonly<\n Partial<Record<HostActionStepType, 'lists' | 'campaigns' | 'workflows' | 'webhooks' | 'datasets'>>\n> = {\n enrollList: 'lists',\n assignCampaign: 'campaigns',\n runWorkflow: 'workflows',\n webhookPost: 'webhooks',\n datasetAppend: 'datasets',\n updateDataset: 'datasets',\n}\n\n/** The kinds of site record an action's steps name, each once. */\nexport function aiActionRecordKinds(action: Pick<HostAction, 'steps'>): Array<keyof AiAutomationRecords> {\n const kinds = (action.steps ?? []).map((step) => AI_OUTLINE_RECORD_KINDS[step?.type as HostActionStepType])\n return [...new Set(kinds.filter((kind): kind is NonNullable<typeof kind> => Boolean(kind)))]\n}\n\n/** Whether the site holds a record by the id or the name a step stores. */\nfunction standing(\n id: unknown,\n name: unknown,\n records: readonly AiAutomationNamedRecord[] | null,\n): string {\n if (automationPlaceholderIn(name) && !String(id ?? '').trim()) {\n return ' — a placeholder nobody has filled in'\n }\n if (!records) return ''\n const found = records.some(\n (record) =>\n (typeof id === 'string' && id && record.id === id) ||\n (typeof name === 'string' && name && record.name.trim() === name.trim()),\n )\n return found ? ' — the site has it' : ' — the site has none by that name'\n}\n\nfunction stepLine(\n step: HostActionStep,\n records: AiAutomationRecords | null,\n): string {\n const label = HOST_ACTION_STEP_LABELS[step.type as HostActionStepType] ?? step.type\n const detail = (() => {\n switch (step.type) {\n case 'sendEmail':\n return `subject ${quoted(step.subject)}, text ${quoted(step.body, AI_OUTLINE_BODY_MAX_CHARS)}, to the event's ${step.toField || 'email'} field`\n case 'notifyAdmins':\n return quoted(step.title)\n case 'enrollList':\n return `${quoted(step.listName || step.listId)}${standing(step.listId, step.listName, records?.lists ?? null)}`\n case 'assignCampaign':\n return `${quoted(step.campaignName || step.campaignId)}${standing(step.campaignId, step.campaignName, records?.campaigns ?? null)}`\n case 'runWorkflow':\n return `${quoted(step.workflowName || step.workflowId)}${standing(step.workflowId, step.workflowName, records?.workflows ?? null)}`\n case 'webhookPost':\n return `${quoted(step.webhookName || step.webhookId)}${standing(step.webhookId, step.webhookName, records?.webhooks ?? null)}`\n case 'datasetAppend':\n case 'updateDataset':\n return `${quoted(step.datasetName || step.datasetId)}${standing(step.datasetId, step.datasetName, records?.datasets ?? null)}`\n case 'siteAlert':\n return quoted(step.message)\n case 'wait':\n return durationOf(step.delayMinutes)\n case 'waitForEvent':\n return `${aiAutomationTriggerLabel(step.eventName)}, giving up after ${durationOf(step.timeoutMinutes)}`\n case 'exitFlow':\n return ''\n case 'setContactStage':\n return `\"${CONTACT_LIFECYCLE_STAGE_LABELS[step.lifecycleStage] ?? step.lifecycleStage}\"`\n case 'addContactTag':\n return quoted(step.tag)\n case 'assignContactOwner':\n return step.roundRobin ? 'by round robin' : 'a named teammate'\n case 'createCrmTask':\n return `${quoted(step.title)}, a ${step.kind}, due in ${step.dueInDays} ${step.dueInDays === 1 ? 'day' : 'days'}${\n step.assigneeUid || step.assigneeEmail ? ', for a named teammate' : ''\n }`\n case 'logCrmActivity':\n return `a ${step.kind}: ${quoted(step.body)}`\n case 'customEvent':\n return quoted(step.eventName)\n default:\n return CLIENT_ACTION_STEP_TYPES.has(step.type) ? 'runs in the visitor’s browser on the page' : ''\n }\n })()\n const guard = (step.when?.conditions ?? []).filter(Boolean)\n const only = guard.length\n ? ` Only if ${guard.map(conditionLine).join(step.when?.combinator === 'or' ? ' or ' : ' and ')}.`\n : ''\n return `${label}${detail ? `: ${detail}` : ''}.${only}`\n}\n\n/** Minutes as a person says them. */\nfunction durationOf(minutes: unknown): string {\n const value = Number(minutes)\n if (!Number.isFinite(value) || value <= 0) return 'no time'\n const units: Array<[number, string]> = [\n [60 * 24 * 7, 'week'],\n [60 * 24, 'day'],\n [60, 'hour'],\n [1, 'minute'],\n ]\n for (const [size, unit] of units) {\n if (value % size === 0) {\n const count = value / size\n return `${count} ${unit}${count === 1 ? '' : 's'}`\n }\n }\n return `${value} minutes`\n}\n\n/** A saved action as an outline. */\nexport function aiActionOutline(\n action: HostAction,\n records: AiAutomationRecords | null,\n): string {\n const lines = [\n `Automation: ${quoted(action.name)} — an action, switched ${action.enabled === false ? 'off' : 'on'}.`,\n `Starts when: ${aiAutomationTriggerLabel(String(action.trigger?.event ?? ''))}.`,\n ]\n const conditions = normalizeTriggerConditions(action.trigger)\n if (conditions.length) {\n lines.push(\n `Only when ${action.trigger?.combinator === 'or' ? 'any' : 'all'} of these hold: ${conditions\n .map(conditionLine)\n .join('; ')}.`,\n )\n }\n if (action.trigger?.filter?.trim()) {\n lines.push(`And only when this expression is true: ${quoted(action.trigger.filter)}.`)\n }\n lines.push('Steps, in order:')\n ;(action.steps ?? []).forEach((step, index) => {\n lines.push(`${index + 1}. ${stepLine(step, records)}`)\n })\n if (!(action.steps ?? []).length) lines.push('(none)')\n return lines.join('\\n')\n}\n\n/**\n * A saved workflow as the workflows plugin's `workflow` record index shares\n * it: its name, and the facts that index documents — the stored step list\n * (each a function call by `functionId`, `functionName`, `args` and\n * `resultName`), the scope name it returns, and its trigger.\n */\nexport interface AiIndexedWorkflow {\n name: string\n steps?: Array<{\n functionId?: string\n functionName?: string\n args?: string[]\n resultName?: string\n }>\n returnValue?: string | null\n trigger?: { event?: string; filter?: string } | null\n}\n\n/** A saved workflow — a pipeline of function calls — as an outline. */\nexport function aiWorkflowOutline(\n workflow: AiIndexedWorkflow,\n functions: readonly AiAutomationNamedRecord[] | null,\n): string {\n const lines = [`Workflow: ${quoted(workflow.name)} — a workflow, a list of function calls.`]\n if (workflow.trigger?.event) {\n const filter = workflow.trigger.filter?.trim()\n lines.push(\n `Starts when: ${aiAutomationTriggerLabel(workflow.trigger.event)}${\n filter ? `, only when this expression is true: ${quoted(filter)}` : ''\n }.`,\n )\n } else {\n lines.push('Starts when: it is called — by an automation, an inbound webhook or a computed variable.')\n }\n lines.push('Steps, in order:')\n ;(workflow.steps ?? []).forEach((step, index) => {\n const name = step.functionName || step.functionId || ''\n const exists = standing(step.functionId, step.functionName, functions)\n const args = (step.args ?? []).filter((arg) => String(arg ?? '').trim())\n lines.push(\n `${index + 1}. Runs the function ${quoted(name)}${exists}${\n args.length ? `, given ${args.map((arg) => quoted(arg)).join(', ')}` : ''\n }; its result is kept as ${quoted(step.resultName || `step${index + 1}`)}.`,\n )\n })\n if (!(workflow.steps ?? []).length) lines.push('(none)')\n if (workflow.returnValue?.trim()) lines.push(`Returns: ${quoted(workflow.returnValue)}.`)\n return lines.join('\\n')\n}\n\n/** What the run history recorded about one run, as far as an outline reads it. */\nexport interface AiRunRecord {\n result?: unknown\n trigger?: unknown\n summary?: unknown\n action?: unknown\n createdAt?: unknown\n}\n\nfunction whenOf(value: unknown): string | null {\n const toDate = (value as { toDate?: () => Date } | null)?.toDate\n const date =\n typeof toDate === 'function'\n ? toDate.call(value)\n : value instanceof Date\n ? value\n : null\n return date && Number.isFinite(date.getTime()) ? `${date.toISOString().slice(0, 16).replace('T', ' ')} UTC` : null\n}\n\n/** The errors a run recorded: the prose after \"with errors:\", or a workflow's failure. */\nexport function aiRunErrors(run: AiRunRecord): string[] {\n const action = String(run.action ?? '')\n const listed = action.match(/with errors:\\s*(.+)$/)?.[1]\n if (listed) {\n return listed\n .split(/;\\s+/)\n .map((error) => error.trim())\n .filter(Boolean)\n .slice(0, AI_OUTLINE_RUN_ERRORS_MAX)\n }\n const failed = action.match(/failed on [^:]+:\\s*(.+)$/)?.[1] ?? (run.result === 'failed' ? String(run.summary ?? '') : '')\n return failed.trim() ? [failed.trim()] : []\n}\n\n/** A failed run as an outline: when, on what, what it did, and what went wrong. */\nexport function aiRunOutline(run: AiRunRecord): string {\n const when = whenOf(run.createdAt)\n const lines = [\n `The run that failed: on ${aiAutomationTriggerLabel(String(run.trigger ?? ''))}${when ? `, ${when}` : ''}.`,\n ]\n const summary = String(run.summary ?? '').trim()\n const errors = aiRunErrors(run)\n if (summary && !errors.includes(summary)) lines.push(`What it did: ${aiRedactPersonal(summary)}.`)\n lines.push('What went wrong:')\n lines.push(...(errors.length ? errors.map((error) => `- ${aiRedactPersonal(error)}`) : ['- (the run recorded no error)']))\n return lines.join('\\n')\n}\n\n/** An explanation as the text a person reads. */\nexport function aiWorkflowExplanationText(\n explanation: AiWorkflowExplanation,\n mode: 'explain' | 'diagnose',\n): string {\n const parts = [explanation.summary]\n if (explanation.points.length) {\n parts.push(\n [\n mode === 'diagnose' ? 'What happened:' : 'What it does:',\n ...explanation.points.map((point, index) => `${index + 1}. ${point}`),\n ].join('\\n'),\n )\n }\n if (explanation.suggestions.length) {\n parts.push(\n [\n mode === 'diagnose' ? 'How to fix it:' : 'Worth checking:',\n ...explanation.suggestions.map((suggestion) => `- ${suggestion}`),\n ].join('\\n'),\n )\n }\n return parts.join('\\n\\n')\n}\n"],"names":["CLIENT_ACTION_STEP_TYPES","HOST_ACTION_STEP_LABELS","normalizeTriggerConditions","automationPlaceholderIn","CONTACT_LIFECYCLE_STAGE_LABELS","aiAutomationTriggerLabel","EMAIL_ADDRESS","AI_OUTLINE_BODY_MAX_CHARS","AI_OUTLINE_VALUE_MAX_CHARS","AI_OUTLINE_RUN_ERRORS_MAX","aiRedactPersonal","text","replace","quoted","value","max","String","trim","length","slice","conditionLine","condition","field","op","stage","placeholder","AI_OUTLINE_RECORD_KINDS","enrollList","assignCampaign","runWorkflow","webhookPost","datasetAppend","updateDataset","aiActionRecordKinds","action","kinds","steps","map","step","type","Set","filter","kind","Boolean","standing","id","name","records","found","some","record","stepLine","label","detail","subject","body","toField","title","listName","listId","lists","campaignName","campaignId","campaigns","workflowName","workflowId","workflows","webhookName","webhookId","webhooks","datasetName","datasetId","datasets","message","durationOf","delayMinutes","eventName","timeoutMinutes","lifecycleStage","tag","roundRobin","dueInDays","assigneeUid","assigneeEmail","has","guard","when","conditions","only","join","combinator","minutes","Number","isFinite","units","size","unit","count","aiActionOutline","lines","enabled","trigger","event","push","forEach","index","aiWorkflowOutline","workflow","functions","functionName","functionId","exists","args","arg","resultName","returnValue","whenOf","toDate","date","call","Date","getTime","toISOString","aiRunErrors","run","listed","match","split","error","failed","result","summary","aiRunOutline","createdAt","errors","includes","aiWorkflowExplanationText","explanation","mode","parts","points","point","suggestions","suggestion"],"mappings":"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"}
|
|
@@ -115,10 +115,35 @@ export interface AiBuildPlanScreen {
|
|
|
115
115
|
*/
|
|
116
116
|
id?: string;
|
|
117
117
|
}
|
|
118
|
+
/** The third-party players a plan may embed: the hosts the Video embed element plays. */
|
|
119
|
+
export type AiBuildPlanEmbedHost = 'youtube' | 'vimeo';
|
|
120
|
+
export declare const AI_BUILD_PLAN_EMBED_HOSTS: readonly AiBuildPlanEmbedHost[];
|
|
121
|
+
/** What each host is called in a sentence. */
|
|
122
|
+
export declare const AI_BUILD_PLAN_EMBED_HOST_NAMES: Record<AiBuildPlanEmbedHost, string>;
|
|
123
|
+
/**
|
|
124
|
+
* A third-party player the brief asks for (rule 16, AGL-3433). It loads its
|
|
125
|
+
* host's code on the page that carries it, so it is planned, where the member
|
|
126
|
+
* reads that cost before confirming, and a build admits only what the
|
|
127
|
+
* confirmed plan lists.
|
|
128
|
+
*/
|
|
129
|
+
export interface AiBuildPlanEmbed {
|
|
130
|
+
host: AiBuildPlanEmbedHost;
|
|
131
|
+
/** Where it plays: a screen of the plan by its slug, or `new:<name>` for a component the plan creates. */
|
|
132
|
+
where: string;
|
|
133
|
+
/** The brief's own words asking for it, which the plan step finds in the brief. */
|
|
134
|
+
asked: string;
|
|
135
|
+
/** The video's link as the brief gives it, or null for the site owner to paste. */
|
|
136
|
+
url: string | null;
|
|
137
|
+
}
|
|
118
138
|
export interface AiBuildPlan {
|
|
119
139
|
reuse: AiBuildPlanReuse[];
|
|
120
140
|
create: AiBuildPlanCreate[];
|
|
121
141
|
screens: AiBuildPlanScreen[];
|
|
142
|
+
/**
|
|
143
|
+
* The third-party players the brief asks for. Absent is none: a plan kept
|
|
144
|
+
* before the list existed, and every plan whose brief asks for no player.
|
|
145
|
+
*/
|
|
146
|
+
embeds?: AiBuildPlanEmbed[];
|
|
122
147
|
}
|
|
123
148
|
/**
|
|
124
149
|
* How much one plan may hold. A job builds what one proposal can describe
|
|
@@ -132,6 +157,7 @@ export declare const AI_BUILD_PLAN_LIMITS: {
|
|
|
132
157
|
readonly sections: 16;
|
|
133
158
|
readonly uses: 8;
|
|
134
159
|
readonly fields: 30;
|
|
160
|
+
readonly embeds: 4;
|
|
135
161
|
readonly text: 200;
|
|
136
162
|
readonly seoDescription: 320;
|
|
137
163
|
readonly items: 500;
|
|
@@ -150,6 +176,18 @@ export declare const AI_BUILD_PLAN_LIMITS: {
|
|
|
150
176
|
* states, so naming the reader's here would contradict the rule.
|
|
151
177
|
*/
|
|
152
178
|
export declare const AI_BUILD_PLAN_TOOL: AiTool;
|
|
179
|
+
/** Words that ask for a video, or a link to one. */
|
|
180
|
+
export declare const AI_PLAN_ASKS_FOR_VIDEO: RegExp;
|
|
181
|
+
/**
|
|
182
|
+
* The plan tool with its list of third-party players, offered only to a brief
|
|
183
|
+
* that asks for a video (AGL-3433). Every other plan's request is the plain
|
|
184
|
+
* tool, byte for byte, so the list costs nothing on the plans that cannot use
|
|
185
|
+
* it, and a model answering a brief that names no video has nowhere to plan a
|
|
186
|
+
* player it was never asked for.
|
|
187
|
+
*/
|
|
188
|
+
export declare const AI_BUILD_PLAN_EMBEDS_TOOL: AiTool;
|
|
189
|
+
/** The plan tool a brief is answered with. */
|
|
190
|
+
export declare function aiBuildPlanToolFor(brief: string): AiTool;
|
|
153
191
|
export type AiBuildPlanParse = {
|
|
154
192
|
ok: true;
|
|
155
193
|
plan: AiBuildPlan;
|
|
@@ -171,6 +209,25 @@ export declare function parseAiBuildPlan(input: unknown): AiBuildPlanParse;
|
|
|
171
209
|
export declare function isAiPlanNewRef(ref: string | null | undefined): ref is string;
|
|
172
210
|
/** The creation a `new:<name>` reference names, case-insensitively. */
|
|
173
211
|
export declare function aiPlanCreateFor(plan: AiBuildPlan, ref: string): AiBuildPlanCreate | undefined;
|
|
212
|
+
/** A slug as two spellings of one path compare: lowercase, one leading slash, no trailing one. */
|
|
213
|
+
export declare function aiPlanSlugKey(slug: string): string;
|
|
214
|
+
/**
|
|
215
|
+
* The players the plan lists for one output (AGL-3433): a screen by its slug,
|
|
216
|
+
* or a creation by its name. A build admits these and no other.
|
|
217
|
+
*/
|
|
218
|
+
export declare function aiPlanEmbedsFor(plan: Pick<AiBuildPlan, 'embeds'> | null | undefined, target: {
|
|
219
|
+
slug: string;
|
|
220
|
+
} | {
|
|
221
|
+
create: string;
|
|
222
|
+
}): AiBuildPlanEmbed[];
|
|
223
|
+
/** The host a video link plays from, or null for a link neither host serves. */
|
|
224
|
+
export declare function aiEmbedHostOf(url: string): AiBuildPlanEmbedHost | null;
|
|
225
|
+
/**
|
|
226
|
+
* One video however its link is spelled — `youtu.be/<id>`, a watch link, an
|
|
227
|
+
* embed or a Shorts link — as `<host>:<id>`, the way the Video embed element
|
|
228
|
+
* reads a link; null for a link it cannot play.
|
|
229
|
+
*/
|
|
230
|
+
export declare function aiEmbedVideoKey(url: string): string | null;
|
|
174
231
|
/**
|
|
175
232
|
* A `new:<name>` reference that names no entry of the plan's create list: a
|
|
176
233
|
* screen's own layout or template, or what one of its sections places, with
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
14
|
* See the License for the specific language governing permissions and
|
|
15
15
|
* limitations under the License.
|
|
16
|
-
*/
|
|
16
|
+
*/ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
17
|
+
/**
|
|
17
18
|
* The plan a generation job builds from (AGL-2935): what the job's plan step
|
|
18
19
|
* (`jobs/ai-job-plan-step.ts`) returns, before a single node is generated.
|
|
19
20
|
*
|
|
@@ -87,6 +88,14 @@ export const AI_BUILD_PLAN_CREATE_KINDS = [
|
|
|
87
88
|
'dataset',
|
|
88
89
|
'email'
|
|
89
90
|
];
|
|
91
|
+
export const AI_BUILD_PLAN_EMBED_HOSTS = [
|
|
92
|
+
'youtube',
|
|
93
|
+
'vimeo'
|
|
94
|
+
];
|
|
95
|
+
/** What each host is called in a sentence. */ export const AI_BUILD_PLAN_EMBED_HOST_NAMES = {
|
|
96
|
+
youtube: 'YouTube',
|
|
97
|
+
vimeo: 'Vimeo'
|
|
98
|
+
};
|
|
90
99
|
/**
|
|
91
100
|
* How much one plan may hold. A job builds what one proposal can describe
|
|
92
101
|
* and one person can read before confirming; a brief asking for more is
|
|
@@ -98,6 +107,7 @@ export const AI_BUILD_PLAN_CREATE_KINDS = [
|
|
|
98
107
|
sections: 16,
|
|
99
108
|
uses: 8,
|
|
100
109
|
fields: 30,
|
|
110
|
+
embeds: 4,
|
|
101
111
|
text: 200,
|
|
102
112
|
seoDescription: 320,
|
|
103
113
|
items: 500
|
|
@@ -143,7 +153,7 @@ const strings = (description)=>({
|
|
|
143
153
|
* states, so naming the reader's here would contradict the rule.
|
|
144
154
|
*/ export const AI_BUILD_PLAN_TOOL = {
|
|
145
155
|
name: 'submit_build_plan',
|
|
146
|
-
description: 'Submit the build plan: what the site already has that this job reuses, what it creates and why, and the
|
|
156
|
+
description: 'Submit the build plan: what the site already has that this job reuses, what it creates and why, and the pages it builds from them.',
|
|
147
157
|
strict: true,
|
|
148
158
|
inputSchema: {
|
|
149
159
|
type: 'object',
|
|
@@ -206,7 +216,7 @@ const strings = (description)=>({
|
|
|
206
216
|
},
|
|
207
217
|
screens: {
|
|
208
218
|
type: 'array',
|
|
209
|
-
description: 'The
|
|
219
|
+
description: 'The pages the job builds, each a new draft.',
|
|
210
220
|
items: {
|
|
211
221
|
type: 'object',
|
|
212
222
|
additionalProperties: false,
|
|
@@ -222,11 +232,11 @@ const strings = (description)=>({
|
|
|
222
232
|
'sections'
|
|
223
233
|
],
|
|
224
234
|
properties: {
|
|
225
|
-
title: string(`The
|
|
235
|
+
title: string(`The page name, ${atMost()}.`),
|
|
226
236
|
slug: string(`The path, ${atMost()}: lowercase words joined by hyphens, e.g. /services/roof-repair.`),
|
|
227
237
|
layout: nullableString('The layout it renders inside: an inventory id or new:<name>.'),
|
|
228
238
|
template: nullableString('The template it applies (inventory id or new:<name>), or null for a one-off page.'),
|
|
229
|
-
duplicateOf: nullableString('The inventory
|
|
239
|
+
duplicateOf: nullableString('The inventory page it starts from as a duplicate, or null.'),
|
|
230
240
|
nav: {
|
|
231
241
|
type: 'boolean',
|
|
232
242
|
description: 'Whether the site navigation gains an entry for it.'
|
|
@@ -260,6 +270,52 @@ const strings = (description)=>({
|
|
|
260
270
|
}
|
|
261
271
|
}
|
|
262
272
|
};
|
|
273
|
+
/** Words that ask for a video, or a link to one. */ export const AI_PLAN_ASKS_FOR_VIDEO = /\b(?:videos?|films?|clips?|youtube|vimeo|player)\b|youtu\.be/i;
|
|
274
|
+
const EMBEDS_SCHEMA = {
|
|
275
|
+
type: 'array',
|
|
276
|
+
description: 'A YouTube or Vimeo player the brief asks for, on a screen or a created component. Empty unless the brief asks; a library film is a Video.',
|
|
277
|
+
items: {
|
|
278
|
+
type: 'object',
|
|
279
|
+
additionalProperties: false,
|
|
280
|
+
required: [
|
|
281
|
+
'host',
|
|
282
|
+
'where',
|
|
283
|
+
'asked',
|
|
284
|
+
'url'
|
|
285
|
+
],
|
|
286
|
+
properties: {
|
|
287
|
+
host: {
|
|
288
|
+
type: 'string',
|
|
289
|
+
enum: [
|
|
290
|
+
...AI_BUILD_PLAN_EMBED_HOSTS
|
|
291
|
+
]
|
|
292
|
+
},
|
|
293
|
+
where: string('A screen slug, or new:<name> of a component.'),
|
|
294
|
+
asked: string('The words of the brief that ask for it, copied exactly.'),
|
|
295
|
+
url: nullableString('The video link the brief gives, or null.')
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
/**
|
|
300
|
+
* The plan tool with its list of third-party players, offered only to a brief
|
|
301
|
+
* that asks for a video (AGL-3433). Every other plan's request is the plain
|
|
302
|
+
* tool, byte for byte, so the list costs nothing on the plans that cannot use
|
|
303
|
+
* it, and a model answering a brief that names no video has nowhere to plan a
|
|
304
|
+
* player it was never asked for.
|
|
305
|
+
*/ export const AI_BUILD_PLAN_EMBEDS_TOOL = _extends({}, AI_BUILD_PLAN_TOOL, {
|
|
306
|
+
inputSchema: _extends({}, AI_BUILD_PLAN_TOOL.inputSchema, {
|
|
307
|
+
required: [
|
|
308
|
+
...AI_BUILD_PLAN_TOOL.inputSchema['required'],
|
|
309
|
+
'embeds'
|
|
310
|
+
],
|
|
311
|
+
properties: _extends({}, AI_BUILD_PLAN_TOOL.inputSchema['properties'], {
|
|
312
|
+
embeds: EMBEDS_SCHEMA
|
|
313
|
+
})
|
|
314
|
+
})
|
|
315
|
+
});
|
|
316
|
+
/** The plan tool a brief is answered with. */ export function aiBuildPlanToolFor(brief) {
|
|
317
|
+
return AI_PLAN_ASKS_FOR_VIDEO.test(brief) ? AI_BUILD_PLAN_EMBEDS_TOOL : AI_BUILD_PLAN_TOOL;
|
|
318
|
+
}
|
|
263
319
|
function isRecord(value) {
|
|
264
320
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
265
321
|
}
|
|
@@ -309,6 +365,7 @@ let PlanShapeError = class PlanShapeError extends Error {
|
|
|
309
365
|
return value;
|
|
310
366
|
};
|
|
311
367
|
try {
|
|
368
|
+
var _root_embeds;
|
|
312
369
|
const root = record(input, 'plan');
|
|
313
370
|
const reuse = list(root['reuse'], 'reuse', AI_BUILD_PLAN_LIMITS.reuse).map((raw, index)=>{
|
|
314
371
|
const entry = record(raw, `reuse[${index}]`);
|
|
@@ -369,13 +426,27 @@ let PlanShapeError = class PlanShapeError extends Error {
|
|
|
369
426
|
})
|
|
370
427
|
};
|
|
371
428
|
});
|
|
429
|
+
const embeds = list((_root_embeds = root['embeds']) != null ? _root_embeds : [], 'embeds', AI_BUILD_PLAN_LIMITS.embeds).map((raw, index)=>{
|
|
430
|
+
const entry = record(raw, `embeds[${index}]`);
|
|
431
|
+
const where = text(entry['where'], `embeds[${index}].where`);
|
|
432
|
+
if (!where) throw new PlanShapeError(`embeds[${index}].where is empty`);
|
|
433
|
+
return {
|
|
434
|
+
host: oneOf(entry['host'], AI_BUILD_PLAN_EMBED_HOSTS, `embeds[${index}].host`),
|
|
435
|
+
where,
|
|
436
|
+
asked: text(entry['asked'], `embeds[${index}].asked`),
|
|
437
|
+
url: nullable(entry['url'], `embeds[${index}].url`)
|
|
438
|
+
};
|
|
439
|
+
});
|
|
440
|
+
// A plan whose brief asks for no player carries no list, as it did before the list existed.
|
|
372
441
|
return {
|
|
373
442
|
ok: true,
|
|
374
|
-
plan: {
|
|
443
|
+
plan: _extends({
|
|
375
444
|
reuse,
|
|
376
445
|
create,
|
|
377
446
|
screens
|
|
378
|
-
},
|
|
447
|
+
}, embeds.length ? {
|
|
448
|
+
embeds
|
|
449
|
+
} : {}),
|
|
379
450
|
repairs
|
|
380
451
|
};
|
|
381
452
|
} catch (error) {
|
|
@@ -394,6 +465,44 @@ let PlanShapeError = class PlanShapeError extends Error {
|
|
|
394
465
|
const name = ref.slice(AI_PLAN_NEW_REF_PREFIX.length).trim().toLowerCase();
|
|
395
466
|
return plan.create.find((entry)=>entry.name.toLowerCase() === name);
|
|
396
467
|
}
|
|
468
|
+
/** A slug as two spellings of one path compare: lowercase, one leading slash, no trailing one. */ export function aiPlanSlugKey(slug) {
|
|
469
|
+
const path = slug.trim().toLowerCase().replace(/^\/*/, '/').replace(/\/+$/, '');
|
|
470
|
+
return path || '/';
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* The players the plan lists for one output (AGL-3433): a screen by its slug,
|
|
474
|
+
* or a creation by its name. A build admits these and no other.
|
|
475
|
+
*/ export function aiPlanEmbedsFor(plan, target) {
|
|
476
|
+
var _ref;
|
|
477
|
+
const key = 'slug' in target ? aiPlanSlugKey(target.slug) : `${AI_PLAN_NEW_REF_PREFIX}${target.create.trim().toLowerCase()}`;
|
|
478
|
+
return ((_ref = plan == null ? void 0 : plan.embeds) != null ? _ref : []).filter((embed)=>'slug' in target ? !isAiPlanNewRef(embed.where) && aiPlanSlugKey(embed.where) === key : embed.where.trim().toLowerCase() === key);
|
|
479
|
+
}
|
|
480
|
+
/** The host a video link plays from, or null for a link neither host serves. */ export function aiEmbedHostOf(url) {
|
|
481
|
+
try {
|
|
482
|
+
const parsed = new URL(url.trim());
|
|
483
|
+
if (parsed.protocol !== 'https:') return null;
|
|
484
|
+
const host = parsed.hostname.replace(/^(?:www|m)\./, '');
|
|
485
|
+
if (host === 'youtu.be' || host === 'youtube.com' || host === 'youtube-nocookie.com') {
|
|
486
|
+
return 'youtube';
|
|
487
|
+
}
|
|
488
|
+
if (host === 'vimeo.com' || host === 'player.vimeo.com') return 'vimeo';
|
|
489
|
+
} catch (unused) {
|
|
490
|
+
/* not a link */ }
|
|
491
|
+
return null;
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* One video however its link is spelled — `youtu.be/<id>`, a watch link, an
|
|
495
|
+
* embed or a Shorts link — as `<host>:<id>`, the way the Video embed element
|
|
496
|
+
* reads a link; null for a link it cannot play.
|
|
497
|
+
*/ export function aiEmbedVideoKey(url) {
|
|
498
|
+
var _parsed_searchParams_get;
|
|
499
|
+
const host = aiEmbedHostOf(url);
|
|
500
|
+
if (!host) return null;
|
|
501
|
+
const parsed = new URL(url.trim());
|
|
502
|
+
const parts = parsed.pathname.split('/').filter(Boolean);
|
|
503
|
+
const id = host === 'vimeo' ? parts.find((part)=>/^\d{6,}$/.test(part)) : parsed.hostname.endsWith('youtu.be') ? parts[0] : (_parsed_searchParams_get = parsed.searchParams.get('v')) != null ? _parsed_searchParams_get : parts[0] === 'embed' || parts[0] === 'shorts' ? parts[1] : undefined;
|
|
504
|
+
return id && /^[\w-]{6,20}$/.test(id) ? `${host}:${id}` : null;
|
|
505
|
+
}
|
|
397
506
|
/**
|
|
398
507
|
* Every `new:<name>` reference the plan's create list does not carry, in plan
|
|
399
508
|
* order: each screen's layout, then its template, then what its sections
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-build-plan.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { AiTool } from '../providers/contract'\n\n/**\n * The plan a generation job builds from (AGL-2935): what the job's plan step\n * (`jobs/ai-job-plan-step.ts`) returns, before a single node is generated.\n *\n * This module is the plan's MODEL — its shape, its tool schema and its\n * reader — and imports nothing at runtime, so the doctrine validators, the\n * plan step, the job document and the proposal can all read it without\n * importing each other.\n *\n * A plan is REFERENCES, never documents (AGL-2937). It names what the site\n * already has by inventory id, what the job will create by a short name, and\n * which screens it builds from which of those — so the doctrine can be held\n * against the plan while fixing it costs a sentence, not a regenerated page,\n * and the customer reads what the job will do before it spends a credit on\n * doing it.\n *\n * `new:<name>` is how an entry refers to something the plan itself creates;\n * any other reference is an id from the site inventory.\n */\n\nexport const AI_PLAN_NEW_REF_PREFIX = 'new:'\n\n/** Records a plan may reuse, by the inventory list they come from. */\nexport type AiBuildPlanReuseKind =\n | 'component'\n | 'layout'\n | 'template'\n | 'form'\n | 'dataset'\n | 'collection'\n | 'screen'\n | 'theme'\n\nexport const AI_BUILD_PLAN_REUSE_KINDS: readonly AiBuildPlanReuseKind[] = [\n 'component',\n 'layout',\n 'template',\n 'form',\n 'dataset',\n 'collection',\n 'screen',\n 'theme',\n]\n\n/** What a plan may create. A theme change is a draft through the theme door, never a literal. */\nexport type AiBuildPlanCreateKind =\n | 'component'\n | 'layout'\n | 'template'\n | 'form'\n | 'theme-change'\n | 'dataset'\n | 'email'\n\n/**\n * What each creation is called in a sentence, and where a member makes one.\n *\n * Total over the union deliberately: a refusal that cannot name a creation is\n * a refusal a member cannot act on, so widening `AiBuildPlanCreateKind`\n * without saying where the new thing is made does not compile.\n */\nexport const AI_BUILD_PLAN_CREATION_NOUNS: Record<\n AiBuildPlanCreateKind,\n { noun: string; where: string }\n> = {\n component: { noun: 'component', where: 'on the Components page' },\n form: { noun: 'form', where: 'on the Forms page' },\n layout: { noun: 'layout', where: 'on the Layouts page' },\n template: { noun: 'template', where: 'in the Templates library' },\n 'theme-change': { noun: 'theme change', where: 'in the Theme section' },\n dataset: { noun: 'dataset', where: 'on the Datasets page' },\n email: { noun: 'email design', where: 'in Emails → Templates' },\n}\n\nexport const AI_BUILD_PLAN_CREATE_KINDS: readonly AiBuildPlanCreateKind[] = [\n 'component',\n 'layout',\n 'template',\n 'form',\n 'theme-change',\n 'dataset',\n 'email',\n]\n\nexport interface AiBuildPlanReuse {\n kind: AiBuildPlanReuseKind\n /** The inventory id. */\n id: string\n /** What the plan uses it for, in a few words. */\n purpose: string\n}\n\nexport interface AiBuildPlanCreate {\n kind: AiBuildPlanCreateKind\n /** Unique within the plan; referenced as `new:<name>`. */\n name: string\n /** Why nothing the site has will do — shown in the proposal. */\n why: string\n /** The inventory id this starts from as a duplicate, or null. */\n duplicateOf: string | null\n /**\n * A component: its props as `name:type`, an icon as `name:icon`\n * (AGL-3054). A layout: the regions it has, from the closed vocabulary\n * `AI_LAYOUT_REGIONS` holds (AGL-3024) — a layout has no props, and a\n * region named here is what the built layout is held to. A template: the\n * binding tokens its page shows, from the subject's own closed catalog\n * (`AI_TEMPLATE_SUBJECT_DEFINITIONS[subject].tokens`, AGL-3143) — the\n * template's analogue of a layout's regions, and what the built template is\n * held to. A form or a dataset: its field names. A theme change: the\n * palette paths it adds or changes.\n */\n fields: string[]\n /**\n * The id the draft this creation becomes is written under, where a unit of\n * the job builds it (AGL-3079). Minted when the job keeps the plan, never\n * part of the model's answer.\n */\n id?: string\n}\n\nexport interface AiBuildPlanSection {\n /** A few words: \"hero\", \"services grid\", \"contact form\". */\n name: string\n /** What the section places: inventory ids or `new:<name>` references. */\n uses: string[]\n /** How many repeated items it shows (cards, rows, tiles); 0 when none. */\n items: number\n}\n\nexport interface AiBuildPlanScreen {\n title: string\n slug: string\n /** The layout it renders inside. */\n layout: string | null\n /** The template it applies, or null for a one-off page. */\n template: string | null\n /** The inventory screen it starts from as a duplicate, or null. */\n duplicateOf: string | null\n /** Whether the site navigation gains an entry for it. */\n nav: boolean\n seoTitle: string\n seoDescription: string\n sections: AiBuildPlanSection[]\n /**\n * The id the draft screen is written under, where a unit of the job builds\n * it (AGL-3079). Minted when the job keeps the plan, never part of the\n * model's answer.\n */\n id?: string\n}\n\nexport interface AiBuildPlan {\n reuse: AiBuildPlanReuse[]\n create: AiBuildPlanCreate[]\n screens: AiBuildPlanScreen[]\n}\n\n/**\n * How much one plan may hold. A job builds what one proposal can describe\n * and one person can read before confirming; a brief asking for more is\n * several jobs.\n */\nexport const AI_BUILD_PLAN_LIMITS = {\n reuse: 40,\n create: 20,\n screens: 12,\n sections: 16,\n uses: 8,\n fields: 30,\n text: 200,\n seoDescription: 320,\n items: 500,\n} as const\n\nconst string = (description: string) => ({ type: 'string', description })\nconst nullableString = (description: string) => ({\n anyOf: [{ type: 'string' }, { type: 'null' }],\n description,\n})\nconst strings = (description: string) => ({\n type: 'array',\n items: { type: 'string' },\n description,\n})\n\n/**\n * The ceiling `parseAiBuildPlan` cuts a field's text at, in words (AGL-3022).\n * The reader repairs an overlong value by cutting it, and a model never told\n * where the cut falls writes a rationale that stops mid-sentence there.\n */\nconst atMost = (limit: number = AI_BUILD_PLAN_LIMITS.text) => `at most ${limit} characters`\n\n/**\n * The plan as a strict tool's input schema. Only the keywords strict\n * structured output accepts: every object closes its properties and lists\n * every one as required, an optional value is a `null` branch, and no\n * length or count bound appears as a keyword — those are enforced by\n * `parseAiBuildPlan`, which every answer passes through anyway.\n *\n * A text ceiling is stated in words instead, on each field the model writes.\n * A reference is copied from the inventory or from a creation's name, so its\n * length is not the model's to choose. A search title and description are\n * held by rule 10 to ceilings far inside the reader's, which the doctrine\n * states, so naming the reader's here would contradict the rule.\n */\nexport const AI_BUILD_PLAN_TOOL: AiTool = {\n name: 'submit_build_plan',\n description:\n 'Submit the build plan: what the site already has that this job reuses, what it creates and why, and the screens it builds from them.',\n strict: true,\n inputSchema: {\n type: 'object',\n additionalProperties: false,\n required: ['reuse', 'create', 'screens'],\n properties: {\n reuse: {\n type: 'array',\n description: 'Records the site already has that this plan uses, by inventory id.',\n items: {\n type: 'object',\n additionalProperties: false,\n required: ['kind', 'id', 'purpose'],\n properties: {\n kind: { type: 'string', enum: [...AI_BUILD_PLAN_REUSE_KINDS] },\n id: string('The id from the site inventory.'),\n purpose: string(`What the plan uses it for, in a few words, ${atMost()}.`),\n },\n },\n },\n create: {\n type: 'array',\n description:\n 'What the plan creates. Creating is the exception: each entry says why nothing listed will do.',\n items: {\n type: 'object',\n additionalProperties: false,\n required: ['kind', 'name', 'why', 'duplicateOf', 'fields'],\n properties: {\n kind: { type: 'string', enum: [...AI_BUILD_PLAN_CREATE_KINDS] },\n name: string(\n `Unique within the plan, ${atMost()}; other entries refer to it as new:<name>.`,\n ),\n why: string(`Why nothing the site already has will do, in one sentence of ${atMost()}.`),\n duplicateOf: nullableString(\n 'The inventory id this starts from as a duplicate, or null.',\n ),\n fields: strings(\n `A component: its props as name:type, an icon as name:icon. A layout: the regions it has, each one of header, nav, sidebar, main, footer — list every region the finished layout has, not only the ones it gains. A form or dataset: its field names. A theme change: the palette paths it adds or changes. Each ${atMost()}.`,\n ),\n },\n },\n },\n screens: {\n type: 'array',\n description: 'The screens the job builds, each a new draft.',\n items: {\n type: 'object',\n additionalProperties: false,\n required: [\n 'title',\n 'slug',\n 'layout',\n 'template',\n 'duplicateOf',\n 'nav',\n 'seoTitle',\n 'seoDescription',\n 'sections',\n ],\n properties: {\n title: string(`The screen name, ${atMost()}.`),\n slug: string(\n `The path, ${atMost()}: lowercase words joined by hyphens, e.g. /services/roof-repair.`,\n ),\n layout: nullableString(\n 'The layout it renders inside: an inventory id or new:<name>.',\n ),\n template: nullableString(\n 'The template it applies (inventory id or new:<name>), or null for a one-off page.',\n ),\n duplicateOf: nullableString(\n 'The inventory screen it starts from as a duplicate, or null.',\n ),\n nav: {\n type: 'boolean',\n description: 'Whether the site navigation gains an entry for it.',\n },\n seoTitle: string('The search-result title.'),\n seoDescription: string('The search-result description.'),\n sections: {\n type: 'array',\n description: 'The sections, top to bottom.',\n items: {\n type: 'object',\n additionalProperties: false,\n required: ['name', 'uses', 'items'],\n properties: {\n name: string(`A few words, ${atMost()}: hero, services grid, contact form.`),\n uses: strings('What the section places: inventory ids or new:<name>.'),\n items: {\n type: 'integer',\n description: 'How many repeated items it shows; 0 when none.',\n },\n },\n },\n },\n },\n },\n },\n },\n },\n}\n\nexport type AiBuildPlanParse =\n | { ok: true; plan: AiBuildPlan; repairs: string[] }\n | { ok: false; error: string }\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nclass PlanShapeError extends Error {}\n\n/**\n * Read a plan out of a tool call, or name what makes it unreadable.\n *\n * Structure is refused — a missing list, an unknown kind, more screens than\n * a job holds, two creations under one name — because a plan that reads\n * differently from what the model meant is worse than a re-ask. Length is\n * repaired: copy past a ceiling is cut and the cut is listed.\n */\nexport function parseAiBuildPlan(input: unknown): AiBuildPlanParse {\n const repairs: string[] = []\n const text = (\n value: unknown,\n path: string,\n limit: number = AI_BUILD_PLAN_LIMITS.text,\n ): string => {\n if (typeof value !== 'string') throw new PlanShapeError(`${path} is not text`)\n const trimmed = value.trim()\n if (trimmed.length > limit) {\n repairs.push(`${path} was over ${limit} characters; truncated`)\n // On the last word break inside the ceiling, not through a word: a\n // rationale is read by the member confirming the plan, and half a word\n // reads as a broken answer rather than a long one (AGL-3022). A run of\n // `limit` characters with no break in it still has to be cut where the\n // ceiling falls.\n const cut = trimmed.slice(0, limit)\n const brk = cut.search(/\\s\\S*$/)\n return (brk > 0 ? cut.slice(0, brk) : cut).trimEnd()\n }\n return trimmed\n }\n const nullable = (value: unknown, path: string): string | null =>\n value === null || value === undefined || value === '' ? null : text(value, path) || null\n const list = (value: unknown, path: string, limit: number): unknown[] => {\n if (!Array.isArray(value)) throw new PlanShapeError(`${path} is not a list`)\n if (value.length > limit) {\n throw new PlanShapeError(`${path} lists ${value.length}; one plan holds at most ${limit}`)\n }\n return value\n }\n const record = (value: unknown, path: string): Record<string, unknown> => {\n if (!isRecord(value)) throw new PlanShapeError(`${path} is not an object`)\n return value\n }\n const oneOf = <T extends string>(value: unknown, allowed: readonly T[], path: string): T => {\n if (typeof value !== 'string' || !(allowed as readonly string[]).includes(value)) {\n throw new PlanShapeError(`${path} is not one of ${allowed.join(', ')}`)\n }\n return value as T\n }\n\n try {\n const root = record(input, 'plan')\n const reuse = list(root['reuse'], 'reuse', AI_BUILD_PLAN_LIMITS.reuse).map((raw, index) => {\n const entry = record(raw, `reuse[${index}]`)\n const id = text(entry['id'], `reuse[${index}].id`)\n if (!id) throw new PlanShapeError(`reuse[${index}].id is empty`)\n return {\n kind: oneOf(entry['kind'], AI_BUILD_PLAN_REUSE_KINDS, `reuse[${index}].kind`),\n id,\n purpose: text(entry['purpose'], `reuse[${index}].purpose`),\n }\n })\n const names = new Set<string>()\n const create = list(root['create'], 'create', AI_BUILD_PLAN_LIMITS.create).map(\n (raw, index) => {\n const entry = record(raw, `create[${index}]`)\n const name = text(entry['name'], `create[${index}].name`)\n if (!name) throw new PlanShapeError(`create[${index}].name is empty`)\n const key = name.toLowerCase()\n if (names.has(key)) {\n throw new PlanShapeError(`create[${index}].name \"${name}\" is used twice`)\n }\n names.add(key)\n return {\n kind: oneOf(entry['kind'], AI_BUILD_PLAN_CREATE_KINDS, `create[${index}].kind`),\n name,\n why: text(entry['why'], `create[${index}].why`),\n duplicateOf: nullable(entry['duplicateOf'], `create[${index}].duplicateOf`),\n fields: list(entry['fields'] ?? [], `create[${index}].fields`, AI_BUILD_PLAN_LIMITS.fields)\n .map((field, fieldIndex) => text(field, `create[${index}].fields[${fieldIndex}]`))\n .filter(Boolean),\n }\n },\n )\n const screens = list(root['screens'], 'screens', AI_BUILD_PLAN_LIMITS.screens).map(\n (raw, index) => {\n const entry = record(raw, `screens[${index}]`)\n if (typeof entry['nav'] !== 'boolean') {\n throw new PlanShapeError(`screens[${index}].nav is not true or false`)\n }\n return {\n title: text(entry['title'], `screens[${index}].title`),\n slug: text(entry['slug'], `screens[${index}].slug`),\n layout: nullable(entry['layout'], `screens[${index}].layout`),\n template: nullable(entry['template'], `screens[${index}].template`),\n duplicateOf: nullable(entry['duplicateOf'], `screens[${index}].duplicateOf`),\n nav: entry['nav'],\n seoTitle: text(entry['seoTitle'], `screens[${index}].seoTitle`),\n seoDescription: text(\n entry['seoDescription'],\n `screens[${index}].seoDescription`,\n AI_BUILD_PLAN_LIMITS.seoDescription,\n ),\n sections: list(\n entry['sections'],\n `screens[${index}].sections`,\n AI_BUILD_PLAN_LIMITS.sections,\n ).map((rawSection, sectionIndex) => {\n const path = `screens[${index}].sections[${sectionIndex}]`\n const section = record(rawSection, path)\n const items = Number(section['items'] ?? 0)\n if (!Number.isInteger(items) || items < 0 || items > AI_BUILD_PLAN_LIMITS.items) {\n throw new PlanShapeError(`${path}.items is not a count`)\n }\n return {\n name: text(section['name'], `${path}.name`),\n uses: list(section['uses'] ?? [], `${path}.uses`, AI_BUILD_PLAN_LIMITS.uses)\n .map((ref, refIndex) => text(ref, `${path}.uses[${refIndex}]`))\n .filter(Boolean),\n items,\n }\n }),\n }\n },\n )\n return { ok: true, plan: { reuse, create, screens }, repairs }\n } catch (error) {\n if (error instanceof PlanShapeError) return { ok: false, error: error.message }\n throw error\n }\n}\n\n/** Whether a reference names something the plan creates. */\nexport function isAiPlanNewRef(ref: string | null | undefined): ref is string {\n return typeof ref === 'string' && ref.startsWith(AI_PLAN_NEW_REF_PREFIX)\n}\n\n/** The creation a `new:<name>` reference names, case-insensitively. */\nexport function aiPlanCreateFor(\n plan: AiBuildPlan,\n ref: string,\n): AiBuildPlanCreate | undefined {\n if (!isAiPlanNewRef(ref)) return undefined\n const name = ref.slice(AI_PLAN_NEW_REF_PREFIX.length).trim().toLowerCase()\n return plan.create.find((entry) => entry.name.toLowerCase() === name)\n}\n\n/**\n * A `new:<name>` reference that names no entry of the plan's create list: a\n * screen's own layout or template, or what one of its sections places, with\n * that section's index.\n */\nexport type AiPlanUndeclaredRef = {\n /** The name the reference gives the creation, as written after `new:`. */\n name: string\n /** Where the plan writes it: `screens[0].template`, `screens[0].sections[4].uses[0]`. */\n path: string\n /** The screen that writes it, by index. */\n screenIndex: number\n} & ({ field: 'layout' | 'template'; sectionIndex: null } | { field: 'uses'; sectionIndex: number })\n\n/**\n * Every `new:<name>` reference the plan's create list does not carry, in plan\n * order: each screen's layout, then its template, then what its sections\n * place. A plan that places a creation it never declares has nothing to build\n * it from, so the plan rules refuse each (AGL-3040), and the page and\n * scaffold doors still name one in a plan confirmed before they did.\n */\nexport function aiPlanUndeclaredRefs(plan: AiBuildPlan): AiPlanUndeclaredRef[] {\n const found: AiPlanUndeclaredRef[] = []\n const undeclared = (ref: string | null): ref is string =>\n isAiPlanNewRef(ref) && !aiPlanCreateFor(plan, ref)\n const nameOf = (ref: string) => ref.slice(AI_PLAN_NEW_REF_PREFIX.length).trim()\n plan.screens.forEach((screen, screenIndex) => {\n for (const field of ['layout', 'template'] as const) {\n const ref = screen[field]\n if (!undeclared(ref)) continue\n found.push({\n name: nameOf(ref),\n path: `screens[${screenIndex}].${field}`,\n screenIndex,\n field,\n sectionIndex: null,\n })\n }\n screen.sections.forEach((section, sectionIndex) => {\n section.uses.forEach((ref, useIndex) => {\n if (!undeclared(ref)) return\n found.push({\n name: nameOf(ref),\n path: `screens[${screenIndex}].sections[${sectionIndex}].uses[${useIndex}]`,\n screenIndex,\n field: 'uses',\n sectionIndex,\n })\n })\n })\n })\n return found\n}\n"],"names":["AI_PLAN_NEW_REF_PREFIX","AI_BUILD_PLAN_REUSE_KINDS","AI_BUILD_PLAN_CREATION_NOUNS","component","noun","where","form","layout","template","dataset","email","AI_BUILD_PLAN_CREATE_KINDS","AI_BUILD_PLAN_LIMITS","reuse","create","screens","sections","uses","fields","text","seoDescription","items","string","description","type","nullableString","anyOf","strings","atMost","limit","AI_BUILD_PLAN_TOOL","name","strict","inputSchema","additionalProperties","required","properties","kind","enum","id","purpose","why","duplicateOf","title","slug","nav","seoTitle","isRecord","value","Array","isArray","PlanShapeError","Error","parseAiBuildPlan","input","repairs","path","trimmed","trim","length","push","cut","slice","brk","search","trimEnd","nullable","undefined","list","record","oneOf","allowed","includes","join","root","map","raw","index","entry","names","Set","key","toLowerCase","has","add","field","fieldIndex","filter","Boolean","rawSection","sectionIndex","section","Number","isInteger","ref","refIndex","ok","plan","error","message","isAiPlanNewRef","startsWith","aiPlanCreateFor","find","aiPlanUndeclaredRefs","found","undeclared","nameOf","forEach","screen","screenIndex","useIndex"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-build-plan.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { AiTool } from '../providers/contract'\n\n/**\n * The plan a generation job builds from (AGL-2935): what the job's plan step\n * (`jobs/ai-job-plan-step.ts`) returns, before a single node is generated.\n *\n * This module is the plan's MODEL — its shape, its tool schema and its\n * reader — and imports nothing at runtime, so the doctrine validators, the\n * plan step, the job document and the proposal can all read it without\n * importing each other.\n *\n * A plan is REFERENCES, never documents (AGL-2937). It names what the site\n * already has by inventory id, what the job will create by a short name, and\n * which screens it builds from which of those — so the doctrine can be held\n * against the plan while fixing it costs a sentence, not a regenerated page,\n * and the customer reads what the job will do before it spends a credit on\n * doing it.\n *\n * `new:<name>` is how an entry refers to something the plan itself creates;\n * any other reference is an id from the site inventory.\n */\n\nexport const AI_PLAN_NEW_REF_PREFIX = 'new:'\n\n/** Records a plan may reuse, by the inventory list they come from. */\nexport type AiBuildPlanReuseKind =\n | 'component'\n | 'layout'\n | 'template'\n | 'form'\n | 'dataset'\n | 'collection'\n | 'screen'\n | 'theme'\n\nexport const AI_BUILD_PLAN_REUSE_KINDS: readonly AiBuildPlanReuseKind[] = [\n 'component',\n 'layout',\n 'template',\n 'form',\n 'dataset',\n 'collection',\n 'screen',\n 'theme',\n]\n\n/** What a plan may create. A theme change is a draft through the theme door, never a literal. */\nexport type AiBuildPlanCreateKind =\n | 'component'\n | 'layout'\n | 'template'\n | 'form'\n | 'theme-change'\n | 'dataset'\n | 'email'\n\n/**\n * What each creation is called in a sentence, and where a member makes one.\n *\n * Total over the union deliberately: a refusal that cannot name a creation is\n * a refusal a member cannot act on, so widening `AiBuildPlanCreateKind`\n * without saying where the new thing is made does not compile.\n */\nexport const AI_BUILD_PLAN_CREATION_NOUNS: Record<\n AiBuildPlanCreateKind,\n { noun: string; where: string }\n> = {\n component: { noun: 'component', where: 'on the Components page' },\n form: { noun: 'form', where: 'on the Forms page' },\n layout: { noun: 'layout', where: 'on the Layouts page' },\n template: { noun: 'template', where: 'in the Templates library' },\n 'theme-change': { noun: 'theme change', where: 'in the Theme section' },\n dataset: { noun: 'dataset', where: 'on the Datasets page' },\n email: { noun: 'email design', where: 'in Emails → Templates' },\n}\n\nexport const AI_BUILD_PLAN_CREATE_KINDS: readonly AiBuildPlanCreateKind[] = [\n 'component',\n 'layout',\n 'template',\n 'form',\n 'theme-change',\n 'dataset',\n 'email',\n]\n\nexport interface AiBuildPlanReuse {\n kind: AiBuildPlanReuseKind\n /** The inventory id. */\n id: string\n /** What the plan uses it for, in a few words. */\n purpose: string\n}\n\nexport interface AiBuildPlanCreate {\n kind: AiBuildPlanCreateKind\n /** Unique within the plan; referenced as `new:<name>`. */\n name: string\n /** Why nothing the site has will do — shown in the proposal. */\n why: string\n /** The inventory id this starts from as a duplicate, or null. */\n duplicateOf: string | null\n /**\n * A component: its props as `name:type`, an icon as `name:icon`\n * (AGL-3054). A layout: the regions it has, from the closed vocabulary\n * `AI_LAYOUT_REGIONS` holds (AGL-3024) — a layout has no props, and a\n * region named here is what the built layout is held to. A template: the\n * binding tokens its page shows, from the subject's own closed catalog\n * (`AI_TEMPLATE_SUBJECT_DEFINITIONS[subject].tokens`, AGL-3143) — the\n * template's analogue of a layout's regions, and what the built template is\n * held to. A form or a dataset: its field names. A theme change: the\n * palette paths it adds or changes.\n */\n fields: string[]\n /**\n * The id the draft this creation becomes is written under, where a unit of\n * the job builds it (AGL-3079). Minted when the job keeps the plan, never\n * part of the model's answer.\n */\n id?: string\n}\n\nexport interface AiBuildPlanSection {\n /** A few words: \"hero\", \"services grid\", \"contact form\". */\n name: string\n /** What the section places: inventory ids or `new:<name>` references. */\n uses: string[]\n /** How many repeated items it shows (cards, rows, tiles); 0 when none. */\n items: number\n}\n\nexport interface AiBuildPlanScreen {\n title: string\n slug: string\n /** The layout it renders inside. */\n layout: string | null\n /** The template it applies, or null for a one-off page. */\n template: string | null\n /** The inventory screen it starts from as a duplicate, or null. */\n duplicateOf: string | null\n /** Whether the site navigation gains an entry for it. */\n nav: boolean\n seoTitle: string\n seoDescription: string\n sections: AiBuildPlanSection[]\n /**\n * The id the draft screen is written under, where a unit of the job builds\n * it (AGL-3079). Minted when the job keeps the plan, never part of the\n * model's answer.\n */\n id?: string\n}\n\n/** The third-party players a plan may embed: the hosts the Video embed element plays. */\nexport type AiBuildPlanEmbedHost = 'youtube' | 'vimeo'\n\nexport const AI_BUILD_PLAN_EMBED_HOSTS: readonly AiBuildPlanEmbedHost[] = ['youtube', 'vimeo']\n\n/** What each host is called in a sentence. */\nexport const AI_BUILD_PLAN_EMBED_HOST_NAMES: Record<AiBuildPlanEmbedHost, string> = {\n youtube: 'YouTube',\n vimeo: 'Vimeo',\n}\n\n/**\n * A third-party player the brief asks for (rule 16, AGL-3433). It loads its\n * host's code on the page that carries it, so it is planned, where the member\n * reads that cost before confirming, and a build admits only what the\n * confirmed plan lists.\n */\nexport interface AiBuildPlanEmbed {\n host: AiBuildPlanEmbedHost\n /** Where it plays: a screen of the plan by its slug, or `new:<name>` for a component the plan creates. */\n where: string\n /** The brief's own words asking for it, which the plan step finds in the brief. */\n asked: string\n /** The video's link as the brief gives it, or null for the site owner to paste. */\n url: string | null\n}\n\nexport interface AiBuildPlan {\n reuse: AiBuildPlanReuse[]\n create: AiBuildPlanCreate[]\n screens: AiBuildPlanScreen[]\n /**\n * The third-party players the brief asks for. Absent is none: a plan kept\n * before the list existed, and every plan whose brief asks for no player.\n */\n embeds?: AiBuildPlanEmbed[]\n}\n\n/**\n * How much one plan may hold. A job builds what one proposal can describe\n * and one person can read before confirming; a brief asking for more is\n * several jobs.\n */\nexport const AI_BUILD_PLAN_LIMITS = {\n reuse: 40,\n create: 20,\n screens: 12,\n sections: 16,\n uses: 8,\n fields: 30,\n embeds: 4,\n text: 200,\n seoDescription: 320,\n items: 500,\n} as const\n\nconst string = (description: string) => ({ type: 'string', description })\nconst nullableString = (description: string) => ({\n anyOf: [{ type: 'string' }, { type: 'null' }],\n description,\n})\nconst strings = (description: string) => ({\n type: 'array',\n items: { type: 'string' },\n description,\n})\n\n/**\n * The ceiling `parseAiBuildPlan` cuts a field's text at, in words (AGL-3022).\n * The reader repairs an overlong value by cutting it, and a model never told\n * where the cut falls writes a rationale that stops mid-sentence there.\n */\nconst atMost = (limit: number = AI_BUILD_PLAN_LIMITS.text) => `at most ${limit} characters`\n\n/**\n * The plan as a strict tool's input schema. Only the keywords strict\n * structured output accepts: every object closes its properties and lists\n * every one as required, an optional value is a `null` branch, and no\n * length or count bound appears as a keyword — those are enforced by\n * `parseAiBuildPlan`, which every answer passes through anyway.\n *\n * A text ceiling is stated in words instead, on each field the model writes.\n * A reference is copied from the inventory or from a creation's name, so its\n * length is not the model's to choose. A search title and description are\n * held by rule 10 to ceilings far inside the reader's, which the doctrine\n * states, so naming the reader's here would contradict the rule.\n */\nexport const AI_BUILD_PLAN_TOOL: AiTool = {\n name: 'submit_build_plan',\n description:\n 'Submit the build plan: what the site already has that this job reuses, what it creates and why, and the pages it builds from them.',\n strict: true,\n inputSchema: {\n type: 'object',\n additionalProperties: false,\n required: ['reuse', 'create', 'screens'],\n properties: {\n reuse: {\n type: 'array',\n description: 'Records the site already has that this plan uses, by inventory id.',\n items: {\n type: 'object',\n additionalProperties: false,\n required: ['kind', 'id', 'purpose'],\n properties: {\n kind: { type: 'string', enum: [...AI_BUILD_PLAN_REUSE_KINDS] },\n id: string('The id from the site inventory.'),\n purpose: string(`What the plan uses it for, in a few words, ${atMost()}.`),\n },\n },\n },\n create: {\n type: 'array',\n description:\n 'What the plan creates. Creating is the exception: each entry says why nothing listed will do.',\n items: {\n type: 'object',\n additionalProperties: false,\n required: ['kind', 'name', 'why', 'duplicateOf', 'fields'],\n properties: {\n kind: { type: 'string', enum: [...AI_BUILD_PLAN_CREATE_KINDS] },\n name: string(\n `Unique within the plan, ${atMost()}; other entries refer to it as new:<name>.`,\n ),\n why: string(`Why nothing the site already has will do, in one sentence of ${atMost()}.`),\n duplicateOf: nullableString(\n 'The inventory id this starts from as a duplicate, or null.',\n ),\n fields: strings(\n `A component: its props as name:type, an icon as name:icon. A layout: the regions it has, each one of header, nav, sidebar, main, footer — list every region the finished layout has, not only the ones it gains. A form or dataset: its field names. A theme change: the palette paths it adds or changes. Each ${atMost()}.`,\n ),\n },\n },\n },\n screens: {\n type: 'array',\n description: 'The pages the job builds, each a new draft.',\n items: {\n type: 'object',\n additionalProperties: false,\n required: [\n 'title',\n 'slug',\n 'layout',\n 'template',\n 'duplicateOf',\n 'nav',\n 'seoTitle',\n 'seoDescription',\n 'sections',\n ],\n properties: {\n title: string(`The page name, ${atMost()}.`),\n slug: string(\n `The path, ${atMost()}: lowercase words joined by hyphens, e.g. /services/roof-repair.`,\n ),\n layout: nullableString(\n 'The layout it renders inside: an inventory id or new:<name>.',\n ),\n template: nullableString(\n 'The template it applies (inventory id or new:<name>), or null for a one-off page.',\n ),\n duplicateOf: nullableString(\n 'The inventory page it starts from as a duplicate, or null.',\n ),\n nav: {\n type: 'boolean',\n description: 'Whether the site navigation gains an entry for it.',\n },\n seoTitle: string('The search-result title.'),\n seoDescription: string('The search-result description.'),\n sections: {\n type: 'array',\n description: 'The sections, top to bottom.',\n items: {\n type: 'object',\n additionalProperties: false,\n required: ['name', 'uses', 'items'],\n properties: {\n name: string(`A few words, ${atMost()}: hero, services grid, contact form.`),\n uses: strings('What the section places: inventory ids or new:<name>.'),\n items: {\n type: 'integer',\n description: 'How many repeated items it shows; 0 when none.',\n },\n },\n },\n },\n },\n },\n },\n },\n },\n}\n\n/** Words that ask for a video, or a link to one. */\nexport const AI_PLAN_ASKS_FOR_VIDEO = /\\b(?:videos?|films?|clips?|youtube|vimeo|player)\\b|youtu\\.be/i\n\nconst EMBEDS_SCHEMA = {\n type: 'array',\n description:\n 'A YouTube or Vimeo player the brief asks for, on a screen or a created component. Empty unless the brief asks; a library film is a Video.',\n items: {\n type: 'object',\n additionalProperties: false,\n required: ['host', 'where', 'asked', 'url'],\n properties: {\n host: { type: 'string', enum: [...AI_BUILD_PLAN_EMBED_HOSTS] },\n where: string('A screen slug, or new:<name> of a component.'),\n asked: string('The words of the brief that ask for it, copied exactly.'),\n url: nullableString('The video link the brief gives, or null.'),\n },\n },\n}\n\n/**\n * The plan tool with its list of third-party players, offered only to a brief\n * that asks for a video (AGL-3433). Every other plan's request is the plain\n * tool, byte for byte, so the list costs nothing on the plans that cannot use\n * it, and a model answering a brief that names no video has nowhere to plan a\n * player it was never asked for.\n */\nexport const AI_BUILD_PLAN_EMBEDS_TOOL: AiTool = {\n ...AI_BUILD_PLAN_TOOL,\n inputSchema: {\n ...AI_BUILD_PLAN_TOOL.inputSchema,\n required: [...(AI_BUILD_PLAN_TOOL.inputSchema['required'] as string[]), 'embeds'],\n properties: {\n ...(AI_BUILD_PLAN_TOOL.inputSchema['properties'] as Record<string, unknown>),\n embeds: EMBEDS_SCHEMA,\n },\n },\n}\n\n/** The plan tool a brief is answered with. */\nexport function aiBuildPlanToolFor(brief: string): AiTool {\n return AI_PLAN_ASKS_FOR_VIDEO.test(brief) ? AI_BUILD_PLAN_EMBEDS_TOOL : AI_BUILD_PLAN_TOOL\n}\n\nexport type AiBuildPlanParse =\n | { ok: true; plan: AiBuildPlan; repairs: string[] }\n | { ok: false; error: string }\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nclass PlanShapeError extends Error {}\n\n/**\n * Read a plan out of a tool call, or name what makes it unreadable.\n *\n * Structure is refused — a missing list, an unknown kind, more screens than\n * a job holds, two creations under one name — because a plan that reads\n * differently from what the model meant is worse than a re-ask. Length is\n * repaired: copy past a ceiling is cut and the cut is listed.\n */\nexport function parseAiBuildPlan(input: unknown): AiBuildPlanParse {\n const repairs: string[] = []\n const text = (\n value: unknown,\n path: string,\n limit: number = AI_BUILD_PLAN_LIMITS.text,\n ): string => {\n if (typeof value !== 'string') throw new PlanShapeError(`${path} is not text`)\n const trimmed = value.trim()\n if (trimmed.length > limit) {\n repairs.push(`${path} was over ${limit} characters; truncated`)\n // On the last word break inside the ceiling, not through a word: a\n // rationale is read by the member confirming the plan, and half a word\n // reads as a broken answer rather than a long one (AGL-3022). A run of\n // `limit` characters with no break in it still has to be cut where the\n // ceiling falls.\n const cut = trimmed.slice(0, limit)\n const brk = cut.search(/\\s\\S*$/)\n return (brk > 0 ? cut.slice(0, brk) : cut).trimEnd()\n }\n return trimmed\n }\n const nullable = (value: unknown, path: string): string | null =>\n value === null || value === undefined || value === '' ? null : text(value, path) || null\n const list = (value: unknown, path: string, limit: number): unknown[] => {\n if (!Array.isArray(value)) throw new PlanShapeError(`${path} is not a list`)\n if (value.length > limit) {\n throw new PlanShapeError(`${path} lists ${value.length}; one plan holds at most ${limit}`)\n }\n return value\n }\n const record = (value: unknown, path: string): Record<string, unknown> => {\n if (!isRecord(value)) throw new PlanShapeError(`${path} is not an object`)\n return value\n }\n const oneOf = <T extends string>(value: unknown, allowed: readonly T[], path: string): T => {\n if (typeof value !== 'string' || !(allowed as readonly string[]).includes(value)) {\n throw new PlanShapeError(`${path} is not one of ${allowed.join(', ')}`)\n }\n return value as T\n }\n\n try {\n const root = record(input, 'plan')\n const reuse = list(root['reuse'], 'reuse', AI_BUILD_PLAN_LIMITS.reuse).map((raw, index) => {\n const entry = record(raw, `reuse[${index}]`)\n const id = text(entry['id'], `reuse[${index}].id`)\n if (!id) throw new PlanShapeError(`reuse[${index}].id is empty`)\n return {\n kind: oneOf(entry['kind'], AI_BUILD_PLAN_REUSE_KINDS, `reuse[${index}].kind`),\n id,\n purpose: text(entry['purpose'], `reuse[${index}].purpose`),\n }\n })\n const names = new Set<string>()\n const create = list(root['create'], 'create', AI_BUILD_PLAN_LIMITS.create).map(\n (raw, index) => {\n const entry = record(raw, `create[${index}]`)\n const name = text(entry['name'], `create[${index}].name`)\n if (!name) throw new PlanShapeError(`create[${index}].name is empty`)\n const key = name.toLowerCase()\n if (names.has(key)) {\n throw new PlanShapeError(`create[${index}].name \"${name}\" is used twice`)\n }\n names.add(key)\n return {\n kind: oneOf(entry['kind'], AI_BUILD_PLAN_CREATE_KINDS, `create[${index}].kind`),\n name,\n why: text(entry['why'], `create[${index}].why`),\n duplicateOf: nullable(entry['duplicateOf'], `create[${index}].duplicateOf`),\n fields: list(entry['fields'] ?? [], `create[${index}].fields`, AI_BUILD_PLAN_LIMITS.fields)\n .map((field, fieldIndex) => text(field, `create[${index}].fields[${fieldIndex}]`))\n .filter(Boolean),\n }\n },\n )\n const screens = list(root['screens'], 'screens', AI_BUILD_PLAN_LIMITS.screens).map(\n (raw, index) => {\n const entry = record(raw, `screens[${index}]`)\n if (typeof entry['nav'] !== 'boolean') {\n throw new PlanShapeError(`screens[${index}].nav is not true or false`)\n }\n return {\n title: text(entry['title'], `screens[${index}].title`),\n slug: text(entry['slug'], `screens[${index}].slug`),\n layout: nullable(entry['layout'], `screens[${index}].layout`),\n template: nullable(entry['template'], `screens[${index}].template`),\n duplicateOf: nullable(entry['duplicateOf'], `screens[${index}].duplicateOf`),\n nav: entry['nav'],\n seoTitle: text(entry['seoTitle'], `screens[${index}].seoTitle`),\n seoDescription: text(\n entry['seoDescription'],\n `screens[${index}].seoDescription`,\n AI_BUILD_PLAN_LIMITS.seoDescription,\n ),\n sections: list(\n entry['sections'],\n `screens[${index}].sections`,\n AI_BUILD_PLAN_LIMITS.sections,\n ).map((rawSection, sectionIndex) => {\n const path = `screens[${index}].sections[${sectionIndex}]`\n const section = record(rawSection, path)\n const items = Number(section['items'] ?? 0)\n if (!Number.isInteger(items) || items < 0 || items > AI_BUILD_PLAN_LIMITS.items) {\n throw new PlanShapeError(`${path}.items is not a count`)\n }\n return {\n name: text(section['name'], `${path}.name`),\n uses: list(section['uses'] ?? [], `${path}.uses`, AI_BUILD_PLAN_LIMITS.uses)\n .map((ref, refIndex) => text(ref, `${path}.uses[${refIndex}]`))\n .filter(Boolean),\n items,\n }\n }),\n }\n },\n )\n const embeds = list(root['embeds'] ?? [], 'embeds', AI_BUILD_PLAN_LIMITS.embeds).map(\n (raw, index) => {\n const entry = record(raw, `embeds[${index}]`)\n const where = text(entry['where'], `embeds[${index}].where`)\n if (!where) throw new PlanShapeError(`embeds[${index}].where is empty`)\n return {\n host: oneOf(entry['host'], AI_BUILD_PLAN_EMBED_HOSTS, `embeds[${index}].host`),\n where,\n asked: text(entry['asked'], `embeds[${index}].asked`),\n url: nullable(entry['url'], `embeds[${index}].url`),\n }\n },\n )\n // A plan whose brief asks for no player carries no list, as it did before the list existed.\n return {\n ok: true,\n plan: { reuse, create, screens, ...(embeds.length ? { embeds } : {}) },\n repairs,\n }\n } catch (error) {\n if (error instanceof PlanShapeError) return { ok: false, error: error.message }\n throw error\n }\n}\n\n/** Whether a reference names something the plan creates. */\nexport function isAiPlanNewRef(ref: string | null | undefined): ref is string {\n return typeof ref === 'string' && ref.startsWith(AI_PLAN_NEW_REF_PREFIX)\n}\n\n/** The creation a `new:<name>` reference names, case-insensitively. */\nexport function aiPlanCreateFor(\n plan: AiBuildPlan,\n ref: string,\n): AiBuildPlanCreate | undefined {\n if (!isAiPlanNewRef(ref)) return undefined\n const name = ref.slice(AI_PLAN_NEW_REF_PREFIX.length).trim().toLowerCase()\n return plan.create.find((entry) => entry.name.toLowerCase() === name)\n}\n\n/** A slug as two spellings of one path compare: lowercase, one leading slash, no trailing one. */\nexport function aiPlanSlugKey(slug: string): string {\n const path = slug.trim().toLowerCase().replace(/^\\/*/, '/').replace(/\\/+$/, '')\n return path || '/'\n}\n\n/**\n * The players the plan lists for one output (AGL-3433): a screen by its slug,\n * or a creation by its name. A build admits these and no other.\n */\nexport function aiPlanEmbedsFor(\n plan: Pick<AiBuildPlan, 'embeds'> | null | undefined,\n target: { slug: string } | { create: string },\n): AiBuildPlanEmbed[] {\n const key =\n 'slug' in target\n ? aiPlanSlugKey(target.slug)\n : `${AI_PLAN_NEW_REF_PREFIX}${target.create.trim().toLowerCase()}`\n return (plan?.embeds ?? []).filter((embed) =>\n 'slug' in target\n ? !isAiPlanNewRef(embed.where) && aiPlanSlugKey(embed.where) === key\n : embed.where.trim().toLowerCase() === key,\n )\n}\n\n/** The host a video link plays from, or null for a link neither host serves. */\nexport function aiEmbedHostOf(url: string): AiBuildPlanEmbedHost | null {\n try {\n const parsed = new URL(url.trim())\n if (parsed.protocol !== 'https:') return null\n const host = parsed.hostname.replace(/^(?:www|m)\\./, '')\n if (host === 'youtu.be' || host === 'youtube.com' || host === 'youtube-nocookie.com') {\n return 'youtube'\n }\n if (host === 'vimeo.com' || host === 'player.vimeo.com') return 'vimeo'\n } catch {\n /* not a link */\n }\n return null\n}\n\n/**\n * One video however its link is spelled — `youtu.be/<id>`, a watch link, an\n * embed or a Shorts link — as `<host>:<id>`, the way the Video embed element\n * reads a link; null for a link it cannot play.\n */\nexport function aiEmbedVideoKey(url: string): string | null {\n const host = aiEmbedHostOf(url)\n if (!host) return null\n const parsed = new URL(url.trim())\n const parts = parsed.pathname.split('/').filter(Boolean)\n const id =\n host === 'vimeo'\n ? parts.find((part) => /^\\d{6,}$/.test(part))\n : parsed.hostname.endsWith('youtu.be')\n ? parts[0]\n : (parsed.searchParams.get('v') ??\n (parts[0] === 'embed' || parts[0] === 'shorts' ? parts[1] : undefined))\n return id && /^[\\w-]{6,20}$/.test(id) ? `${host}:${id}` : null\n}\n\n/**\n * A `new:<name>` reference that names no entry of the plan's create list: a\n * screen's own layout or template, or what one of its sections places, with\n * that section's index.\n */\nexport type AiPlanUndeclaredRef = {\n /** The name the reference gives the creation, as written after `new:`. */\n name: string\n /** Where the plan writes it: `screens[0].template`, `screens[0].sections[4].uses[0]`. */\n path: string\n /** The screen that writes it, by index. */\n screenIndex: number\n} & ({ field: 'layout' | 'template'; sectionIndex: null } | { field: 'uses'; sectionIndex: number })\n\n/**\n * Every `new:<name>` reference the plan's create list does not carry, in plan\n * order: each screen's layout, then its template, then what its sections\n * place. A plan that places a creation it never declares has nothing to build\n * it from, so the plan rules refuse each (AGL-3040), and the page and\n * scaffold doors still name one in a plan confirmed before they did.\n */\nexport function aiPlanUndeclaredRefs(plan: AiBuildPlan): AiPlanUndeclaredRef[] {\n const found: AiPlanUndeclaredRef[] = []\n const undeclared = (ref: string | null): ref is string =>\n isAiPlanNewRef(ref) && !aiPlanCreateFor(plan, ref)\n const nameOf = (ref: string) => ref.slice(AI_PLAN_NEW_REF_PREFIX.length).trim()\n plan.screens.forEach((screen, screenIndex) => {\n for (const field of ['layout', 'template'] as const) {\n const ref = screen[field]\n if (!undeclared(ref)) continue\n found.push({\n name: nameOf(ref),\n path: `screens[${screenIndex}].${field}`,\n screenIndex,\n field,\n sectionIndex: null,\n })\n }\n screen.sections.forEach((section, sectionIndex) => {\n section.uses.forEach((ref, useIndex) => {\n if (!undeclared(ref)) return\n found.push({\n name: nameOf(ref),\n path: `screens[${screenIndex}].sections[${sectionIndex}].uses[${useIndex}]`,\n screenIndex,\n field: 'uses',\n sectionIndex,\n })\n })\n })\n })\n return found\n}\n"],"names":["AI_PLAN_NEW_REF_PREFIX","AI_BUILD_PLAN_REUSE_KINDS","AI_BUILD_PLAN_CREATION_NOUNS","component","noun","where","form","layout","template","dataset","email","AI_BUILD_PLAN_CREATE_KINDS","AI_BUILD_PLAN_EMBED_HOSTS","AI_BUILD_PLAN_EMBED_HOST_NAMES","youtube","vimeo","AI_BUILD_PLAN_LIMITS","reuse","create","screens","sections","uses","fields","embeds","text","seoDescription","items","string","description","type","nullableString","anyOf","strings","atMost","limit","AI_BUILD_PLAN_TOOL","name","strict","inputSchema","additionalProperties","required","properties","kind","enum","id","purpose","why","duplicateOf","title","slug","nav","seoTitle","AI_PLAN_ASKS_FOR_VIDEO","EMBEDS_SCHEMA","host","asked","url","AI_BUILD_PLAN_EMBEDS_TOOL","aiBuildPlanToolFor","brief","test","isRecord","value","Array","isArray","PlanShapeError","Error","parseAiBuildPlan","input","repairs","path","trimmed","trim","length","push","cut","slice","brk","search","trimEnd","nullable","undefined","list","record","oneOf","allowed","includes","join","root","map","raw","index","entry","names","Set","key","toLowerCase","has","add","field","fieldIndex","filter","Boolean","rawSection","sectionIndex","section","Number","isInteger","ref","refIndex","ok","plan","error","message","isAiPlanNewRef","startsWith","aiPlanCreateFor","find","aiPlanSlugKey","replace","aiPlanEmbedsFor","target","embed","aiEmbedHostOf","parsed","URL","protocol","hostname","aiEmbedVideoKey","parts","pathname","split","part","endsWith","searchParams","get","aiPlanUndeclaredRefs","found","undeclared","nameOf","forEach","screen","screenIndex","useIndex"],"mappings":"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"}
|