@aglyn/plugins-ai 1.0.0-beta.228 → 1.0.0-beta.230
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/package.json +17 -17
- package/src/lib/activity/ai-activity-actions.js +7 -1
- package/src/lib/activity/ai-activity-actions.js.map +1 -1
- package/src/lib/components/ai-automation-zones.d.ts +38 -0
- package/src/lib/components/ai-automation-zones.js +5 -1
- package/src/lib/components/ai-automation-zones.js.map +1 -1
- package/src/lib/components/ai-brief-dialog.component.d.ts +20 -2
- package/src/lib/components/ai-brief-dialog.component.js +87 -8
- package/src/lib/components/ai-brief-dialog.component.js.map +1 -1
- package/src/lib/components/ai-campaign-create.component.d.ts +54 -0
- package/src/lib/components/ai-campaign-create.component.js +270 -0
- package/src/lib/components/ai-campaign-create.component.js.map +1 -0
- package/src/lib/components/ai-describe-automation.component.d.ts +8 -3
- package/src/lib/components/ai-describe-automation.component.js +15 -6
- package/src/lib/components/ai-describe-automation.component.js.map +1 -1
- package/src/lib/components/ai-describe-button.component.d.ts +20 -8
- package/src/lib/components/ai-describe-button.component.js +19 -36
- package/src/lib/components/ai-describe-button.component.js.map +1 -1
- package/src/lib/components/ai-describe-email.component.d.ts +31 -0
- package/src/lib/components/ai-describe-email.component.js +22 -0
- package/src/lib/components/ai-describe-email.component.js.map +1 -0
- package/src/lib/components/ai-describe-org-automation.component.d.ts +36 -0
- package/src/lib/components/ai-describe-org-automation.component.js +238 -0
- package/src/lib/components/ai-describe-org-automation.component.js.map +1 -0
- package/src/lib/components/ai-experiment-cards.component.d.ts +9 -1
- package/src/lib/components/ai-experiment-cards.component.js +104 -18
- package/src/lib/components/ai-experiment-cards.component.js.map +1 -1
- package/src/lib/components/ai-explain-automation.component.js +1 -5
- package/src/lib/components/ai-explain-automation.component.js.map +1 -1
- package/src/lib/components/ai-funnel-zones.component.d.ts +61 -0
- package/src/lib/components/ai-funnel-zones.component.js +178 -0
- package/src/lib/components/ai-funnel-zones.component.js.map +1 -0
- package/src/lib/components/ai-insight-card.component.d.ts +42 -0
- package/src/lib/components/ai-insight-card.component.js +137 -0
- package/src/lib/components/ai-insight-card.component.js.map +1 -0
- package/src/lib/components/ai-insight-dialog.component.d.ts +6 -0
- package/src/lib/components/ai-insight-dialog.component.js +9 -3
- package/src/lib/components/ai-insight-dialog.component.js.map +1 -1
- package/src/lib/components/ai-job-plan.component.d.ts +7 -2
- package/src/lib/components/ai-job-plan.component.js +35 -5
- package/src/lib/components/ai-job-plan.component.js.map +1 -1
- package/src/lib/components/ai-job-requests.d.ts +17 -1
- package/src/lib/components/ai-job-requests.js +39 -3
- package/src/lib/components/ai-job-requests.js.map +1 -1
- package/src/lib/components/ai-jobs-list-page.component.js +12 -1
- package/src/lib/components/ai-jobs-list-page.component.js.map +1 -1
- package/src/lib/components/ai-logic-zones.d.ts +64 -0
- package/src/lib/components/ai-logic-zones.js +18 -0
- package/src/lib/components/ai-logic-zones.js.map +1 -0
- package/src/lib/components/ai-logic.component.d.ts +77 -0
- package/src/lib/components/ai-logic.component.js +553 -0
- package/src/lib/components/ai-logic.component.js.map +1 -0
- package/src/lib/components/ai-marketing-insight.component.d.ts +30 -0
- package/src/lib/components/ai-marketing-insight.component.js +80 -0
- package/src/lib/components/ai-marketing-insight.component.js.map +1 -0
- package/src/lib/components/ai-media-create.component.d.ts +55 -0
- package/src/lib/components/ai-media-create.component.js +433 -0
- package/src/lib/components/ai-media-create.component.js.map +1 -0
- package/src/lib/components/ai-overlay-cards.component.d.ts +83 -0
- package/src/lib/components/ai-overlay-cards.component.js +505 -0
- package/src/lib/components/ai-overlay-cards.component.js.map +1 -0
- package/src/lib/components/ai-product-zones.d.ts +5 -0
- package/src/lib/components/ai-product-zones.js +1 -1
- package/src/lib/components/ai-product-zones.js.map +1 -1
- package/src/lib/components/ai-products-create-button.component.d.ts +42 -0
- package/src/lib/components/ai-products-create-button.component.js +67 -0
- package/src/lib/components/ai-products-create-button.component.js.map +1 -0
- package/src/lib/components/ai-products-hub-card.component.js +100 -81
- package/src/lib/components/ai-products-hub-card.component.js.map +1 -1
- package/src/lib/components/ai-revise-automation.component.d.ts +16 -0
- package/src/lib/components/ai-revise-automation.component.js +207 -0
- package/src/lib/components/ai-revise-automation.component.js.map +1 -0
- package/src/lib/components/ai-site-build-page.component.d.ts +1 -1
- package/src/lib/components/ai-site-build-page.component.js +177 -20
- package/src/lib/components/ai-site-build-page.component.js.map +1 -1
- package/src/lib/components/ai-site-start-card.component.d.ts +10 -1
- package/src/lib/components/ai-site-start-card.component.js +7 -4
- package/src/lib/components/ai-site-start-card.component.js.map +1 -1
- package/src/lib/components/ai-upsell-dialog.component.d.ts +53 -0
- package/src/lib/components/ai-upsell-dialog.component.js +177 -0
- package/src/lib/components/ai-upsell-dialog.component.js.map +1 -0
- package/src/lib/components/assist-build-card.component.d.ts +42 -0
- package/src/lib/components/assist-build-card.component.js +183 -0
- package/src/lib/components/assist-build-card.component.js.map +1 -0
- package/src/lib/components/assist-edit-canvas.d.ts +2 -0
- package/src/lib/components/assist-edit-canvas.js +56 -1
- package/src/lib/components/assist-edit-canvas.js.map +1 -1
- package/src/lib/components/assist-jobs-drawer.component.d.ts +11 -0
- package/src/lib/components/assist-jobs-drawer.component.js +85 -4
- package/src/lib/components/assist-jobs-drawer.component.js.map +1 -1
- package/src/lib/components/assist-panel.component.js +51 -2
- package/src/lib/components/assist-panel.component.js.map +1 -1
- package/src/lib/components/use-ai-job-run.d.ts +3 -14
- package/src/lib/components/use-ai-job-run.js +0 -50
- package/src/lib/components/use-ai-job-run.js.map +1 -1
- package/src/lib/jobs/ai-build-capabilities.d.ts +58 -0
- package/src/lib/jobs/ai-build-capabilities.js +256 -0
- package/src/lib/jobs/ai-build-capabilities.js.map +1 -0
- package/src/lib/jobs/ai-build-unit-outcome.d.ts +52 -0
- package/src/lib/jobs/ai-build-unit-outcome.js +138 -0
- package/src/lib/jobs/ai-build-unit-outcome.js.map +1 -0
- package/src/lib/jobs/ai-job-build-step.d.ts +144 -0
- package/src/lib/jobs/ai-job-build-step.js +657 -0
- package/src/lib/jobs/ai-job-build-step.js.map +1 -0
- package/src/lib/jobs/ai-job-draft-ids.js +9 -1
- package/src/lib/jobs/ai-job-draft-ids.js.map +1 -1
- package/src/lib/jobs/ai-job-drafts.d.ts +14 -4
- package/src/lib/jobs/ai-job-drafts.js +25 -4
- package/src/lib/jobs/ai-job-drafts.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-site-pages.d.ts +70 -0
- package/src/lib/jobs/ai-job-layout-site-pages.js +216 -0
- package/src/lib/jobs/ai-job-layout-site-pages.js.map +1 -0
- package/src/lib/jobs/ai-job-layout-step.d.ts +2 -2
- package/src/lib/jobs/ai-job-layout-step.js +19 -1
- package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
- package/src/lib/jobs/ai-job-logic-step.d.ts +107 -0
- package/src/lib/jobs/ai-job-logic-step.js +395 -0
- package/src/lib/jobs/ai-job-logic-step.js.map +1 -0
- package/src/lib/jobs/ai-job-overlay-copy.d.ts +70 -0
- package/src/lib/jobs/ai-job-overlay-copy.js +178 -0
- package/src/lib/jobs/ai-job-overlay-copy.js.map +1 -0
- package/src/lib/jobs/ai-job-page-step.js +6 -6
- package/src/lib/jobs/ai-job-page-step.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-step.d.ts +26 -1
- package/src/lib/jobs/ai-job-plan-step.js +79 -11
- package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-step.d.ts +15 -1
- package/src/lib/jobs/ai-job-site-step.js +207 -58
- package/src/lib/jobs/ai-job-site-step.js.map +1 -1
- package/src/lib/jobs/ai-job-text-step.d.ts +22 -1
- package/src/lib/jobs/ai-job-text-step.js +12 -0
- package/src/lib/jobs/ai-job-text-step.js.map +1 -1
- package/src/lib/jobs/ai-job-workflow-step.d.ts +54 -4
- package/src/lib/jobs/ai-job-workflow-step.js +318 -13
- package/src/lib/jobs/ai-job-workflow-step.js.map +1 -1
- package/src/lib/jobs/ai-jobs.d.ts +90 -2
- package/src/lib/jobs/ai-jobs.js +495 -61
- package/src/lib/jobs/ai-jobs.js.map +1 -1
- package/src/lib/jobs/ai-logic-records.d.ts +40 -0
- package/src/lib/jobs/ai-logic-records.js +55 -0
- package/src/lib/jobs/ai-logic-records.js.map +1 -0
- package/src/lib/jobs/ai-site-publish.d.ts +7 -0
- package/src/lib/jobs/ai-site-publish.js +9 -0
- package/src/lib/jobs/ai-site-publish.js.map +1 -1
- package/src/lib/jobs/ai-workflow-records.d.ts +4 -0
- package/src/lib/jobs/ai-workflow-records.js +40 -1
- package/src/lib/jobs/ai-workflow-records.js.map +1 -1
- package/src/lib/model/ai-automation-draft.d.ts +18 -0
- package/src/lib/model/ai-automation-draft.js +86 -0
- package/src/lib/model/ai-automation-draft.js.map +1 -1
- package/src/lib/model/ai-build-job.d.ts +201 -0
- package/src/lib/model/ai-build-job.js +471 -0
- package/src/lib/model/ai-build-job.js.map +1 -0
- package/src/lib/model/ai-build-plan.d.ts +61 -0
- package/src/lib/model/ai-build-plan.js +154 -9
- package/src/lib/model/ai-build-plan.js.map +1 -1
- package/src/lib/model/ai-build-progress.d.ts +56 -0
- package/src/lib/model/ai-build-progress.js +128 -0
- package/src/lib/model/ai-build-progress.js.map +1 -0
- package/src/lib/model/ai-insight.d.ts +20 -10
- package/src/lib/model/ai-insight.js +72 -9
- package/src/lib/model/ai-insight.js.map +1 -1
- package/src/lib/model/ai-job-activity.js +3 -1
- package/src/lib/model/ai-job-activity.js.map +1 -1
- package/src/lib/model/ai-job-failure-copy.d.ts +20 -1
- package/src/lib/model/ai-job-failure-copy.js +28 -1
- package/src/lib/model/ai-job-failure-copy.js.map +1 -1
- package/src/lib/model/ai-job-notice.d.ts +3 -0
- package/src/lib/model/ai-job-notice.js +11 -2
- package/src/lib/model/ai-job-notice.js.map +1 -1
- package/src/lib/model/ai-jobs.types.d.ts +87 -2
- package/src/lib/model/ai-jobs.types.js +3 -1
- package/src/lib/model/ai-jobs.types.js.map +1 -1
- package/src/lib/model/ai-logic-job.d.ts +108 -0
- package/src/lib/model/ai-logic-job.js +327 -0
- package/src/lib/model/ai-logic-job.js.map +1 -0
- package/src/lib/model/ai-media-credits.d.ts +52 -0
- package/src/lib/model/ai-media-credits.js +61 -0
- package/src/lib/model/ai-media-credits.js.map +1 -0
- package/src/lib/model/ai-overlay-copy.d.ts +114 -0
- package/src/lib/model/ai-overlay-copy.js +175 -0
- package/src/lib/model/ai-overlay-copy.js.map +1 -0
- package/src/lib/model/ai-site-build-progress.d.ts +28 -3
- package/src/lib/model/ai-site-build-progress.js +231 -17
- package/src/lib/model/ai-site-build-progress.js.map +1 -1
- package/src/lib/model/ai-site-start-seo.d.ts +16 -0
- package/src/lib/model/ai-site-start-seo.js +54 -4
- package/src/lib/model/ai-site-start-seo.js.map +1 -1
- package/src/lib/model/ai-site-start.d.ts +14 -0
- package/src/lib/model/ai-site-start.js +43 -0
- package/src/lib/model/ai-site-start.js.map +1 -1
- package/src/lib/model/ai-svg.d.ts +69 -0
- package/src/lib/model/ai-svg.js +265 -0
- package/src/lib/model/ai-svg.js.map +1 -0
- package/src/lib/model/ai-usage-by-user.d.ts +3 -2
- package/src/lib/model/ai-usage-by-user.js.map +1 -1
- package/src/lib/model/ai-workflow-job.d.ts +92 -4
- package/src/lib/model/ai-workflow-job.js +124 -7
- package/src/lib/model/ai-workflow-job.js.map +1 -1
- package/src/lib/model/assist-build.d.ts +58 -0
- package/src/lib/model/assist-build.js +52 -0
- package/src/lib/model/assist-build.js.map +1 -0
- package/src/lib/model/assist-edit.d.ts +48 -3
- package/src/lib/model/assist-edit.js +48 -4
- package/src/lib/model/assist-edit.js.map +1 -1
- package/src/lib/plugin-config.js +2 -0
- package/src/lib/plugin-config.js.map +1 -1
- package/src/lib/plugin.js +240 -11
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/providers/catalog.d.ts +81 -4
- package/src/lib/providers/catalog.js +80 -9
- package/src/lib/providers/catalog.js.map +1 -1
- package/src/lib/providers/image-contract.d.ts +116 -0
- package/src/lib/providers/image-contract.js +74 -0
- package/src/lib/providers/image-contract.js.map +1 -0
- package/src/lib/providers/model-choice.js +11 -0
- 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.d.ts +121 -0
- package/src/lib/providers/vertex-image.js +312 -0
- package/src/lib/providers/vertex-image.js.map +1 -0
- package/src/lib/runtime/ai-doctrine-validators.d.ts +19 -0
- package/src/lib/runtime/ai-doctrine-validators.js +89 -0
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-doctrine.d.ts +6 -0
- package/src/lib/runtime/ai-doctrine.js +16 -6
- 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 +15 -1
- package/src/lib/runtime/ai-eval.js +52 -1
- package/src/lib/runtime/ai-eval.js.map +1 -1
- package/src/lib/runtime/experiment-variant-copy.d.ts +34 -0
- package/src/lib/runtime/experiment-variant-copy.js +102 -0
- package/src/lib/runtime/experiment-variant-copy.js.map +1 -0
- package/src/lib/server/ai-experiment-versions.d.ts +51 -0
- package/src/lib/server/ai-experiment-versions.js +270 -0
- package/src/lib/server/ai-experiment-versions.js.map +1 -0
- package/src/lib/server/ai-jobs-resume.js +74 -4
- package/src/lib/server/ai-jobs-resume.js.map +1 -1
- package/src/lib/server/ai-media-image.d.ts +66 -0
- package/src/lib/server/ai-media-image.js +521 -0
- package/src/lib/server/ai-media-image.js.map +1 -0
- package/src/lib/server/ai-media-svg.d.ts +91 -0
- package/src/lib/server/ai-media-svg.js +186 -0
- package/src/lib/server/ai-media-svg.js.map +1 -0
- package/src/lib/server/assist-build.d.ts +54 -0
- package/src/lib/server/assist-build.js +121 -0
- package/src/lib/server/assist-build.js.map +1 -0
- package/src/lib/server/assist-chat.js +108 -12
- package/src/lib/server/assist-chat.js.map +1 -1
- package/src/lib/server/assist-edit.js +213 -3
- package/src/lib/server/assist-edit.js.map +1 -1
- package/src/lib/server/assist-view-context.js +242 -4
- package/src/lib/server/assist-view-context.js.map +1 -1
- package/src/lib/server.js +26 -0
- package/src/lib/server.js.map +1 -1
- package/src/lib/tools/ai-logic-tool.d.ts +27 -0
- package/src/lib/tools/ai-logic-tool.js +207 -0
- package/src/lib/tools/ai-logic-tool.js.map +1 -0
- package/src/lib/tools/ai-overlay-tool.d.ts +35 -0
- package/src/lib/tools/ai-overlay-tool.js +107 -0
- package/src/lib/tools/ai-overlay-tool.js.map +1 -0
- package/src/lib/tools/ai-workflow-tool.d.ts +11 -0
- package/src/lib/tools/ai-workflow-tool.js +31 -0
- package/src/lib/tools/ai-workflow-tool.js.map +1 -1
- package/src/lib/usage/assist-job-refund.d.ts +31 -3
- package/src/lib/usage/assist-job-refund.js +46 -10
- package/src/lib/usage/assist-job-refund.js.map +1 -1
- package/src/lib/usage/assist-usage.d.ts +5 -0
- package/src/lib/usage/assist-usage.js +2 -0
- package/src/lib/usage/assist-usage.js.map +1 -1
|
@@ -0,0 +1,657 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
17
|
+
import { resolveEffectivePlan } from "@aglyn/aglyn/app-utils/plan-entitlements";
|
|
18
|
+
import { resolveOrgIdForHost } from "@aglyn/tenant-data-admin/server/organizations";
|
|
19
|
+
import { AI_BUILD_LIMITS, AI_BUILD_PUBLISH_INPUT, aiArticle, aiBuildCreditEstimate, aiBuildDegradation, aiBuildInitialLedger, aiBuildItemDelivered, aiBuildItemOpen, aiBuildNextUnit, aiBuildOrder, aiBuildPlacedItemLines, aiBuildPlanShapeRefusal, aiBuildUnits } from "../model/ai-build-job.js";
|
|
20
|
+
import { isAiPlanNewRef } from "../model/ai-build-plan.js";
|
|
21
|
+
import { AI_SITE_MAX_SECTIONS } from "../model/ai-site-job.js";
|
|
22
|
+
import { FREE_AI_TASTE_CREDITS_PER_MONTH } from "../plan-entitlements.js";
|
|
23
|
+
import { aiModelForStep } from "../providers/routing.js";
|
|
24
|
+
import { AI_BUILD_UNIT_EMPTY_COPY, aiBuildBuiltRefs, aiUnitErrorRetryable, aiUnitFailure, aiUnitSpend } from "./ai-build-unit-outcome.js";
|
|
25
|
+
import { assistCreditsFromUsd } from "../usage/assist-credits.js";
|
|
26
|
+
import { ASSIST_RETURNED_USD_FIELD, assistSpendAfterReturnsUsd } from "../usage/assist-credit-returns.js";
|
|
27
|
+
import { freeAccountUsageRef, freeAssistAccount } from "../usage/assist-free-taste.js";
|
|
28
|
+
import { aiBuildOps } from "./ai-build-capabilities.js";
|
|
29
|
+
import { aiJobAdmissionRefusal, registerAiJobAdmission } from "./ai-job-admission.js";
|
|
30
|
+
import { aiOriginJobId } from "./ai-job-draft-ids.js";
|
|
31
|
+
import { readAiDraftNodes } from "./ai-job-drafts.js";
|
|
32
|
+
import { aiConfirmedPlan, aiUnspentOutcome } from "./ai-job-generation.js";
|
|
33
|
+
import { AI_JOB_PAGE_STEP_MINIMUM_MS } from "./ai-job-page-budget.js";
|
|
34
|
+
import { aiPluginDraftAdmissionRefusal, aiPluginDraftWriter } from "./ai-job-plugin-drafts.js";
|
|
35
|
+
import { aiSitePageWritten, aiSiteResolvedRef } from "./ai-job-site-step.js";
|
|
36
|
+
import { AI_JOB_BRIEF_MAX_CHARS } from "./ai-job-text-step.js";
|
|
37
|
+
import { aiJobPublishesBuild, aiPublishGuidedSite } from "./ai-site-publish.js";
|
|
38
|
+
import { AI_PLANNED_JOB_KINDS, aiJobStepRunMinimumMs, aiJobStepRunnerFor, registerAiJobStep, registerAiJobStepPasses } from "./ai-jobs.js";
|
|
39
|
+
/**
|
|
40
|
+
* The build step (AGL-3616): the generation step of a `build` job, run once
|
|
41
|
+
* the member confirmed the plan their request produced.
|
|
42
|
+
*
|
|
43
|
+
* Like the site scaffold it generalizes, it builds nothing itself and asks no
|
|
44
|
+
* model of its own. Each pass takes the next unit in dependency order and
|
|
45
|
+
* hands it to what builds that kind of thing — a page, layout, form,
|
|
46
|
+
* component, email design or template to this plugin's own step, under a job
|
|
47
|
+
* derived for the unit; another plugin's resource to that plugin's writer on
|
|
48
|
+
* the resource-draft seam, with the content the owner's capability derives
|
|
49
|
+
* from the item's arguments. One unit, one pass, one recorded spend.
|
|
50
|
+
*
|
|
51
|
+
* ── Settled item by item ────────────────────────────────────────────────
|
|
52
|
+
*
|
|
53
|
+
* Where a unit stands is read from the job's ITEM LEDGER, never from counting
|
|
54
|
+
* outputs: the machine writes each pass's row in the transaction that
|
|
55
|
+
* records its spend (`recordStep`). A unit that fails is a FAILED ROW, not a
|
|
56
|
+
* failed job: the pass reports it, the machine gives back that item's spend
|
|
57
|
+
* where the failure was ours, and the next pass builds the next unit. What
|
|
58
|
+
* depended on it is built without it, or skipped where it cannot stand
|
|
59
|
+
* without it (`aiBuildDegradation`). The machine ends the job when nothing is
|
|
60
|
+
* left: `done` when anything was delivered, `failed` — its planning given
|
|
61
|
+
* back too — when nothing was.
|
|
62
|
+
*
|
|
63
|
+
* ── Drafts, unless asked and confirmed ───────────────────────────────────
|
|
64
|
+
*
|
|
65
|
+
* Every unit writes the draft its own kind writes, which nothing publishes.
|
|
66
|
+
* Only a build whose request asked to publish AND whose member ticked the
|
|
67
|
+
* plan card's box puts its built pages live, once, on its last pass, through
|
|
68
|
+
* the guided start's publish.
|
|
69
|
+
*/ /** What a build answers with no confirmed plan to build. */ export const AI_BUILD_NO_PLAN_COPY = 'This build has no confirmed plan to build.';
|
|
70
|
+
/** What a unit says when nothing on this site builds its operation any more. */ export const aiBuildUnavailableCopy = (noun)=>`Not built: ${aiArticle(noun)} cannot be made on this site right now.`;
|
|
71
|
+
/** What a page that was reported built but holds none of its plan says. */ export const AI_BUILD_PAGE_NOT_WRITTEN_COPY = 'The page was not written from its plan.';
|
|
72
|
+
/**
|
|
73
|
+
* The most passes a build's step may take: every unit at a page's worst — its
|
|
74
|
+
* sections and its listing — so a runner that never finishes is still
|
|
75
|
+
* bounded while the largest plan is not. Try again starts the count over.
|
|
76
|
+
*/ export const AI_BUILD_MAX_PASSES = AI_BUILD_LIMITS.units * (AI_SITE_MAX_SECTIONS + 1);
|
|
77
|
+
/** The job kind a creation of the build is built by. */ const CREATION_JOB_KINDS = {
|
|
78
|
+
layout: 'layout',
|
|
79
|
+
form: 'form',
|
|
80
|
+
component: 'component',
|
|
81
|
+
email: 'email'
|
|
82
|
+
};
|
|
83
|
+
/** The job kind a unit is built by, where an AI runner builds it. */ export function aiBuildUnitJobKind(unit, ops) {
|
|
84
|
+
var _CREATION_JOB_KINDS_unit_creation_kind;
|
|
85
|
+
var _ops_get;
|
|
86
|
+
if (unit.screen) return 'page';
|
|
87
|
+
if (unit.creation) return (_CREATION_JOB_KINDS_unit_creation_kind = CREATION_JOB_KINDS[unit.creation.kind]) != null ? _CREATION_JOB_KINDS_unit_creation_kind : null;
|
|
88
|
+
const runnerKind = (_ops_get = ops.get(unit.op)) == null ? void 0 : _ops_get.runnerKind;
|
|
89
|
+
return runnerKind ? runnerKind : null;
|
|
90
|
+
}
|
|
91
|
+
/** The id a unit's job, and so its draft, is named by. */ export function aiBuildUnitJobId(job, unit) {
|
|
92
|
+
var _ref, _ref1, _ref2;
|
|
93
|
+
var _unit_creation, _unit_screen, _unit_item;
|
|
94
|
+
return (_ref = (_ref1 = (_ref2 = (_unit_creation = unit.creation) == null ? void 0 : _unit_creation.id) != null ? _ref2 : (_unit_screen = unit.screen) == null ? void 0 : _unit_screen.id) != null ? _ref1 : (_unit_item = unit.item) == null ? void 0 : _unit_item.id) != null ? _ref : `${job.$id}-${unit.slot}`;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* The job a unit is built under (generalizing `aiSiteUnitJob`): this job's
|
|
98
|
+
* org, site, creator and model; the kind that builds the unit; a plan
|
|
99
|
+
* narrowed to the unit, its `new:` references resolved to what was built and
|
|
100
|
+
* the failed ones left out; and a brief that says what the unit is, what it
|
|
101
|
+
* is built without, and which blocks its sections place.
|
|
102
|
+
*/ export function aiBuildUnitJob(job, unit, context) {
|
|
103
|
+
var _job_outputs, _plan_labels, _context_briefLines, _ref;
|
|
104
|
+
const plan = job.plan;
|
|
105
|
+
const built = aiBuildBuiltRefs(context.units, context.ledger, (_job_outputs = job.outputs) != null ? _job_outputs : []);
|
|
106
|
+
const labels = _extends({}, (_plan_labels = plan.labels) != null ? _plan_labels : {});
|
|
107
|
+
for (const entry of built.values())labels[entry.id] = entry.label;
|
|
108
|
+
const brief = [
|
|
109
|
+
job.brief.trim()
|
|
110
|
+
];
|
|
111
|
+
const unitPlan = _extends({}, plan, {
|
|
112
|
+
labels,
|
|
113
|
+
reuse: [
|
|
114
|
+
...plan.reuse
|
|
115
|
+
],
|
|
116
|
+
create: [],
|
|
117
|
+
screens: []
|
|
118
|
+
});
|
|
119
|
+
delete unitPlan.items;
|
|
120
|
+
const placedByScreens = new Set(plan.screens.flatMap((screen)=>screen.sections.flatMap((section)=>section.uses)));
|
|
121
|
+
if (unit.creation) {
|
|
122
|
+
unitPlan.reuse = plan.reuse.filter((entry)=>!placedByScreens.has(entry.id));
|
|
123
|
+
unitPlan.create = [
|
|
124
|
+
_extends({}, unit.creation, {
|
|
125
|
+
duplicateOf: aiSiteResolvedRef(unit.creation.duplicateOf, built)
|
|
126
|
+
})
|
|
127
|
+
];
|
|
128
|
+
brief.push(`Build the ${unit.creation.kind} “${unit.creation.name}”: ${unit.creation.why}`);
|
|
129
|
+
}
|
|
130
|
+
if (unit.screen) {
|
|
131
|
+
var _aiSiteResolvedRef, _aiSiteResolvedRef1;
|
|
132
|
+
const screen = unit.screen;
|
|
133
|
+
for (const entry of built.values()){
|
|
134
|
+
unitPlan.reuse.push({
|
|
135
|
+
kind: entry.kind,
|
|
136
|
+
id: entry.id,
|
|
137
|
+
purpose: `the ${entry.kind} this request built`
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
let builtLayout = null;
|
|
141
|
+
for (const entry of built.values())if (entry.kind === 'layout') builtLayout = builtLayout != null ? builtLayout : entry.id;
|
|
142
|
+
unitPlan.screens = [
|
|
143
|
+
_extends({}, screen, {
|
|
144
|
+
// A page whose layout failed renders inside the site's own: the page
|
|
145
|
+
// step falls back to it when the plan names none.
|
|
146
|
+
layout: (_aiSiteResolvedRef = aiSiteResolvedRef(screen.layout, built)) != null ? _aiSiteResolvedRef : isAiPlanNewRef(screen.layout) ? builtLayout : null,
|
|
147
|
+
template: aiSiteResolvedRef(screen.template, built),
|
|
148
|
+
duplicateOf: aiSiteResolvedRef(screen.duplicateOf, built),
|
|
149
|
+
record: screen.record ? _extends({}, screen.record, {
|
|
150
|
+
dataset: (_aiSiteResolvedRef1 = aiSiteResolvedRef(screen.record.dataset, built)) != null ? _aiSiteResolvedRef1 : screen.record.dataset
|
|
151
|
+
}) : null,
|
|
152
|
+
sections: screen.sections.map((section)=>_extends({}, section, {
|
|
153
|
+
uses: section.uses.map((ref)=>aiSiteResolvedRef(ref, built)).filter((ref)=>!!ref)
|
|
154
|
+
}))
|
|
155
|
+
})
|
|
156
|
+
];
|
|
157
|
+
brief.push(`Build the page “${screen.title}”, at ${screen.slug}.`);
|
|
158
|
+
brief.push(...aiBuildPlacedItemLines(unit, {
|
|
159
|
+
units: context.units,
|
|
160
|
+
ledger: context.ledger,
|
|
161
|
+
ops: context.ops
|
|
162
|
+
}));
|
|
163
|
+
}
|
|
164
|
+
const item = unit.item;
|
|
165
|
+
if (item) {
|
|
166
|
+
var _ref1;
|
|
167
|
+
var _context_ops_get;
|
|
168
|
+
const noun = (_ref1 = (_context_ops_get = context.ops.get(item.op)) == null ? void 0 : _context_ops_get.noun) != null ? _ref1 : item.op;
|
|
169
|
+
brief.push(`Build the ${noun} “${item.name}”${item.why ? `: ${item.why}` : '.'}`);
|
|
170
|
+
// A planned kind's step builds the plan's first creation of its kind.
|
|
171
|
+
unitPlan.create = AI_PLANNED_JOB_KINDS.includes(context.kind) ? [
|
|
172
|
+
_extends({
|
|
173
|
+
kind: context.kind,
|
|
174
|
+
name: item.name,
|
|
175
|
+
why: item.why,
|
|
176
|
+
duplicateOf: null,
|
|
177
|
+
fields: []
|
|
178
|
+
}, item.id ? {
|
|
179
|
+
id: item.id
|
|
180
|
+
} : {})
|
|
181
|
+
] : [];
|
|
182
|
+
}
|
|
183
|
+
brief.push(...(_context_briefLines = context.briefLines) != null ? _context_briefLines : []);
|
|
184
|
+
const inputs = _extends({}, job.inputs, (_ref = item == null ? void 0 : item.args) != null ? _ref : {}, {
|
|
185
|
+
originJobId: aiOriginJobId(job)
|
|
186
|
+
});
|
|
187
|
+
delete inputs[AI_BUILD_PUBLISH_INPUT];
|
|
188
|
+
return _extends({}, job, {
|
|
189
|
+
$id: aiBuildUnitJobId(job, unit),
|
|
190
|
+
kind: context.kind,
|
|
191
|
+
steps: [],
|
|
192
|
+
outputs: [],
|
|
193
|
+
items: null,
|
|
194
|
+
plan: item && !AI_PLANNED_JOB_KINDS.includes(context.kind) ? null : unitPlan,
|
|
195
|
+
inputs,
|
|
196
|
+
brief: brief.join('\n').slice(0, AI_JOB_BRIEF_MAX_CHARS)
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
/** The unit's failure, for its row: ours or not, why, in customer-safe words. */ function failed(slot, failure, extra = {}) {
|
|
200
|
+
return _extends({
|
|
201
|
+
slot,
|
|
202
|
+
status: 'failed',
|
|
203
|
+
failure
|
|
204
|
+
}, extra);
|
|
205
|
+
}
|
|
206
|
+
/** Whether a build's workspace spends the Free taste. */ export function aiBuildFreeTaste(org) {
|
|
207
|
+
return Boolean(org) && resolveEffectivePlan(org) === 'free';
|
|
208
|
+
}
|
|
209
|
+
/** The site document a gate reads, or `null`. */ async function readHost(firestore, hostId) {
|
|
210
|
+
var _data;
|
|
211
|
+
if (!hostId) return null;
|
|
212
|
+
return (_data = (await firestore.collection('hosts').doc(hostId).get()).data()) != null ? _data : null;
|
|
213
|
+
}
|
|
214
|
+
export function createAiJobBuildStep(deps = {}) {
|
|
215
|
+
var _deps_runnerFor, _deps_writerFor, _deps_opsFor, _deps_admissionFor, _deps_pluginAdmission, _deps_readNodes, _deps_publish;
|
|
216
|
+
const runnerFor = (_deps_runnerFor = deps.runnerFor) != null ? _deps_runnerFor : aiJobStepRunnerFor;
|
|
217
|
+
const writerFor = (_deps_writerFor = deps.writerFor) != null ? _deps_writerFor : aiPluginDraftWriter;
|
|
218
|
+
const opsFor = (_deps_opsFor = deps.opsFor) != null ? _deps_opsFor : (context)=>aiBuildOps(context);
|
|
219
|
+
const admissionFor = (_deps_admissionFor = deps.admissionFor) != null ? _deps_admissionFor : aiJobAdmissionRefusal;
|
|
220
|
+
const pluginAdmission = (_deps_pluginAdmission = deps.pluginAdmission) != null ? _deps_pluginAdmission : aiPluginDraftAdmissionRefusal;
|
|
221
|
+
const readNodes = (_deps_readNodes = deps.readNodes) != null ? _deps_readNodes : readAiDraftNodes;
|
|
222
|
+
const publish = (_deps_publish = deps.publish) != null ? _deps_publish : aiPublishGuidedSite;
|
|
223
|
+
return async (context)=>{
|
|
224
|
+
var _ref, _context_org, _aiBuildOrder, _job_hostId, _job_hostId1, _ref1;
|
|
225
|
+
var _job_items, _rows_get;
|
|
226
|
+
const { job, firestore } = context;
|
|
227
|
+
const model = (_ref = context.modelFor == null ? void 0 : context.modelFor.call(context, 'job.plan')) != null ? _ref : aiModelForStep('job.plan');
|
|
228
|
+
const plan = aiConfirmedPlan(job);
|
|
229
|
+
if (!plan) return _extends({}, aiUnspentOutcome(model), {
|
|
230
|
+
failure: AI_BUILD_NO_PLAN_COPY
|
|
231
|
+
});
|
|
232
|
+
const org = (_context_org = context.org) != null ? _context_org : null;
|
|
233
|
+
const units = aiBuildUnits(plan);
|
|
234
|
+
const ordered = (_aiBuildOrder = aiBuildOrder(units)) != null ? _aiBuildOrder : units;
|
|
235
|
+
const starting = !((_job_items = job.items) == null ? void 0 : _job_items.length);
|
|
236
|
+
const ledger = starting ? aiBuildInitialLedger(units) : job.items;
|
|
237
|
+
const init = starting ? {
|
|
238
|
+
items: ledger
|
|
239
|
+
} : {};
|
|
240
|
+
const unit = aiBuildNextUnit(ordered, ledger);
|
|
241
|
+
const rows = new Map(ledger.map((row)=>[
|
|
242
|
+
row.slot,
|
|
243
|
+
row
|
|
244
|
+
]));
|
|
245
|
+
const host = await readHost(firestore, (_job_hostId = job.hostId) != null ? _job_hostId : null);
|
|
246
|
+
const ops = await opsFor({
|
|
247
|
+
orgId: job.orgId,
|
|
248
|
+
hostId: (_job_hostId1 = job.hostId) != null ? _job_hostId1 : null,
|
|
249
|
+
org,
|
|
250
|
+
host,
|
|
251
|
+
freeTaste: aiBuildFreeTaste(org)
|
|
252
|
+
});
|
|
253
|
+
/** Whether units other than this one are still to build. */ const othersOpen = (slot)=>ordered.some((one)=>{
|
|
254
|
+
var _rows_get;
|
|
255
|
+
return one.slot !== slot && aiBuildItemOpen((_rows_get = rows.get(one.slot)) != null ? _rows_get : {
|
|
256
|
+
status: 'pending'
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
/** The build's last pass puts its pages live, where it was asked and confirmed. */ const finish = async (outcome, extraPages = [])=>{
|
|
260
|
+
var _job_outputs;
|
|
261
|
+
if (!aiJobPublishesBuild(job) || job.sitePublish || !job.hostId) return outcome;
|
|
262
|
+
const pageIds = new Set(units.filter((one)=>{
|
|
263
|
+
var _rows_get;
|
|
264
|
+
return one.screen && aiBuildItemDelivered((_rows_get = rows.get(one.slot)) != null ? _rows_get : {
|
|
265
|
+
status: 'pending'
|
|
266
|
+
});
|
|
267
|
+
}).flatMap((one)=>{
|
|
268
|
+
var _ref;
|
|
269
|
+
var _rows_get;
|
|
270
|
+
return (_ref = (_rows_get = rows.get(one.slot)) == null ? void 0 : _rows_get.outputs) != null ? _ref : [];
|
|
271
|
+
}));
|
|
272
|
+
const pages = [
|
|
273
|
+
...((_job_outputs = job.outputs) != null ? _job_outputs : []).filter((output)=>output.resource === 'screen' && pageIds.has(output.id)),
|
|
274
|
+
...extraPages
|
|
275
|
+
];
|
|
276
|
+
if (!pages.length) return outcome;
|
|
277
|
+
const sitePublish = await publish(firestore, {
|
|
278
|
+
job,
|
|
279
|
+
outputs: pages,
|
|
280
|
+
now: context.now
|
|
281
|
+
}).catch((error)=>{
|
|
282
|
+
console.error('ai build publish threw', {
|
|
283
|
+
orgId: job.orgId,
|
|
284
|
+
jobId: job.$id,
|
|
285
|
+
error
|
|
286
|
+
});
|
|
287
|
+
return null;
|
|
288
|
+
});
|
|
289
|
+
return sitePublish ? _extends({}, outcome, {
|
|
290
|
+
sitePublish
|
|
291
|
+
}) : outcome;
|
|
292
|
+
};
|
|
293
|
+
if (!unit) return finish(_extends({}, aiUnspentOutcome(model), init));
|
|
294
|
+
const settle = (item, spent = aiUnspentOutcome(model))=>_extends({}, spent, init, {
|
|
295
|
+
item
|
|
296
|
+
}, item.status === 'running' || othersOpen(unit.slot) ? {
|
|
297
|
+
continue: true
|
|
298
|
+
} : {});
|
|
299
|
+
// What its dependencies came to.
|
|
300
|
+
const degradation = aiBuildDegradation(unit, {
|
|
301
|
+
units,
|
|
302
|
+
ledger,
|
|
303
|
+
ops
|
|
304
|
+
});
|
|
305
|
+
if (degradation.skip) {
|
|
306
|
+
return settle({
|
|
307
|
+
slot: unit.slot,
|
|
308
|
+
status: 'skipped',
|
|
309
|
+
note: degradation.skip,
|
|
310
|
+
degradedBy: degradation.degradedBy
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
const capability = ops.get(unit.op);
|
|
314
|
+
const noun = (_ref1 = capability == null ? void 0 : capability.noun) != null ? _ref1 : unit.op;
|
|
315
|
+
if (!capability) return settle({
|
|
316
|
+
slot: unit.slot,
|
|
317
|
+
status: 'skipped',
|
|
318
|
+
note: aiBuildUnavailableCopy(noun)
|
|
319
|
+
});
|
|
320
|
+
const degraded = degradation.degradedBy.length > 0;
|
|
321
|
+
const note = degradation.notes.length ? degradation.notes.join(' ') : null;
|
|
322
|
+
// Another plugin's resource: its writer, with the content its capability derives.
|
|
323
|
+
if (capability.draftResource) {
|
|
324
|
+
var _ref2;
|
|
325
|
+
const item = unit.item;
|
|
326
|
+
const writer = writerFor(capability.draftResource);
|
|
327
|
+
if (!item || !writer || !job.hostId) {
|
|
328
|
+
return settle({
|
|
329
|
+
slot: unit.slot,
|
|
330
|
+
status: 'skipped',
|
|
331
|
+
note: aiBuildUnavailableCopy(noun)
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
const admission = {
|
|
335
|
+
firestore,
|
|
336
|
+
orgId: job.orgId,
|
|
337
|
+
hostId: job.hostId,
|
|
338
|
+
inputs: {},
|
|
339
|
+
org,
|
|
340
|
+
uid: job.createdBy
|
|
341
|
+
};
|
|
342
|
+
const refusal = await pluginAdmission(admission, {
|
|
343
|
+
kind: 'build',
|
|
344
|
+
drafts: [
|
|
345
|
+
{
|
|
346
|
+
resource: capability.draftResource,
|
|
347
|
+
label: capability.noun
|
|
348
|
+
}
|
|
349
|
+
],
|
|
350
|
+
writerFor
|
|
351
|
+
});
|
|
352
|
+
if (refusal) return settle({
|
|
353
|
+
slot: unit.slot,
|
|
354
|
+
status: 'skipped',
|
|
355
|
+
note: `Not built: ${refusal.error}`
|
|
356
|
+
});
|
|
357
|
+
const dependencies = {};
|
|
358
|
+
for (const slot of unit.deps){
|
|
359
|
+
const dep = units.find((one)=>one.slot === slot);
|
|
360
|
+
const row = rows.get(slot);
|
|
361
|
+
if (!dep || !row || !aiBuildItemDelivered(row) || !row.outputs.length) continue;
|
|
362
|
+
dependencies[`new:${dep.label}`] = {
|
|
363
|
+
op: dep.op,
|
|
364
|
+
id: row.outputs[0]
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
const content = (_ref2 = capability.draftContent == null ? void 0 : capability.draftContent.call(capability, {
|
|
368
|
+
name: item.name,
|
|
369
|
+
args: item.args
|
|
370
|
+
}, {
|
|
371
|
+
hostId: job.hostId,
|
|
372
|
+
dependencies
|
|
373
|
+
})) != null ? _ref2 : item.args;
|
|
374
|
+
const checked = writer.check(content, {
|
|
375
|
+
hostId: job.hostId
|
|
376
|
+
});
|
|
377
|
+
if (checked.ok === false) {
|
|
378
|
+
var _checked_problems_;
|
|
379
|
+
return settle(failed(unit.slot, {
|
|
380
|
+
ours: true,
|
|
381
|
+
reason: 'step-failure',
|
|
382
|
+
message: (_checked_problems_ = checked.problems[0]) != null ? _checked_problems_ : AI_BUILD_UNIT_EMPTY_COPY
|
|
383
|
+
}));
|
|
384
|
+
}
|
|
385
|
+
const id = aiBuildUnitJobId(job, unit);
|
|
386
|
+
const written = await writer.write({
|
|
387
|
+
orgId: job.orgId,
|
|
388
|
+
hostId: job.hostId,
|
|
389
|
+
uid: job.createdBy,
|
|
390
|
+
org,
|
|
391
|
+
now: context.now,
|
|
392
|
+
id,
|
|
393
|
+
name: item.name,
|
|
394
|
+
content
|
|
395
|
+
});
|
|
396
|
+
if (written.ok === false) {
|
|
397
|
+
return settle(failed(unit.slot, {
|
|
398
|
+
ours: false,
|
|
399
|
+
reason: 'review',
|
|
400
|
+
message: written.error
|
|
401
|
+
}));
|
|
402
|
+
}
|
|
403
|
+
const output = {
|
|
404
|
+
resource: 'draft',
|
|
405
|
+
id: written.id,
|
|
406
|
+
versionId: written.versionId,
|
|
407
|
+
hostId: job.hostId,
|
|
408
|
+
label: written.name,
|
|
409
|
+
draftResource: capability.draftResource,
|
|
410
|
+
note: `A draft, in ${capability.where}.`
|
|
411
|
+
};
|
|
412
|
+
return settle(_extends({
|
|
413
|
+
slot: unit.slot,
|
|
414
|
+
status: degraded ? 'degraded' : 'succeeded',
|
|
415
|
+
outputs: [
|
|
416
|
+
written.id
|
|
417
|
+
],
|
|
418
|
+
note
|
|
419
|
+
}, degraded ? {
|
|
420
|
+
degradedBy: degradation.degradedBy
|
|
421
|
+
} : {}), _extends({}, aiUnspentOutcome(model), {
|
|
422
|
+
outputs: [
|
|
423
|
+
output
|
|
424
|
+
]
|
|
425
|
+
}));
|
|
426
|
+
}
|
|
427
|
+
// This plugin's own runner, under a job derived for the unit.
|
|
428
|
+
const kind = aiBuildUnitJobKind(unit, ops);
|
|
429
|
+
const runner = kind ? runnerFor(kind) : null;
|
|
430
|
+
if (!kind || !runner) return settle({
|
|
431
|
+
slot: unit.slot,
|
|
432
|
+
status: 'skipped',
|
|
433
|
+
note: aiBuildUnavailableCopy(noun)
|
|
434
|
+
});
|
|
435
|
+
const derived = aiBuildUnitJob(job, unit, {
|
|
436
|
+
kind,
|
|
437
|
+
units,
|
|
438
|
+
ledger,
|
|
439
|
+
ops,
|
|
440
|
+
briefLines: degradation.briefLines
|
|
441
|
+
});
|
|
442
|
+
// An item's kind is asked its own admission first (a campaign's plan,
|
|
443
|
+
// a template's subject), once, before its first pass.
|
|
444
|
+
if (unit.item && ((_rows_get = rows.get(unit.slot)) == null ? void 0 : _rows_get.status) !== 'running') {
|
|
445
|
+
var _job_hostId2;
|
|
446
|
+
const refusal = await admissionFor(kind, {
|
|
447
|
+
firestore,
|
|
448
|
+
orgId: job.orgId,
|
|
449
|
+
hostId: (_job_hostId2 = job.hostId) != null ? _job_hostId2 : null,
|
|
450
|
+
inputs: derived.inputs,
|
|
451
|
+
org,
|
|
452
|
+
uid: job.createdBy
|
|
453
|
+
}).catch((error)=>{
|
|
454
|
+
console.error('ai build item admission failed', {
|
|
455
|
+
orgId: job.orgId,
|
|
456
|
+
jobId: job.$id,
|
|
457
|
+
slot: unit.slot,
|
|
458
|
+
error
|
|
459
|
+
});
|
|
460
|
+
return {
|
|
461
|
+
status: 403,
|
|
462
|
+
error: AI_BUILD_UNIT_EMPTY_COPY
|
|
463
|
+
};
|
|
464
|
+
});
|
|
465
|
+
if (refusal) return settle({
|
|
466
|
+
slot: unit.slot,
|
|
467
|
+
status: 'skipped',
|
|
468
|
+
note: `Not built: ${refusal.error}`
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
let outcome;
|
|
472
|
+
try {
|
|
473
|
+
outcome = await runner(_extends({}, context, {
|
|
474
|
+
job: derived
|
|
475
|
+
}));
|
|
476
|
+
} catch (error) {
|
|
477
|
+
// A provider that will answer later is the machine's to retry; anything
|
|
478
|
+
// else is this item's failure, and the build goes on.
|
|
479
|
+
if (aiUnitErrorRetryable(error)) throw error;
|
|
480
|
+
console.error('ai build unit threw', {
|
|
481
|
+
orgId: job.orgId,
|
|
482
|
+
jobId: job.$id,
|
|
483
|
+
slot: unit.slot,
|
|
484
|
+
error
|
|
485
|
+
});
|
|
486
|
+
return settle(failed(unit.slot, {
|
|
487
|
+
ours: true,
|
|
488
|
+
reason: 'provider',
|
|
489
|
+
message: AI_BUILD_UNIT_EMPTY_COPY
|
|
490
|
+
}));
|
|
491
|
+
}
|
|
492
|
+
const spent = aiUnitSpend(outcome);
|
|
493
|
+
const ids = outcome.outputs.map((output)=>output.id);
|
|
494
|
+
const stopped = aiUnitFailure(unit.slot, outcome);
|
|
495
|
+
if (stopped) return settle(stopped, spent);
|
|
496
|
+
// A page counts as built only when its plan's sections are in it (AGL-3596).
|
|
497
|
+
if (unit.screen && job.hostId) {
|
|
498
|
+
const page = outcome.outputs.find((output)=>output.resource === 'screen');
|
|
499
|
+
const written = page !== undefined && await aiSitePageWritten(firestore, {
|
|
500
|
+
hostId: job.hostId,
|
|
501
|
+
unitJobId: derived.$id,
|
|
502
|
+
screen: unit.screen,
|
|
503
|
+
draftId: page.id
|
|
504
|
+
}, readNodes);
|
|
505
|
+
if (!written) {
|
|
506
|
+
return settle(failed(unit.slot, {
|
|
507
|
+
ours: true,
|
|
508
|
+
reason: 'step-failure',
|
|
509
|
+
message: AI_BUILD_PAGE_NOT_WRITTEN_COPY
|
|
510
|
+
}), _extends({}, spent, {
|
|
511
|
+
outputs: outcome.outputs.filter((output)=>output.resource !== 'screen')
|
|
512
|
+
}));
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
const done = settle(_extends({
|
|
516
|
+
slot: unit.slot,
|
|
517
|
+
status: degraded ? 'degraded' : 'succeeded',
|
|
518
|
+
outputs: ids,
|
|
519
|
+
note
|
|
520
|
+
}, degraded ? {
|
|
521
|
+
degradedBy: degradation.degradedBy
|
|
522
|
+
} : {}), spent);
|
|
523
|
+
if (done.continue) return done;
|
|
524
|
+
// The last unit: what was built goes live where it was asked and confirmed.
|
|
525
|
+
rows.set(unit.slot, _extends({}, rows.get(unit.slot), {
|
|
526
|
+
status: degraded ? 'degraded' : 'succeeded',
|
|
527
|
+
outputs: ids
|
|
528
|
+
}));
|
|
529
|
+
return finish(done, unit.screen ? outcome.outputs.filter((output)=>output.resource === 'screen') : []);
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
export const runAiJobBuildStep = createAiJobBuildStep();
|
|
533
|
+
/**
|
|
534
|
+
* The least time a build's next pass needs: what the step that builds its
|
|
535
|
+
* next unit registers for the job derived for it; a page pass's when the
|
|
536
|
+
* next unit is another plugin's draft, which asks no model.
|
|
537
|
+
*/ export function aiBuildJobRunMinimumMs(job) {
|
|
538
|
+
var _aiBuildOrder, _CREATION_JOB_KINDS_unit_creation_kind, _AI_BUILD_ITEM_RUNNER_KINDS_unit_op;
|
|
539
|
+
var _job_items;
|
|
540
|
+
const plan = aiConfirmedPlan(job);
|
|
541
|
+
if (!plan) return AI_JOB_PAGE_STEP_MINIMUM_MS;
|
|
542
|
+
const units = aiBuildUnits(plan);
|
|
543
|
+
const ordered = (_aiBuildOrder = aiBuildOrder(units)) != null ? _aiBuildOrder : units;
|
|
544
|
+
const ledger = ((_job_items = job.items) == null ? void 0 : _job_items.length) ? job.items : aiBuildInitialLedger(units);
|
|
545
|
+
const unit = aiBuildNextUnit(ordered, ledger);
|
|
546
|
+
if (!unit) return AI_JOB_PAGE_STEP_MINIMUM_MS;
|
|
547
|
+
const kind = unit.screen ? 'page' : unit.creation ? (_CREATION_JOB_KINDS_unit_creation_kind = CREATION_JOB_KINDS[unit.creation.kind]) != null ? _CREATION_JOB_KINDS_unit_creation_kind : null : (_AI_BUILD_ITEM_RUNNER_KINDS_unit_op = AI_BUILD_ITEM_RUNNER_KINDS[unit.op]) != null ? _AI_BUILD_ITEM_RUNNER_KINDS_unit_op : null;
|
|
548
|
+
if (!kind) return AI_JOB_PAGE_STEP_MINIMUM_MS;
|
|
549
|
+
return aiJobStepRunMinimumMs(aiBuildUnitJob(job, unit, {
|
|
550
|
+
kind,
|
|
551
|
+
units,
|
|
552
|
+
ledger,
|
|
553
|
+
ops: new Map()
|
|
554
|
+
}));
|
|
555
|
+
}
|
|
556
|
+
/** The runner kinds this plugin's own item operations use, for the time a pass needs without the registry. */ const AI_BUILD_ITEM_RUNNER_KINDS = {
|
|
557
|
+
template: 'template',
|
|
558
|
+
campaign: 'campaign',
|
|
559
|
+
workflow: 'workflow'
|
|
560
|
+
};
|
|
561
|
+
/**
|
|
562
|
+
* Why a Free build cannot be confirmed: its estimate is more than the owner's
|
|
563
|
+
* Free allowance has left this month. Read off the account month the meter
|
|
564
|
+
* writes, net of give-backs; a read that fails admits, since the reservation
|
|
565
|
+
* still refuses at the wall.
|
|
566
|
+
*/ export async function aiBuildFreeBalanceRefusal(input) {
|
|
567
|
+
const account = freeAssistAccount(input.org);
|
|
568
|
+
if (!(account == null ? void 0 : account.accountUid)) return null;
|
|
569
|
+
try {
|
|
570
|
+
const month = input.now.toISOString().slice(0, 7);
|
|
571
|
+
const snapshot = await freeAccountUsageRef(input.firestore, account.accountUid, month).get();
|
|
572
|
+
const spent = assistCreditsFromUsd(assistSpendAfterReturnsUsd(snapshot.get('estCostUsd'), snapshot.get(ASSIST_RETURNED_USD_FIELD)));
|
|
573
|
+
const left = Math.max(0, FREE_AI_TASTE_CREDITS_PER_MONTH - spent);
|
|
574
|
+
return input.estimate > left ? `This plan is estimated at ${input.estimate} credits, and your Free AI credits have ${left} left this month. Ask for less, or upgrade for more.` : null;
|
|
575
|
+
} catch (error) {
|
|
576
|
+
console.error('ai build free balance read failed', {
|
|
577
|
+
error
|
|
578
|
+
});
|
|
579
|
+
return null;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
583
|
+
* A build is admitted for a site of the job's own org where a page can be
|
|
584
|
+
* built; and confirmed only for a plan it can build here — its shape, its
|
|
585
|
+
* operations as this site has them, and on the Free taste an estimate the
|
|
586
|
+
* month's Free credits still cover.
|
|
587
|
+
*/ export function createAiBuildJobAdmission(deps = {}) {
|
|
588
|
+
var _deps_opsFor, _deps_freeBalanceRefusal, _deps_ownerOf;
|
|
589
|
+
const opsFor = (_deps_opsFor = deps.opsFor) != null ? _deps_opsFor : (context)=>aiBuildOps(context);
|
|
590
|
+
const freeBalanceRefusal = (_deps_freeBalanceRefusal = deps.freeBalanceRefusal) != null ? _deps_freeBalanceRefusal : aiBuildFreeBalanceRefusal;
|
|
591
|
+
const ownerOf = (_deps_ownerOf = deps.ownerOf) != null ? _deps_ownerOf : resolveOrgIdForHost;
|
|
592
|
+
return async (context)=>{
|
|
593
|
+
if (!context.hostId) return {
|
|
594
|
+
status: 400,
|
|
595
|
+
error: 'Open the site to build on before asking Assist to build'
|
|
596
|
+
};
|
|
597
|
+
const owner = await ownerOf(context.hostId);
|
|
598
|
+
if (!owner || owner !== context.orgId) return {
|
|
599
|
+
status: 404,
|
|
600
|
+
error: 'Unknown site'
|
|
601
|
+
};
|
|
602
|
+
if (!aiJobStepRunnerFor('page')) return {
|
|
603
|
+
status: 400,
|
|
604
|
+
error: 'Building from a request is not available yet.'
|
|
605
|
+
};
|
|
606
|
+
const plan = context.plan;
|
|
607
|
+
if (!plan) return null;
|
|
608
|
+
const org = context.org;
|
|
609
|
+
const freeTaste = aiBuildFreeTaste(org);
|
|
610
|
+
const host = await readHost(context.firestore, context.hostId);
|
|
611
|
+
const ops = await opsFor({
|
|
612
|
+
orgId: context.orgId,
|
|
613
|
+
hostId: context.hostId,
|
|
614
|
+
org,
|
|
615
|
+
host,
|
|
616
|
+
freeTaste
|
|
617
|
+
});
|
|
618
|
+
const shape = aiBuildPlanShapeRefusal(plan, {
|
|
619
|
+
freeTaste,
|
|
620
|
+
ops
|
|
621
|
+
});
|
|
622
|
+
if (shape) return {
|
|
623
|
+
status: 400,
|
|
624
|
+
error: shape
|
|
625
|
+
};
|
|
626
|
+
if (freeTaste) {
|
|
627
|
+
var _ref;
|
|
628
|
+
const refusal = await freeBalanceRefusal({
|
|
629
|
+
firestore: context.firestore,
|
|
630
|
+
org,
|
|
631
|
+
estimate: aiBuildCreditEstimate(plan, {
|
|
632
|
+
ops
|
|
633
|
+
}),
|
|
634
|
+
now: (_ref = deps.now == null ? void 0 : deps.now.call(deps)) != null ? _ref : new Date()
|
|
635
|
+
});
|
|
636
|
+
if (refusal) return {
|
|
637
|
+
status: 403,
|
|
638
|
+
error: refusal
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
return null;
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
export const aiBuildJobAdmission = createAiBuildJobAdmission();
|
|
645
|
+
/**
|
|
646
|
+
* Registers the build with the least time a pass needs, the time its next
|
|
647
|
+
* pass needs, the passes its largest plan may take, and its admission.
|
|
648
|
+
*/ export function registerAiBuildJob() {
|
|
649
|
+
registerAiJobStep('build', runAiJobBuildStep, {
|
|
650
|
+
minimumMs: AI_JOB_PAGE_STEP_MINIMUM_MS,
|
|
651
|
+
minimumMsFor: aiBuildJobRunMinimumMs
|
|
652
|
+
});
|
|
653
|
+
registerAiJobStepPasses('build', AI_BUILD_MAX_PASSES);
|
|
654
|
+
registerAiJobAdmission('build', aiBuildJobAdmission);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
//# sourceMappingURL=ai-job-build-step.js.map
|