@aglyn/plugins-ai 1.0.0-beta.230 → 1.0.0-beta.232
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/package.json +17 -17
- package/src/lib/activity/ai-activity-actions.js +8 -4
- package/src/lib/activity/ai-activity-actions.js.map +1 -1
- package/src/lib/components/ai-assist-provider-on-host.component.d.ts +66 -0
- package/src/lib/components/ai-assist-provider-on-host.component.js +78 -0
- package/src/lib/components/ai-assist-provider-on-host.component.js.map +1 -0
- package/src/lib/components/ai-assist-provider.component.js +9 -10
- package/src/lib/components/ai-assist-provider.component.js.map +1 -1
- package/src/lib/components/ai-job-follow.component.js +1 -0
- package/src/lib/components/ai-job-follow.component.js.map +1 -1
- package/src/lib/components/ai-job-links.js +13 -0
- package/src/lib/components/ai-job-links.js.map +1 -1
- package/src/lib/components/ai-job-plan.component.js +3 -1
- package/src/lib/components/ai-job-plan.component.js.map +1 -1
- package/src/lib/components/ai-jobs-list-page.component.js +1 -0
- package/src/lib/components/ai-jobs-list-page.component.js.map +1 -1
- package/src/lib/components/ai-media-create.component.d.ts +20 -7
- package/src/lib/components/ai-media-create.component.js +107 -55
- package/src/lib/components/ai-media-create.component.js.map +1 -1
- package/src/lib/components/ai-permissions-on-host.component.d.ts +3 -46
- package/src/lib/components/ai-permissions-on-host.component.js +5 -53
- package/src/lib/components/ai-permissions-on-host.component.js.map +1 -1
- package/src/lib/components/ai-seo-fields-card.component.js +1 -0
- package/src/lib/components/ai-seo-fields-card.component.js.map +1 -1
- package/src/lib/components/ai-site-build-page.component.js +20 -8
- package/src/lib/components/ai-site-build-page.component.js.map +1 -1
- package/src/lib/components/ai-site-memory-card.component.d.ts +32 -0
- package/src/lib/components/ai-site-memory-card.component.js +187 -0
- package/src/lib/components/ai-site-memory-card.component.js.map +1 -0
- package/src/lib/components/ai-site-start-card.component.js +80 -77
- package/src/lib/components/ai-site-start-card.component.js.map +1 -1
- package/src/lib/components/ai-theme-proposal-card.component.js +4 -0
- package/src/lib/components/ai-theme-proposal-card.component.js.map +1 -1
- package/src/lib/components/ai-usage-strip.component.js +1 -0
- package/src/lib/components/ai-usage-strip.component.js.map +1 -1
- package/src/lib/components/assist-panel.component.d.ts +10 -0
- package/src/lib/components/assist-panel.component.js +119 -9
- package/src/lib/components/assist-panel.component.js.map +1 -1
- package/src/lib/components/assist-signals-page.component.js +2 -0
- package/src/lib/components/assist-signals-page.component.js.map +1 -1
- package/src/lib/components/billing-ai-allotments.component.js +3 -0
- package/src/lib/components/billing-ai-allotments.component.js.map +1 -1
- package/src/lib/components/billing-ai-top-users.component.js +1 -0
- package/src/lib/components/billing-ai-top-users.component.js.map +1 -1
- package/src/lib/components/billing-assist-overage-card.component.js +2 -0
- package/src/lib/components/billing-assist-overage-card.component.js.map +1 -1
- package/src/lib/components/member-ai-usage-card.component.js +10 -0
- package/src/lib/components/member-ai-usage-card.component.js.map +1 -1
- package/src/lib/components/staff-org-ai-card.component.js +1 -0
- package/src/lib/components/staff-org-ai-card.component.js.map +1 -1
- package/src/lib/components/staff-org-ai-conversations.component.d.ts +23 -0
- package/src/lib/components/staff-org-ai-conversations.component.js +346 -0
- package/src/lib/components/staff-org-ai-conversations.component.js.map +1 -0
- package/src/lib/jobs/ai-build-capabilities.d.ts +13 -1
- package/src/lib/jobs/ai-build-capabilities.js +101 -7
- package/src/lib/jobs/ai-build-capabilities.js.map +1 -1
- package/src/lib/jobs/ai-build-unit-outcome.js +4 -2
- package/src/lib/jobs/ai-build-unit-outcome.js.map +1 -1
- package/src/lib/jobs/ai-business-profile-prefill.d.ts +29 -0
- package/src/lib/jobs/ai-business-profile-prefill.js +85 -0
- package/src/lib/jobs/ai-business-profile-prefill.js.map +1 -0
- package/src/lib/jobs/ai-job-auto-confirm.d.ts +8 -0
- package/src/lib/jobs/ai-job-auto-confirm.js +30 -6
- package/src/lib/jobs/ai-job-auto-confirm.js.map +1 -1
- package/src/lib/jobs/ai-job-build-step.js +14 -1
- package/src/lib/jobs/ai-job-build-step.js.map +1 -1
- package/src/lib/jobs/ai-job-edit-step.d.ts +225 -0
- package/src/lib/jobs/ai-job-edit-step.js +631 -0
- package/src/lib/jobs/ai-job-edit-step.js.map +1 -0
- package/src/lib/jobs/ai-job-generation.d.ts +8 -0
- package/src/lib/jobs/ai-job-generation.js +13 -2
- package/src/lib/jobs/ai-job-generation.js.map +1 -1
- package/src/lib/jobs/ai-job-insight-step.js +29 -5
- package/src/lib/jobs/ai-job-insight-step.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-language.d.ts +78 -0
- package/src/lib/jobs/ai-job-layout-language.js +200 -0
- package/src/lib/jobs/ai-job-layout-language.js.map +1 -0
- package/src/lib/jobs/ai-job-layout-site-pages.d.ts +13 -2
- package/src/lib/jobs/ai-job-layout-site-pages.js +18 -2
- package/src/lib/jobs/ai-job-layout-site-pages.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-step.d.ts +5 -0
- package/src/lib/jobs/ai-job-layout-step.js +107 -6
- package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
- package/src/lib/jobs/ai-job-logic-step.d.ts +20 -0
- package/src/lib/jobs/ai-job-logic-step.js +87 -6
- package/src/lib/jobs/ai-job-logic-step.js.map +1 -1
- package/src/lib/jobs/ai-job-page-language.d.ts +194 -0
- package/src/lib/jobs/ai-job-page-language.js +418 -0
- package/src/lib/jobs/ai-job-page-language.js.map +1 -0
- package/src/lib/jobs/ai-job-page-sections.d.ts +37 -1
- package/src/lib/jobs/ai-job-page-sections.js +93 -11
- package/src/lib/jobs/ai-job-page-sections.js.map +1 -1
- package/src/lib/jobs/ai-job-page-step.d.ts +4 -4
- package/src/lib/jobs/ai-job-page-step.js +115 -14
- package/src/lib/jobs/ai-job-page-step.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-step.d.ts +20 -6
- package/src/lib/jobs/ai-job-plan-step.js +62 -22
- package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-content.d.ts +163 -0
- package/src/lib/jobs/ai-job-site-content.js +443 -0
- package/src/lib/jobs/ai-job-site-content.js.map +1 -0
- package/src/lib/jobs/ai-job-site-look.d.ts +112 -0
- package/src/lib/jobs/ai-job-site-look.js +322 -0
- package/src/lib/jobs/ai-job-site-look.js.map +1 -0
- package/src/lib/jobs/ai-job-site-step.d.ts +30 -5
- package/src/lib/jobs/ai-job-site-step.js +183 -27
- package/src/lib/jobs/ai-job-site-step.js.map +1 -1
- package/src/lib/jobs/ai-job-workflow-step.js +2 -2
- package/src/lib/jobs/ai-job-workflow-step.js.map +1 -1
- package/src/lib/jobs/ai-jobs-notify.d.ts +6 -1
- package/src/lib/jobs/ai-jobs-notify.js +15 -3
- package/src/lib/jobs/ai-jobs-notify.js.map +1 -1
- package/src/lib/jobs/ai-jobs.d.ts +1 -1
- package/src/lib/jobs/ai-jobs.js +17 -8
- package/src/lib/jobs/ai-jobs.js.map +1 -1
- package/src/lib/jobs/ai-layout-full-height.d.ts +40 -0
- package/src/lib/jobs/ai-layout-full-height.js +82 -0
- package/src/lib/jobs/ai-layout-full-height.js.map +1 -0
- package/src/lib/jobs/ai-layout-site-facts.d.ts +54 -0
- package/src/lib/jobs/ai-layout-site-facts.js +153 -0
- package/src/lib/jobs/ai-layout-site-facts.js.map +1 -0
- package/src/lib/layout-language/ai-layout-compiler.d.ts +154 -0
- package/src/lib/layout-language/ai-layout-compiler.js +1235 -0
- package/src/lib/layout-language/ai-layout-compiler.js.map +1 -0
- package/src/lib/layout-language/ai-layout-copy.d.ts +73 -0
- package/src/lib/layout-language/ai-layout-copy.js +137 -0
- package/src/lib/layout-language/ai-layout-copy.js.map +1 -0
- package/src/lib/layout-language/ai-layout-frame.d.ts +52 -0
- package/src/lib/layout-language/ai-layout-frame.js +458 -0
- package/src/lib/layout-language/ai-layout-frame.js.map +1 -0
- package/src/lib/layout-language/ai-layout-gaps.d.ts +63 -0
- package/src/lib/layout-language/ai-layout-gaps.js +345 -0
- package/src/lib/layout-language/ai-layout-gaps.js.map +1 -0
- package/src/lib/layout-language/ai-layout-language.d.ts +168 -0
- package/src/lib/layout-language/ai-layout-language.js +529 -0
- package/src/lib/layout-language/ai-layout-language.js.map +1 -0
- package/src/lib/layout-language/ai-layout-links.d.ts +80 -0
- package/src/lib/layout-language/ai-layout-links.js +109 -0
- package/src/lib/layout-language/ai-layout-links.js.map +1 -0
- package/src/lib/layout-language/ai-layout-pictures.d.ts +144 -0
- package/src/lib/layout-language/ai-layout-pictures.js +214 -0
- package/src/lib/layout-language/ai-layout-pictures.js.map +1 -0
- package/src/lib/layout-language/ai-layout-store.d.ts +41 -0
- package/src/lib/layout-language/ai-layout-store.js +75 -0
- package/src/lib/layout-language/ai-layout-store.js.map +1 -0
- package/src/lib/layout-language/ai-layout-tree.d.ts +59 -0
- package/src/lib/layout-language/ai-layout-tree.js +84 -0
- package/src/lib/layout-language/ai-layout-tree.js.map +1 -0
- package/src/lib/lazy-widget.d.ts +45 -0
- package/src/lib/lazy-widget.js +53 -0
- package/src/lib/lazy-widget.js.map +1 -0
- package/src/lib/model/ai-build-job.js +2 -1
- package/src/lib/model/ai-build-job.js.map +1 -1
- package/src/lib/model/ai-build-progress.d.ts +20 -3
- package/src/lib/model/ai-build-progress.js +52 -7
- package/src/lib/model/ai-build-progress.js.map +1 -1
- package/src/lib/model/ai-edit-job.d.ts +109 -0
- package/src/lib/model/ai-edit-job.js +157 -0
- package/src/lib/model/ai-edit-job.js.map +1 -0
- package/src/lib/model/ai-host-publish-context.d.ts +51 -0
- package/src/lib/model/ai-host-publish-context.js +104 -0
- package/src/lib/model/ai-host-publish-context.js.map +1 -0
- package/src/lib/model/ai-job-activity.d.ts +22 -0
- package/src/lib/model/ai-job-activity.js +23 -1
- package/src/lib/model/ai-job-activity.js.map +1 -1
- package/src/lib/model/ai-job-failure-copy.d.ts +38 -1
- package/src/lib/model/ai-job-failure-copy.js +56 -1
- package/src/lib/model/ai-job-failure-copy.js.map +1 -1
- package/src/lib/model/ai-job-notice.d.ts +3 -1
- package/src/lib/model/ai-job-notice.js +31 -9
- package/src/lib/model/ai-job-notice.js.map +1 -1
- package/src/lib/model/ai-jobs.types.d.ts +22 -1
- package/src/lib/model/ai-jobs.types.js.map +1 -1
- package/src/lib/model/ai-logic-job.d.ts +18 -1
- package/src/lib/model/ai-logic-job.js +24 -3
- package/src/lib/model/ai-logic-job.js.map +1 -1
- package/src/lib/model/ai-media-credits.d.ts +8 -4
- package/src/lib/model/ai-media-credits.js +11 -4
- package/src/lib/model/ai-media-credits.js.map +1 -1
- package/src/lib/model/ai-media-kinds.d.ts +68 -0
- package/src/lib/model/ai-media-kinds.js +98 -0
- package/src/lib/model/ai-media-kinds.js.map +1 -0
- package/src/lib/model/ai-site-build-progress.d.ts +9 -1
- package/src/lib/model/ai-site-build-progress.js +54 -14
- package/src/lib/model/ai-site-build-progress.js.map +1 -1
- package/src/lib/model/ai-site-context.d.ts +111 -0
- package/src/lib/model/ai-site-context.js +222 -0
- package/src/lib/model/ai-site-context.js.map +1 -0
- package/src/lib/model/ai-site-job.d.ts +18 -2
- package/src/lib/model/ai-site-job.js +21 -5
- package/src/lib/model/ai-site-job.js.map +1 -1
- package/src/lib/model/ai-site-kinds.d.ts +164 -0
- package/src/lib/model/ai-site-kinds.js +1942 -0
- package/src/lib/model/ai-site-kinds.js.map +1 -0
- package/src/lib/model/ai-site-look.d.ts +150 -0
- package/src/lib/model/ai-site-look.js +1236 -0
- package/src/lib/model/ai-site-look.js.map +1 -0
- package/src/lib/model/ai-site-memory.d.ts +80 -0
- package/src/lib/model/ai-site-memory.js +171 -0
- package/src/lib/model/ai-site-memory.js.map +1 -0
- package/src/lib/model/ai-site-start-seo.d.ts +1 -1
- package/src/lib/model/ai-site-start.d.ts +15 -3
- package/src/lib/model/ai-site-start.js +21 -4
- package/src/lib/model/ai-site-start.js.map +1 -1
- package/src/lib/model/assist-follow-up.d.ts +92 -0
- package/src/lib/model/assist-follow-up.js +128 -0
- package/src/lib/model/assist-follow-up.js.map +1 -0
- package/src/lib/plugin-config.js +2 -0
- package/src/lib/plugin-config.js.map +1 -1
- package/src/lib/plugin.js +99 -48
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/providers/anthropic.d.ts +9 -1
- package/src/lib/providers/anthropic.js +143 -9
- package/src/lib/providers/anthropic.js.map +1 -1
- package/src/lib/providers/catalog.d.ts +38 -8
- package/src/lib/providers/catalog.js +106 -14
- package/src/lib/providers/catalog.js.map +1 -1
- package/src/lib/providers/contract.d.ts +29 -0
- package/src/lib/providers/contract.js.map +1 -1
- package/src/lib/providers/image-contract.d.ts +22 -1
- package/src/lib/providers/image-contract.js +40 -1
- package/src/lib/providers/image-contract.js.map +1 -1
- package/src/lib/providers/image-endpoints.d.ts +24 -0
- package/src/lib/providers/image-endpoints.js +28 -0
- package/src/lib/providers/image-endpoints.js.map +1 -0
- package/src/lib/providers/model-choice.js +23 -2
- package/src/lib/providers/model-choice.js.map +1 -1
- package/src/lib/providers/routing.js +18 -0
- package/src/lib/providers/routing.js.map +1 -1
- package/src/lib/providers/vertex-image-endpoint.d.ts +48 -0
- package/src/lib/providers/vertex-image-endpoint.js +80 -0
- package/src/lib/providers/vertex-image-endpoint.js.map +1 -0
- package/src/lib/providers/vertex-image.d.ts +13 -27
- package/src/lib/providers/vertex-image.js +15 -42
- package/src/lib/providers/vertex-image.js.map +1 -1
- package/src/lib/runtime/ai-blog-post-generation.d.ts +62 -0
- package/src/lib/runtime/ai-blog-post-generation.js +89 -0
- package/src/lib/runtime/ai-blog-post-generation.js.map +1 -0
- package/src/lib/runtime/ai-dev-env.d.ts +53 -0
- package/src/lib/runtime/ai-dev-env.js +72 -0
- package/src/lib/runtime/ai-dev-env.js.map +1 -0
- package/src/lib/runtime/ai-dev-replay.d.ts +116 -0
- package/src/lib/runtime/ai-dev-replay.js +313 -0
- package/src/lib/runtime/ai-dev-replay.js.map +1 -0
- package/src/lib/runtime/ai-doctrine-validators.d.ts +82 -0
- package/src/lib/runtime/ai-doctrine-validators.js +436 -4
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-doctrine.js +16 -3
- package/src/lib/runtime/ai-doctrine.js.map +1 -1
- package/src/lib/runtime/ai-eval-live.js +4 -0
- package/src/lib/runtime/ai-eval-live.js.map +1 -1
- package/src/lib/runtime/ai-eval.d.ts +13 -1
- package/src/lib/runtime/ai-eval.js +84 -1
- package/src/lib/runtime/ai-eval.js.map +1 -1
- package/src/lib/runtime/ai-icon-library.d.ts +257 -0
- package/src/lib/runtime/ai-icon-library.js +254 -0
- package/src/lib/runtime/ai-icon-library.js.map +1 -0
- package/src/lib/runtime/ai-live-batch.d.ts +45 -0
- package/src/lib/runtime/ai-live-batch.js +89 -0
- package/src/lib/runtime/ai-live-batch.js.map +1 -0
- package/src/lib/runtime/ai-node-tree.d.ts +9 -0
- package/src/lib/runtime/ai-node-tree.js +30 -4
- package/src/lib/runtime/ai-node-tree.js.map +1 -1
- package/src/lib/runtime/ai-palette.d.ts +6 -0
- package/src/lib/runtime/ai-palette.generated.js +70 -0
- package/src/lib/runtime/ai-palette.generated.js.map +1 -1
- package/src/lib/runtime/ai-palette.js.map +1 -1
- package/src/lib/runtime/ai-runtime.js +46 -2
- package/src/lib/runtime/ai-runtime.js.map +1 -1
- package/src/lib/runtime/site-context.d.ts +58 -0
- package/src/lib/runtime/site-context.js +127 -0
- package/src/lib/runtime/site-context.js.map +1 -0
- package/src/lib/server/ai-admin-conversations.d.ts +22 -0
- package/src/lib/server/ai-admin-conversations.js +197 -0
- package/src/lib/server/ai-admin-conversations.js.map +1 -0
- package/src/lib/server/ai-media-image.d.ts +5 -5
- package/src/lib/server/ai-media-image.js +25 -11
- package/src/lib/server/ai-media-image.js.map +1 -1
- package/src/lib/server/ai-media-raster-prompt.d.ts +34 -0
- package/src/lib/server/ai-media-raster-prompt.js +53 -0
- package/src/lib/server/ai-media-raster-prompt.js.map +1 -0
- package/src/lib/server/assist-chat.d.ts +11 -0
- package/src/lib/server/assist-chat.js +57 -18
- package/src/lib/server/assist-chat.js.map +1 -1
- package/src/lib/server/assist-edit-applied.js +16 -3
- package/src/lib/server/assist-edit-applied.js.map +1 -1
- package/src/lib/server/assist-view-context.d.ts +19 -1
- package/src/lib/server/assist-view-context.js +38 -1
- package/src/lib/server/assist-view-context.js.map +1 -1
- package/src/lib/server.js +10 -0
- package/src/lib/server.js.map +1 -1
- package/src/lib/subprocessors.js +17 -1
- package/src/lib/subprocessors.js.map +1 -1
- package/src/lib/tools/ai-blog-post-tool.d.ts +60 -0
- package/src/lib/tools/ai-blog-post-tool.js +175 -0
- package/src/lib/tools/ai-blog-post-tool.js.map +1 -0
- package/src/lib/tools/ai-workflow-tool.d.ts +6 -2
- package/src/lib/tools/ai-workflow-tool.js +16 -14
- package/src/lib/tools/ai-workflow-tool.js.map +1 -1
- package/src/lib/usage/assist-usage.d.ts +3 -0
- package/src/lib/usage/assist-usage.js +4 -2
- package/src/lib/usage/assist-usage.js.map +1 -1
- package/src/lib/usage/staff-org-ai-conversations.d.ts +103 -0
- package/src/lib/usage/staff-org-ai-conversations.js +152 -0
- package/src/lib/usage/staff-org-ai-conversations.js.map +1 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-workflow-step.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { ACTION_MAX_CONDITIONS, ACTION_MAX_STEPS } from '@aglyn/aglyn/app-utils/site-interactions'\nimport {\n CONTACT_LIFECYCLE_STAGES,\n CONTACT_TAG_MAX_LENGTH,\n CRM_TASK_MAX_DUE_DAYS,\n} from '@aglyn/aglyn/app-utils/crm'\nimport { HOST_EVENT_PAYLOAD_KEYS, type HostEventType } from '@aglyn/aglyn/app-utils/host-events'\nimport { checkEntitlement } from '@aglyn/aglyn/app-utils/plan-entitlements'\nimport { isHostPluginEnabled, isPluginEnabled } from '@aglyn/aglyn/plugin-manager/enabled-plugins'\nimport type { AglynOrgBilling } from '@aglyn/aglyn/foundation/definitions/org-billing.types'\nimport { filterEnabledPluginsByReleaseFlags } from '@aglyn/tenant-data-admin/server/release-flags'\nimport { resolveOrgIdForHost } from '@aglyn/tenant-data-admin/server/organizations'\nimport {\n aiAutomationDraft,\n aiAutomationDraftNote,\n aiAutomationRevisionChanges,\n aiAutomationRevisionNote,\n type AiAutomationDraft,\n type AiAutomationRecords,\n} from '../model/ai-automation-draft'\nimport {\n AI_AUTOMATION_TIMED_OUT_FIELD,\n AI_AUTOMATION_WAIT_MINUTES,\n aiStepLabel,\n} from '../model/ai-automation-format'\nimport {\n aiActionOutline,\n aiActionRecordKinds,\n aiRunOutline,\n aiWorkflowExplanationText,\n aiWorkflowOutline,\n type AiRunRecord,\n} from '../model/ai-automation-outline'\nimport type { AiJobOutput } from '../model/ai-jobs.types'\nimport {\n AI_AUTOMATION_NEED_LABELS,\n AI_AUTOMATION_RESOURCE,\n AI_AUTOMATION_STEP_NEEDS,\n AI_AUTOMATION_STEP_TYPES,\n AI_AUTOMATION_TRIGGER_NEEDS,\n AI_AUTOMATION_TRIGGERS,\n AI_AUTOMATION_UNSUPPORTED_COPY,\n AI_WORKFLOW_GONE_COPY,\n AI_WORKFLOW_NO_DRAFT_COPY,\n AI_WORKFLOW_NO_EXPLANATION_COPY,\n AI_WORKFLOW_NO_SITE_COPY,\n AI_WORKFLOW_RUN_GONE_COPY,\n AI_WORKFLOW_RUN_NOT_FAILED_COPY,\n AI_WORKFLOW_SAVE_FAILURE_COPY,\n AI_WORKFLOW_REVISE_BLOCKER_COPY,\n AI_WORKFLOW_REVISE_WORKFLOW_COPY,\n AI_WORKFLOW_UNAVAILABLE_COPY,\n AI_ORG_AUTOMATION_PLAN_COPY,\n AI_ORG_AUTOMATION_RESOURCE,\n AI_ORG_AUTOMATION_UNAVAILABLE_COPY,\n aiActionReviseBlocker,\n aiAutomationCapabilities,\n aiAutomationTriggerLabel,\n parseAiWorkflowJobInputs,\n type AiAutomationCapabilities,\n type AiAutomationStepType,\n type AiOrgAutomationVocabulary,\n} from '../model/ai-workflow-job'\nimport { AI_STEP_TIERS } from '../providers/catalog'\nimport { AI_ROUTING_TABLE, aiModelForStep } from '../providers/routing'\nimport {\n runValidatedGeneration,\n type AiCustomGenerationInput,\n type AiGenerationCheck,\n} from '../runtime/ai-doctrine'\nimport type { AiSystemBlock } from '../runtime/ai-runtime'\nimport {\n AI_AUTOMATION_TOOL_NAME,\n AI_WORKFLOW_EXPLANATION_TOOL_NAME,\n aiAutomationTool,\n aiWorkflowExplanationTool,\n holdAiAutomationAnswerToVocabulary,\n readAiAutomationAnswer,\n readAiWorkflowExplanation,\n type AiAutomationAnswer,\n type AiWorkflowExplanation,\n} from '../tools/ai-workflow-tool'\nimport { registerAiJobAdmission, type AiJobAdmission, type AiJobAdmissionRefusal } from './ai-job-admission'\nimport { aiJobStepBudget } from './ai-job-budget'\nimport { aiJobDraftId } from './ai-job-draft-ids'\nimport { aiDoctrineReview, aiGenerationSpent, aiLimitReview, aiUnspentOutcome } from './ai-job-generation'\nimport {\n aiPluginDraftAdmissionRefusal,\n aiPluginDraftOwner,\n aiPluginDraftUnavailable,\n aiPluginDraftWriter,\n type AiPluginDraftWriterLookup,\n} from './ai-job-plugin-drafts'\nimport { AI_JOB_BRIEF_MAX_CHARS, type AiJobStepOutcome, type AiJobStepRunner } from './ai-job-text-step'\nimport { registerAiJobStep } from './ai-jobs'\nimport {\n readAiAutomationRecords,\n readAiOrgAutomationRecords,\n readAiWorkflowFunctions,\n readAiWorkflowRun,\n readAiWorkflowTarget,\n} from './ai-workflow-records'\n\n/**\n * The `workflow` step (AGL-2919): an automation drafted from a description,\n * or a saved automation explained.\n *\n * ## Drafting\n *\n * The model is shown the platform's automation vocabulary — every trigger with\n * the fields it carries, every step with the fields it takes — in a cached\n * block, and in the user turn what this workspace can run, the site's forms\n * and datasets by name, and the description. It answers through\n * `submit_automation`, and the answer is held to the vocabulary and to what\n * the workspace can run before anything is written.\n *\n * A record the description names — a list, a campaign, a workflow, a webhook,\n * a dataset, a form, a deal stage — is answered in the description's WORDS,\n * and looked up among the site's records in code, after the answer\n * (`model/ai-automation-draft.ts`). The model is never shown the site's lists,\n * campaigns, workflows, webhooks or pipelines. Words that name no record, or\n * more than one, are kept as a placeholder the person fills in.\n *\n * The automation is written through the workflows plugin's draft writer —\n * that plugin's stored shape, validator, role, plan and cap — keyed by the\n * job's id, and it is written OFF. A run cut off after the write finds its\n * draft and spends nothing.\n *\n * ## Explaining\n *\n * The model is shown an outline of the saved automation, and for a failed run\n * what the run history recorded about it (`model/ai-automation-outline.ts`):\n * never the stored documents, never an email address, never a run's event\n * payload. It answers through `submit_explanation`, which becomes a `text`\n * output. Nothing is changed.\n *\n * ## Revising (AGL-3603)\n *\n * A saved ACTION and a change asked of it. The model is sent the drafting\n * request — the same cached vocabulary, the same tool, the same reach, forms\n * and datasets — with the action's outline in the user turn and the change as\n * the brief, and answers the whole action as it would be after the change.\n * That answer is held to the vocabulary and matched to the site's records as\n * a draft is, and written as a NEW draft, OFF, under the job's draft id: the\n * saved action is read and never written. What changed is worked out in code\n * from the two stored shapes. An action holding what the vocabulary cannot\n * write is refused before the model is asked, so a revision never drops an\n * on-page step the person did not ask to lose.\n *\n * ## A workspace's automation (AGL-3603)\n *\n * A draft with `scope: 'org'` names no site. It is asked as a draft is — the\n * same cached vocabulary, the same tool — with the org automation's narrower\n * vocabulary stated in the user turn and held by the check, and matched\n * against the workspace's lists, campaigns, datasets and stages. Nothing is\n * written: the automation rides on an `orgAutomation` output, which the Org\n * automations editor opens unsaved, where its sites are picked and its save\n * route — the workflows plugin's — holds it to that plugin's rules.\n */\n\n/** The longest an explanation or a draft's generation may take reading the site first. */\nexport const AI_WORKFLOW_RECORDS_READ_MS = 4_000\n\n/**\n * The step's time (AGL-3035): its answer and its re-ask at the routing\n * table's `job.workflow` ceiling on the tier the step is served from. It sends\n * no site inventory block, so it makes no lookup; its own reads — the records\n * a draft is matched against, or the automation and run an explanation\n * reads — are the declared `AI_WORKFLOW_RECORDS_READ_MS`.\n */\nexport const AI_JOB_WORKFLOW_STEP_BUDGET = aiJobStepBudget({\n tier: AI_STEP_TIERS['job.workflow'],\n maxTokens: AI_ROUTING_TABLE['job.workflow'].maxTokens,\n lookups: 0,\n ownReadsMs: AI_WORKFLOW_RECORDS_READ_MS,\n})\n\n/** The least time one workflow step needs before it starts. */\nexport const AI_JOB_WORKFLOW_STEP_MINIMUM_MS = AI_JOB_WORKFLOW_STEP_BUDGET.minimumMs\n\n/** The routing table's answer for `job.workflow`. */\nexport function aiJobWorkflowModel(): string {\n return aiModelForStep('job.workflow')\n}\n\n/** How many forms and datasets the user turn lists, and how many fields a form. */\nexport const AI_WORKFLOW_FORMS_LISTED = 40\nexport const AI_WORKFLOW_FORM_FIELDS_LISTED = 20\nexport const AI_WORKFLOW_DATASETS_LISTED = 40\n\n// ── The vocabulary, for the model ─────────────────────────────────────────\n\nconst STEP_USES: Readonly<Record<AiAutomationStepType, string>> = {\n sendEmail: 'emails the person the event is about. Fields: subject, body (plain text), toField',\n notifyAdmins: 'notifies the site’s admins in the console. Fields: text (what the admins read)',\n enrollList: 'adds the person to an email list. Fields: reference (the list)',\n assignCampaign: 'files the person under a campaign. Fields: reference (the campaign)',\n runWorkflow: 'runs one of the site’s workflows. Fields: reference (the workflow)',\n datasetAppend: 'saves the event as a new record in a dataset. Fields: reference (the dataset)',\n updateDataset: 'updates the event’s record in a dataset. Fields: reference (the dataset)',\n webhookPost: 'sends the event to one of the site’s outbound webhooks. Fields: reference (the webhook)',\n siteAlert: 'shows the visitor who caused the event a short message. Fields: text (the message), severity',\n wait: 'waits before the next step. Fields: minutes',\n waitForEvent: 'waits until something else happens to the same person, or gives up. Fields: event, minutes',\n exitFlow: 'ends the automation here; with a when it is a branch. Fields: none',\n setContactStage: 'sets the person’s lifecycle stage. Fields: stage',\n addContactTag: 'tags the person. Fields: text (the tag)',\n assignContactOwner: 'gives the person an owner. Fields: text (the owner)',\n createCrmTask: 'creates a follow-up task. Fields: text (the task), taskKind, priority, dueInDays',\n logCrmActivity: 'logs what happened on the person’s record. Fields: activityKind, direction (a call’s or an email’s), text (what happened)',\n}\n\nfunction triggerLine(event: HostEventType): string {\n const fields =\n event === 'formSubmission'\n ? 'formName (the form’s name, as the request lists it), path, and every field the form collects, by its field name'\n : (HOST_EVENT_PAYLOAD_KEYS[event] ?? []).join(', ') || 'none'\n const need = AI_AUTOMATION_TRIGGER_NEEDS[event]\n return `- ${event} (${aiAutomationTriggerLabel(event)}). Fields: ${fields}.${\n need ? ` Needs ${AI_AUTOMATION_NEED_LABELS[need]}.` : ''\n }`\n}\n\nfunction stepLine(type: AiAutomationStepType): string {\n const need = AI_AUTOMATION_STEP_NEEDS[type]\n return `- ${type} (${aiStepLabel(type)}) ${STEP_USES[type]}.${\n need ? ` Needs ${AI_AUTOMATION_NEED_LABELS[need]}.` : ''\n }`\n}\n\n/**\n * The drafting instructions, byte-identical on every request so they cache.\n * Built from the platform's own trigger and step catalogs, so the vocabulary\n * the model is shown is the vocabulary the answer is held to.\n */\nexport const AI_JOB_WORKFLOW_DRAFT_INSTRUCTIONS: readonly AiSystemBlock[] = [\n {\n text: [\n `You turn a description into one automation for the Actions page of a website builder: something that happens on the site (the trigger), the conditions it must meet, and the steps that run, in order. Answer by calling ${AI_AUTOMATION_TOOL_NAME} exactly once. A reply in prose cannot be used.`,\n '',\n 'A step is written as when it runs and what it does: a when beside an action that carries its type and only that type’s fields. Use only these triggers and steps; each step lists the fields its action takes, and it carries only those.',\n '',\n 'Triggers, with the fields their conditions can read:',\n ...AI_AUTOMATION_TRIGGERS.map(triggerLine),\n '',\n 'Steps:',\n ...AI_AUTOMATION_STEP_TYPES.map(stepLine),\n '',\n 'Rules:',\n '- A step that names a list, campaign, workflow, webhook or dataset writes it as reference, in the description’s own words, such as \"newsletter\". The platform finds the site’s record by those words. Never write an id for one.',\n '- A form is named by the id or the name the request lists: formId on contactCreated, formName on formSubmission. A form the description names that the request does not list is written in the description’s words.',\n '- A deal stage in a condition on stageId or previousStageId is written as the stage’s name.',\n `- A lifecycle stage is one of: ${CONTACT_LIFECYCLE_STAGES.join(', ')}. A new lead is the stage lead.`,\n `- The trigger holds at most ${ACTION_MAX_CONDITIONS} conditions; combinator is and unless the description means any of them. A step's when holds one condition of its own at most, over the same fields. After waitForEvent, when on the field \"${AI_AUTOMATION_TIMED_OUT_FIELD}\" with notEmpty means the wait ran out of time.`,\n `- At most ${ACTION_MAX_STEPS} steps. minutes are whole minutes, at most ${AI_AUTOMATION_WAIT_MINUTES.max}; dueInDays is from 0 to ${CRM_TASK_MAX_DUE_DAYS}; addContactTag’s text is at most ${CONTACT_TAG_MAX_LENGTH} characters.`,\n '- assignContactOwner’s text is \"round robin\", or the teammate’s email address when the description gives one. A teammate named without an address goes in notes.',\n '- Write an email’s subject and body, and any text a person reads, in the site’s voice and the language of the description. Where the description lacks a fact such as a phone number, an address or a price, write a short description of it in square brackets instead of inventing it.',\n '- A trigger or a step the request says this workspace cannot use is never used. When the description needs one, or needs something no trigger or step here does, answer with unsupported set to the reason, no steps, and trigger event formSubmission with no conditions. unsupported is empty on every automation that is built.',\n '- notes holds what the person must decide that the automation cannot hold, in at most three short sentences; empty when there is nothing.',\n '- The automation is drafted switched off, and a person reviews it and switches it on. Never ask to turn it on or to run it.',\n ].join('\\n'),\n cacheBreakpoint: true,\n },\n]\n\n/** The explaining instructions, byte-identical on every request so they cache. */\nexport const AI_JOB_WORKFLOW_EXPLAIN_INSTRUCTIONS: readonly AiSystemBlock[] = [\n {\n text: [\n `You explain an automation on a website builder to the person who owns the site, in plain words for someone who does not write code. You are shown the automation as it is set up — what starts it, its conditions and its steps in order — and, when you are asked why a run failed, what the run history recorded about that run. Answer by calling ${AI_WORKFLOW_EXPLANATION_TOOL_NAME} exactly once.`,\n '',\n '- summary: in one or two sentences, what the automation does; or, for a failed run, the most likely reason it failed.',\n '- points: what happens, step by step, in the order it happens; or, for a failed run, what the run did and where it went wrong. One sentence each.',\n '- suggestions: what the person can check or change — a list, campaign or workflow the site no longer has, a condition that can never match, a placeholder nobody has filled in, a step the plan does not include. For a failed run, how to fix it. Empty when there is nothing to suggest.',\n '',\n 'Rules:',\n '- Explain only what you are shown. Never guess at settings, records or history you were not given, and never say you changed anything: you change nothing.',\n '- Name triggers and steps the way you are shown them, not by internal names.',\n '- A value in square brackets is a placeholder nobody has filled in yet.',\n '- Personal details were removed from what you are shown, and are written as [email address] or a named teammate. Never ask for them.',\n '- Write plain text, with no markdown and no HTML.',\n ].join('\\n'),\n cacheBreakpoint: true,\n },\n]\n\n// ── The user turns ────────────────────────────────────────────────────────\n\nfunction yesNo(value: boolean): string {\n return value ? 'yes' : 'no'\n}\n\n/**\n * What a draft is asked from: the workspace's reach, the site's forms and\n * datasets, and the brief. A revision adds the saved action's outline, and its\n * brief is the change asked for.\n */\nexport function aiJobWorkflowDraftPrompt(input: {\n brief: string\n capabilities: AiAutomationCapabilities\n records: Pick<AiAutomationRecords, 'forms' | 'datasets'>\n /** The saved action a revision starts from, as its outline. */\n revising?: { outline: string } | null\n /** A workspace's automation (AGL-3603): what it may start on and do. */\n org?: AiOrgAutomationVocabulary | null\n}): string {\n const { crm, webhooks, bookings } = input.capabilities\n const forms = input.records.forms.slice(0, AI_WORKFLOW_FORMS_LISTED)\n const datasets = input.records.datasets.slice(0, AI_WORKFLOW_DATASETS_LISTED)\n return [\n ...(input.org\n ? [\n 'This automation belongs to the workspace: it runs on each site it is placed on, as that site. It may start only on these triggers, and use only these steps; anything else, even if listed above, is never used.',\n `Triggers: ${input.org.triggers.join(', ')}.`,\n `Steps: ${input.org.steps.join(', ')}.`,\n ]\n : []),\n `This workspace can use: the CRM — ${yesNo(crm)}; webhooks — ${yesNo(webhooks)}; bookings — ${yesNo(bookings)}.`,\n ...(input.org\n ? ['Forms: an automation of the workspace runs on several sites, so none is listed; a form is named in the description’s words.']\n : [\n 'Forms on this site (id · name · fields):',\n ...(forms.length\n ? forms.map(\n (form) =>\n `- ${form.id} · ${form.name} · ${form.fields.slice(0, AI_WORKFLOW_FORM_FIELDS_LISTED).join(', ') || 'no fields'}`,\n )\n : ['- none']),\n ]),\n input.org ? 'Datasets in this workspace (name):' : 'Datasets on this site (name):',\n ...(datasets.length ? datasets.map((dataset) => `- ${dataset.name}`) : ['- none']),\n ...(input.revising\n ? [\n 'The automation to change, as it is set up now:',\n input.revising.outline,\n 'Answer with the whole automation as it should be after the change below. Keep every condition and step the change does not touch as it is, with the same records named in the same words, and keep its name unless the change asks for another. A detail shown as [email address] or a named teammate is written as a placeholder in square brackets.',\n `Change: ${input.brief.slice(0, AI_JOB_BRIEF_MAX_CHARS)}`,\n ]\n : [`Brief: ${input.brief.slice(0, AI_JOB_BRIEF_MAX_CHARS)}`]),\n ].join('\\n')\n}\n\n/** What an explanation is asked from: the mode, the outlines, and the member's question. */\nexport function aiJobWorkflowExplainPrompt(input: {\n brief: string\n mode: 'explain' | 'diagnose'\n outline: string\n run: string | null\n}): string {\n return [\n input.mode === 'diagnose'\n ? 'Asked: why a run of this automation failed.'\n : 'Asked: what this automation does.',\n input.outline,\n ...(input.run ? [input.run] : []),\n `Question: ${input.brief.slice(0, AI_JOB_BRIEF_MAX_CHARS)}`,\n ].join('\\n\\n')\n}\n\n/** The routing row's thinking and effort, as a generation input spells them. */\nfunction routedEffort(): Pick<AiCustomGenerationInput<unknown>, 'thinking' | 'effort'> {\n const row = AI_ROUTING_TABLE['job.workflow']\n return {\n ...(row.thinking ? { thinking: row.thinking } : {}),\n ...(row.effort ? { effort: row.effort } : {}),\n }\n}\n\n/** The draft generation as the doctrine's loop runs it; the eval harness records one through this same call. */\nexport function aiJobWorkflowDraftGeneration(request: {\n brief: string\n capabilities: AiAutomationCapabilities\n records: Pick<AiAutomationRecords, 'forms' | 'datasets'>\n revising?: { outline: string } | null\n org?: AiOrgAutomationVocabulary | null\n model: string\n signal?: AbortSignal\n}): AiCustomGenerationInput<AiAutomationAnswer> {\n const org = request.org\n const check: AiGenerationCheck<AiAutomationAnswer> = (answer) =>\n org\n ? holdAiAutomationAnswerToVocabulary(readAiAutomationAnswer(answer, request.capabilities), org)\n : readAiAutomationAnswer(answer, request.capabilities)\n return {\n step: 'job.workflow',\n model: request.model,\n instructions: AI_JOB_WORKFLOW_DRAFT_INSTRUCTIONS,\n messages: [{ role: 'user', content: aiJobWorkflowDraftPrompt(request) }],\n tool: aiAutomationTool(),\n maxTokens: AI_JOB_WORKFLOW_STEP_BUDGET.maxTokens(request.model),\n ...routedEffort(),\n ...(request.signal ? { signal: request.signal } : {}),\n check,\n }\n}\n\n/** The explanation generation as the doctrine's loop runs it. */\nexport function aiJobWorkflowExplainGeneration(request: {\n brief: string\n mode: 'explain' | 'diagnose'\n outline: string\n run: string | null\n model: string\n signal?: AbortSignal\n}): AiCustomGenerationInput<AiWorkflowExplanation> {\n return {\n step: 'job.workflow',\n model: request.model,\n instructions: AI_JOB_WORKFLOW_EXPLAIN_INSTRUCTIONS,\n messages: [{ role: 'user', content: aiJobWorkflowExplainPrompt(request) }],\n tool: aiWorkflowExplanationTool(),\n maxTokens: AI_JOB_WORKFLOW_STEP_BUDGET.maxTokens(request.model),\n ...routedEffort(),\n ...(request.signal ? { signal: request.signal } : {}),\n check: readAiWorkflowExplanation,\n }\n}\n\n// ── The runner ────────────────────────────────────────────────────────────\n\nexport interface AiJobWorkflowStepDeps {\n /** How the automation draft writer is found; the core's registry otherwise. */\n writerFor?: AiPluginDraftWriterLookup\n readRecords?: typeof readAiAutomationRecords\n readOrgRecords?: typeof readAiOrgAutomationRecords\n readTarget?: typeof readAiWorkflowTarget\n readRun?: typeof readAiWorkflowRun\n readFunctions?: typeof readAiWorkflowFunctions\n}\n\n/** A job label: the automation's name in quotes, cut to a line. */\nfunction quotedName(name: string): string {\n const clean = name.replace(/\\s+/g, ' ').trim()\n return `“${clean.length > 60 ? `${clean.slice(0, 60)}…` : clean}”`\n}\n\nexport function createAiJobWorkflowStep(deps: AiJobWorkflowStepDeps = {}): AiJobStepRunner {\n const writerFor = deps.writerFor ?? aiPluginDraftWriter\n const readRecords = deps.readRecords ?? readAiAutomationRecords\n const readOrgRecords = deps.readOrgRecords ?? readAiOrgAutomationRecords\n const readTarget = deps.readTarget ?? readAiWorkflowTarget\n const readRun = deps.readRun ?? readAiWorkflowRun\n const readFunctions = deps.readFunctions ?? readAiWorkflowFunctions\n return async ({ job, now, signal, firestore, org, modelFor }): Promise<AiJobStepOutcome> => {\n // The switch's answer for this job, else the routing table's (AGL-2942).\n const model = modelFor?.('job.workflow') ?? aiJobWorkflowModel()\n const unspent = (failure: string): AiJobStepOutcome => ({ ...aiUnspentOutcome(model), failure })\n const inputs = parseAiWorkflowJobInputs(job.inputs)\n if (typeof inputs === 'string') return unspent(inputs)\n\n if (inputs.mode === 'draft' && inputs.scope === 'org') {\n // A workspace's automation (AGL-3603): no site, and nothing written.\n const orgData = (org ?? (await firestore.collection('orgs').doc(job.orgId).get()).data() ?? null) as Record<\n string,\n unknown\n > | null\n if (!checkEntitlement(orgData as Partial<AglynOrgBilling> | null, 'actions')) {\n return aiUnspentOutcome(model, { review: aiLimitReview(AI_ORG_AUTOMATION_PLAN_COPY) })\n }\n const capabilities = aiAutomationCapabilities(orgData)\n const vocabulary = { triggers: inputs.triggers, steps: inputs.steps }\n const records = await readOrgRecords(firestore, { orgId: job.orgId, crm: capabilities.crm })\n const generation = await runValidatedGeneration(\n 'workflow',\n aiJobWorkflowDraftGeneration({\n brief: job.brief,\n capabilities,\n records,\n org: vocabulary,\n model,\n ...(signal ? { signal } : {}),\n }),\n )\n const spent = { ...aiGenerationSpent(generation), ...(generation.effort ? { effort: generation.effort } : {}) }\n if (generation.status === 'refused') return { ...spent, refused: true }\n if (generation.status === 'needs_input') return { ...spent, review: aiDoctrineReview(generation) }\n const answer = generation.value\n if (answer.unsupported) {\n return {\n ...spent,\n failure:\n answer.unsupported === 'no-trigger' || answer.unsupported === 'no-step'\n ? AI_ORG_AUTOMATION_UNSUPPORTED_COPY\n : AI_AUTOMATION_UNSUPPORTED_COPY[answer.unsupported],\n }\n }\n if (!answer.steps.length) return { ...spent, failure: AI_WORKFLOW_NO_DRAFT_COPY }\n const draft = aiAutomationDraft(answer, records)\n return { ...spent, outputs: [orgProposalOutput(draft, answer.notes)] }\n }\n\n const hostId = job.hostId\n if (!hostId) return unspent(AI_WORKFLOW_NO_SITE_COPY)\n // The door checks the caller's membership of the org the job is metered\n // against; the site is checked against that org here, before anything\n // about it is read into a prompt.\n const host = await firestore.collection('hosts').doc(hostId).get()\n if (!host.exists || host.get('orgId') !== job.orgId) return unspent(AI_WORKFLOW_NO_SITE_COPY)\n const subdomain = host.get('subdomain')\n const hostSubdomain = typeof subdomain === 'string' && subdomain ? subdomain : null\n const orgData = (org ?? (await firestore.collection('orgs').doc(job.orgId).get()).data() ?? null) as Record<\n string,\n unknown\n > | null\n const capabilities = aiAutomationCapabilities(orgData)\n\n if (inputs.mode === 'draft') {\n const writer = writerFor(AI_AUTOMATION_RESOURCE)\n if (!writer) return unspent(AI_WORKFLOW_UNAVAILABLE_COPY)\n const place = { hostId, hostSubdomain }\n // What an earlier run of this same job already wrote, under the id the job recorded.\n const draftId = aiJobDraftId(job, 'workflow')\n const written = await writer.read({ hostId, id: draftId })\n if (written) {\n return aiUnspentOutcome(model, {\n outputs: [draftOutput(written.id, written.name, place, 'It is off until you switch it on.')],\n })\n }\n const context = { orgId: job.orgId, hostId, uid: job.createdBy, org: orgData, now }\n const refusal = await writer.refusal(context)\n if (refusal) {\n return refusal.status === 403\n ? aiUnspentOutcome(model, { review: aiLimitReview(refusal.error) })\n : unspent(refusal.error)\n }\n const records = await readRecords(firestore, { orgId: job.orgId, hostId, crm: capabilities.crm })\n const generation = await runValidatedGeneration(\n 'workflow',\n aiJobWorkflowDraftGeneration({\n brief: job.brief,\n capabilities,\n records,\n model,\n ...(signal ? { signal } : {}),\n }),\n )\n const spent = { ...aiGenerationSpent(generation), ...(generation.effort ? { effort: generation.effort } : {}) }\n if (generation.status === 'refused') return { ...spent, refused: true }\n if (generation.status === 'needs_input') return { ...spent, review: aiDoctrineReview(generation) }\n const answer = generation.value\n if (answer.unsupported) return { ...spent, failure: AI_AUTOMATION_UNSUPPORTED_COPY[answer.unsupported] }\n if (!answer.steps.length) return { ...spent, failure: AI_WORKFLOW_NO_DRAFT_COPY }\n const draft = aiAutomationDraft(answer, records)\n const write = await writer.write({\n ...context,\n id: draftId,\n name: draft.action.name,\n content: { action: draft.action },\n })\n if (write.ok === false) {\n return write.status === 403\n ? { ...spent, review: aiLimitReview(write.error) }\n : { ...spent, failure: AI_WORKFLOW_SAVE_FAILURE_COPY }\n }\n return {\n ...spent,\n outputs: [draftOutput(write.id, write.name, place, aiAutomationDraftNote(draft, answer.notes))],\n }\n }\n\n if (inputs.mode === 'revise') {\n const writer = writerFor(AI_AUTOMATION_RESOURCE)\n if (!writer) return unspent(AI_WORKFLOW_UNAVAILABLE_COPY)\n const place = { hostId, hostSubdomain }\n const draftId = aiJobDraftId(job, 'workflow')\n const written = await writer.read({ hostId, id: draftId })\n if (written) {\n return aiUnspentOutcome(model, {\n outputs: [draftOutput(written.id, written.name, place, 'It is off until you switch it on.')],\n })\n }\n const saved = await readTarget({ hostId, type: 'action', id: inputs.targetId })\n if (!saved) return unspent(AI_WORKFLOW_GONE_COPY)\n if (saved.type !== 'action') return unspent(AI_WORKFLOW_REVISE_WORKFLOW_COPY)\n const blocker = aiActionReviseBlocker(saved.action)\n if (blocker) return unspent(AI_WORKFLOW_REVISE_BLOCKER_COPY[blocker])\n const context = { orgId: job.orgId, hostId, uid: job.createdBy, org: orgData, now }\n const refusal = await writer.refusal(context)\n if (refusal) {\n return refusal.status === 403\n ? aiUnspentOutcome(model, { review: aiLimitReview(refusal.error) })\n : unspent(refusal.error)\n }\n const records = await readRecords(firestore, { orgId: job.orgId, hostId, crm: capabilities.crm })\n const generation = await runValidatedGeneration(\n 'workflow',\n aiJobWorkflowDraftGeneration({\n brief: job.brief,\n capabilities,\n records,\n revising: { outline: aiActionOutline(saved.action, records) },\n model,\n ...(signal ? { signal } : {}),\n }),\n )\n const spent = { ...aiGenerationSpent(generation), ...(generation.effort ? { effort: generation.effort } : {}) }\n if (generation.status === 'refused') return { ...spent, refused: true }\n if (generation.status === 'needs_input') return { ...spent, review: aiDoctrineReview(generation) }\n const answer = generation.value\n if (answer.unsupported) return { ...spent, failure: AI_AUTOMATION_UNSUPPORTED_COPY[answer.unsupported] }\n if (!answer.steps.length) return { ...spent, failure: AI_WORKFLOW_NO_DRAFT_COPY }\n const draft = aiAutomationDraft(answer, records)\n // The copy never takes the saved action's name: two rows by one name\n // in the list are two rows nobody can tell apart.\n const name =\n draft.action.name.trim() && draft.action.name.trim() !== saved.name.trim()\n ? draft.action.name\n : aiRevisedName(saved.name)\n const write = await writer.write({ ...context, id: draftId, name, content: { action: { ...draft.action, name } } })\n if (write.ok === false) {\n return write.status === 403\n ? { ...spent, review: aiLimitReview(write.error) }\n : { ...spent, failure: AI_WORKFLOW_SAVE_FAILURE_COPY }\n }\n const changes = aiAutomationRevisionChanges(saved.action, draft.action, aiStepLabel)\n return {\n ...spent,\n outputs: [\n draftOutput(\n write.id,\n write.name,\n place,\n aiAutomationRevisionNote({ fromName: saved.name, changes, draft, notes: answer.notes }),\n ),\n ],\n }\n }\n\n const target = await readTarget({ hostId, type: inputs.targetType, id: inputs.targetId })\n if (!target) return unspent(AI_WORKFLOW_GONE_COPY)\n let run: AiRunRecord | null = null\n if (inputs.mode === 'diagnose') {\n const read = await readRun(firestore, { hostId, targetId: inputs.targetId, runId: inputs.runId })\n if (read.ok === false) {\n return unspent(read.reason === 'gone' ? AI_WORKFLOW_RUN_GONE_COPY : AI_WORKFLOW_RUN_NOT_FAILED_COPY)\n }\n run = read.run\n }\n // Only the kinds of record the automation's steps name are read, to say\n // whether each still exists.\n const outline =\n target.type === 'action'\n ? aiActionOutline(\n target.action,\n await readRecords(firestore, {\n orgId: job.orgId,\n hostId,\n crm: false,\n only: aiActionRecordKinds(target.action),\n }),\n )\n : aiWorkflowOutline(target.workflow, await readFunctions(firestore, hostId))\n const generation = await runValidatedGeneration(\n 'workflow',\n aiJobWorkflowExplainGeneration({\n brief: job.brief,\n mode: inputs.mode,\n outline,\n run: run ? aiRunOutline(run) : null,\n model,\n ...(signal ? { signal } : {}),\n }),\n )\n const spent = { ...aiGenerationSpent(generation), ...(generation.effort ? { effort: generation.effort } : {}) }\n if (generation.status === 'refused') return { ...spent, refused: true }\n if (generation.status === 'needs_input') return { ...spent, failure: AI_WORKFLOW_NO_EXPLANATION_COPY }\n const output: AiJobOutput = {\n resource: 'text',\n // An explanation has no document of its own; the id names what it is\n // within the job, as a text output's `draft` does.\n id: inputs.mode === 'diagnose' ? 'diagnosis' : 'explanation',\n hostId,\n hostSubdomain,\n label:\n inputs.mode === 'diagnose'\n ? `Why a run of ${quotedName(target.name)} failed`\n : `How ${quotedName(target.name)} works`,\n text: aiWorkflowExplanationText(generation.value, inputs.mode),\n }\n return { ...spent, outputs: [output] }\n }\n}\n\n/** What a person is told when a workspace's automation would need what only one site runs. */\nexport const AI_ORG_AUTOMATION_UNSUPPORTED_COPY =\n 'An org automation cannot do what this description asks — it starts only on what the server sees and uses only the steps every site runs the same way — so nothing was drafted. Build it as an action on the site instead.'\n\n/**\n * A workspace's drafted automation as the output that proposes it\n * (AGL-3603): the stored shape the Org automations editor opens — its name,\n * trigger and steps, never a placement, which the person picks — and what\n * to fill in before switching it on.\n */\nexport function orgProposalOutput(draft: AiAutomationDraft, notes: readonly string[]): AiJobOutput {\n const { name, trigger, steps } = draft.action\n return {\n resource: AI_ORG_AUTOMATION_RESOURCE as AiJobOutput['resource'],\n // A proposal has no document; the id names what it is within the job.\n id: 'proposal',\n hostId: null,\n hostSubdomain: null,\n label: name,\n note: [\n 'Nothing is saved yet: it opens in the org automation editor, switched off, where you choose the sites it runs on and save it.',\n ...(draft.placeholders.length\n ? [\n `Fill in ${draft.placeholders.length === 1 ? 'its placeholder' : `its ${draft.placeholders.length} placeholders`} before switching it on.`,\n ]\n : []),\n ...notes,\n ].join(' '),\n proposal: { automation: { name, trigger, steps } } as unknown as Record<string, unknown>,\n }\n}\n\n/** The name a revised copy is saved under when the change asked for no other. */\nexport function aiRevisedName(name: string): string {\n const clean = name.replace(/\\s+/g, ' ').trim() || 'Automation'\n return `${clean.length > 80 ? clean.slice(0, 80) : clean} (revised)`\n}\n\nfunction draftOutput(\n id: string,\n name: string,\n place: { hostId: string; hostSubdomain: string | null },\n note: string,\n): AiJobOutput {\n return {\n resource: 'workflow',\n id,\n versionId: null,\n hostId: place.hostId,\n hostSubdomain: place.hostSubdomain,\n label: name,\n note,\n }\n}\n\nexport const runAiJobWorkflowStep = createAiJobWorkflowStep()\n\n// ── Admission ─────────────────────────────────────────────────────────────\n\n/**\n * Whether the plugin that writes automations runs on this site: switched on,\n * and past its release flag. The owner is whichever plugin registered the\n * automation writer (AGL-3080); none registered is the same refusal.\n */\nasync function workflowsPluginRefusal(\n context: Parameters<AiJobAdmission>[0],\n hostId: string,\n): Promise<AiJobAdmissionRefusal | null> {\n const owner = aiPluginDraftOwner(AI_AUTOMATION_RESOURCE)\n if (!owner) return { status: 403, error: aiPluginDraftUnavailable('Automation') }\n const host = (await context.firestore.collection('hosts').doc(hostId).get()).data() ?? null\n const org = context.org as { enabledPlugins?: string[] } | null\n const released = await filterEnabledPluginsByReleaseFlags([owner], {\n orgId: context.orgId,\n authorization: null,\n })\n return released.includes(owner) && isHostPluginEnabled(org, host, owner)\n ? null\n : { status: 403, error: aiPluginDraftUnavailable('Automation') }\n}\n\n/**\n * Whether a workspace's automation may be drafted (AGL-3603): no site is\n * named, the plugin that keeps automations runs for the workspace and is\n * past its release flag, and the plan includes the actions builder that\n * section is sold under. Nothing is written, so no allowance is asked: the\n * section's save route counts its cap when the person saves.\n */\nasync function orgAutomationRefusal(context: Parameters<AiJobAdmission>[0]): Promise<AiJobAdmissionRefusal | null> {\n if (context.hostId) return { status: 400, error: 'An org automation belongs to no one site' }\n const owner = aiPluginDraftOwner(AI_AUTOMATION_RESOURCE)\n if (!owner) return { status: 403, error: AI_ORG_AUTOMATION_UNAVAILABLE_COPY }\n const org = context.org as ({ enabledPlugins?: string[] } & Partial<AglynOrgBilling>) | null\n const released = await filterEnabledPluginsByReleaseFlags([owner], {\n orgId: context.orgId,\n authorization: null,\n })\n if (!released.includes(owner) || !isPluginEnabled(org, owner)) {\n return { status: 403, error: AI_ORG_AUTOMATION_UNAVAILABLE_COPY }\n }\n return checkEntitlement(org, 'actions') ? null : { status: 403, error: AI_ORG_AUTOMATION_PLAN_COPY }\n}\n\nexport interface AiWorkflowJobAdmissionDeps {\n readTarget?: typeof readAiWorkflowTarget\n readRun?: typeof readAiWorkflowRun\n writerFor?: AiPluginDraftWriterLookup\n}\n\n/**\n * A workflow job is admitted for a site of its org with the Automation plugin\n * on. A draft also needs the draft writer and the member's room and role in\n * its own words; an explanation needs the automation it names to exist, and\n * a run's explanation a failed run of it.\n */\nexport function createAiWorkflowJobAdmission(deps: AiWorkflowJobAdmissionDeps = {}): AiJobAdmission {\n const readTarget = deps.readTarget ?? readAiWorkflowTarget\n const readRun = deps.readRun ?? readAiWorkflowRun\n return async (context) => {\n const inputs = parseAiWorkflowJobInputs(context.inputs)\n if (typeof inputs === 'string') return { status: 400, error: inputs }\n if (inputs.mode === 'draft' && inputs.scope === 'org') return orgAutomationRefusal(context)\n if (inputs.mode === 'draft') {\n return aiPluginDraftAdmissionRefusal(context, {\n kind: 'workflow',\n drafts: [{ resource: AI_AUTOMATION_RESOURCE, label: 'Automation' }],\n ...(deps.writerFor ? { writerFor: deps.writerFor } : {}),\n })\n }\n if (inputs.mode === 'revise') {\n // A revision writes a draft as a description does, and also needs the\n // action it starts from to exist and to hold only what AI writes.\n return aiPluginDraftAdmissionRefusal(context, {\n kind: 'workflow',\n drafts: [{ resource: AI_AUTOMATION_RESOURCE, label: 'Automation' }],\n ...(deps.writerFor ? { writerFor: deps.writerFor } : {}),\n ownCheck: async (hostId) => {\n const saved = await readTarget({ hostId, type: 'action', id: inputs.targetId })\n if (!saved || saved.type !== 'action') return { status: 404, error: AI_WORKFLOW_GONE_COPY }\n const blocker = aiActionReviseBlocker(saved.action)\n return blocker ? { status: 400, error: AI_WORKFLOW_REVISE_BLOCKER_COPY[blocker] } : null\n },\n })\n }\n const hostId = context.hostId\n if (!hostId) return { status: 400, error: 'Open the site the automation is on before asking about it' }\n const owner = await resolveOrgIdForHost(hostId)\n if (!owner || owner !== context.orgId) return { status: 404, error: 'Unknown site' }\n const plugin = await workflowsPluginRefusal(context, hostId)\n if (plugin) return plugin\n const target = await readTarget({ hostId, type: inputs.targetType, id: inputs.targetId })\n if (!target) return { status: 404, error: AI_WORKFLOW_GONE_COPY }\n if (inputs.mode === 'diagnose') {\n const run = await readRun(context.firestore, { hostId, targetId: inputs.targetId, runId: inputs.runId })\n if (run.ok === false) {\n return run.reason === 'gone'\n ? { status: 404, error: AI_WORKFLOW_RUN_GONE_COPY }\n : { status: 400, error: AI_WORKFLOW_RUN_NOT_FAILED_COPY }\n }\n }\n return null\n }\n}\n\nexport const aiWorkflowJobAdmission = createAiWorkflowJobAdmission()\n\n/** Registers the workflow step and the check a workflow job passes before it is created or resumed. */\nexport function registerAiWorkflowJob(): void {\n registerAiJobStep('workflow', runAiJobWorkflowStep, { minimumMs: AI_JOB_WORKFLOW_STEP_MINIMUM_MS })\n registerAiJobAdmission('workflow', aiWorkflowJobAdmission)\n}\n"],"names":["ACTION_MAX_CONDITIONS","ACTION_MAX_STEPS","CONTACT_LIFECYCLE_STAGES","CONTACT_TAG_MAX_LENGTH","CRM_TASK_MAX_DUE_DAYS","HOST_EVENT_PAYLOAD_KEYS","checkEntitlement","isHostPluginEnabled","isPluginEnabled","filterEnabledPluginsByReleaseFlags","resolveOrgIdForHost","aiAutomationDraft","aiAutomationDraftNote","aiAutomationRevisionChanges","aiAutomationRevisionNote","AI_AUTOMATION_TIMED_OUT_FIELD","AI_AUTOMATION_WAIT_MINUTES","aiStepLabel","aiActionOutline","aiActionRecordKinds","aiRunOutline","aiWorkflowExplanationText","aiWorkflowOutline","AI_AUTOMATION_NEED_LABELS","AI_AUTOMATION_RESOURCE","AI_AUTOMATION_STEP_NEEDS","AI_AUTOMATION_STEP_TYPES","AI_AUTOMATION_TRIGGER_NEEDS","AI_AUTOMATION_TRIGGERS","AI_AUTOMATION_UNSUPPORTED_COPY","AI_WORKFLOW_GONE_COPY","AI_WORKFLOW_NO_DRAFT_COPY","AI_WORKFLOW_NO_EXPLANATION_COPY","AI_WORKFLOW_NO_SITE_COPY","AI_WORKFLOW_RUN_GONE_COPY","AI_WORKFLOW_RUN_NOT_FAILED_COPY","AI_WORKFLOW_SAVE_FAILURE_COPY","AI_WORKFLOW_REVISE_BLOCKER_COPY","AI_WORKFLOW_REVISE_WORKFLOW_COPY","AI_WORKFLOW_UNAVAILABLE_COPY","AI_ORG_AUTOMATION_PLAN_COPY","AI_ORG_AUTOMATION_RESOURCE","AI_ORG_AUTOMATION_UNAVAILABLE_COPY","aiActionReviseBlocker","aiAutomationCapabilities","aiAutomationTriggerLabel","parseAiWorkflowJobInputs","AI_STEP_TIERS","AI_ROUTING_TABLE","aiModelForStep","runValidatedGeneration","AI_AUTOMATION_TOOL_NAME","AI_WORKFLOW_EXPLANATION_TOOL_NAME","aiAutomationTool","aiWorkflowExplanationTool","holdAiAutomationAnswerToVocabulary","readAiAutomationAnswer","readAiWorkflowExplanation","registerAiJobAdmission","aiJobStepBudget","aiJobDraftId","aiDoctrineReview","aiGenerationSpent","aiLimitReview","aiUnspentOutcome","aiPluginDraftAdmissionRefusal","aiPluginDraftOwner","aiPluginDraftUnavailable","aiPluginDraftWriter","AI_JOB_BRIEF_MAX_CHARS","registerAiJobStep","readAiAutomationRecords","readAiOrgAutomationRecords","readAiWorkflowFunctions","readAiWorkflowRun","readAiWorkflowTarget","AI_WORKFLOW_RECORDS_READ_MS","AI_JOB_WORKFLOW_STEP_BUDGET","tier","maxTokens","lookups","ownReadsMs","AI_JOB_WORKFLOW_STEP_MINIMUM_MS","minimumMs","aiJobWorkflowModel","AI_WORKFLOW_FORMS_LISTED","AI_WORKFLOW_FORM_FIELDS_LISTED","AI_WORKFLOW_DATASETS_LISTED","STEP_USES","sendEmail","notifyAdmins","enrollList","assignCampaign","runWorkflow","datasetAppend","updateDataset","webhookPost","siteAlert","wait","waitForEvent","exitFlow","setContactStage","addContactTag","assignContactOwner","createCrmTask","logCrmActivity","triggerLine","event","fields","join","need","stepLine","type","AI_JOB_WORKFLOW_DRAFT_INSTRUCTIONS","text","map","max","cacheBreakpoint","AI_JOB_WORKFLOW_EXPLAIN_INSTRUCTIONS","yesNo","value","aiJobWorkflowDraftPrompt","input","crm","webhooks","bookings","capabilities","forms","records","slice","datasets","org","triggers","steps","length","form","id","name","dataset","revising","outline","brief","aiJobWorkflowExplainPrompt","mode","run","routedEffort","row","thinking","effort","aiJobWorkflowDraftGeneration","request","check","answer","step","model","instructions","messages","role","content","tool","signal","aiJobWorkflowExplainGeneration","quotedName","clean","replace","trim","createAiJobWorkflowStep","deps","writerFor","readRecords","readOrgRecords","readTarget","readRun","readFunctions","job","now","firestore","modelFor","unspent","failure","inputs","scope","orgData","collection","doc","orgId","get","data","review","vocabulary","generation","spent","status","refused","unsupported","AI_ORG_AUTOMATION_UNSUPPORTED_COPY","draft","outputs","orgProposalOutput","notes","hostId","host","exists","subdomain","hostSubdomain","writer","place","draftId","written","read","draftOutput","context","uid","createdBy","refusal","error","write","action","ok","saved","targetId","blocker","aiRevisedName","changes","fromName","target","targetType","runId","reason","only","workflow","output","resource","label","trigger","note","placeholders","proposal","automation","versionId","runAiJobWorkflowStep","workflowsPluginRefusal","owner","released","authorization","includes","orgAutomationRefusal","createAiWorkflowJobAdmission","kind","drafts","ownCheck","plugin","aiWorkflowJobAdmission","registerAiWorkflowJob"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-workflow-step.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { ACTION_MAX_CONDITIONS, ACTION_MAX_STEPS } from '@aglyn/aglyn/app-utils/site-interactions'\nimport {\n CONTACT_LIFECYCLE_STAGES,\n CONTACT_TAG_MAX_LENGTH,\n CRM_TASK_MAX_DUE_DAYS,\n} from '@aglyn/aglyn/app-utils/crm'\nimport { HOST_EVENT_PAYLOAD_KEYS, type HostEventType } from '@aglyn/aglyn/app-utils/host-events'\nimport { checkEntitlement } from '@aglyn/aglyn/app-utils/plan-entitlements'\nimport { isHostPluginEnabled, isPluginEnabled } from '@aglyn/aglyn/plugin-manager/enabled-plugins'\nimport type { AglynOrgBilling } from '@aglyn/aglyn/foundation/definitions/org-billing.types'\nimport { filterEnabledPluginsByReleaseFlags } from '@aglyn/tenant-data-admin/server/release-flags'\nimport { resolveOrgIdForHost } from '@aglyn/tenant-data-admin/server/organizations'\nimport {\n aiAutomationDraft,\n aiAutomationDraftNote,\n aiAutomationRevisionChanges,\n aiAutomationRevisionNote,\n type AiAutomationDraft,\n type AiAutomationRecords,\n} from '../model/ai-automation-draft'\nimport {\n AI_AUTOMATION_TIMED_OUT_FIELD,\n AI_AUTOMATION_WAIT_MINUTES,\n aiStepLabel,\n} from '../model/ai-automation-format'\nimport {\n aiActionOutline,\n aiActionRecordKinds,\n aiRunOutline,\n aiWorkflowExplanationText,\n aiWorkflowOutline,\n type AiRunRecord,\n} from '../model/ai-automation-outline'\nimport type { AiJobOutput } from '../model/ai-jobs.types'\nimport {\n AI_AUTOMATION_NEED_LABELS,\n AI_AUTOMATION_RESOURCE,\n AI_AUTOMATION_STEP_NEEDS,\n AI_AUTOMATION_STEP_TYPES,\n AI_AUTOMATION_TRIGGER_NEEDS,\n AI_AUTOMATION_TRIGGERS,\n AI_AUTOMATION_UNSUPPORTED_COPY,\n AI_WORKFLOW_GONE_COPY,\n AI_WORKFLOW_NO_DRAFT_COPY,\n AI_WORKFLOW_NO_EXPLANATION_COPY,\n AI_WORKFLOW_NO_SITE_COPY,\n AI_WORKFLOW_RUN_GONE_COPY,\n AI_WORKFLOW_RUN_NOT_FAILED_COPY,\n AI_WORKFLOW_SAVE_FAILURE_COPY,\n AI_WORKFLOW_REVISE_BLOCKER_COPY,\n AI_WORKFLOW_REVISE_WORKFLOW_COPY,\n AI_WORKFLOW_UNAVAILABLE_COPY,\n AI_ORG_AUTOMATION_PLAN_COPY,\n AI_ORG_AUTOMATION_RESOURCE,\n AI_ORG_AUTOMATION_UNAVAILABLE_COPY,\n aiActionReviseBlocker,\n aiAutomationCapabilities,\n aiAutomationTriggerLabel,\n parseAiWorkflowJobInputs,\n type AiAutomationCapabilities,\n type AiAutomationStepType,\n type AiOrgAutomationVocabulary,\n} from '../model/ai-workflow-job'\nimport { AI_STEP_TIERS } from '../providers/catalog'\nimport { AI_ROUTING_TABLE, aiModelForStep } from '../providers/routing'\nimport {\n runValidatedGeneration,\n type AiCustomGenerationInput,\n type AiGenerationCheck,\n} from '../runtime/ai-doctrine'\nimport type { AiSystemBlock } from '../runtime/ai-runtime'\nimport {\n AI_AUTOMATION_TOOL_NAME,\n AI_WORKFLOW_EXPLANATION_TOOL_NAME,\n aiAutomationTool,\n aiWorkflowExplanationTool,\n holdAiAutomationAnswerToVocabulary,\n readAiAutomationAnswer,\n readAiWorkflowExplanation,\n type AiAutomationAnswer,\n type AiWorkflowExplanation,\n} from '../tools/ai-workflow-tool'\nimport { registerAiJobAdmission, type AiJobAdmission, type AiJobAdmissionRefusal } from './ai-job-admission'\nimport { aiJobStepBudget } from './ai-job-budget'\nimport { aiJobDraftId } from './ai-job-draft-ids'\nimport { aiDoctrineReview, aiGenerationSpent, aiLimitReview, aiUnspentOutcome } from './ai-job-generation'\nimport {\n aiPluginDraftAdmissionRefusal,\n aiPluginDraftOwner,\n aiPluginDraftUnavailable,\n aiPluginDraftWriter,\n type AiPluginDraftWriterLookup,\n} from './ai-job-plugin-drafts'\nimport { AI_JOB_BRIEF_MAX_CHARS, type AiJobStepOutcome, type AiJobStepRunner } from './ai-job-text-step'\nimport { registerAiJobStep } from './ai-jobs'\nimport {\n readAiAutomationRecords,\n readAiOrgAutomationRecords,\n readAiWorkflowFunctions,\n readAiWorkflowRun,\n readAiWorkflowTarget,\n} from './ai-workflow-records'\n\n/**\n * The `workflow` step (AGL-2919): an automation drafted from a description,\n * or a saved automation explained.\n *\n * ## Drafting\n *\n * The model is shown the platform's automation vocabulary — every trigger with\n * the fields it carries, every step with the fields it takes — in a cached\n * block, and in the user turn what this workspace can run, the site's forms\n * and datasets by name, and the description. It answers through\n * `submit_automation`, and the answer is held to the vocabulary and to what\n * the workspace can run before anything is written.\n *\n * A record the description names — a list, a campaign, a workflow, a webhook,\n * a dataset, a form, a deal stage — is answered in the description's WORDS,\n * and looked up among the site's records in code, after the answer\n * (`model/ai-automation-draft.ts`). The model is never shown the site's lists,\n * campaigns, workflows, webhooks or pipelines. Words that name no record, or\n * more than one, are kept as a placeholder the person fills in.\n *\n * The automation is written through the workflows plugin's draft writer —\n * that plugin's stored shape, validator, role, plan and cap — keyed by the\n * job's id, and it is written OFF. A run cut off after the write finds its\n * draft and spends nothing.\n *\n * ## Explaining\n *\n * The model is shown an outline of the saved automation, and for a failed run\n * what the run history recorded about it (`model/ai-automation-outline.ts`):\n * never the stored documents, never an email address, never a run's event\n * payload. It answers through `submit_explanation`, which becomes a `text`\n * output. Nothing is changed.\n *\n * ## Revising (AGL-3603)\n *\n * A saved ACTION and a change asked of it. The model is sent the drafting\n * request — the same cached vocabulary, the same tool, the same reach, forms\n * and datasets — with the action's outline in the user turn and the change as\n * the brief, and answers the whole action as it would be after the change.\n * That answer is held to the vocabulary and matched to the site's records as\n * a draft is, and written as a NEW draft, OFF, under the job's draft id: the\n * saved action is read and never written. What changed is worked out in code\n * from the two stored shapes. An action holding what the vocabulary cannot\n * write is refused before the model is asked, so a revision never drops an\n * on-page step the person did not ask to lose.\n *\n * ## A workspace's automation (AGL-3603)\n *\n * A draft with `scope: 'org'` names no site. It is asked as a draft is — the\n * same cached vocabulary, the same tool — with the org automation's narrower\n * vocabulary stated in the user turn and held by the check, and matched\n * against the workspace's lists, campaigns, datasets and stages. Nothing is\n * written: the automation rides on an `orgAutomation` output, which the Org\n * automations editor opens unsaved, where its sites are picked and its save\n * route — the workflows plugin's — holds it to that plugin's rules.\n */\n\n/** The longest an explanation or a draft's generation may take reading the site first. */\nexport const AI_WORKFLOW_RECORDS_READ_MS = 4_000\n\n/**\n * The step's time (AGL-3035): its answer and its re-ask at the routing\n * table's `job.workflow` ceiling on the tier the step is served from. It sends\n * no site inventory block, so it makes no lookup; its own reads — the records\n * a draft is matched against, or the automation and run an explanation\n * reads — are the declared `AI_WORKFLOW_RECORDS_READ_MS`.\n */\nexport const AI_JOB_WORKFLOW_STEP_BUDGET = aiJobStepBudget({\n tier: AI_STEP_TIERS['job.workflow'],\n maxTokens: AI_ROUTING_TABLE['job.workflow'].maxTokens,\n lookups: 0,\n ownReadsMs: AI_WORKFLOW_RECORDS_READ_MS,\n})\n\n/** The least time one workflow step needs before it starts. */\nexport const AI_JOB_WORKFLOW_STEP_MINIMUM_MS = AI_JOB_WORKFLOW_STEP_BUDGET.minimumMs\n\n/** The routing table's answer for `job.workflow`. */\nexport function aiJobWorkflowModel(): string {\n return aiModelForStep('job.workflow')\n}\n\n/** How many forms and datasets the user turn lists, and how many fields a form. */\nexport const AI_WORKFLOW_FORMS_LISTED = 40\nexport const AI_WORKFLOW_FORM_FIELDS_LISTED = 20\nexport const AI_WORKFLOW_DATASETS_LISTED = 40\n\n// ── The vocabulary, for the model ─────────────────────────────────────────\n\nconst STEP_USES: Readonly<Record<AiAutomationStepType, string>> = {\n sendEmail: 'emails the person the event is about. Fields: subject, body (plain text), toField',\n notifyAdmins: 'notifies the site’s admins in the console. Fields: text (what the admins read)',\n enrollList: 'adds the person to an email list. Fields: reference (the list)',\n assignCampaign: 'files the person under a campaign. Fields: reference (the campaign)',\n runWorkflow: 'runs one of the site’s workflows. Fields: reference (the workflow)',\n datasetAppend: 'saves the event as a new record in a dataset. Fields: reference (the dataset)',\n updateDataset: 'updates the event’s record in a dataset. Fields: reference (the dataset)',\n webhookPost: 'sends the event to one of the site’s outbound webhooks. Fields: reference (the webhook)',\n siteAlert: 'shows the visitor who caused the event a short message. Fields: text (the message), severity',\n wait: 'waits before the next step. Fields: event (empty), minutes',\n waitForEvent: 'waits until something else happens to the same person, or gives up. Fields: event, minutes',\n exitFlow: 'ends the automation here; with a when it is a branch. Fields: reference (empty)',\n setContactStage: 'sets the person’s lifecycle stage. Fields: stage',\n addContactTag: 'tags the person. Fields: text (the tag)',\n assignContactOwner: 'gives the person an owner. Fields: text (the owner)',\n createCrmTask: 'creates a follow-up task. Fields: text (the task), taskKind, priority, dueInDays',\n logCrmActivity: 'logs what happened on the person’s record. Fields: activityKind, direction (a call’s or an email’s), text (what happened)',\n}\n\nfunction triggerLine(event: HostEventType): string {\n const fields =\n event === 'formSubmission'\n ? 'formName (the form’s name, as the request lists it), path, and every field the form collects, by its field name'\n : (HOST_EVENT_PAYLOAD_KEYS[event] ?? []).join(', ') || 'none'\n const need = AI_AUTOMATION_TRIGGER_NEEDS[event]\n return `- ${event} (${aiAutomationTriggerLabel(event)}). Fields: ${fields}.${\n need ? ` Needs ${AI_AUTOMATION_NEED_LABELS[need]}.` : ''\n }`\n}\n\nfunction stepLine(type: AiAutomationStepType): string {\n const need = AI_AUTOMATION_STEP_NEEDS[type]\n return `- ${type} (${aiStepLabel(type)}) ${STEP_USES[type]}.${\n need ? ` Needs ${AI_AUTOMATION_NEED_LABELS[need]}.` : ''\n }`\n}\n\n/**\n * The drafting instructions, byte-identical on every request so they cache.\n * Built from the platform's own trigger and step catalogs, so the vocabulary\n * the model is shown is the vocabulary the answer is held to.\n */\nexport const AI_JOB_WORKFLOW_DRAFT_INSTRUCTIONS: readonly AiSystemBlock[] = [\n {\n text: [\n `You turn a description into one automation for the Actions page of a website builder: something that happens on the site (the trigger), the conditions it must meet, and the steps that run, in order. Answer by calling ${AI_AUTOMATION_TOOL_NAME} exactly once. A reply in prose cannot be used.`,\n '',\n 'A step is written as when it runs and what it does: a when beside an action that carries its type and only that type’s fields. Use only these triggers and steps; each step lists the fields its action takes, and it carries only those.',\n '',\n 'Triggers, with the fields their conditions can read:',\n ...AI_AUTOMATION_TRIGGERS.map(triggerLine),\n '',\n 'Steps:',\n ...AI_AUTOMATION_STEP_TYPES.map(stepLine),\n '',\n 'Rules:',\n '- A step that names a list, campaign, workflow, webhook or dataset writes it as reference, in the description’s own words, such as \"newsletter\". The platform finds the site’s record by those words. Never write an id for one.',\n '- A form is named by the id or the name the request lists: formId on contactCreated, formName on formSubmission. A form the description names that the request does not list is written in the description’s words.',\n '- A deal stage in a condition on stageId or previousStageId is written as the stage’s name.',\n `- A lifecycle stage is one of: ${CONTACT_LIFECYCLE_STAGES.join(', ')}. A new lead is the stage lead.`,\n `- The trigger holds at most ${ACTION_MAX_CONDITIONS} conditions; combinator is and unless the description means any of them. A step's when holds one condition of its own at most, over the same fields. After waitForEvent, when on the field \"${AI_AUTOMATION_TIMED_OUT_FIELD}\" with notEmpty means the wait ran out of time.`,\n `- At most ${ACTION_MAX_STEPS} steps. minutes are whole minutes, at most ${AI_AUTOMATION_WAIT_MINUTES.max}; dueInDays is from 0 to ${CRM_TASK_MAX_DUE_DAYS}; addContactTag’s text is at most ${CONTACT_TAG_MAX_LENGTH} characters.`,\n '- assignContactOwner’s text is \"round robin\", or the teammate’s email address when the description gives one. A teammate named without an address goes in notes.',\n '- Write an email’s subject and body, and any text a person reads, in the site’s voice and the language of the description. Where the description lacks a fact such as a phone number, an address or a price, write a short description of it in square brackets instead of inventing it.',\n '- A trigger or a step the request says this workspace cannot use is never used. When the description needs one, or needs something no trigger or step here does, answer with unsupported set to the reason, no steps, and trigger event formSubmission with no conditions. unsupported is empty on every automation that is built.',\n '- notes holds what the person must decide that the automation cannot hold, in at most three short sentences; empty when there is nothing.',\n '- The automation is drafted switched off, and a person reviews it and switches it on. Never ask to turn it on or to run it.',\n ].join('\\n'),\n cacheBreakpoint: true,\n },\n]\n\n/** The explaining instructions, byte-identical on every request so they cache. */\nexport const AI_JOB_WORKFLOW_EXPLAIN_INSTRUCTIONS: readonly AiSystemBlock[] = [\n {\n text: [\n `You explain an automation on a website builder to the person who owns the site, in plain words for someone who does not write code. You are shown the automation as it is set up — what starts it, its conditions and its steps in order — and, when you are asked why a run failed, what the run history recorded about that run. Answer by calling ${AI_WORKFLOW_EXPLANATION_TOOL_NAME} exactly once.`,\n '',\n '- summary: in one or two sentences, what the automation does; or, for a failed run, the most likely reason it failed.',\n '- points: what happens, step by step, in the order it happens; or, for a failed run, what the run did and where it went wrong. One sentence each.',\n '- suggestions: what the person can check or change — a list, campaign or workflow the site no longer has, a condition that can never match, a placeholder nobody has filled in, a step the plan does not include. For a failed run, how to fix it. Empty when there is nothing to suggest.',\n '',\n 'Rules:',\n '- Explain only what you are shown. Never guess at settings, records or history you were not given, and never say you changed anything: you change nothing.',\n '- Name triggers and steps the way you are shown them, not by internal names.',\n '- A value in square brackets is a placeholder nobody has filled in yet.',\n '- Personal details were removed from what you are shown, and are written as [email address] or a named teammate. Never ask for them.',\n '- Write plain text, with no markdown and no HTML.',\n ].join('\\n'),\n cacheBreakpoint: true,\n },\n]\n\n// ── The user turns ────────────────────────────────────────────────────────\n\nfunction yesNo(value: boolean): string {\n return value ? 'yes' : 'no'\n}\n\n/**\n * What a draft is asked from: the workspace's reach, the site's forms and\n * datasets, and the brief. A revision adds the saved action's outline, and its\n * brief is the change asked for.\n */\nexport function aiJobWorkflowDraftPrompt(input: {\n brief: string\n capabilities: AiAutomationCapabilities\n records: Pick<AiAutomationRecords, 'forms' | 'datasets'>\n /** The saved action a revision starts from, as its outline. */\n revising?: { outline: string } | null\n /** A workspace's automation (AGL-3603): what it may start on and do. */\n org?: AiOrgAutomationVocabulary | null\n}): string {\n const { crm, webhooks, bookings } = input.capabilities\n const forms = input.records.forms.slice(0, AI_WORKFLOW_FORMS_LISTED)\n const datasets = input.records.datasets.slice(0, AI_WORKFLOW_DATASETS_LISTED)\n return [\n ...(input.org\n ? [\n 'This automation belongs to the workspace: it runs on each site it is placed on, as that site. It may start only on these triggers, and use only these steps; anything else, even if listed above, is never used.',\n `Triggers: ${input.org.triggers.join(', ')}.`,\n `Steps: ${input.org.steps.join(', ')}.`,\n ]\n : []),\n `This workspace can use: the CRM — ${yesNo(crm)}; webhooks — ${yesNo(webhooks)}; bookings — ${yesNo(bookings)}.`,\n ...(input.org\n ? ['Forms: an automation of the workspace runs on several sites, so none is listed; a form is named in the description’s words.']\n : [\n 'Forms on this site (id · name · fields):',\n ...(forms.length\n ? forms.map(\n (form) =>\n `- ${form.id} · ${form.name} · ${form.fields.slice(0, AI_WORKFLOW_FORM_FIELDS_LISTED).join(', ') || 'no fields'}`,\n )\n : ['- none']),\n ]),\n input.org ? 'Datasets in this workspace (name):' : 'Datasets on this site (name):',\n ...(datasets.length ? datasets.map((dataset) => `- ${dataset.name}`) : ['- none']),\n ...(input.revising\n ? [\n 'The automation to change, as it is set up now:',\n input.revising.outline,\n 'Answer with the whole automation as it should be after the change below. Keep every condition and step the change does not touch as it is, with the same records named in the same words, and keep its name unless the change asks for another. A detail shown as [email address] or a named teammate is written as a placeholder in square brackets.',\n `Change: ${input.brief.slice(0, AI_JOB_BRIEF_MAX_CHARS)}`,\n ]\n : [`Brief: ${input.brief.slice(0, AI_JOB_BRIEF_MAX_CHARS)}`]),\n ].join('\\n')\n}\n\n/** What an explanation is asked from: the mode, the outlines, and the member's question. */\nexport function aiJobWorkflowExplainPrompt(input: {\n brief: string\n mode: 'explain' | 'diagnose'\n outline: string\n run: string | null\n}): string {\n return [\n input.mode === 'diagnose'\n ? 'Asked: why a run of this automation failed.'\n : 'Asked: what this automation does.',\n input.outline,\n ...(input.run ? [input.run] : []),\n `Question: ${input.brief.slice(0, AI_JOB_BRIEF_MAX_CHARS)}`,\n ].join('\\n\\n')\n}\n\n/** The routing row's thinking and effort, as a generation input spells them. */\nfunction routedEffort(): Pick<AiCustomGenerationInput<unknown>, 'thinking' | 'effort'> {\n const row = AI_ROUTING_TABLE['job.workflow']\n return {\n ...(row.thinking ? { thinking: row.thinking } : {}),\n ...(row.effort ? { effort: row.effort } : {}),\n }\n}\n\n/** The draft generation as the doctrine's loop runs it; the eval harness records one through this same call. */\nexport function aiJobWorkflowDraftGeneration(request: {\n brief: string\n capabilities: AiAutomationCapabilities\n records: Pick<AiAutomationRecords, 'forms' | 'datasets'>\n revising?: { outline: string } | null\n org?: AiOrgAutomationVocabulary | null\n model: string\n signal?: AbortSignal\n}): AiCustomGenerationInput<AiAutomationAnswer> {\n const org = request.org\n const check: AiGenerationCheck<AiAutomationAnswer> = (answer) =>\n org\n ? holdAiAutomationAnswerToVocabulary(readAiAutomationAnswer(answer, request.capabilities), org)\n : readAiAutomationAnswer(answer, request.capabilities)\n return {\n step: 'job.workflow',\n model: request.model,\n instructions: AI_JOB_WORKFLOW_DRAFT_INSTRUCTIONS,\n messages: [{ role: 'user', content: aiJobWorkflowDraftPrompt(request) }],\n tool: aiAutomationTool(),\n maxTokens: AI_JOB_WORKFLOW_STEP_BUDGET.maxTokens(request.model),\n ...routedEffort(),\n ...(request.signal ? { signal: request.signal } : {}),\n check,\n }\n}\n\n/** The explanation generation as the doctrine's loop runs it. */\nexport function aiJobWorkflowExplainGeneration(request: {\n brief: string\n mode: 'explain' | 'diagnose'\n outline: string\n run: string | null\n model: string\n signal?: AbortSignal\n}): AiCustomGenerationInput<AiWorkflowExplanation> {\n return {\n step: 'job.workflow',\n model: request.model,\n instructions: AI_JOB_WORKFLOW_EXPLAIN_INSTRUCTIONS,\n messages: [{ role: 'user', content: aiJobWorkflowExplainPrompt(request) }],\n tool: aiWorkflowExplanationTool(),\n maxTokens: AI_JOB_WORKFLOW_STEP_BUDGET.maxTokens(request.model),\n ...routedEffort(),\n ...(request.signal ? { signal: request.signal } : {}),\n check: readAiWorkflowExplanation,\n }\n}\n\n// ── The runner ────────────────────────────────────────────────────────────\n\nexport interface AiJobWorkflowStepDeps {\n /** How the automation draft writer is found; the core's registry otherwise. */\n writerFor?: AiPluginDraftWriterLookup\n readRecords?: typeof readAiAutomationRecords\n readOrgRecords?: typeof readAiOrgAutomationRecords\n readTarget?: typeof readAiWorkflowTarget\n readRun?: typeof readAiWorkflowRun\n readFunctions?: typeof readAiWorkflowFunctions\n}\n\n/** A job label: the automation's name in quotes, cut to a line. */\nfunction quotedName(name: string): string {\n const clean = name.replace(/\\s+/g, ' ').trim()\n return `“${clean.length > 60 ? `${clean.slice(0, 60)}…` : clean}”`\n}\n\nexport function createAiJobWorkflowStep(deps: AiJobWorkflowStepDeps = {}): AiJobStepRunner {\n const writerFor = deps.writerFor ?? aiPluginDraftWriter\n const readRecords = deps.readRecords ?? readAiAutomationRecords\n const readOrgRecords = deps.readOrgRecords ?? readAiOrgAutomationRecords\n const readTarget = deps.readTarget ?? readAiWorkflowTarget\n const readRun = deps.readRun ?? readAiWorkflowRun\n const readFunctions = deps.readFunctions ?? readAiWorkflowFunctions\n return async ({ job, now, signal, firestore, org, modelFor }): Promise<AiJobStepOutcome> => {\n // The switch's answer for this job, else the routing table's (AGL-2942).\n const model = modelFor?.('job.workflow') ?? aiJobWorkflowModel()\n const unspent = (failure: string): AiJobStepOutcome => ({ ...aiUnspentOutcome(model), failure })\n const inputs = parseAiWorkflowJobInputs(job.inputs)\n if (typeof inputs === 'string') return unspent(inputs)\n\n if (inputs.mode === 'draft' && inputs.scope === 'org') {\n // A workspace's automation (AGL-3603): no site, and nothing written.\n const orgData = (org ?? (await firestore.collection('orgs').doc(job.orgId).get()).data() ?? null) as Record<\n string,\n unknown\n > | null\n if (!checkEntitlement(orgData as Partial<AglynOrgBilling> | null, 'actions')) {\n return aiUnspentOutcome(model, { review: aiLimitReview(AI_ORG_AUTOMATION_PLAN_COPY) })\n }\n const capabilities = aiAutomationCapabilities(orgData)\n const vocabulary = { triggers: inputs.triggers, steps: inputs.steps }\n const records = await readOrgRecords(firestore, { orgId: job.orgId, crm: capabilities.crm })\n const generation = await runValidatedGeneration(\n 'workflow',\n aiJobWorkflowDraftGeneration({\n brief: job.brief,\n capabilities,\n records,\n org: vocabulary,\n model,\n ...(signal ? { signal } : {}),\n }),\n )\n const spent = { ...aiGenerationSpent(generation), ...(generation.effort ? { effort: generation.effort } : {}) }\n if (generation.status === 'refused') return { ...spent, refused: true }\n if (generation.status === 'needs_input') return { ...spent, review: aiDoctrineReview(generation) }\n const answer = generation.value\n if (answer.unsupported) {\n return {\n ...spent,\n failure:\n answer.unsupported === 'no-trigger' || answer.unsupported === 'no-step'\n ? AI_ORG_AUTOMATION_UNSUPPORTED_COPY\n : AI_AUTOMATION_UNSUPPORTED_COPY[answer.unsupported],\n }\n }\n if (!answer.steps.length) return { ...spent, failure: AI_WORKFLOW_NO_DRAFT_COPY }\n const draft = aiAutomationDraft(answer, records)\n return { ...spent, outputs: [orgProposalOutput(draft, answer.notes)] }\n }\n\n const hostId = job.hostId\n if (!hostId) return unspent(AI_WORKFLOW_NO_SITE_COPY)\n // The door checks the caller's membership of the org the job is metered\n // against; the site is checked against that org here, before anything\n // about it is read into a prompt.\n const host = await firestore.collection('hosts').doc(hostId).get()\n if (!host.exists || host.get('orgId') !== job.orgId) return unspent(AI_WORKFLOW_NO_SITE_COPY)\n const subdomain = host.get('subdomain')\n const hostSubdomain = typeof subdomain === 'string' && subdomain ? subdomain : null\n const orgData = (org ?? (await firestore.collection('orgs').doc(job.orgId).get()).data() ?? null) as Record<\n string,\n unknown\n > | null\n const capabilities = aiAutomationCapabilities(orgData)\n\n if (inputs.mode === 'draft') {\n const writer = writerFor(AI_AUTOMATION_RESOURCE)\n if (!writer) return unspent(AI_WORKFLOW_UNAVAILABLE_COPY)\n const place = { hostId, hostSubdomain }\n // What an earlier run of this same job already wrote, under the id the job recorded.\n const draftId = aiJobDraftId(job, 'workflow')\n const written = await writer.read({ hostId, id: draftId })\n if (written) {\n return aiUnspentOutcome(model, {\n outputs: [draftOutput(written.id, written.name, place, 'It is off until you switch it on.')],\n })\n }\n const context = { orgId: job.orgId, hostId, uid: job.createdBy, org: orgData, now }\n const refusal = await writer.refusal(context)\n if (refusal) {\n return refusal.status === 403\n ? aiUnspentOutcome(model, { review: aiLimitReview(refusal.error) })\n : unspent(refusal.error)\n }\n const records = await readRecords(firestore, { orgId: job.orgId, hostId, crm: capabilities.crm })\n const generation = await runValidatedGeneration(\n 'workflow',\n aiJobWorkflowDraftGeneration({\n brief: job.brief,\n capabilities,\n records,\n model,\n ...(signal ? { signal } : {}),\n }),\n )\n const spent = { ...aiGenerationSpent(generation), ...(generation.effort ? { effort: generation.effort } : {}) }\n if (generation.status === 'refused') return { ...spent, refused: true }\n if (generation.status === 'needs_input') return { ...spent, review: aiDoctrineReview(generation) }\n const answer = generation.value\n if (answer.unsupported) return { ...spent, failure: AI_AUTOMATION_UNSUPPORTED_COPY[answer.unsupported] }\n if (!answer.steps.length) return { ...spent, failure: AI_WORKFLOW_NO_DRAFT_COPY }\n const draft = aiAutomationDraft(answer, records)\n const write = await writer.write({\n ...context,\n id: draftId,\n name: draft.action.name,\n content: { action: draft.action },\n })\n if (write.ok === false) {\n return write.status === 403\n ? { ...spent, review: aiLimitReview(write.error) }\n : { ...spent, failure: AI_WORKFLOW_SAVE_FAILURE_COPY }\n }\n return {\n ...spent,\n outputs: [draftOutput(write.id, write.name, place, aiAutomationDraftNote(draft, answer.notes))],\n }\n }\n\n if (inputs.mode === 'revise') {\n const writer = writerFor(AI_AUTOMATION_RESOURCE)\n if (!writer) return unspent(AI_WORKFLOW_UNAVAILABLE_COPY)\n const place = { hostId, hostSubdomain }\n const draftId = aiJobDraftId(job, 'workflow')\n const written = await writer.read({ hostId, id: draftId })\n if (written) {\n return aiUnspentOutcome(model, {\n outputs: [draftOutput(written.id, written.name, place, 'It is off until you switch it on.')],\n })\n }\n const saved = await readTarget({ hostId, type: 'action', id: inputs.targetId })\n if (!saved) return unspent(AI_WORKFLOW_GONE_COPY)\n if (saved.type !== 'action') return unspent(AI_WORKFLOW_REVISE_WORKFLOW_COPY)\n const blocker = aiActionReviseBlocker(saved.action)\n if (blocker) return unspent(AI_WORKFLOW_REVISE_BLOCKER_COPY[blocker])\n const context = { orgId: job.orgId, hostId, uid: job.createdBy, org: orgData, now }\n const refusal = await writer.refusal(context)\n if (refusal) {\n return refusal.status === 403\n ? aiUnspentOutcome(model, { review: aiLimitReview(refusal.error) })\n : unspent(refusal.error)\n }\n const records = await readRecords(firestore, { orgId: job.orgId, hostId, crm: capabilities.crm })\n const generation = await runValidatedGeneration(\n 'workflow',\n aiJobWorkflowDraftGeneration({\n brief: job.brief,\n capabilities,\n records,\n revising: { outline: aiActionOutline(saved.action, records) },\n model,\n ...(signal ? { signal } : {}),\n }),\n )\n const spent = { ...aiGenerationSpent(generation), ...(generation.effort ? { effort: generation.effort } : {}) }\n if (generation.status === 'refused') return { ...spent, refused: true }\n if (generation.status === 'needs_input') return { ...spent, review: aiDoctrineReview(generation) }\n const answer = generation.value\n if (answer.unsupported) return { ...spent, failure: AI_AUTOMATION_UNSUPPORTED_COPY[answer.unsupported] }\n if (!answer.steps.length) return { ...spent, failure: AI_WORKFLOW_NO_DRAFT_COPY }\n const draft = aiAutomationDraft(answer, records)\n // The copy never takes the saved action's name: two rows by one name\n // in the list are two rows nobody can tell apart.\n const name =\n draft.action.name.trim() && draft.action.name.trim() !== saved.name.trim()\n ? draft.action.name\n : aiRevisedName(saved.name)\n const write = await writer.write({ ...context, id: draftId, name, content: { action: { ...draft.action, name } } })\n if (write.ok === false) {\n return write.status === 403\n ? { ...spent, review: aiLimitReview(write.error) }\n : { ...spent, failure: AI_WORKFLOW_SAVE_FAILURE_COPY }\n }\n const changes = aiAutomationRevisionChanges(saved.action, draft.action, aiStepLabel)\n return {\n ...spent,\n outputs: [\n draftOutput(\n write.id,\n write.name,\n place,\n aiAutomationRevisionNote({ fromName: saved.name, changes, draft, notes: answer.notes }),\n ),\n ],\n }\n }\n\n const target = await readTarget({ hostId, type: inputs.targetType, id: inputs.targetId })\n if (!target) return unspent(AI_WORKFLOW_GONE_COPY)\n let run: AiRunRecord | null = null\n if (inputs.mode === 'diagnose') {\n const read = await readRun(firestore, { hostId, targetId: inputs.targetId, runId: inputs.runId })\n if (read.ok === false) {\n return unspent(read.reason === 'gone' ? AI_WORKFLOW_RUN_GONE_COPY : AI_WORKFLOW_RUN_NOT_FAILED_COPY)\n }\n run = read.run\n }\n // Only the kinds of record the automation's steps name are read, to say\n // whether each still exists.\n const outline =\n target.type === 'action'\n ? aiActionOutline(\n target.action,\n await readRecords(firestore, {\n orgId: job.orgId,\n hostId,\n crm: false,\n only: aiActionRecordKinds(target.action),\n }),\n )\n : aiWorkflowOutline(target.workflow, await readFunctions(firestore, hostId))\n const generation = await runValidatedGeneration(\n 'workflow',\n aiJobWorkflowExplainGeneration({\n brief: job.brief,\n mode: inputs.mode,\n outline,\n run: run ? aiRunOutline(run) : null,\n model,\n ...(signal ? { signal } : {}),\n }),\n )\n const spent = { ...aiGenerationSpent(generation), ...(generation.effort ? { effort: generation.effort } : {}) }\n if (generation.status === 'refused') return { ...spent, refused: true }\n if (generation.status === 'needs_input') return { ...spent, failure: AI_WORKFLOW_NO_EXPLANATION_COPY }\n const output: AiJobOutput = {\n resource: 'text',\n // An explanation has no document of its own; the id names what it is\n // within the job, as a text output's `draft` does.\n id: inputs.mode === 'diagnose' ? 'diagnosis' : 'explanation',\n hostId,\n hostSubdomain,\n label:\n inputs.mode === 'diagnose'\n ? `Why a run of ${quotedName(target.name)} failed`\n : `How ${quotedName(target.name)} works`,\n text: aiWorkflowExplanationText(generation.value, inputs.mode),\n }\n return { ...spent, outputs: [output] }\n }\n}\n\n/** What a person is told when a workspace's automation would need what only one site runs. */\nexport const AI_ORG_AUTOMATION_UNSUPPORTED_COPY =\n 'An org automation cannot do what this description asks — it starts only on what the server sees and uses only the steps every site runs the same way — so nothing was drafted. Build it as an action on the site instead.'\n\n/**\n * A workspace's drafted automation as the output that proposes it\n * (AGL-3603): the stored shape the Org automations editor opens — its name,\n * trigger and steps, never a placement, which the person picks — and what\n * to fill in before switching it on.\n */\nexport function orgProposalOutput(draft: AiAutomationDraft, notes: readonly string[]): AiJobOutput {\n const { name, trigger, steps } = draft.action\n return {\n resource: AI_ORG_AUTOMATION_RESOURCE as AiJobOutput['resource'],\n // A proposal has no document; the id names what it is within the job.\n id: 'proposal',\n hostId: null,\n hostSubdomain: null,\n label: name,\n note: [\n 'Nothing is saved yet: it opens in the org automation editor, switched off, where you choose the sites it runs on and save it.',\n ...(draft.placeholders.length\n ? [\n `Fill in ${draft.placeholders.length === 1 ? 'its placeholder' : `its ${draft.placeholders.length} placeholders`} before switching it on.`,\n ]\n : []),\n ...notes,\n ].join(' '),\n proposal: { automation: { name, trigger, steps } } as unknown as Record<string, unknown>,\n }\n}\n\n/** The name a revised copy is saved under when the change asked for no other. */\nexport function aiRevisedName(name: string): string {\n const clean = name.replace(/\\s+/g, ' ').trim() || 'Automation'\n return `${clean.length > 80 ? clean.slice(0, 80) : clean} (revised)`\n}\n\nfunction draftOutput(\n id: string,\n name: string,\n place: { hostId: string; hostSubdomain: string | null },\n note: string,\n): AiJobOutput {\n return {\n resource: 'workflow',\n id,\n versionId: null,\n hostId: place.hostId,\n hostSubdomain: place.hostSubdomain,\n label: name,\n note,\n }\n}\n\nexport const runAiJobWorkflowStep = createAiJobWorkflowStep()\n\n// ── Admission ─────────────────────────────────────────────────────────────\n\n/**\n * Whether the plugin that writes automations runs on this site: switched on,\n * and past its release flag. The owner is whichever plugin registered the\n * automation writer (AGL-3080); none registered is the same refusal.\n */\nasync function workflowsPluginRefusal(\n context: Parameters<AiJobAdmission>[0],\n hostId: string,\n): Promise<AiJobAdmissionRefusal | null> {\n const owner = aiPluginDraftOwner(AI_AUTOMATION_RESOURCE)\n if (!owner) return { status: 403, error: aiPluginDraftUnavailable('Automation') }\n const host = (await context.firestore.collection('hosts').doc(hostId).get()).data() ?? null\n const org = context.org as { enabledPlugins?: string[] } | null\n const released = await filterEnabledPluginsByReleaseFlags([owner], {\n orgId: context.orgId,\n authorization: null,\n })\n return released.includes(owner) && isHostPluginEnabled(org, host, owner)\n ? null\n : { status: 403, error: aiPluginDraftUnavailable('Automation') }\n}\n\n/**\n * Whether a workspace's automation may be drafted (AGL-3603): no site is\n * named, the plugin that keeps automations runs for the workspace and is\n * past its release flag, and the plan includes the actions builder that\n * section is sold under. Nothing is written, so no allowance is asked: the\n * section's save route counts its cap when the person saves.\n */\nasync function orgAutomationRefusal(context: Parameters<AiJobAdmission>[0]): Promise<AiJobAdmissionRefusal | null> {\n if (context.hostId) return { status: 400, error: 'An org automation belongs to no one site' }\n const owner = aiPluginDraftOwner(AI_AUTOMATION_RESOURCE)\n if (!owner) return { status: 403, error: AI_ORG_AUTOMATION_UNAVAILABLE_COPY }\n const org = context.org as ({ enabledPlugins?: string[] } & Partial<AglynOrgBilling>) | null\n const released = await filterEnabledPluginsByReleaseFlags([owner], {\n orgId: context.orgId,\n authorization: null,\n })\n if (!released.includes(owner) || !isPluginEnabled(org, owner)) {\n return { status: 403, error: AI_ORG_AUTOMATION_UNAVAILABLE_COPY }\n }\n return checkEntitlement(org, 'actions') ? null : { status: 403, error: AI_ORG_AUTOMATION_PLAN_COPY }\n}\n\nexport interface AiWorkflowJobAdmissionDeps {\n readTarget?: typeof readAiWorkflowTarget\n readRun?: typeof readAiWorkflowRun\n writerFor?: AiPluginDraftWriterLookup\n}\n\n/**\n * A workflow job is admitted for a site of its org with the Automation plugin\n * on. A draft also needs the draft writer and the member's room and role in\n * its own words; an explanation needs the automation it names to exist, and\n * a run's explanation a failed run of it.\n */\nexport function createAiWorkflowJobAdmission(deps: AiWorkflowJobAdmissionDeps = {}): AiJobAdmission {\n const readTarget = deps.readTarget ?? readAiWorkflowTarget\n const readRun = deps.readRun ?? readAiWorkflowRun\n return async (context) => {\n const inputs = parseAiWorkflowJobInputs(context.inputs)\n if (typeof inputs === 'string') return { status: 400, error: inputs }\n if (inputs.mode === 'draft' && inputs.scope === 'org') return orgAutomationRefusal(context)\n if (inputs.mode === 'draft') {\n return aiPluginDraftAdmissionRefusal(context, {\n kind: 'workflow',\n drafts: [{ resource: AI_AUTOMATION_RESOURCE, label: 'Automation' }],\n ...(deps.writerFor ? { writerFor: deps.writerFor } : {}),\n })\n }\n if (inputs.mode === 'revise') {\n // A revision writes a draft as a description does, and also needs the\n // action it starts from to exist and to hold only what AI writes.\n return aiPluginDraftAdmissionRefusal(context, {\n kind: 'workflow',\n drafts: [{ resource: AI_AUTOMATION_RESOURCE, label: 'Automation' }],\n ...(deps.writerFor ? { writerFor: deps.writerFor } : {}),\n ownCheck: async (hostId) => {\n const saved = await readTarget({ hostId, type: 'action', id: inputs.targetId })\n if (!saved || saved.type !== 'action') return { status: 404, error: AI_WORKFLOW_GONE_COPY }\n const blocker = aiActionReviseBlocker(saved.action)\n return blocker ? { status: 400, error: AI_WORKFLOW_REVISE_BLOCKER_COPY[blocker] } : null\n },\n })\n }\n const hostId = context.hostId\n if (!hostId) return { status: 400, error: 'Open the site the automation is on before asking about it' }\n const owner = await resolveOrgIdForHost(hostId)\n if (!owner || owner !== context.orgId) return { status: 404, error: 'Unknown site' }\n const plugin = await workflowsPluginRefusal(context, hostId)\n if (plugin) return plugin\n const target = await readTarget({ hostId, type: inputs.targetType, id: inputs.targetId })\n if (!target) return { status: 404, error: AI_WORKFLOW_GONE_COPY }\n if (inputs.mode === 'diagnose') {\n const run = await readRun(context.firestore, { hostId, targetId: inputs.targetId, runId: inputs.runId })\n if (run.ok === false) {\n return run.reason === 'gone'\n ? { status: 404, error: AI_WORKFLOW_RUN_GONE_COPY }\n : { status: 400, error: AI_WORKFLOW_RUN_NOT_FAILED_COPY }\n }\n }\n return null\n }\n}\n\nexport const aiWorkflowJobAdmission = createAiWorkflowJobAdmission()\n\n/** Registers the workflow step and the check a workflow job passes before it is created or resumed. */\nexport function registerAiWorkflowJob(): void {\n registerAiJobStep('workflow', runAiJobWorkflowStep, { minimumMs: AI_JOB_WORKFLOW_STEP_MINIMUM_MS })\n registerAiJobAdmission('workflow', aiWorkflowJobAdmission)\n}\n"],"names":["ACTION_MAX_CONDITIONS","ACTION_MAX_STEPS","CONTACT_LIFECYCLE_STAGES","CONTACT_TAG_MAX_LENGTH","CRM_TASK_MAX_DUE_DAYS","HOST_EVENT_PAYLOAD_KEYS","checkEntitlement","isHostPluginEnabled","isPluginEnabled","filterEnabledPluginsByReleaseFlags","resolveOrgIdForHost","aiAutomationDraft","aiAutomationDraftNote","aiAutomationRevisionChanges","aiAutomationRevisionNote","AI_AUTOMATION_TIMED_OUT_FIELD","AI_AUTOMATION_WAIT_MINUTES","aiStepLabel","aiActionOutline","aiActionRecordKinds","aiRunOutline","aiWorkflowExplanationText","aiWorkflowOutline","AI_AUTOMATION_NEED_LABELS","AI_AUTOMATION_RESOURCE","AI_AUTOMATION_STEP_NEEDS","AI_AUTOMATION_STEP_TYPES","AI_AUTOMATION_TRIGGER_NEEDS","AI_AUTOMATION_TRIGGERS","AI_AUTOMATION_UNSUPPORTED_COPY","AI_WORKFLOW_GONE_COPY","AI_WORKFLOW_NO_DRAFT_COPY","AI_WORKFLOW_NO_EXPLANATION_COPY","AI_WORKFLOW_NO_SITE_COPY","AI_WORKFLOW_RUN_GONE_COPY","AI_WORKFLOW_RUN_NOT_FAILED_COPY","AI_WORKFLOW_SAVE_FAILURE_COPY","AI_WORKFLOW_REVISE_BLOCKER_COPY","AI_WORKFLOW_REVISE_WORKFLOW_COPY","AI_WORKFLOW_UNAVAILABLE_COPY","AI_ORG_AUTOMATION_PLAN_COPY","AI_ORG_AUTOMATION_RESOURCE","AI_ORG_AUTOMATION_UNAVAILABLE_COPY","aiActionReviseBlocker","aiAutomationCapabilities","aiAutomationTriggerLabel","parseAiWorkflowJobInputs","AI_STEP_TIERS","AI_ROUTING_TABLE","aiModelForStep","runValidatedGeneration","AI_AUTOMATION_TOOL_NAME","AI_WORKFLOW_EXPLANATION_TOOL_NAME","aiAutomationTool","aiWorkflowExplanationTool","holdAiAutomationAnswerToVocabulary","readAiAutomationAnswer","readAiWorkflowExplanation","registerAiJobAdmission","aiJobStepBudget","aiJobDraftId","aiDoctrineReview","aiGenerationSpent","aiLimitReview","aiUnspentOutcome","aiPluginDraftAdmissionRefusal","aiPluginDraftOwner","aiPluginDraftUnavailable","aiPluginDraftWriter","AI_JOB_BRIEF_MAX_CHARS","registerAiJobStep","readAiAutomationRecords","readAiOrgAutomationRecords","readAiWorkflowFunctions","readAiWorkflowRun","readAiWorkflowTarget","AI_WORKFLOW_RECORDS_READ_MS","AI_JOB_WORKFLOW_STEP_BUDGET","tier","maxTokens","lookups","ownReadsMs","AI_JOB_WORKFLOW_STEP_MINIMUM_MS","minimumMs","aiJobWorkflowModel","AI_WORKFLOW_FORMS_LISTED","AI_WORKFLOW_FORM_FIELDS_LISTED","AI_WORKFLOW_DATASETS_LISTED","STEP_USES","sendEmail","notifyAdmins","enrollList","assignCampaign","runWorkflow","datasetAppend","updateDataset","webhookPost","siteAlert","wait","waitForEvent","exitFlow","setContactStage","addContactTag","assignContactOwner","createCrmTask","logCrmActivity","triggerLine","event","fields","join","need","stepLine","type","AI_JOB_WORKFLOW_DRAFT_INSTRUCTIONS","text","map","max","cacheBreakpoint","AI_JOB_WORKFLOW_EXPLAIN_INSTRUCTIONS","yesNo","value","aiJobWorkflowDraftPrompt","input","crm","webhooks","bookings","capabilities","forms","records","slice","datasets","org","triggers","steps","length","form","id","name","dataset","revising","outline","brief","aiJobWorkflowExplainPrompt","mode","run","routedEffort","row","thinking","effort","aiJobWorkflowDraftGeneration","request","check","answer","step","model","instructions","messages","role","content","tool","signal","aiJobWorkflowExplainGeneration","quotedName","clean","replace","trim","createAiJobWorkflowStep","deps","writerFor","readRecords","readOrgRecords","readTarget","readRun","readFunctions","job","now","firestore","modelFor","unspent","failure","inputs","scope","orgData","collection","doc","orgId","get","data","review","vocabulary","generation","spent","status","refused","unsupported","AI_ORG_AUTOMATION_UNSUPPORTED_COPY","draft","outputs","orgProposalOutput","notes","hostId","host","exists","subdomain","hostSubdomain","writer","place","draftId","written","read","draftOutput","context","uid","createdBy","refusal","error","write","action","ok","saved","targetId","blocker","aiRevisedName","changes","fromName","target","targetType","runId","reason","only","workflow","output","resource","label","trigger","note","placeholders","proposal","automation","versionId","runAiJobWorkflowStep","workflowsPluginRefusal","owner","released","authorization","includes","orgAutomationRefusal","createAiWorkflowJobAdmission","kind","drafts","ownCheck","plugin","aiWorkflowJobAdmission","registerAiWorkflowJob"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,qBAAqB,EAAEC,gBAAgB,QAAQ,2CAA0C;AAClG,SACEC,wBAAwB,EACxBC,sBAAsB,EACtBC,qBAAqB,QAChB,6BAA4B;AACnC,SAASC,uBAAuB,QAA4B,qCAAoC;AAChG,SAASC,gBAAgB,QAAQ,2CAA0C;AAC3E,SAASC,mBAAmB,EAAEC,eAAe,QAAQ,8CAA6C;AAElG,SAASC,kCAAkC,QAAQ,gDAA+C;AAClG,SAASC,mBAAmB,QAAQ,gDAA+C;AACnF,SACEC,iBAAiB,EACjBC,qBAAqB,EACrBC,2BAA2B,EAC3BC,wBAAwB,QAGnB,kCAA8B;AACrC,SACEC,6BAA6B,EAC7BC,0BAA0B,EAC1BC,WAAW,QACN,mCAA+B;AACtC,SACEC,eAAe,EACfC,mBAAmB,EACnBC,YAAY,EACZC,yBAAyB,EACzBC,iBAAiB,QAEZ,oCAAgC;AAEvC,SACEC,yBAAyB,EACzBC,sBAAsB,EACtBC,wBAAwB,EACxBC,wBAAwB,EACxBC,2BAA2B,EAC3BC,sBAAsB,EACtBC,8BAA8B,EAC9BC,qBAAqB,EACrBC,yBAAyB,EACzBC,+BAA+B,EAC/BC,wBAAwB,EACxBC,yBAAyB,EACzBC,+BAA+B,EAC/BC,6BAA6B,EAC7BC,+BAA+B,EAC/BC,gCAAgC,EAChCC,4BAA4B,EAC5BC,2BAA2B,EAC3BC,0BAA0B,EAC1BC,kCAAkC,EAClCC,qBAAqB,EACrBC,wBAAwB,EACxBC,wBAAwB,EACxBC,wBAAwB,QAInB,8BAA0B;AACjC,SAASC,aAAa,QAAQ,0BAAsB;AACpD,SAASC,gBAAgB,EAAEC,cAAc,QAAQ,0BAAsB;AACvE,SACEC,sBAAsB,QAGjB,4BAAwB;AAE/B,SACEC,uBAAuB,EACvBC,iCAAiC,EACjCC,gBAAgB,EAChBC,yBAAyB,EACzBC,kCAAkC,EAClCC,sBAAsB,EACtBC,yBAAyB,QAGpB,+BAA2B;AAClC,SAASC,sBAAsB,QAAyD,wBAAoB;AAC5G,SAASC,eAAe,QAAQ,qBAAiB;AACjD,SAASC,YAAY,QAAQ,wBAAoB;AACjD,SAASC,gBAAgB,EAAEC,iBAAiB,EAAEC,aAAa,EAAEC,gBAAgB,QAAQ,yBAAqB;AAC1G,SACEC,6BAA6B,EAC7BC,kBAAkB,EAClBC,wBAAwB,EACxBC,mBAAmB,QAEd,4BAAwB;AAC/B,SAASC,sBAAsB,QAAqD,wBAAoB;AACxG,SAASC,iBAAiB,QAAQ,eAAW;AAC7C,SACEC,uBAAuB,EACvBC,0BAA0B,EAC1BC,uBAAuB,EACvBC,iBAAiB,EACjBC,oBAAoB,QACf,2BAAuB;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuDC,GAED,wFAAwF,GACxF,OAAO,MAAMC,8BAA8B,KAAK;AAEhD;;;;;;CAMC,GACD,OAAO,MAAMC,8BAA8BlB,gBAAgB;IACzDmB,MAAM/B,aAAa,CAAC,eAAe;IACnCgC,WAAW/B,gBAAgB,CAAC,eAAe,CAAC+B,SAAS;IACrDC,SAAS;IACTC,YAAYL;AACd,GAAE;AAEF,6DAA6D,GAC7D,OAAO,MAAMM,kCAAkCL,4BAA4BM,SAAS,CAAA;AAEpF,mDAAmD,GACnD,OAAO,SAASC;IACd,OAAOnC,eAAe;AACxB;AAEA,iFAAiF,GACjF,OAAO,MAAMoC,2BAA2B,GAAE;AAC1C,OAAO,MAAMC,iCAAiC,GAAE;AAChD,OAAO,MAAMC,8BAA8B,GAAE;AAE7C,6EAA6E;AAE7E,MAAMC,YAA4D;IAChEC,WAAW;IACXC,cAAc;IACdC,YAAY;IACZC,gBAAgB;IAChBC,aAAa;IACbC,eAAe;IACfC,eAAe;IACfC,aAAa;IACbC,WAAW;IACXC,MAAM;IACNC,cAAc;IACdC,UAAU;IACVC,iBAAiB;IACjBC,eAAe;IACfC,oBAAoB;IACpBC,eAAe;IACfC,gBAAgB;AAClB;AAEA,SAASC,YAAYC,KAAoB;QAIhCtG;IAHP,MAAMuG,SACJD,UAAU,mBACN,oHACA,EAACtG,iCAAAA,uBAAuB,CAACsG,MAAM,YAA9BtG,iCAAkC,EAAE,EAAEwG,IAAI,CAAC,SAAS;IAC3D,MAAMC,OAAOnF,2BAA2B,CAACgF,MAAM;IAC/C,OAAO,CAAC,EAAE,EAAEA,MAAM,EAAE,EAAE9D,yBAAyB8D,OAAO,WAAW,EAAEC,OAAO,CAAC,EACzEE,OAAO,CAAC,OAAO,EAAEvF,yBAAyB,CAACuF,KAAK,CAAC,CAAC,CAAC,GAAG,IACtD;AACJ;AAEA,SAASC,SAASC,IAA0B;IAC1C,MAAMF,OAAOrF,wBAAwB,CAACuF,KAAK;IAC3C,OAAO,CAAC,EAAE,EAAEA,KAAK,EAAE,EAAE/F,YAAY+F,MAAM,EAAE,EAAExB,SAAS,CAACwB,KAAK,CAAC,CAAC,EAC1DF,OAAO,CAAC,OAAO,EAAEvF,yBAAyB,CAACuF,KAAK,CAAC,CAAC,CAAC,GAAG,IACtD;AACJ;AAEA;;;;CAIC,GACD,OAAO,MAAMG,qCAA+D;IAC1E;QACEC,MAAM;YACJ,CAAC,yNAAyN,EAAE/D,wBAAwB,+CAA+C,CAAC;YACpS;YACA;YACA;YACA;eACGvB,uBAAuBuF,GAAG,CAACT;YAC9B;YACA;eACGhF,yBAAyByF,GAAG,CAACJ;YAChC;YACA;YACA;YACA;YACA;YACA,CAAC,+BAA+B,EAAE7G,yBAAyB2G,IAAI,CAAC,MAAM,+BAA+B,CAAC;YACtG,CAAC,4BAA4B,EAAE7G,sBAAsB,4LAA4L,EAAEe,8BAA8B,+CAA+C,CAAC;YACjU,CAAC,UAAU,EAAEd,iBAAiB,2CAA2C,EAAEe,2BAA2BoG,GAAG,CAAC,yBAAyB,EAAEhH,sBAAsB,kCAAkC,EAAED,uBAAuB,YAAY,CAAC;YACnO;YACA;YACA;YACA;YACA;SACD,CAAC0G,IAAI,CAAC;QACPQ,iBAAiB;IACnB;CACD,CAAA;AAED,gFAAgF,GAChF,OAAO,MAAMC,uCAAiE;IAC5E;QACEJ,MAAM;YACJ,CAAC,qVAAqV,EAAE9D,kCAAkC,cAAc,CAAC;YACzY;YACA;YACA;YACA;YACA;YACA;YACA;YACA;YACA;YACA;YACA;SACD,CAACyD,IAAI,CAAC;QACPQ,iBAAiB;IACnB;CACD,CAAA;AAED,6EAA6E;AAE7E,SAASE,MAAMC,KAAc;IAC3B,OAAOA,QAAQ,QAAQ;AACzB;AAEA;;;;CAIC,GACD,OAAO,SAASC,yBAAyBC,KAQxC;IACC,MAAM,EAAEC,GAAG,EAAEC,QAAQ,EAAEC,QAAQ,EAAE,GAAGH,MAAMI,YAAY;IACtD,MAAMC,QAAQL,MAAMM,OAAO,CAACD,KAAK,CAACE,KAAK,CAAC,GAAG5C;IAC3C,MAAM6C,WAAWR,MAAMM,OAAO,CAACE,QAAQ,CAACD,KAAK,CAAC,GAAG1C;IACjD,OAAO;WACDmC,MAAMS,GAAG,GACT;YACE;YACA,CAAC,UAAU,EAAET,MAAMS,GAAG,CAACC,QAAQ,CAACvB,IAAI,CAAC,MAAM,CAAC,CAAC;YAC7C,CAAC,OAAO,EAAEa,MAAMS,GAAG,CAACE,KAAK,CAACxB,IAAI,CAAC,MAAM,CAAC,CAAC;SACxC,GACD,EAAE;QACN,CAAC,kCAAkC,EAAEU,MAAMI,KAAK,aAAa,EAAEJ,MAAMK,UAAU,aAAa,EAAEL,MAAMM,UAAU,CAAC,CAAC;WAC5GH,MAAMS,GAAG,GACT;YAAC;SAA8H,GAC/H;YACE;eACIJ,MAAMO,MAAM,GACZP,MAAMZ,GAAG,CACP,CAACoB,OACC,CAAC,EAAE,EAAEA,KAAKC,EAAE,CAAC,GAAG,EAAED,KAAKE,IAAI,CAAC,GAAG,EAAEF,KAAK3B,MAAM,CAACqB,KAAK,CAAC,GAAG3C,gCAAgCuB,IAAI,CAAC,SAAS,aAAa,IAErH;gBAAC;aAAS;SACf;QACLa,MAAMS,GAAG,GAAG,uCAAuC;WAC/CD,SAASI,MAAM,GAAGJ,SAASf,GAAG,CAAC,CAACuB,UAAY,CAAC,EAAE,EAAEA,QAAQD,IAAI,EAAE,IAAI;YAAC;SAAS;WAC7Ef,MAAMiB,QAAQ,GACd;YACE;YACAjB,MAAMiB,QAAQ,CAACC,OAAO;YACtB;YACA,CAAC,QAAQ,EAAElB,MAAMmB,KAAK,CAACZ,KAAK,CAAC,GAAG5D,yBAAyB;SAC1D,GACD;YAAC,CAAC,OAAO,EAAEqD,MAAMmB,KAAK,CAACZ,KAAK,CAAC,GAAG5D,yBAAyB;SAAC;KAC/D,CAACwC,IAAI,CAAC;AACT;AAEA,0FAA0F,GAC1F,OAAO,SAASiC,2BAA2BpB,KAK1C;IACC,OAAO;QACLA,MAAMqB,IAAI,KAAK,aACX,gDACA;QACJrB,MAAMkB,OAAO;WACTlB,MAAMsB,GAAG,GAAG;YAACtB,MAAMsB,GAAG;SAAC,GAAG,EAAE;QAChC,CAAC,UAAU,EAAEtB,MAAMmB,KAAK,CAACZ,KAAK,CAAC,GAAG5D,yBAAyB;KAC5D,CAACwC,IAAI,CAAC;AACT;AAEA,8EAA8E,GAC9E,SAASoC;IACP,MAAMC,MAAMlG,gBAAgB,CAAC,eAAe;IAC5C,OAAO,aACDkG,IAAIC,QAAQ,GAAG;QAAEA,UAAUD,IAAIC,QAAQ;IAAC,IAAI,CAAC,GAC7CD,IAAIE,MAAM,GAAG;QAAEA,QAAQF,IAAIE,MAAM;IAAC,IAAI,CAAC;AAE/C;AAEA,8GAA8G,GAC9G,OAAO,SAASC,6BAA6BC,OAQ5C;IACC,MAAMnB,MAAMmB,QAAQnB,GAAG;IACvB,MAAMoB,QAA+C,CAACC,SACpDrB,MACI5E,mCAAmCC,uBAAuBgG,QAAQF,QAAQxB,YAAY,GAAGK,OACzF3E,uBAAuBgG,QAAQF,QAAQxB,YAAY;IACzD,OAAO;QACL2B,MAAM;QACNC,OAAOJ,QAAQI,KAAK;QACpBC,cAAc1C;QACd2C,UAAU;YAAC;gBAAEC,MAAM;gBAAQC,SAASrC,yBAAyB6B;YAAS;SAAE;QACxES,MAAM1G;QACN0B,WAAWF,4BAA4BE,SAAS,CAACuE,QAAQI,KAAK;OAC3DT,gBACCK,QAAQU,MAAM,GAAG;QAAEA,QAAQV,QAAQU,MAAM;IAAC,IAAI,CAAC;QACnDT;;AAEJ;AAEA,+DAA+D,GAC/D,OAAO,SAASU,+BAA+BX,OAO9C;IACC,OAAO;QACLG,MAAM;QACNC,OAAOJ,QAAQI,KAAK;QACpBC,cAAcrC;QACdsC,UAAU;YAAC;gBAAEC,MAAM;gBAAQC,SAAShB,2BAA2BQ;YAAS;SAAE;QAC1ES,MAAMzG;QACNyB,WAAWF,4BAA4BE,SAAS,CAACuE,QAAQI,KAAK;OAC3DT,gBACCK,QAAQU,MAAM,GAAG;QAAEA,QAAQV,QAAQU,MAAM;IAAC,IAAI,CAAC;QACnDT,OAAO9F;;AAEX;AAcA,iEAAiE,GACjE,SAASyG,WAAWzB,IAAY;IAC9B,MAAM0B,QAAQ1B,KAAK2B,OAAO,CAAC,QAAQ,KAAKC,IAAI;IAC5C,OAAO,CAAC,CAAC,EAAEF,MAAM7B,MAAM,GAAG,KAAK,GAAG6B,MAAMlC,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC,GAAGkC,MAAM,CAAC,CAAC;AACpE;AAEA,OAAO,SAASG,wBAAwBC,OAA8B,CAAC,CAAC;QACpDA,iBACEA,mBACGA,sBACJA,kBACHA,eACMA;IALtB,MAAMC,aAAYD,kBAAAA,KAAKC,SAAS,YAAdD,kBAAkBnG;IACpC,MAAMqG,eAAcF,oBAAAA,KAAKE,WAAW,YAAhBF,oBAAoBhG;IACxC,MAAMmG,kBAAiBH,uBAAAA,KAAKG,cAAc,YAAnBH,uBAAuB/F;IAC9C,MAAMmG,cAAaJ,mBAAAA,KAAKI,UAAU,YAAfJ,mBAAmB5F;IACtC,MAAMiG,WAAUL,gBAAAA,KAAKK,OAAO,YAAZL,gBAAgB7F;IAChC,MAAMmG,iBAAgBN,sBAAAA,KAAKM,aAAa,YAAlBN,sBAAsB9F;IAC5C,OAAO,OAAO,EAAEqG,GAAG,EAAEC,GAAG,EAAEf,MAAM,EAAEgB,SAAS,EAAE7C,GAAG,EAAE8C,QAAQ,EAAE;kBAyDzC9C;QAxDjB,yEAAyE;QACzE,MAAMuB,gBAAQuB,4BAAAA,SAAW,kCAAmB7F;QAC5C,MAAM8F,UAAU,CAACC,UAAuC,aAAKnH,iBAAiB0F;gBAAQyB;;QACtF,MAAMC,SAAStI,yBAAyBgI,IAAIM,MAAM;QAClD,IAAI,OAAOA,WAAW,UAAU,OAAOF,QAAQE;QAE/C,IAAIA,OAAOrC,IAAI,KAAK,WAAWqC,OAAOC,KAAK,KAAK,OAAO;gBAEpClD;YADjB,qEAAqE;YACrE,MAAMmD,WAAWnD,QAAAA,cAAAA,MAAO,AAAC,CAAA,MAAM6C,UAAUO,UAAU,CAAC,QAAQC,GAAG,CAACV,IAAIW,KAAK,EAAEC,GAAG,EAAC,EAAGC,IAAI,cAArExD,QAA2E;YAI5F,IAAI,CAAC7H,iBAAiBgL,SAA4C,YAAY;gBAC5E,OAAOtH,iBAAiB0F,OAAO;oBAAEkC,QAAQ7H,cAAcvB;gBAA6B;YACtF;YACA,MAAMsF,eAAelF,yBAAyB0I;YAC9C,MAAMO,aAAa;gBAAEzD,UAAUgD,OAAOhD,QAAQ;gBAAEC,OAAO+C,OAAO/C,KAAK;YAAC;YACpE,MAAML,UAAU,MAAM0C,eAAeM,WAAW;gBAAES,OAAOX,IAAIW,KAAK;gBAAE9D,KAAKG,aAAaH,GAAG;YAAC;YAC1F,MAAMmE,aAAa,MAAM5I,uBACvB,YACAmG,6BAA6B;gBAC3BR,OAAOiC,IAAIjC,KAAK;gBAChBf;gBACAE;gBACAG,KAAK0D;gBACLnC;eACIM,SAAS;gBAAEA;YAAO,IAAI,CAAC;YAG/B,MAAM+B,QAAQ,aAAKjI,kBAAkBgI,aAAiBA,WAAW1C,MAAM,GAAG;gBAAEA,QAAQ0C,WAAW1C,MAAM;YAAC,IAAI,CAAC;YAC3G,IAAI0C,WAAWE,MAAM,KAAK,WAAW,OAAO,aAAKD;gBAAOE,SAAS;;YACjE,IAAIH,WAAWE,MAAM,KAAK,eAAe,OAAO,aAAKD;gBAAOH,QAAQ/H,iBAAiBiI;;YACrF,MAAMtC,SAASsC,WAAWtE,KAAK;YAC/B,IAAIgC,OAAO0C,WAAW,EAAE;gBACtB,OAAO,aACFH;oBACHZ,SACE3B,OAAO0C,WAAW,KAAK,gBAAgB1C,OAAO0C,WAAW,KAAK,YAC1DC,qCACAtK,8BAA8B,CAAC2H,OAAO0C,WAAW,CAAC;;YAE5D;YACA,IAAI,CAAC1C,OAAOnB,KAAK,CAACC,MAAM,EAAE,OAAO,aAAKyD;gBAAOZ,SAASpJ;;YACtD,MAAMqK,QAAQzL,kBAAkB6I,QAAQxB;YACxC,OAAO,aAAK+D;gBAAOM,SAAS;oBAACC,kBAAkBF,OAAO5C,OAAO+C,KAAK;iBAAE;;QACtE;QAEA,MAAMC,SAAS1B,IAAI0B,MAAM;QACzB,IAAI,CAACA,QAAQ,OAAOtB,QAAQjJ;QAC5B,wEAAwE;QACxE,sEAAsE;QACtE,kCAAkC;QAClC,MAAMwK,OAAO,MAAMzB,UAAUO,UAAU,CAAC,SAASC,GAAG,CAACgB,QAAQd,GAAG;QAChE,IAAI,CAACe,KAAKC,MAAM,IAAID,KAAKf,GAAG,CAAC,aAAaZ,IAAIW,KAAK,EAAE,OAAOP,QAAQjJ;QACpE,MAAM0K,YAAYF,KAAKf,GAAG,CAAC;QAC3B,MAAMkB,gBAAgB,OAAOD,cAAc,YAAYA,YAAYA,YAAY;QAC/E,MAAMrB,WAAWnD,QAAAA,cAAAA,MAAO,AAAC,CAAA,MAAM6C,UAAUO,UAAU,CAAC,QAAQC,GAAG,CAACV,IAAIW,KAAK,EAAEC,GAAG,EAAC,EAAGC,IAAI,cAArExD,QAA2E;QAI5F,MAAML,eAAelF,yBAAyB0I;QAE9C,IAAIF,OAAOrC,IAAI,KAAK,SAAS;YAC3B,MAAM8D,SAASrC,UAAUhJ;YACzB,IAAI,CAACqL,QAAQ,OAAO3B,QAAQ3I;YAC5B,MAAMuK,QAAQ;gBAAEN;gBAAQI;YAAc;YACtC,qFAAqF;YACrF,MAAMG,UAAUnJ,aAAakH,KAAK;YAClC,MAAMkC,UAAU,MAAMH,OAAOI,IAAI,CAAC;gBAAET;gBAAQhE,IAAIuE;YAAQ;YACxD,IAAIC,SAAS;gBACX,OAAOhJ,iBAAiB0F,OAAO;oBAC7B2C,SAAS;wBAACa,YAAYF,QAAQxE,EAAE,EAAEwE,QAAQvE,IAAI,EAAEqE,OAAO;qBAAqC;gBAC9F;YACF;YACA,MAAMK,UAAU;gBAAE1B,OAAOX,IAAIW,KAAK;gBAAEe;gBAAQY,KAAKtC,IAAIuC,SAAS;gBAAElF,KAAKmD;gBAASP;YAAI;YAClF,MAAMuC,UAAU,MAAMT,OAAOS,OAAO,CAACH;YACrC,IAAIG,SAAS;gBACX,OAAOA,QAAQtB,MAAM,KAAK,MACtBhI,iBAAiB0F,OAAO;oBAAEkC,QAAQ7H,cAAcuJ,QAAQC,KAAK;gBAAE,KAC/DrC,QAAQoC,QAAQC,KAAK;YAC3B;YACA,MAAMvF,UAAU,MAAMyC,YAAYO,WAAW;gBAAES,OAAOX,IAAIW,KAAK;gBAAEe;gBAAQ7E,KAAKG,aAAaH,GAAG;YAAC;YAC/F,MAAMmE,aAAa,MAAM5I,uBACvB,YACAmG,6BAA6B;gBAC3BR,OAAOiC,IAAIjC,KAAK;gBAChBf;gBACAE;gBACA0B;eACIM,SAAS;gBAAEA;YAAO,IAAI,CAAC;YAG/B,MAAM+B,QAAQ,aAAKjI,kBAAkBgI,aAAiBA,WAAW1C,MAAM,GAAG;gBAAEA,QAAQ0C,WAAW1C,MAAM;YAAC,IAAI,CAAC;YAC3G,IAAI0C,WAAWE,MAAM,KAAK,WAAW,OAAO,aAAKD;gBAAOE,SAAS;;YACjE,IAAIH,WAAWE,MAAM,KAAK,eAAe,OAAO,aAAKD;gBAAOH,QAAQ/H,iBAAiBiI;;YACrF,MAAMtC,SAASsC,WAAWtE,KAAK;YAC/B,IAAIgC,OAAO0C,WAAW,EAAE,OAAO,aAAKH;gBAAOZ,SAAStJ,8BAA8B,CAAC2H,OAAO0C,WAAW,CAAC;;YACtG,IAAI,CAAC1C,OAAOnB,KAAK,CAACC,MAAM,EAAE,OAAO,aAAKyD;gBAAOZ,SAASpJ;;YACtD,MAAMqK,QAAQzL,kBAAkB6I,QAAQxB;YACxC,MAAMwF,QAAQ,MAAMX,OAAOW,KAAK,CAAC,aAC5BL;gBACH3E,IAAIuE;gBACJtE,MAAM2D,MAAMqB,MAAM,CAAChF,IAAI;gBACvBqB,SAAS;oBAAE2D,QAAQrB,MAAMqB,MAAM;gBAAC;;YAElC,IAAID,MAAME,EAAE,KAAK,OAAO;gBACtB,OAAOF,MAAMxB,MAAM,KAAK,MACpB,aAAKD;oBAAOH,QAAQ7H,cAAcyJ,MAAMD,KAAK;qBAC7C,aAAKxB;oBAAOZ,SAAS/I;;YAC3B;YACA,OAAO,aACF2J;gBACHM,SAAS;oBAACa,YAAYM,MAAMhF,EAAE,EAAEgF,MAAM/E,IAAI,EAAEqE,OAAOlM,sBAAsBwL,OAAO5C,OAAO+C,KAAK;iBAAG;;QAEnG;QAEA,IAAInB,OAAOrC,IAAI,KAAK,UAAU;YAC5B,MAAM8D,SAASrC,UAAUhJ;YACzB,IAAI,CAACqL,QAAQ,OAAO3B,QAAQ3I;YAC5B,MAAMuK,QAAQ;gBAAEN;gBAAQI;YAAc;YACtC,MAAMG,UAAUnJ,aAAakH,KAAK;YAClC,MAAMkC,UAAU,MAAMH,OAAOI,IAAI,CAAC;gBAAET;gBAAQhE,IAAIuE;YAAQ;YACxD,IAAIC,SAAS;gBACX,OAAOhJ,iBAAiB0F,OAAO;oBAC7B2C,SAAS;wBAACa,YAAYF,QAAQxE,EAAE,EAAEwE,QAAQvE,IAAI,EAAEqE,OAAO;qBAAqC;gBAC9F;YACF;YACA,MAAMa,QAAQ,MAAMhD,WAAW;gBAAE6B;gBAAQxF,MAAM;gBAAUwB,IAAI4C,OAAOwC,QAAQ;YAAC;YAC7E,IAAI,CAACD,OAAO,OAAOzC,QAAQpJ;YAC3B,IAAI6L,MAAM3G,IAAI,KAAK,UAAU,OAAOkE,QAAQ5I;YAC5C,MAAMuL,UAAUlL,sBAAsBgL,MAAMF,MAAM;YAClD,IAAII,SAAS,OAAO3C,QAAQ7I,+BAA+B,CAACwL,QAAQ;YACpE,MAAMV,UAAU;gBAAE1B,OAAOX,IAAIW,KAAK;gBAAEe;gBAAQY,KAAKtC,IAAIuC,SAAS;gBAAElF,KAAKmD;gBAASP;YAAI;YAClF,MAAMuC,UAAU,MAAMT,OAAOS,OAAO,CAACH;YACrC,IAAIG,SAAS;gBACX,OAAOA,QAAQtB,MAAM,KAAK,MACtBhI,iBAAiB0F,OAAO;oBAAEkC,QAAQ7H,cAAcuJ,QAAQC,KAAK;gBAAE,KAC/DrC,QAAQoC,QAAQC,KAAK;YAC3B;YACA,MAAMvF,UAAU,MAAMyC,YAAYO,WAAW;gBAAES,OAAOX,IAAIW,KAAK;gBAAEe;gBAAQ7E,KAAKG,aAAaH,GAAG;YAAC;YAC/F,MAAMmE,aAAa,MAAM5I,uBACvB,YACAmG,6BAA6B;gBAC3BR,OAAOiC,IAAIjC,KAAK;gBAChBf;gBACAE;gBACAW,UAAU;oBAAEC,SAAS1H,gBAAgByM,MAAMF,MAAM,EAAEzF;gBAAS;gBAC5D0B;eACIM,SAAS;gBAAEA;YAAO,IAAI,CAAC;YAG/B,MAAM+B,QAAQ,aAAKjI,kBAAkBgI,aAAiBA,WAAW1C,MAAM,GAAG;gBAAEA,QAAQ0C,WAAW1C,MAAM;YAAC,IAAI,CAAC;YAC3G,IAAI0C,WAAWE,MAAM,KAAK,WAAW,OAAO,aAAKD;gBAAOE,SAAS;;YACjE,IAAIH,WAAWE,MAAM,KAAK,eAAe,OAAO,aAAKD;gBAAOH,QAAQ/H,iBAAiBiI;;YACrF,MAAMtC,SAASsC,WAAWtE,KAAK;YAC/B,IAAIgC,OAAO0C,WAAW,EAAE,OAAO,aAAKH;gBAAOZ,SAAStJ,8BAA8B,CAAC2H,OAAO0C,WAAW,CAAC;;YACtG,IAAI,CAAC1C,OAAOnB,KAAK,CAACC,MAAM,EAAE,OAAO,aAAKyD;gBAAOZ,SAASpJ;;YACtD,MAAMqK,QAAQzL,kBAAkB6I,QAAQxB;YACxC,qEAAqE;YACrE,kDAAkD;YAClD,MAAMS,OACJ2D,MAAMqB,MAAM,CAAChF,IAAI,CAAC4B,IAAI,MAAM+B,MAAMqB,MAAM,CAAChF,IAAI,CAAC4B,IAAI,OAAOsD,MAAMlF,IAAI,CAAC4B,IAAI,KACpE+B,MAAMqB,MAAM,CAAChF,IAAI,GACjBqF,cAAcH,MAAMlF,IAAI;YAC9B,MAAM+E,QAAQ,MAAMX,OAAOW,KAAK,CAAC,aAAKL;gBAAS3E,IAAIuE;gBAAStE;gBAAMqB,SAAS;oBAAE2D,QAAQ,aAAKrB,MAAMqB,MAAM;wBAAEhF;;gBAAO;;YAC/G,IAAI+E,MAAME,EAAE,KAAK,OAAO;gBACtB,OAAOF,MAAMxB,MAAM,KAAK,MACpB,aAAKD;oBAAOH,QAAQ7H,cAAcyJ,MAAMD,KAAK;qBAC7C,aAAKxB;oBAAOZ,SAAS/I;;YAC3B;YACA,MAAM2L,UAAUlN,4BAA4B8M,MAAMF,MAAM,EAAErB,MAAMqB,MAAM,EAAExM;YACxE,OAAO,aACF8K;gBACHM,SAAS;oBACPa,YACEM,MAAMhF,EAAE,EACRgF,MAAM/E,IAAI,EACVqE,OACAhM,yBAAyB;wBAAEkN,UAAUL,MAAMlF,IAAI;wBAAEsF;wBAAS3B;wBAAOG,OAAO/C,OAAO+C,KAAK;oBAAC;iBAExF;;QAEL;QAEA,MAAM0B,SAAS,MAAMtD,WAAW;YAAE6B;YAAQxF,MAAMoE,OAAO8C,UAAU;YAAE1F,IAAI4C,OAAOwC,QAAQ;QAAC;QACvF,IAAI,CAACK,QAAQ,OAAO/C,QAAQpJ;QAC5B,IAAIkH,MAA0B;QAC9B,IAAIoC,OAAOrC,IAAI,KAAK,YAAY;YAC9B,MAAMkE,OAAO,MAAMrC,QAAQI,WAAW;gBAAEwB;gBAAQoB,UAAUxC,OAAOwC,QAAQ;gBAAEO,OAAO/C,OAAO+C,KAAK;YAAC;YAC/F,IAAIlB,KAAKS,EAAE,KAAK,OAAO;gBACrB,OAAOxC,QAAQ+B,KAAKmB,MAAM,KAAK,SAASlM,4BAA4BC;YACtE;YACA6G,MAAMiE,KAAKjE,GAAG;QAChB;QACA,wEAAwE;QACxE,6BAA6B;QAC7B,MAAMJ,UACJqF,OAAOjH,IAAI,KAAK,WACZ9F,gBACE+M,OAAOR,MAAM,EACb,MAAMhD,YAAYO,WAAW;YAC3BS,OAAOX,IAAIW,KAAK;YAChBe;YACA7E,KAAK;YACL0G,MAAMlN,oBAAoB8M,OAAOR,MAAM;QACzC,MAEFnM,kBAAkB2M,OAAOK,QAAQ,EAAE,MAAMzD,cAAcG,WAAWwB;QACxE,MAAMV,aAAa,MAAM5I,uBACvB,YACA+G,+BAA+B;YAC7BpB,OAAOiC,IAAIjC,KAAK;YAChBE,MAAMqC,OAAOrC,IAAI;YACjBH;YACAI,KAAKA,MAAM5H,aAAa4H,OAAO;YAC/BU;WACIM,SAAS;YAAEA;QAAO,IAAI,CAAC;QAG/B,MAAM+B,QAAQ,aAAKjI,kBAAkBgI,aAAiBA,WAAW1C,MAAM,GAAG;YAAEA,QAAQ0C,WAAW1C,MAAM;QAAC,IAAI,CAAC;QAC3G,IAAI0C,WAAWE,MAAM,KAAK,WAAW,OAAO,aAAKD;YAAOE,SAAS;;QACjE,IAAIH,WAAWE,MAAM,KAAK,eAAe,OAAO,aAAKD;YAAOZ,SAASnJ;;QACrE,MAAMuM,SAAsB;YAC1BC,UAAU;YACV,qEAAqE;YACrE,mDAAmD;YACnDhG,IAAI4C,OAAOrC,IAAI,KAAK,aAAa,cAAc;YAC/CyD;YACAI;YACA6B,OACErD,OAAOrC,IAAI,KAAK,aACZ,CAAC,aAAa,EAAEmB,WAAW+D,OAAOxF,IAAI,EAAE,OAAO,CAAC,GAChD,CAAC,IAAI,EAAEyB,WAAW+D,OAAOxF,IAAI,EAAE,MAAM,CAAC;YAC5CvB,MAAM7F,0BAA0ByK,WAAWtE,KAAK,EAAE4D,OAAOrC,IAAI;QAC/D;QACA,OAAO,aAAKgD;YAAOM,SAAS;gBAACkC;aAAO;;IACtC;AACF;AAEA,4FAA4F,GAC5F,OAAO,MAAMpC,qCACX,4NAA2N;AAE7N;;;;;CAKC,GACD,OAAO,SAASG,kBAAkBF,KAAwB,EAAEG,KAAwB;IAClF,MAAM,EAAE9D,IAAI,EAAEiG,OAAO,EAAErG,KAAK,EAAE,GAAG+D,MAAMqB,MAAM;IAC7C,OAAO;QACLe,UAAU/L;QACV,sEAAsE;QACtE+F,IAAI;QACJgE,QAAQ;QACRI,eAAe;QACf6B,OAAOhG;QACPkG,MAAM;YACJ;eACIvC,MAAMwC,YAAY,CAACtG,MAAM,GACzB;gBACE,CAAC,QAAQ,EAAE8D,MAAMwC,YAAY,CAACtG,MAAM,KAAK,IAAI,oBAAoB,CAAC,IAAI,EAAE8D,MAAMwC,YAAY,CAACtG,MAAM,CAAC,aAAa,CAAC,CAAC,wBAAwB,CAAC;aAC3I,GACD,EAAE;eACHiE;SACJ,CAAC1F,IAAI,CAAC;QACPgI,UAAU;YAAEC,YAAY;gBAAErG;gBAAMiG;gBAASrG;YAAM;QAAE;IACnD;AACF;AAEA,+EAA+E,GAC/E,OAAO,SAASyF,cAAcrF,IAAY;IACxC,MAAM0B,QAAQ1B,KAAK2B,OAAO,CAAC,QAAQ,KAAKC,IAAI,MAAM;IAClD,OAAO,GAAGF,MAAM7B,MAAM,GAAG,KAAK6B,MAAMlC,KAAK,CAAC,GAAG,MAAMkC,MAAM,UAAU,CAAC;AACtE;AAEA,SAAS+C,YACP1E,EAAU,EACVC,IAAY,EACZqE,KAAuD,EACvD6B,IAAY;IAEZ,OAAO;QACLH,UAAU;QACVhG;QACAuG,WAAW;QACXvC,QAAQM,MAAMN,MAAM;QACpBI,eAAeE,MAAMF,aAAa;QAClC6B,OAAOhG;QACPkG;IACF;AACF;AAEA,OAAO,MAAMK,uBAAuB1E,0BAAyB;AAE7D,6EAA6E;AAE7E;;;;CAIC,GACD,eAAe2E,uBACb9B,OAAsC,EACtCX,MAAc;QAID;IAFb,MAAM0C,QAAQhL,mBAAmB1C;IACjC,IAAI,CAAC0N,OAAO,OAAO;QAAElD,QAAQ;QAAKuB,OAAOpJ,yBAAyB;IAAc;IAChF,MAAMsI,QAAO,QAAA,AAAC,CAAA,MAAMU,QAAQnC,SAAS,CAACO,UAAU,CAAC,SAASC,GAAG,CAACgB,QAAQd,GAAG,EAAC,EAAGC,IAAI,cAApE,QAA0E;IACvF,MAAMxD,MAAMgF,QAAQhF,GAAG;IACvB,MAAMgH,WAAW,MAAM1O,mCAAmC;QAACyO;KAAM,EAAE;QACjEzD,OAAO0B,QAAQ1B,KAAK;QACpB2D,eAAe;IACjB;IACA,OAAOD,SAASE,QAAQ,CAACH,UAAU3O,oBAAoB4H,KAAKsE,MAAMyC,SAC9D,OACA;QAAElD,QAAQ;QAAKuB,OAAOpJ,yBAAyB;IAAc;AACnE;AAEA;;;;;;CAMC,GACD,eAAemL,qBAAqBnC,OAAsC;IACxE,IAAIA,QAAQX,MAAM,EAAE,OAAO;QAAER,QAAQ;QAAKuB,OAAO;IAA2C;IAC5F,MAAM2B,QAAQhL,mBAAmB1C;IACjC,IAAI,CAAC0N,OAAO,OAAO;QAAElD,QAAQ;QAAKuB,OAAO7K;IAAmC;IAC5E,MAAMyF,MAAMgF,QAAQhF,GAAG;IACvB,MAAMgH,WAAW,MAAM1O,mCAAmC;QAACyO;KAAM,EAAE;QACjEzD,OAAO0B,QAAQ1B,KAAK;QACpB2D,eAAe;IACjB;IACA,IAAI,CAACD,SAASE,QAAQ,CAACH,UAAU,CAAC1O,gBAAgB2H,KAAK+G,QAAQ;QAC7D,OAAO;YAAElD,QAAQ;YAAKuB,OAAO7K;QAAmC;IAClE;IACA,OAAOpC,iBAAiB6H,KAAK,aAAa,OAAO;QAAE6D,QAAQ;QAAKuB,OAAO/K;IAA4B;AACrG;AAQA;;;;;CAKC,GACD,OAAO,SAAS+M,6BAA6BhF,OAAmC,CAAC,CAAC;QAC7DA,kBACHA;IADhB,MAAMI,cAAaJ,mBAAAA,KAAKI,UAAU,YAAfJ,mBAAmB5F;IACtC,MAAMiG,WAAUL,gBAAAA,KAAKK,OAAO,YAAZL,gBAAgB7F;IAChC,OAAO,OAAOyI;QACZ,MAAM/B,SAAStI,yBAAyBqK,QAAQ/B,MAAM;QACtD,IAAI,OAAOA,WAAW,UAAU,OAAO;YAAEY,QAAQ;YAAKuB,OAAOnC;QAAO;QACpE,IAAIA,OAAOrC,IAAI,KAAK,WAAWqC,OAAOC,KAAK,KAAK,OAAO,OAAOiE,qBAAqBnC;QACnF,IAAI/B,OAAOrC,IAAI,KAAK,SAAS;YAC3B,OAAO9E,8BAA8BkJ,SAAS;gBAC5CqC,MAAM;gBACNC,QAAQ;oBAAC;wBAAEjB,UAAUhN;wBAAwBiN,OAAO;oBAAa;iBAAE;eAC/DlE,KAAKC,SAAS,GAAG;gBAAEA,WAAWD,KAAKC,SAAS;YAAC,IAAI,CAAC;QAE1D;QACA,IAAIY,OAAOrC,IAAI,KAAK,UAAU;YAC5B,sEAAsE;YACtE,kEAAkE;YAClE,OAAO9E,8BAA8BkJ,SAAS;gBAC5CqC,MAAM;gBACNC,QAAQ;oBAAC;wBAAEjB,UAAUhN;wBAAwBiN,OAAO;oBAAa;iBAAE;eAC/DlE,KAAKC,SAAS,GAAG;gBAAEA,WAAWD,KAAKC,SAAS;YAAC,IAAI,CAAC;gBACtDkF,UAAU,OAAOlD;oBACf,MAAMmB,QAAQ,MAAMhD,WAAW;wBAAE6B;wBAAQxF,MAAM;wBAAUwB,IAAI4C,OAAOwC,QAAQ;oBAAC;oBAC7E,IAAI,CAACD,SAASA,MAAM3G,IAAI,KAAK,UAAU,OAAO;wBAAEgF,QAAQ;wBAAKuB,OAAOzL;oBAAsB;oBAC1F,MAAM+L,UAAUlL,sBAAsBgL,MAAMF,MAAM;oBAClD,OAAOI,UAAU;wBAAE7B,QAAQ;wBAAKuB,OAAOlL,+BAA+B,CAACwL,QAAQ;oBAAC,IAAI;gBACtF;;QAEJ;QACA,MAAMrB,SAASW,QAAQX,MAAM;QAC7B,IAAI,CAACA,QAAQ,OAAO;YAAER,QAAQ;YAAKuB,OAAO;QAA4D;QACtG,MAAM2B,QAAQ,MAAMxO,oBAAoB8L;QACxC,IAAI,CAAC0C,SAASA,UAAU/B,QAAQ1B,KAAK,EAAE,OAAO;YAAEO,QAAQ;YAAKuB,OAAO;QAAe;QACnF,MAAMoC,SAAS,MAAMV,uBAAuB9B,SAASX;QACrD,IAAImD,QAAQ,OAAOA;QACnB,MAAM1B,SAAS,MAAMtD,WAAW;YAAE6B;YAAQxF,MAAMoE,OAAO8C,UAAU;YAAE1F,IAAI4C,OAAOwC,QAAQ;QAAC;QACvF,IAAI,CAACK,QAAQ,OAAO;YAAEjC,QAAQ;YAAKuB,OAAOzL;QAAsB;QAChE,IAAIsJ,OAAOrC,IAAI,KAAK,YAAY;YAC9B,MAAMC,MAAM,MAAM4B,QAAQuC,QAAQnC,SAAS,EAAE;gBAAEwB;gBAAQoB,UAAUxC,OAAOwC,QAAQ;gBAAEO,OAAO/C,OAAO+C,KAAK;YAAC;YACtG,IAAInF,IAAI0E,EAAE,KAAK,OAAO;gBACpB,OAAO1E,IAAIoF,MAAM,KAAK,SAClB;oBAAEpC,QAAQ;oBAAKuB,OAAOrL;gBAA0B,IAChD;oBAAE8J,QAAQ;oBAAKuB,OAAOpL;gBAAgC;YAC5D;QACF;QACA,OAAO;IACT;AACF;AAEA,OAAO,MAAMyN,yBAAyBL,+BAA8B;AAEpE,qGAAqG,GACrG,OAAO,SAASM;IACdvL,kBAAkB,YAAY0K,sBAAsB;QAAE7J,WAAWD;IAAgC;IACjGxB,uBAAuB,YAAYkM;AACrC"}
|
|
@@ -33,5 +33,10 @@ import { type AiJobTransitionListener } from './ai-jobs';
|
|
|
33
33
|
* per-workspace and per-site preferences.
|
|
34
34
|
*/
|
|
35
35
|
export declare function aiJobTransitionNotifier(notify?: typeof notifyUsers): AiJobTransitionListener;
|
|
36
|
-
/**
|
|
36
|
+
/**
|
|
37
|
+
* Registers what the jobs machine tells about each change: the notifier, and
|
|
38
|
+
* the business profile's prefill from a site job (AGL-3661). One listener,
|
|
39
|
+
* the two in turn; the prefill swallows its own failures, so it never costs
|
|
40
|
+
* the person their notice.
|
|
41
|
+
*/
|
|
37
42
|
export declare function registerAiJobsNotify(): void;
|
|
@@ -13,8 +13,10 @@
|
|
|
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
|
-
*/ import {
|
|
16
|
+
*/ import { firebaseAdmin } from "@aglyn/tenant-data-admin/server/firebase-admin";
|
|
17
|
+
import { notifyUsers } from "@aglyn/tenant-data-admin/server/notifications";
|
|
17
18
|
import { aiJobNotice } from "../model/ai-job-notice.js";
|
|
19
|
+
import { aiBusinessProfilePrefiller } from "./ai-business-profile-prefill.js";
|
|
18
20
|
import { aiJobAutoConfirms } from "./ai-job-auto-confirm.js";
|
|
19
21
|
import { registerAiJobTransitionListener } from "./ai-jobs.js";
|
|
20
22
|
/**
|
|
@@ -44,8 +46,18 @@ import { registerAiJobTransitionListener } from "./ai-jobs.js";
|
|
|
44
46
|
], aiJobNotice(job, to));
|
|
45
47
|
};
|
|
46
48
|
}
|
|
47
|
-
/**
|
|
48
|
-
|
|
49
|
+
/**
|
|
50
|
+
* Registers what the jobs machine tells about each change: the notifier, and
|
|
51
|
+
* the business profile's prefill from a site job (AGL-3661). One listener,
|
|
52
|
+
* the two in turn; the prefill swallows its own failures, so it never costs
|
|
53
|
+
* the person their notice.
|
|
54
|
+
*/ export function registerAiJobsNotify() {
|
|
55
|
+
const notify = aiJobTransitionNotifier();
|
|
56
|
+
const prefill = aiBusinessProfilePrefiller(()=>firebaseAdmin.app().firestore());
|
|
57
|
+
registerAiJobTransitionListener(async (change)=>{
|
|
58
|
+
await notify(change);
|
|
59
|
+
await prefill(change);
|
|
60
|
+
});
|
|
49
61
|
}
|
|
50
62
|
|
|
51
63
|
//# sourceMappingURL=ai-jobs-notify.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-jobs-notify.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 { notifyUsers } from '@aglyn/tenant-data-admin/server/notifications'\nimport { aiJobNotice } from '../model/ai-job-notice'\nimport { aiJobAutoConfirms } from './ai-job-auto-confirm'\nimport { registerAiJobTransitionListener, type AiJobTransitionListener } from './ai-jobs'\n\n/**\n * The person who started an AI job is told when it needs them, when it\n * finishes and when it stops (AGL-3593), through the console's notifications\n * — the bell, and the generic notification email for a person who switched\n * that channel on for the category. No other channel: there is no push.\n *\n * The machine calls this where the job's state changes, once per change\n * (`registerAiJobTransitionListener`): the request that ran a step inline,\n * or the beat that ran it with nobody watching — which is exactly when a\n * person needs telling. The recipient is the job's creator, whose job it is\n * and who alone confirms its plan; a teammate is not told about someone\n * else's draft.\n *\n * `notifyUsers` never throws, and honors the person's per-category,\n * per-workspace and per-site preferences.\n */\nexport function aiJobTransitionNotifier(\n notify: typeof notifyUsers = notifyUsers,\n): AiJobTransitionListener {\n return async ({ job, to }) => {\n if (!job.createdBy) return\n // A guided site start's plan is confirmed for it the moment it is kept\n // (AGL-3594): there is nothing for the person to confirm.\n if (to === 'needs-review' && job.review?.reason === 'plan' && aiJobAutoConfirms(job)) return\n await notify([job.createdBy], aiJobNotice(job, to))\n }\n}\n\n
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-jobs-notify.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 { firebaseAdmin } from '@aglyn/tenant-data-admin/server/firebase-admin'\nimport { notifyUsers } from '@aglyn/tenant-data-admin/server/notifications'\nimport { aiJobNotice } from '../model/ai-job-notice'\nimport { aiBusinessProfilePrefiller } from './ai-business-profile-prefill'\nimport { aiJobAutoConfirms } from './ai-job-auto-confirm'\nimport { registerAiJobTransitionListener, type AiJobTransitionListener } from './ai-jobs'\n\n/**\n * The person who started an AI job is told when it needs them, when it\n * finishes and when it stops (AGL-3593), through the console's notifications\n * — the bell, and the generic notification email for a person who switched\n * that channel on for the category. No other channel: there is no push.\n *\n * The machine calls this where the job's state changes, once per change\n * (`registerAiJobTransitionListener`): the request that ran a step inline,\n * or the beat that ran it with nobody watching — which is exactly when a\n * person needs telling. The recipient is the job's creator, whose job it is\n * and who alone confirms its plan; a teammate is not told about someone\n * else's draft.\n *\n * `notifyUsers` never throws, and honors the person's per-category,\n * per-workspace and per-site preferences.\n */\nexport function aiJobTransitionNotifier(\n notify: typeof notifyUsers = notifyUsers,\n): AiJobTransitionListener {\n return async ({ job, to }) => {\n if (!job.createdBy) return\n // A guided site start's plan is confirmed for it the moment it is kept\n // (AGL-3594): there is nothing for the person to confirm.\n if (to === 'needs-review' && job.review?.reason === 'plan' && aiJobAutoConfirms(job)) return\n await notify([job.createdBy], aiJobNotice(job, to))\n }\n}\n\n/**\n * Registers what the jobs machine tells about each change: the notifier, and\n * the business profile's prefill from a site job (AGL-3661). One listener,\n * the two in turn; the prefill swallows its own failures, so it never costs\n * the person their notice.\n */\nexport function registerAiJobsNotify(): void {\n const notify = aiJobTransitionNotifier()\n const prefill = aiBusinessProfilePrefiller(() => firebaseAdmin.app().firestore())\n registerAiJobTransitionListener(async (change) => {\n await notify(change)\n await prefill(change)\n })\n}\n"],"names":["firebaseAdmin","notifyUsers","aiJobNotice","aiBusinessProfilePrefiller","aiJobAutoConfirms","registerAiJobTransitionListener","aiJobTransitionNotifier","notify","job","to","createdBy","review","reason","registerAiJobsNotify","prefill","app","firestore","change"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,aAAa,QAAQ,iDAAgD;AAC9E,SAASC,WAAW,QAAQ,gDAA+C;AAC3E,SAASC,WAAW,QAAQ,4BAAwB;AACpD,SAASC,0BAA0B,QAAQ,mCAA+B;AAC1E,SAASC,iBAAiB,QAAQ,2BAAuB;AACzD,SAASC,+BAA+B,QAAsC,eAAW;AAEzF;;;;;;;;;;;;;;;CAeC,GACD,OAAO,SAASC,wBACdC,SAA6BN,WAAW;IAExC,OAAO,OAAO,EAAEO,GAAG,EAAEC,EAAE,EAAE;YAIMD;QAH7B,IAAI,CAACA,IAAIE,SAAS,EAAE;QACpB,uEAAuE;QACvE,0DAA0D;QAC1D,IAAID,OAAO,kBAAkBD,EAAAA,cAAAA,IAAIG,MAAM,qBAAVH,YAAYI,MAAM,MAAK,UAAUR,kBAAkBI,MAAM;QACtF,MAAMD,OAAO;YAACC,IAAIE,SAAS;SAAC,EAAER,YAAYM,KAAKC;IACjD;AACF;AAEA;;;;;CAKC,GACD,OAAO,SAASI;IACd,MAAMN,SAASD;IACf,MAAMQ,UAAUX,2BAA2B,IAAMH,cAAce,GAAG,GAAGC,SAAS;IAC9EX,gCAAgC,OAAOY;QACrC,MAAMV,OAAOU;QACb,MAAMH,QAAQG;IAChB;AACF"}
|
|
@@ -357,7 +357,7 @@ export interface AiJobItemRecord extends AiJobItemOutcome {
|
|
|
357
357
|
* the item's row, its status, failure, note and outputs, and what was given
|
|
358
358
|
* back for it. Pure; the machine applies it inside `recordStep`.
|
|
359
359
|
*/
|
|
360
|
-
export declare function aiApplyJobItemRecord(ledger: readonly AiJobItemLedger[], record: AiJobItemRecord, credits: number): AiJobItemLedger[];
|
|
360
|
+
export declare function aiApplyJobItemRecord(ledger: readonly AiJobItemLedger[], record: AiJobItemRecord, credits: number, now?: Date): AiJobItemLedger[];
|
|
361
361
|
/** One run of a step's runner, as the machine measured it (AGL-2937). */
|
|
362
362
|
export interface AiJobStepTokenRun {
|
|
363
363
|
usage: AiUsage;
|
package/src/lib/jobs/ai-jobs.js
CHANGED
|
@@ -43,7 +43,7 @@ import { aiJobItemRefundKey, aiJobRefundCredits, refundJobCredits } from "../usa
|
|
|
43
43
|
import { aiBuildSettlement, aiBuildUnits, aiBuildRetryLedger, aiBuildCreditEstimate, aiLedgerUnits, aiJobCreditEstimate } from "../model/ai-build-job.js";
|
|
44
44
|
import { freeAssistAccount } from "../usage/assist-free-taste.js";
|
|
45
45
|
import { aiJobAutoConfirms } from "./ai-job-auto-confirm.js";
|
|
46
|
-
import { AI_SITE_GUIDED_BUILD_FAILED_COPY } from "../model/ai-job-failure-copy.js";
|
|
46
|
+
import { AI_SITE_GUIDED_BUILD_FAILED_COPY, aiCustomerSafeCopy, aiCustomerSafeItem, aiCustomerSafeReview } from "../model/ai-job-failure-copy.js";
|
|
47
47
|
import { aiSiteStartInputsOf } from "../model/ai-site-start.js";
|
|
48
48
|
import { addAdminAudit } from "@aglyn/tenant-data-admin/server/admin-audit-write";
|
|
49
49
|
/**
|
|
@@ -456,7 +456,7 @@ export async function getAiJob(firestore, orgId, jobId) {
|
|
|
456
456
|
return code === 9 || code === 'failed-precondition' || code === 'FAILED_PRECONDITION';
|
|
457
457
|
}
|
|
458
458
|
/** The wire form. `running` is the lease, read against `now`. */ export function aiJobSummary(job, now = new Date()) {
|
|
459
|
-
var _job_hostId, _job_steps, _job_outputs, _job_creditsReserved, _job_creditsSpent, _job_refundedCredits, _toIso, _toIso1, _job_error, _job_plan_labels, _job_plan_confirmedBy,
|
|
459
|
+
var _job_hostId, _job_steps, _job_outputs, _job_creditsReserved, _job_creditsSpent, _job_refundedCredits, _toIso, _toIso1, _job_error, _job_plan_labels, _job_plan_confirmedBy, _job_applied_versions, _job_applied_staged;
|
|
460
460
|
var _job_lease, _job_inputs, _job_items, _job_inputs1;
|
|
461
461
|
const leaseUntil = toMillis((_job_lease = job.lease) == null ? void 0 : _job_lease.until);
|
|
462
462
|
return _extends({
|
|
@@ -475,7 +475,7 @@ export async function getAiJob(firestore, orgId, jobId) {
|
|
|
475
475
|
startedAt: toIso(step.startedAt),
|
|
476
476
|
endedAt: toIso(step.endedAt),
|
|
477
477
|
creditsSpent: (_step_creditsSpent = step.creditsSpent) != null ? _step_creditsSpent : 0,
|
|
478
|
-
error: (_step_error = step.error) != null ? _step_error : null
|
|
478
|
+
error: aiCustomerSafeCopy((_step_error = step.error) != null ? _step_error : null)
|
|
479
479
|
};
|
|
480
480
|
}),
|
|
481
481
|
outputs: (_job_outputs = job.outputs) != null ? _job_outputs : [],
|
|
@@ -485,7 +485,10 @@ export async function getAiJob(firestore, orgId, jobId) {
|
|
|
485
485
|
createdBy: job.createdBy,
|
|
486
486
|
createdAt: (_toIso = toIso(job.createdAt)) != null ? _toIso : new Date(0).toISOString(),
|
|
487
487
|
updatedAt: (_toIso1 = toIso(job.updatedAt)) != null ? _toIso1 : new Date(0).toISOString(),
|
|
488
|
-
|
|
488
|
+
// The doctrine's own words never reach a customer (AGL-3596).
|
|
489
|
+
error: aiCustomerSafeCopy((_job_error = job.error) != null ? _job_error : null, {
|
|
490
|
+
page: job.kind === 'page'
|
|
491
|
+
}),
|
|
489
492
|
running: job.status === 'running' && leaseUntil !== null && leaseUntil > now.getTime(),
|
|
490
493
|
plan: job.plan ? _extends({}, job.plan, {
|
|
491
494
|
labels: (_job_plan_labels = job.plan.labels) != null ? _job_plan_labels : {},
|
|
@@ -493,7 +496,9 @@ export async function getAiJob(firestore, orgId, jobId) {
|
|
|
493
496
|
confirmedAt: toIso(job.plan.confirmedAt),
|
|
494
497
|
confirmedBy: (_job_plan_confirmedBy = job.plan.confirmedBy) != null ? _job_plan_confirmedBy : null
|
|
495
498
|
}) : null,
|
|
496
|
-
review:
|
|
499
|
+
review: job.review ? aiCustomerSafeReview(job.review, {
|
|
500
|
+
page: job.kind === 'page'
|
|
501
|
+
}) : null,
|
|
497
502
|
applied: job.applied ? {
|
|
498
503
|
at: toIso(job.applied.at),
|
|
499
504
|
by: job.applied.by,
|
|
@@ -504,8 +509,10 @@ export async function getAiJob(firestore, orgId, jobId) {
|
|
|
504
509
|
sitePublish: job.sitePublish
|
|
505
510
|
} : {}, job.kind === 'site' ? {
|
|
506
511
|
siteInputs: aiSiteStartInputsOf(job.inputs)
|
|
512
|
+
} : {}, aiJobAutoConfirms(job) ? {
|
|
513
|
+
autoConfirm: true
|
|
507
514
|
} : {}, ((_job_items = job.items) == null ? void 0 : _job_items.length) ? {
|
|
508
|
-
items: job.items
|
|
515
|
+
items: job.items.map(aiCustomerSafeItem)
|
|
509
516
|
} : {}, job.orchestration ? {
|
|
510
517
|
orchestration: job.orchestration
|
|
511
518
|
} : {}, job.kind === 'build' && ((_job_inputs1 = job.inputs) == null ? void 0 : _job_inputs1['publish']) === true ? {
|
|
@@ -633,7 +640,7 @@ function nextStepIndex(job) {
|
|
|
633
640
|
* A build's ledger with one pass applied (AGL-3616): the pass's credits on
|
|
634
641
|
* the item's row, its status, failure, note and outputs, and what was given
|
|
635
642
|
* back for it. Pure; the machine applies it inside `recordStep`.
|
|
636
|
-
*/ export function aiApplyJobItemRecord(ledger, record, credits) {
|
|
643
|
+
*/ export function aiApplyJobItemRecord(ledger, record, credits, now) {
|
|
637
644
|
return ledger.map((row)=>{
|
|
638
645
|
var _row_creditsSpent, _row_attemptCredits, _row_creditsRefunded, _record_creditsRefunded, _row_outputs, _record_outputs;
|
|
639
646
|
var _record_degradedBy;
|
|
@@ -650,6 +657,8 @@ function nextStepIndex(job) {
|
|
|
650
657
|
note: record.note
|
|
651
658
|
} : {}, ((_record_degradedBy = record.degradedBy) == null ? void 0 : _record_degradedBy.length) ? {
|
|
652
659
|
degradedBy: record.degradedBy
|
|
660
|
+
} : {}, now && record.status !== 'running' && record.status !== 'pending' ? {
|
|
661
|
+
settledAt: now.toISOString()
|
|
653
662
|
} : {}, {
|
|
654
663
|
outputs: [
|
|
655
664
|
...(_row_outputs = row.outputs) != null ? _row_outputs : [],
|
|
@@ -739,7 +748,7 @@ function nextStepIndex(job) {
|
|
|
739
748
|
const remaining = steps.filter((step)=>step.status === 'pending' || step.status === 'running').length;
|
|
740
749
|
// A build's item ledger moves with its spend, in this one write (AGL-3616).
|
|
741
750
|
const ledger = (_ref = (_input_items = input.items) != null ? _input_items : job.items) != null ? _ref : null;
|
|
742
|
-
const items = ledger && input.item ? aiApplyJobItemRecord(ledger, input.item, input.creditsSpent) : input.items ? ledger : null;
|
|
751
|
+
const items = ledger && input.item ? aiApplyJobItemRecord(ledger, input.item, input.creditsSpent, now) : input.items ? ledger : null;
|
|
743
752
|
const settled = input.status !== 'pending';
|
|
744
753
|
const ownsLease = ((_job_lease = job.lease) == null ? void 0 : _job_lease.owner) === owner;
|
|
745
754
|
const parksForReview = Boolean(input.review) && !isAiJobTerminal(job.status);
|