@aglyn/plugins-ai 1.0.0-beta.237 → 1.0.0-beta.239
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/components/ai-credits-prompt.component.d.ts +11 -4
- package/src/lib/components/ai-credits-prompt.component.js +84 -34
- package/src/lib/components/ai-credits-prompt.component.js.map +1 -1
- package/src/lib/components/ai-job-cancel.component.d.ts +38 -0
- package/src/lib/components/ai-job-cancel.component.js +156 -0
- package/src/lib/components/ai-job-cancel.component.js.map +1 -0
- package/src/lib/components/ai-job-links.js +8 -0
- package/src/lib/components/ai-job-links.js.map +1 -1
- package/src/lib/components/ai-job-plan.component.js +12 -1
- package/src/lib/components/ai-job-plan.component.js.map +1 -1
- package/src/lib/components/ai-jobs-list-page.component.js +141 -72
- package/src/lib/components/ai-jobs-list-page.component.js.map +1 -1
- package/src/lib/components/ai-jobs-store.d.ts +12 -0
- package/src/lib/components/ai-jobs-store.js +15 -0
- package/src/lib/components/ai-jobs-store.js.map +1 -1
- package/src/lib/components/ai-site-build-page.component.js +22 -2
- package/src/lib/components/ai-site-build-page.component.js.map +1 -1
- package/src/lib/components/ai-usage-meter-refresh.d.ts +35 -0
- package/src/lib/components/ai-usage-meter-refresh.js +63 -0
- package/src/lib/components/ai-usage-meter-refresh.js.map +1 -0
- package/src/lib/components/ai-usage-strip.component.d.ts +19 -2
- package/src/lib/components/ai-usage-strip.component.js +40 -9
- package/src/lib/components/ai-usage-strip.component.js.map +1 -1
- package/src/lib/components/assist-jobs-drawer.component.js +12 -16
- package/src/lib/components/assist-jobs-drawer.component.js.map +1 -1
- package/src/lib/components/assist-panel.component.js +91 -51
- package/src/lib/components/assist-panel.component.js.map +1 -1
- package/src/lib/components/use-ai-usage-meter.d.ts +2 -0
- package/src/lib/components/use-ai-usage-meter.js +8 -3
- package/src/lib/components/use-ai-usage-meter.js.map +1 -1
- package/src/lib/jobs/ai-build-unit-outcome.d.ts +3 -1
- package/src/lib/jobs/ai-build-unit-outcome.js +19 -3
- package/src/lib/jobs/ai-build-unit-outcome.js.map +1 -1
- package/src/lib/jobs/ai-job-build-step.js +33 -3
- package/src/lib/jobs/ai-job-build-step.js.map +1 -1
- package/src/lib/jobs/ai-job-cancel.d.ts +71 -0
- package/src/lib/jobs/ai-job-cancel.js +102 -0
- package/src/lib/jobs/ai-job-cancel.js.map +1 -0
- package/src/lib/jobs/ai-job-component-step.js +4 -1
- package/src/lib/jobs/ai-job-component-step.js.map +1 -1
- package/src/lib/jobs/ai-job-draft-ids.js +7 -2
- package/src/lib/jobs/ai-job-draft-ids.js.map +1 -1
- package/src/lib/jobs/ai-job-drafts.js +2 -0
- package/src/lib/jobs/ai-job-drafts.js.map +1 -1
- package/src/lib/jobs/ai-job-form-dataset.d.ts +100 -0
- package/src/lib/jobs/ai-job-form-dataset.js +161 -0
- package/src/lib/jobs/ai-job-form-dataset.js.map +1 -0
- package/src/lib/jobs/ai-job-form-step.d.ts +15 -3
- package/src/lib/jobs/ai-job-form-step.js +41 -10
- package/src/lib/jobs/ai-job-form-step.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-language.d.ts +2 -0
- package/src/lib/jobs/ai-job-layout-language.js +13 -1
- package/src/lib/jobs/ai-job-layout-language.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-step.js +6 -1
- package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
- package/src/lib/jobs/ai-job-page-language.d.ts +9 -0
- package/src/lib/jobs/ai-job-page-language.js +45 -2
- package/src/lib/jobs/ai-job-page-language.js.map +1 -1
- package/src/lib/jobs/ai-job-page-step.js +12 -1
- package/src/lib/jobs/ai-job-page-step.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-step.d.ts +38 -1
- package/src/lib/jobs/ai-job-plan-step.js +90 -13
- package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-content.d.ts +15 -0
- package/src/lib/jobs/ai-job-site-content.js +66 -7
- package/src/lib/jobs/ai-job-site-content.js.map +1 -1
- package/src/lib/jobs/ai-job-site-datasets.d.ts +170 -0
- package/src/lib/jobs/ai-job-site-datasets.js +499 -0
- package/src/lib/jobs/ai-job-site-datasets.js.map +1 -0
- package/src/lib/jobs/ai-job-site-step.d.ts +15 -8
- package/src/lib/jobs/ai-job-site-step.js +152 -30
- package/src/lib/jobs/ai-job-site-step.js.map +1 -1
- package/src/lib/jobs/ai-job-template-step.js +4 -1
- package/src/lib/jobs/ai-job-template-step.js.map +1 -1
- package/src/lib/jobs/ai-jobs.d.ts +37 -12
- package/src/lib/jobs/ai-jobs.js +184 -28
- package/src/lib/jobs/ai-jobs.js.map +1 -1
- package/src/lib/jobs/ai-layout-stock-photos.d.ts +143 -70
- package/src/lib/jobs/ai-layout-stock-photos.js +649 -176
- package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
- package/src/lib/jobs/fixtures/ai-ceramics-portfolio-site-plan.d.ts +30 -0
- package/src/lib/jobs/fixtures/ai-ceramics-portfolio-site-plan.js +220 -0
- package/src/lib/jobs/fixtures/ai-ceramics-portfolio-site-plan.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-dataset-site-plan-recordings.d.ts +30 -0
- package/src/lib/jobs/fixtures/ai-dataset-site-plan-recordings.js +637 -0
- package/src/lib/jobs/fixtures/ai-dataset-site-plan-recordings.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-pixabay-recordings.d.ts +27 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-pixabay-recordings.js +151 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-pixabay-recordings.js.map +1 -0
- package/src/lib/layout-language/ai-layout-compiler.d.ts +16 -0
- package/src/lib/layout-language/ai-layout-compiler.js +397 -9
- package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
- package/src/lib/layout-language/ai-layout-design.d.ts +6 -3
- package/src/lib/layout-language/ai-layout-design.js +5 -3
- package/src/lib/layout-language/ai-layout-design.js.map +1 -1
- package/src/lib/layout-language/ai-layout-frame.d.ts +12 -0
- package/src/lib/layout-language/ai-layout-frame.js +58 -3
- package/src/lib/layout-language/ai-layout-frame.js.map +1 -1
- package/src/lib/layout-language/ai-layout-gaps.js +17 -1
- package/src/lib/layout-language/ai-layout-gaps.js.map +1 -1
- package/src/lib/layout-language/ai-layout-listings.d.ts +44 -3
- package/src/lib/layout-language/ai-layout-listings.js +118 -50
- package/src/lib/layout-language/ai-layout-listings.js.map +1 -1
- package/src/lib/layout-language/ai-layout-pictures.d.ts +8 -0
- package/src/lib/layout-language/ai-layout-pictures.js.map +1 -1
- package/src/lib/layout-language/ai-layout-storefront.d.ts +82 -0
- package/src/lib/layout-language/ai-layout-storefront.js +223 -0
- package/src/lib/layout-language/ai-layout-storefront.js.map +1 -0
- package/src/lib/model/ai-build-job.d.ts +11 -1
- package/src/lib/model/ai-build-job.js +35 -6
- package/src/lib/model/ai-build-job.js.map +1 -1
- package/src/lib/model/ai-build-plan.d.ts +23 -3
- package/src/lib/model/ai-build-plan.js +47 -6
- package/src/lib/model/ai-build-plan.js.map +1 -1
- package/src/lib/model/ai-build-progress.d.ts +2 -2
- package/src/lib/model/ai-build-progress.js +6 -4
- package/src/lib/model/ai-build-progress.js.map +1 -1
- package/src/lib/model/ai-credit-estimate.d.ts +69 -2
- package/src/lib/model/ai-credit-estimate.js +91 -3
- package/src/lib/model/ai-credit-estimate.js.map +1 -1
- package/src/lib/model/ai-job-cancel-copy.d.ts +44 -0
- package/src/lib/model/ai-job-cancel-copy.js +53 -0
- package/src/lib/model/ai-job-cancel-copy.js.map +1 -0
- package/src/lib/model/ai-job-failure-copy.d.ts +4 -1
- package/src/lib/model/ai-job-failure-copy.js +8 -3
- package/src/lib/model/ai-job-failure-copy.js.map +1 -1
- package/src/lib/model/ai-jobs.types.d.ts +20 -0
- package/src/lib/model/ai-jobs.types.js.map +1 -1
- package/src/lib/model/ai-plan-capabilities.d.ts +7 -0
- package/src/lib/model/ai-plan-capabilities.js.map +1 -1
- package/src/lib/model/ai-site-build-progress.js +7 -4
- package/src/lib/model/ai-site-build-progress.js.map +1 -1
- package/src/lib/model/ai-site-job.d.ts +38 -4
- package/src/lib/model/ai-site-job.js +50 -14
- package/src/lib/model/ai-site-job.js.map +1 -1
- package/src/lib/model/ai-site-kinds.d.ts +4 -1
- package/src/lib/model/ai-site-kinds.js +34 -6
- package/src/lib/model/ai-site-kinds.js.map +1 -1
- package/src/lib/runtime/ai-dataset-generation.d.ts +77 -0
- package/src/lib/runtime/ai-dataset-generation.js +127 -0
- package/src/lib/runtime/ai-dataset-generation.js.map +1 -0
- package/src/lib/runtime/ai-doctrine-validators.d.ts +77 -1
- package/src/lib/runtime/ai-doctrine-validators.js +280 -6
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-doctrine.js +10 -4
- package/src/lib/runtime/ai-doctrine.js.map +1 -1
- package/src/lib/runtime/ai-node-tree.d.ts +2 -0
- package/src/lib/runtime/ai-node-tree.js +16 -0
- package/src/lib/runtime/ai-node-tree.js.map +1 -1
- package/src/lib/runtime/ai-palette.generated.js +53 -7
- package/src/lib/runtime/ai-palette.generated.js.map +1 -1
- package/src/lib/server/ai-jobs-cancel.d.ts +13 -5
- package/src/lib/server/ai-jobs-cancel.js +27 -9
- package/src/lib/server/ai-jobs-cancel.js.map +1 -1
- package/src/lib/server/ai-jobs-gate.d.ts +12 -2
- package/src/lib/server/ai-jobs-gate.js +20 -9
- package/src/lib/server/ai-jobs-gate.js.map +1 -1
- package/src/lib/tools/ai-dataset-tool.d.ts +68 -0
- package/src/lib/tools/ai-dataset-tool.js +231 -0
- package/src/lib/tools/ai-dataset-tool.js.map +1 -0
- package/src/lib/usage/ai-usage-meter.d.ts +2 -1
- package/src/lib/usage/ai-usage-meter.js +17 -1
- package/src/lib/usage/ai-usage-meter.js.map +1 -1
- package/src/lib/usage/ai-usage-wire.d.ts +6 -0
- package/src/lib/usage/ai-usage-wire.js.map +1 -1
- package/src/lib/usage/assist-usage.d.ts +7 -0
- package/src/lib/usage/assist-usage.js +14 -4
- package/src/lib/usage/assist-usage.js.map +1 -1
- package/src/lib/usage/free-ai-credits-left.d.ts +7 -0
- package/src/lib/usage/free-ai-credits-left.js +30 -4
- package/src/lib/usage/free-ai-credits-left.js.map +1 -1
|
@@ -102,6 +102,13 @@ export interface AiPlanCapabilities {
|
|
|
102
102
|
* typed out twice, and rule 1 asks no component of them (AGL-3660).
|
|
103
103
|
*/
|
|
104
104
|
repeatsCompiled?: boolean;
|
|
105
|
+
/**
|
|
106
|
+
* The most datasets this job's plan keeps (AGL-3616): a site start builds
|
|
107
|
+
* at most a few. A plan naming more is settled in code before the rules
|
|
108
|
+
* read it (`aiSettlePlanDatasets`), never re-asked. Absent is no cap beyond
|
|
109
|
+
* `create.dataset.left`.
|
|
110
|
+
*/
|
|
111
|
+
datasetsMax?: number;
|
|
105
112
|
}
|
|
106
113
|
/** What a job of one kind builds from its own plan, where it builds only some creations. */
|
|
107
114
|
export interface AiPlanJobScope {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-plan-capabilities.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n AI_BUILD_PLAN_CREATE_KINDS,\n AI_BUILD_PLAN_CREATION_NOUNS,\n type AiBuildPlan,\n type AiBuildPlanCreateKind,\n} from './ai-build-plan'\n\n/**\n * What a plan may create on one site (AGL-3030): the workspace's plan and the\n * site's counts against its limits, narrowed to what the job itself builds.\n *\n * A plan that names a creation nothing can make is a dead end the member pays\n * for twice: once for the plan, and again for the plan they describe after\n * being told to make the creation by hand — which, on a workspace whose plan\n * does not include it, they cannot. So the plan step is TOLD what may be\n * created before it answers, and the plan rules refuse a creation outside it\n * with the one re-ask every other rule gets.\n *\n * ── Where the doctrine builds inline ─────────────────────────────────────\n *\n * The building doctrine makes a repeat a reusable component and a form a\n * saved form placed by id. A workspace whose plan keeps no reusable\n * components draws a list's repeated items in one section instead\n * (AGL-3071); `reusableComponents` is the one flag every validator reads for\n * that.\n *\n * A form is never drawn inline (AGL-3596): it is a saved form, counted\n * against the site's form allowance like one a member makes. Where the site\n * has none to place and may create none, the page places no form at all.\n *\n * ── Kept out of the cached prefix ────────────────────────────────────────\n *\n * The capabilities are per workspace and per site, so they ride the plan's\n * USER turn, never a system block: the doctrine's cached prefix stays one\n * entry for the platform. The doctrine states the rule once — create only\n * what the request says may be created, and build inline where it says the\n * workspace keeps no reusable components — and the request states the facts.\n *\n * This module imports nothing at runtime beyond the plan's own model, so the\n * validators, the plan step, the doors and their specs read one vocabulary.\n * What a workspace's plan and counts come to is read by\n * `readAiPlanCapabilities` in `jobs/ai-job-drafts.ts`, beside the band\n * arithmetic the create route and the draft writer share.\n */\n\n/** One creation kind, as a job on this site may make it or not. */\nexport interface AiPlanCreation {\n allowed: boolean\n /** How many more the site may hold; `null` when the plan counts none. */\n left: number | null\n /**\n * Why not, as a clause a sentence completes: \"this workspace's plan does\n * not include reusable components\". `null` when allowed.\n */\n reason: string | null\n}\n\nexport interface AiPlanCapabilities {\n /**\n * Whether the workspace keeps reusable components. Where it does not, a\n * list's repeated items are drawn in one section. Forms are not read off\n * it: whether one may be created is `create.form` (AGL-3596).\n */\n reusableComponents: boolean\n /** Every creation kind a plan may name, and whether this job may make one here. */\n create: Readonly<Record<AiBuildPlanCreateKind, AiPlanCreation>>\n /**\n * Whether the workspace spends the Free taste, whose monthly credits are a\n * wall (AGL-2925): a plan is then held to the sections the wall's worst\n * case pays for (AGL-3070). Absent is `false`.\n */\n freeTaste?: boolean\n /**\n * A Free workspace's site scaffold (AGL-3594): the most pages its plan may\n * build, which the plan step sets for a `site` job on the Free taste. The\n * Free wall then holds the plan to that many pages and to the sections a\n * SITE plan's worst case leaves room for. Absent for every other job.\n */\n freeSitePages?: number\n /**\n * Whether this job's pages are written in the compact layout language\n * (AGL-3660), whose compiler draws a section's repeated items itself: as\n * instances of a component the plan places there, or, with none, in their\n * compact form, a title over its text, which is smaller than a block rule 1\n * calls a repeat. A plan is then not asked for a component for a section's\n * repeated items (AGL-3616); a section two pages share still is one. The\n * plan step sets it for a `build` job. Absent is `false`.\n *\n * It is the plan half of one rule. The page half is the tree check's\n * `AiDoctrineTreeContext.repeatsCompiled`, which every page the layout\n * language compiles is checked with: where the site's design draws a\n * section's items as picture cards — larger than the compact form, and so\n * a block rule 1 would count — those cards are the compiler's, not a block\n * typed out twice, and rule 1 asks no component of them (AGL-3660).\n */\n repeatsCompiled?: boolean\n}\n\n/** What a job of one kind builds from its own plan, where it builds only some creations. */\nexport interface AiPlanJobScope {\n /** The job as a refusal names it: \"a page job\". */\n noun: string\n /** The creation kinds the job builds itself; any other is refused before the plan is kept. */\n creates: readonly AiBuildPlanCreateKind[]\n}\n\nconst ALLOWED: AiPlanCreation = { allowed: true, left: null, reason: null }\n\n/**\n * Capabilities that restrict nothing: what a plan is held to when no\n * workspace was read — an org-level job, an eval, a spec about something\n * else. The doctrine applies whole.\n */\nexport function aiUnrestrictedPlanCapabilities(): AiPlanCapabilities {\n return {\n reusableComponents: true,\n create: Object.fromEntries(\n AI_BUILD_PLAN_CREATE_KINDS.map((kind) => [kind, ALLOWED]),\n ) as Record<AiBuildPlanCreateKind, AiPlanCreation>,\n }\n}\n\n/**\n * The workspace's capabilities narrowed to what a job of one kind builds.\n * A creation the workspace itself cannot make keeps the workspace's reason,\n * which is the one a member can act on; one the job does not build says so.\n */\nexport function aiPlanCapabilitiesForJob(\n capabilities: AiPlanCapabilities,\n scope: AiPlanJobScope | null | undefined,\n): AiPlanCapabilities {\n if (!scope) return capabilities\n const create = { ...capabilities.create }\n for (const kind of AI_BUILD_PLAN_CREATE_KINDS) {\n if (!create[kind].allowed || scope.creates.includes(kind)) continue\n create[kind] = { allowed: false, left: null, reason: `${scope.noun} does not build one` }\n }\n return { ...capabilities, create }\n}\n\n/** \"a layout\", \"an email design\": a creation kind with its article. */\nexport function aiCreationNoun(kind: AiBuildPlanCreateKind): string {\n const noun = AI_BUILD_PLAN_CREATION_NOUNS[kind].noun\n return `${/^[aeiou]/.test(noun) ? 'an' : 'a'} ${noun}`\n}\n\n/**\n * The sentence a request states when the workspace keeps no reusable\n * components. It says a list's repeated items go in one section (AGL-3071):\n * \"a repeated item in its own section\" reads just as well as a section for\n * each item, which is how a live About page's four practice areas came to be\n * planned as four sections of one item.\n */\nexport const AI_PLAN_INLINE_SENTENCE =\n \"This workspace keeps no reusable components: draw a list's repeated items in one section.\"\n\n/**\n * The sentence a build's request states (AGL-3616): its pages are written in\n * the layout language, which draws a section's repeated items itself, so the\n * plan counts them in the section's `items` and needs no component for them.\n */\nexport const AI_PLAN_COMPILED_REPEATS_SENTENCE =\n \"A section's repeated items need no component here: count them in its items and the page draws them. A section two pages share is still one component.\"\n\n/**\n * The sentence a request states when the site has no saved form and this job\n * may create none (AGL-3596): the plan places no form, rather than one drawn\n * on the page with no saved form behind it.\n */\nexport const AI_PLAN_NO_FORM_SENTENCE =\n 'This site has no saved form and may create none: plan no form, and no section that collects answers.'\n\n/**\n * What the plan step's user turn says the job may create here: one line a\n * kind, and the inline sentence where the workspace keeps no reusable\n * components. Per workspace and per site, so it is never a system block.\n */\nexport function aiPlanCapabilityLines(capabilities: AiPlanCapabilities): string[] {\n const lines = ['What this job may create on this site:']\n for (const kind of AI_BUILD_PLAN_CREATE_KINDS) {\n const entry = capabilities.create[kind]\n const noun = AI_BUILD_PLAN_CREATION_NOUNS[kind].noun\n if (entry.allowed) {\n lines.push(`- ${noun}: yes${entry.left === null ? '' : `, ${entry.left} more`}`)\n } else {\n lines.push(`- ${noun}: no, because ${entry.reason ?? 'it cannot be made here'}`)\n }\n }\n if (!capabilities.reusableComponents) lines.push(AI_PLAN_INLINE_SENTENCE)\n return lines\n}\n\n/**\n * Whether a plan on this site can place a saved form at all (AGL-3596): the\n * site has one, or the job may create one. Absent capabilities restrict\n * nothing, as everywhere else.\n */\nexport function aiPlanCanPlaceForm(\n inventory: { forms: readonly unknown[] } | null,\n capabilities: Pick<AiPlanCapabilities, 'create'> | null,\n): boolean {\n if (inventory?.forms.length) return true\n return capabilities?.create.form.allowed !== false\n}\n\n/** What to do instead of a creation the job may not make, for the re-ask and the review. */\nfunction insteadOf(kind: AiBuildPlanCreateKind, capabilities: AiPlanCapabilities): string {\n switch (kind) {\n case 'component':\n return capabilities.reusableComponents\n ? 'Place a component the site already has.'\n : \"Draw a list's repeated items in one section instead.\"\n case 'form':\n return 'Place a form the site already has, or leave the form off the page.'\n case 'layout':\n return 'Put the page in a layout the site already has.'\n case 'template':\n return 'Build the page without a template.'\n case 'theme-change':\n return \"Use the colors the theme already has.\"\n case 'dataset':\n return 'Bind a dataset or collection the site already has, or keep the list short enough to write out.'\n case 'email':\n return 'Leave the email out of this job.'\n }\n}\n\n/** One creation a plan names that this job may not make here. */\nexport interface AiPlanUncreatable {\n kind: AiBuildPlanCreateKind\n name: string\n /** The plan path of the creation: `create[2]`. */\n path: string\n /** Why not, as a clause: \"this site has room for 1 more\". */\n reason: string\n /** One customer-safe sentence: what was refused, why, and what to do instead. */\n message: string\n}\n\n/**\n * Why the nth creation of a kind may not be made here, as a clause; `null`\n * when it may. A kind the site may make only so many more of refuses the ones\n * past that count, not the first of them.\n */\nfunction refusedReason(capability: AiPlanCreation, nth: number): string | null {\n const overCount = capability.allowed && capability.left !== null && nth > capability.left\n if (capability.allowed && !overCount) return null\n return overCount\n ? `this site has room for ${capability.left} more`\n : (capability.reason ?? 'it cannot be made here')\n}\n\n/** The creations a plan names that the capabilities refuse, in plan order. */\nexport function aiPlanUncreatable(\n plan: Pick<AiBuildPlan, 'create'>,\n capabilities: AiPlanCapabilities,\n): AiPlanUncreatable[] {\n const refused: AiPlanUncreatable[] = []\n const counted = new Map<AiBuildPlanCreateKind, number>()\n plan.create.forEach((entry, index) => {\n const nth = (counted.get(entry.kind) ?? 0) + 1\n counted.set(entry.kind, nth)\n const reason = refusedReason(capabilities.create[entry.kind], nth)\n if (reason === null) return\n refused.push({\n kind: entry.kind,\n name: entry.name,\n path: `create[${index}]`,\n reason,\n message: `The plan creates ${aiCreationNoun(entry.kind)} named \"${entry.name}\", and ${reason}. ${insteadOf(entry.kind, capabilities)}`,\n })\n })\n return refused\n}\n\n/**\n * Why the plan could not add one more creation of a kind here, and what to\n * do instead; `null` when it could (AGL-3040). Counted past the creations of\n * that kind the plan already names, as `aiPlanUncreatable` counts them, so a\n * plan told to declare a creation is never told to declare one the next\n * answer is refused for.\n */\nexport function aiPlanUncreatableKind(\n plan: Pick<AiBuildPlan, 'create'>,\n kind: AiBuildPlanCreateKind,\n capabilities: AiPlanCapabilities,\n): { reason: string; instead: string } | null {\n const planned = plan.create.filter((entry) => entry.kind === kind).length\n const reason = refusedReason(capabilities.create[kind], planned + 1)\n return reason === null ? null : { reason, instead: insteadOf(kind, capabilities) }\n}\n"],"names":["AI_BUILD_PLAN_CREATE_KINDS","AI_BUILD_PLAN_CREATION_NOUNS","ALLOWED","allowed","left","reason","aiUnrestrictedPlanCapabilities","reusableComponents","create","Object","fromEntries","map","kind","aiPlanCapabilitiesForJob","capabilities","scope","creates","includes","noun","aiCreationNoun","test","AI_PLAN_INLINE_SENTENCE","AI_PLAN_COMPILED_REPEATS_SENTENCE","AI_PLAN_NO_FORM_SENTENCE","aiPlanCapabilityLines","lines","entry","push","aiPlanCanPlaceForm","inventory","forms","length","form","insteadOf","refusedReason","capability","nth","overCount","aiPlanUncreatable","plan","refused","counted","Map","forEach","index","get","set","name","path","message","aiPlanUncreatableKind","planned","filter","instead"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-plan-capabilities.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n AI_BUILD_PLAN_CREATE_KINDS,\n AI_BUILD_PLAN_CREATION_NOUNS,\n type AiBuildPlan,\n type AiBuildPlanCreateKind,\n} from './ai-build-plan'\n\n/**\n * What a plan may create on one site (AGL-3030): the workspace's plan and the\n * site's counts against its limits, narrowed to what the job itself builds.\n *\n * A plan that names a creation nothing can make is a dead end the member pays\n * for twice: once for the plan, and again for the plan they describe after\n * being told to make the creation by hand — which, on a workspace whose plan\n * does not include it, they cannot. So the plan step is TOLD what may be\n * created before it answers, and the plan rules refuse a creation outside it\n * with the one re-ask every other rule gets.\n *\n * ── Where the doctrine builds inline ─────────────────────────────────────\n *\n * The building doctrine makes a repeat a reusable component and a form a\n * saved form placed by id. A workspace whose plan keeps no reusable\n * components draws a list's repeated items in one section instead\n * (AGL-3071); `reusableComponents` is the one flag every validator reads for\n * that.\n *\n * A form is never drawn inline (AGL-3596): it is a saved form, counted\n * against the site's form allowance like one a member makes. Where the site\n * has none to place and may create none, the page places no form at all.\n *\n * ── Kept out of the cached prefix ────────────────────────────────────────\n *\n * The capabilities are per workspace and per site, so they ride the plan's\n * USER turn, never a system block: the doctrine's cached prefix stays one\n * entry for the platform. The doctrine states the rule once — create only\n * what the request says may be created, and build inline where it says the\n * workspace keeps no reusable components — and the request states the facts.\n *\n * This module imports nothing at runtime beyond the plan's own model, so the\n * validators, the plan step, the doors and their specs read one vocabulary.\n * What a workspace's plan and counts come to is read by\n * `readAiPlanCapabilities` in `jobs/ai-job-drafts.ts`, beside the band\n * arithmetic the create route and the draft writer share.\n */\n\n/** One creation kind, as a job on this site may make it or not. */\nexport interface AiPlanCreation {\n allowed: boolean\n /** How many more the site may hold; `null` when the plan counts none. */\n left: number | null\n /**\n * Why not, as a clause a sentence completes: \"this workspace's plan does\n * not include reusable components\". `null` when allowed.\n */\n reason: string | null\n}\n\nexport interface AiPlanCapabilities {\n /**\n * Whether the workspace keeps reusable components. Where it does not, a\n * list's repeated items are drawn in one section. Forms are not read off\n * it: whether one may be created is `create.form` (AGL-3596).\n */\n reusableComponents: boolean\n /** Every creation kind a plan may name, and whether this job may make one here. */\n create: Readonly<Record<AiBuildPlanCreateKind, AiPlanCreation>>\n /**\n * Whether the workspace spends the Free taste, whose monthly credits are a\n * wall (AGL-2925): a plan is then held to the sections the wall's worst\n * case pays for (AGL-3070). Absent is `false`.\n */\n freeTaste?: boolean\n /**\n * A Free workspace's site scaffold (AGL-3594): the most pages its plan may\n * build, which the plan step sets for a `site` job on the Free taste. The\n * Free wall then holds the plan to that many pages and to the sections a\n * SITE plan's worst case leaves room for. Absent for every other job.\n */\n freeSitePages?: number\n /**\n * Whether this job's pages are written in the compact layout language\n * (AGL-3660), whose compiler draws a section's repeated items itself: as\n * instances of a component the plan places there, or, with none, in their\n * compact form, a title over its text, which is smaller than a block rule 1\n * calls a repeat. A plan is then not asked for a component for a section's\n * repeated items (AGL-3616); a section two pages share still is one. The\n * plan step sets it for a `build` job. Absent is `false`.\n *\n * It is the plan half of one rule. The page half is the tree check's\n * `AiDoctrineTreeContext.repeatsCompiled`, which every page the layout\n * language compiles is checked with: where the site's design draws a\n * section's items as picture cards — larger than the compact form, and so\n * a block rule 1 would count — those cards are the compiler's, not a block\n * typed out twice, and rule 1 asks no component of them (AGL-3660).\n */\n repeatsCompiled?: boolean\n /**\n * The most datasets this job's plan keeps (AGL-3616): a site start builds\n * at most a few. A plan naming more is settled in code before the rules\n * read it (`aiSettlePlanDatasets`), never re-asked. Absent is no cap beyond\n * `create.dataset.left`.\n */\n datasetsMax?: number\n}\n\n/** What a job of one kind builds from its own plan, where it builds only some creations. */\nexport interface AiPlanJobScope {\n /** The job as a refusal names it: \"a page job\". */\n noun: string\n /** The creation kinds the job builds itself; any other is refused before the plan is kept. */\n creates: readonly AiBuildPlanCreateKind[]\n}\n\nconst ALLOWED: AiPlanCreation = { allowed: true, left: null, reason: null }\n\n/**\n * Capabilities that restrict nothing: what a plan is held to when no\n * workspace was read — an org-level job, an eval, a spec about something\n * else. The doctrine applies whole.\n */\nexport function aiUnrestrictedPlanCapabilities(): AiPlanCapabilities {\n return {\n reusableComponents: true,\n create: Object.fromEntries(\n AI_BUILD_PLAN_CREATE_KINDS.map((kind) => [kind, ALLOWED]),\n ) as Record<AiBuildPlanCreateKind, AiPlanCreation>,\n }\n}\n\n/**\n * The workspace's capabilities narrowed to what a job of one kind builds.\n * A creation the workspace itself cannot make keeps the workspace's reason,\n * which is the one a member can act on; one the job does not build says so.\n */\nexport function aiPlanCapabilitiesForJob(\n capabilities: AiPlanCapabilities,\n scope: AiPlanJobScope | null | undefined,\n): AiPlanCapabilities {\n if (!scope) return capabilities\n const create = { ...capabilities.create }\n for (const kind of AI_BUILD_PLAN_CREATE_KINDS) {\n if (!create[kind].allowed || scope.creates.includes(kind)) continue\n create[kind] = { allowed: false, left: null, reason: `${scope.noun} does not build one` }\n }\n return { ...capabilities, create }\n}\n\n/** \"a layout\", \"an email design\": a creation kind with its article. */\nexport function aiCreationNoun(kind: AiBuildPlanCreateKind): string {\n const noun = AI_BUILD_PLAN_CREATION_NOUNS[kind].noun\n return `${/^[aeiou]/.test(noun) ? 'an' : 'a'} ${noun}`\n}\n\n/**\n * The sentence a request states when the workspace keeps no reusable\n * components. It says a list's repeated items go in one section (AGL-3071):\n * \"a repeated item in its own section\" reads just as well as a section for\n * each item, which is how a live About page's four practice areas came to be\n * planned as four sections of one item.\n */\nexport const AI_PLAN_INLINE_SENTENCE =\n \"This workspace keeps no reusable components: draw a list's repeated items in one section.\"\n\n/**\n * The sentence a build's request states (AGL-3616): its pages are written in\n * the layout language, which draws a section's repeated items itself, so the\n * plan counts them in the section's `items` and needs no component for them.\n */\nexport const AI_PLAN_COMPILED_REPEATS_SENTENCE =\n \"A section's repeated items need no component here: count them in its items and the page draws them. A section two pages share is still one component.\"\n\n/**\n * The sentence a request states when the site has no saved form and this job\n * may create none (AGL-3596): the plan places no form, rather than one drawn\n * on the page with no saved form behind it.\n */\nexport const AI_PLAN_NO_FORM_SENTENCE =\n 'This site has no saved form and may create none: plan no form, and no section that collects answers.'\n\n/**\n * What the plan step's user turn says the job may create here: one line a\n * kind, and the inline sentence where the workspace keeps no reusable\n * components. Per workspace and per site, so it is never a system block.\n */\nexport function aiPlanCapabilityLines(capabilities: AiPlanCapabilities): string[] {\n const lines = ['What this job may create on this site:']\n for (const kind of AI_BUILD_PLAN_CREATE_KINDS) {\n const entry = capabilities.create[kind]\n const noun = AI_BUILD_PLAN_CREATION_NOUNS[kind].noun\n if (entry.allowed) {\n lines.push(`- ${noun}: yes${entry.left === null ? '' : `, ${entry.left} more`}`)\n } else {\n lines.push(`- ${noun}: no, because ${entry.reason ?? 'it cannot be made here'}`)\n }\n }\n if (!capabilities.reusableComponents) lines.push(AI_PLAN_INLINE_SENTENCE)\n return lines\n}\n\n/**\n * Whether a plan on this site can place a saved form at all (AGL-3596): the\n * site has one, or the job may create one. Absent capabilities restrict\n * nothing, as everywhere else.\n */\nexport function aiPlanCanPlaceForm(\n inventory: { forms: readonly unknown[] } | null,\n capabilities: Pick<AiPlanCapabilities, 'create'> | null,\n): boolean {\n if (inventory?.forms.length) return true\n return capabilities?.create.form.allowed !== false\n}\n\n/** What to do instead of a creation the job may not make, for the re-ask and the review. */\nfunction insteadOf(kind: AiBuildPlanCreateKind, capabilities: AiPlanCapabilities): string {\n switch (kind) {\n case 'component':\n return capabilities.reusableComponents\n ? 'Place a component the site already has.'\n : \"Draw a list's repeated items in one section instead.\"\n case 'form':\n return 'Place a form the site already has, or leave the form off the page.'\n case 'layout':\n return 'Put the page in a layout the site already has.'\n case 'template':\n return 'Build the page without a template.'\n case 'theme-change':\n return \"Use the colors the theme already has.\"\n case 'dataset':\n return 'Bind a dataset or collection the site already has, or keep the list short enough to write out.'\n case 'email':\n return 'Leave the email out of this job.'\n }\n}\n\n/** One creation a plan names that this job may not make here. */\nexport interface AiPlanUncreatable {\n kind: AiBuildPlanCreateKind\n name: string\n /** The plan path of the creation: `create[2]`. */\n path: string\n /** Why not, as a clause: \"this site has room for 1 more\". */\n reason: string\n /** One customer-safe sentence: what was refused, why, and what to do instead. */\n message: string\n}\n\n/**\n * Why the nth creation of a kind may not be made here, as a clause; `null`\n * when it may. A kind the site may make only so many more of refuses the ones\n * past that count, not the first of them.\n */\nfunction refusedReason(capability: AiPlanCreation, nth: number): string | null {\n const overCount = capability.allowed && capability.left !== null && nth > capability.left\n if (capability.allowed && !overCount) return null\n return overCount\n ? `this site has room for ${capability.left} more`\n : (capability.reason ?? 'it cannot be made here')\n}\n\n/** The creations a plan names that the capabilities refuse, in plan order. */\nexport function aiPlanUncreatable(\n plan: Pick<AiBuildPlan, 'create'>,\n capabilities: AiPlanCapabilities,\n): AiPlanUncreatable[] {\n const refused: AiPlanUncreatable[] = []\n const counted = new Map<AiBuildPlanCreateKind, number>()\n plan.create.forEach((entry, index) => {\n const nth = (counted.get(entry.kind) ?? 0) + 1\n counted.set(entry.kind, nth)\n const reason = refusedReason(capabilities.create[entry.kind], nth)\n if (reason === null) return\n refused.push({\n kind: entry.kind,\n name: entry.name,\n path: `create[${index}]`,\n reason,\n message: `The plan creates ${aiCreationNoun(entry.kind)} named \"${entry.name}\", and ${reason}. ${insteadOf(entry.kind, capabilities)}`,\n })\n })\n return refused\n}\n\n/**\n * Why the plan could not add one more creation of a kind here, and what to\n * do instead; `null` when it could (AGL-3040). Counted past the creations of\n * that kind the plan already names, as `aiPlanUncreatable` counts them, so a\n * plan told to declare a creation is never told to declare one the next\n * answer is refused for.\n */\nexport function aiPlanUncreatableKind(\n plan: Pick<AiBuildPlan, 'create'>,\n kind: AiBuildPlanCreateKind,\n capabilities: AiPlanCapabilities,\n): { reason: string; instead: string } | null {\n const planned = plan.create.filter((entry) => entry.kind === kind).length\n const reason = refusedReason(capabilities.create[kind], planned + 1)\n return reason === null ? null : { reason, instead: insteadOf(kind, capabilities) }\n}\n"],"names":["AI_BUILD_PLAN_CREATE_KINDS","AI_BUILD_PLAN_CREATION_NOUNS","ALLOWED","allowed","left","reason","aiUnrestrictedPlanCapabilities","reusableComponents","create","Object","fromEntries","map","kind","aiPlanCapabilitiesForJob","capabilities","scope","creates","includes","noun","aiCreationNoun","test","AI_PLAN_INLINE_SENTENCE","AI_PLAN_COMPILED_REPEATS_SENTENCE","AI_PLAN_NO_FORM_SENTENCE","aiPlanCapabilityLines","lines","entry","push","aiPlanCanPlaceForm","inventory","forms","length","form","insteadOf","refusedReason","capability","nth","overCount","aiPlanUncreatable","plan","refused","counted","Map","forEach","index","get","set","name","path","message","aiPlanUncreatableKind","planned","filter","instead"],"mappings":"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"}
|
|
@@ -14,7 +14,8 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
|
14
14
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
15
|
* See the License for the specific language governing permissions and
|
|
16
16
|
* limitations under the License.
|
|
17
|
-
*/ import {
|
|
17
|
+
*/ import { AI_JOB_CANCELED_LEDE, aiJobCanceledCreditsCopy } from "./ai-job-cancel-copy.js";
|
|
18
|
+
import { aiJobConfirmingOwnPlan, aiJobKindNoun } from "./ai-job-activity.js";
|
|
18
19
|
import { aiJobRefundCopy } from "./ai-job-failure-copy.js";
|
|
19
20
|
import { aiBuildItemRows, aiBuildOpNoun, aiBuildOutcomeLine, aiSitePartialCopy } from "./ai-build-progress.js";
|
|
20
21
|
import { AI_JOB_PAUSED_NEXT_COPY, aiJobPausedTitle } from "./ai-job-notice.js";
|
|
@@ -306,6 +307,8 @@ function aiSiteBuildRowsOf(job) {
|
|
|
306
307
|
// from the job's recorded give-back.
|
|
307
308
|
const refund = phase === 'working' || phase === 'done' ? null : aiJobRefundCopy(job);
|
|
308
309
|
if (refund && refunded > 0) return refund;
|
|
310
|
+
// A person's cancel keeps what ran charged, and says how much (AGL-3616).
|
|
311
|
+
if (phase === 'canceled') return aiJobCanceledCreditsCopy(job);
|
|
309
312
|
if (phase === 'done') return `${subject} used ${net} credits.`;
|
|
310
313
|
if (phase === 'working') return `Credits used so far: ${net}`;
|
|
311
314
|
if (refund) return `${subject} used ${net} credits. ${refund}`;
|
|
@@ -385,8 +388,8 @@ function aiSiteBuildRowsOf(job) {
|
|
|
385
388
|
}
|
|
386
389
|
if (phase === 'canceled') {
|
|
387
390
|
return {
|
|
388
|
-
heading: generic ? 'Your AI job was canceled' : `
|
|
389
|
-
lede:
|
|
391
|
+
heading: generic ? 'Your AI job was canceled' : `You canceled your ${noun}`,
|
|
392
|
+
lede: AI_JOB_CANCELED_LEDE
|
|
390
393
|
};
|
|
391
394
|
}
|
|
392
395
|
return {
|
|
@@ -465,7 +468,7 @@ const joinWords = (names)=>{
|
|
|
465
468
|
}
|
|
466
469
|
if (phase === 'canceled') return {
|
|
467
470
|
heading: 'Your build was canceled',
|
|
468
|
-
lede:
|
|
471
|
+
lede: AI_JOB_CANCELED_LEDE
|
|
469
472
|
};
|
|
470
473
|
return {
|
|
471
474
|
heading: 'Nothing could be built',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-site-build-progress.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 { aiJobConfirmingOwnPlan, aiJobKindNoun } from './ai-job-activity'\nimport { aiJobRefundCopy } from './ai-job-failure-copy'\nimport { aiBuildItemRows, aiBuildOpNoun, aiBuildOutcomeLine, aiSitePartialCopy } from './ai-build-progress'\nimport { AI_JOB_PAUSED_NEXT_COPY, aiJobPausedTitle } from './ai-job-notice'\nimport type { AiJobSummary } from './ai-jobs.types'\n\n/**\n * What the \"Building your site\" page shows of a site job (AGL-3594): one row\n * a stage — planning the pages, then each page — with its state, the state\n * the whole job is in, and the job's one price.\n *\n * Read off the job summary every surface already follows: the plan step's\n * status, the plan's pages, and the pages the build has reported as outputs.\n * A scaffold builds its units in plan order and reports each page when it is\n * written, so the pages written so far are the first pages of the plan.\n *\n * Pure: no React, no request. The page and its spec read the same rows.\n */\n\n/**\n * `paused` (AGL-3660): the row a job the meter paused stopped at — not\n * running, not failed, and carried on from by Resume.\n */\nexport type AiSiteBuildRowState = 'done' | 'active' | 'waiting' | 'failed' | 'skipped' | 'paused'\n\nexport interface AiSiteBuildRow {\n id: string\n label: string\n state: AiSiteBuildRowState\n /** What the person should know about this row (AGL-3616): a build item's failure, note or refund. */\n detail?: string | null\n /** What the row is doing while it is the active one, in a sentence. */\n hint?: string | null\n /** A page's planned sections, in order, so the person sees what is being written. */\n sections?: string[]\n /** The credits this row's work used, once it has finished; absent while it runs. */\n credits?: number\n /** When this row's work started, for the elapsed time beside the active row. */\n startedAt?: string | null\n}\n\n/** Where the whole job stands, as the page's header and actions read it. */\nexport type AiSiteBuildPhase = 'working' | 'done' | 'stopped' | 'failed' | 'canceled'\n\nexport function aiSiteBuildPhase(\n job: Pick<AiJobSummary, 'status'> & Partial<Pick<AiJobSummary, 'review' | 'autoConfirm' | 'updatedAt'>>,\n): AiSiteBuildPhase {\n // A guided start's plan parked for the instant its confirmation takes is\n // the build going on (AGL-3596), never a stop.\n if (aiJobConfirmingOwnPlan(job)) return 'working'\n switch (job.status) {\n case 'done':\n return 'done'\n case 'failed':\n return 'failed'\n case 'canceled':\n return 'canceled'\n case 'needs_review':\n case 'needs_input':\n return 'stopped'\n default:\n return 'working'\n }\n}\n\n/** The creations a scaffold builds before its pages, in its build order, and what each row says. */\nconst CREATION_ROWS: ReadonlyArray<{ kind: string; resource: string; label: (name: string) => string }> = [\n { kind: 'theme-change', resource: 'theme', label: () => AI_SITE_LOOK_ROW_LABEL },\n { kind: 'layout', resource: 'layout', label: (name) => `Building the header and footer: ${name}` },\n { kind: 'form', resource: 'form', label: (name) => `Building the form: ${name}` },\n]\n\n/**\n * One row a stage: planning, then each creation the plan builds before its\n * pages — the colors, the layout, the form — then each page (AGL-3596). A\n * scaffold builds them in that order and reports each as an output when it\n * is written, so the first row without its output is where the build is, and\n * where a failure stopped it: a layout that broke is the layout's row, never\n * the first page's.\n */\nexport function aiSiteBuildRows(\n job: Pick<AiJobSummary, 'status' | 'steps' | 'plan' | 'outputs' | 'review'> &\n Partial<Pick<AiJobSummary, 'items' | 'kind' | 'siteInputs' | 'sitePublish'>>,\n): AiSiteBuildRow[] {\n const rows = aiSiteBuildRowsOf(job)\n return job.status === 'needs_input' ? aiPausedRows(rows) : rows\n}\n\n/**\n * A job the meter paused (AGL-3660) shows no spinner: the row it stopped at —\n * one still marked running, else the first not yet built — reads `paused`,\n * with no running clock and no \"building now\" hint, and every row after it\n * keeps waiting.\n */\nfunction aiPausedRows(rows: AiSiteBuildRow[]): AiSiteBuildRow[] {\n const at = rows.findIndex((row) => row.state === 'active' || row.state === 'paused')\n const index = at >= 0 ? at : rows.findIndex((row) => row.state === 'waiting')\n return rows.map((row, position) => {\n if (row.state === 'active' && position !== index) return { ...row, state: 'waiting', hint: null, startedAt: null }\n if (position !== index) return row\n return { ...row, state: 'paused', hint: null, startedAt: null }\n })\n}\n\nfunction aiSiteBuildRowsOf(\n job: Pick<AiJobSummary, 'status' | 'steps' | 'plan' | 'outputs' | 'review'> &\n Partial<Pick<AiJobSummary, 'items' | 'kind' | 'siteInputs' | 'sitePublish'>>,\n): AiSiteBuildRow[] {\n const phase = aiSiteBuildPhase(job)\n // A build is read off its item ledger (AGL-3616): one row an item, each with\n // its own state and what became of its credits.\n if (job.items?.length) {\n const site = job.kind !== 'build'\n const pages = (job.items ?? []).filter((row) => row.op === 'page')\n // The plan keeps the credits it used once the items take over the page.\n const planStep = job.steps.find((step) => step.name === 'plan')\n return [\n {\n id: 'plan',\n label: site ? 'Planning your pages' : 'Planning what to build',\n state: 'done',\n ...(planStep ? { credits: Math.max(0, Math.floor(planStep.creditsSpent ?? 0)) } : {}),\n },\n ...aiBuildItemRows(job).map((row, index) => {\n const ledger = (job.items ?? [])[index]\n // A site keeps the scaffold's own words for each stage (AGL-3596).\n const label =\n !site || !ledger\n ? row.label\n : ledger.op === 'theme'\n ? AI_SITE_LOOK_ROW_LABEL\n : ledger.op === 'layout'\n ? `Building the header and footer: ${ledger.label}`\n : ledger.op === 'form'\n ? `Building the form: ${ledger.label}`\n : ledger.op === 'page'\n ? `Writing page ${pages.indexOf(ledger) + 1} of ${pages.length}: ${ledger.label}`\n : ledger.op === 'email'\n ? 'Writing your welcome email'\n : row.label\n const screen = ledger?.op === 'page' ? aiPlanScreenFor(job.plan, ledger.label, pages.indexOf(ledger)) : null\n const finished = row.state === 'done' || row.state === 'failed'\n const net = ledger ? Math.max(0, Math.floor((ledger.creditsSpent ?? 0) - (ledger.creditsRefunded ?? 0))) : 0\n return {\n id: row.slot,\n label,\n state: row.state,\n // A part that failed while the rest is still being built says the\n // build goes on (AGL-3596): read alone, it looked like the site had.\n detail:\n row.state === 'failed' && phase === 'working'\n ? [row.detail, site ? 'The rest of your site keeps building.' : 'The rest keeps building.'].filter(Boolean).join(' ')\n : row.detail,\n ...(row.state === 'active'\n ? {\n startedAt: row.startedAt ?? null,\n hint:\n ledger?.op === 'page'\n ? AI_SITE_PAGE_HINT\n : site && ledger?.op === 'theme'\n ? AI_SITE_LOOK_HINT\n : AI_SITE_ITEM_HINT,\n }\n : {}),\n ...(screen?.sections.length ? { sections: screen.sections.map((section) => section.name) } : {}),\n ...(finished && ledger ? { credits: net } : {}),\n }\n }),\n ...aiSitePublishRow(job, phase),\n ]\n }\n const planStep = job.steps.find((step) => step.name === 'plan')\n const planDone = planStep?.status === 'done' || Boolean(job.plan)\n const stopped = phase === 'failed' || phase === 'stopped'\n const paused = job.status === 'needs_input'\n const planState: AiSiteBuildRowState = planDone\n ? 'done'\n : paused\n ? 'paused'\n : stopped\n ? 'failed'\n : phase === 'working'\n ? 'active'\n : 'waiting'\n const rows: AiSiteBuildRow[] = [\n {\n id: 'plan',\n label: 'Planning your pages',\n state: planState,\n startedAt: planStep?.startedAt ?? null,\n hint: planState === 'active' ? AI_SITE_PLAN_HINT : null,\n ...(planDone && planStep ? { credits: Math.max(0, Math.floor(planStep.creditsSpent ?? 0)) } : {}),\n },\n ]\n // Before there is a plan, the stages a guided start always goes through\n // are named from its answers, so the page shows the whole way from the\n // start rather than one row.\n if (!job.plan && job.kind !== 'build') {\n const pagesAsked = typeof job.siteInputs?.['pages'] === 'number' ? Math.round(job.siteInputs['pages'] as number) : 0\n if (job.siteInputs) {\n // The look is designed first, before the header and footer (AGL-3660).\n rows.push({ id: 'look', label: AI_SITE_LOOK_ROW_LABEL, state: 'waiting' })\n rows.push({ id: 'layout', label: 'Building the header and footer', state: 'waiting' })\n // A guided start that says where its contact form's submissions go\n // plans that form, built after the layout and before the pages (AGL-3596).\n const submissions = job.siteInputs['submissions']\n if (submissions === 'inbox' || submissions === 'lead') {\n rows.push({ id: 'form', label: 'Building your contact form', state: 'waiting' })\n }\n rows.push({\n id: 'pages',\n label: pagesAsked > 1 ? `Writing your ${pagesAsked} pages` : 'Writing your pages',\n state: 'waiting',\n })\n rows.push(...aiSitePublishRow(job, phase))\n }\n return rows\n }\n const built = new Map<string, number>()\n for (const output of job.outputs) built.set(output.resource, (built.get(output.resource) ?? 0) + 1)\n const take = (resource: string): boolean => {\n const left = built.get(resource) ?? 0\n if (left <= 0) return false\n built.set(resource, left - 1)\n return true\n }\n const stages: Array<{ id: string; label: string; resource: string; sections?: string[]; hint?: string }> = []\n // A scaffold designs its look first on every start (AGL-3660), and writes\n // its item ledger only with that first pass's outcome — so while the look\n // is being designed, the job is read here, without a ledger, and the look\n // is the stage it is on: a moving site job that has built nothing yet. A\n // job read without its ledger that has built something and no look is one\n // from before the look had a unit of its own, and has the stage only where\n // it built one.\n const moving = (phase === 'working' || paused) && job.outputs.length === 0\n for (const row of CREATION_ROWS) {\n const creation = job.plan?.create.find((entry) => entry.kind === row.kind)\n const look = row.resource === 'theme'\n if (\n creation ||\n (look && job.kind !== 'build' && moving) ||\n (look && job.outputs.some((output) => output.resource === 'theme'))\n ) {\n stages.push({\n id: look ? 'look' : row.resource,\n label: row.label(creation?.name ?? ''),\n resource: row.resource,\n ...(look ? { hint: AI_SITE_LOOK_HINT } : {}),\n })\n }\n }\n const pages = job.plan?.screens ?? []\n pages.forEach((page, index) => {\n stages.push({\n id: `page-${index}`,\n label: `Writing page ${index + 1} of ${pages.length}: ${page.title}`,\n resource: 'screen',\n sections: page.sections.map((section) => section.name),\n })\n })\n // The build step's start: when the first stage after the plan began.\n const buildStartedAt = job.steps.find((step) => step.name !== 'plan' && step.startedAt)?.startedAt ?? null\n let reached = false\n for (const stage of stages) {\n const done = phase === 'done' || take(stage.resource)\n const current = !done && !reached && planDone\n if (current) reached = true\n const state: AiSiteBuildRowState = done\n ? 'done'\n : current && paused\n ? 'paused'\n : current && stopped\n ? 'failed'\n : current && phase === 'working'\n ? 'active'\n : 'waiting'\n rows.push({\n id: stage.id,\n label: stage.label,\n state,\n ...(state === 'active' && stage.hint ? { hint: stage.hint, startedAt: buildStartedAt } : {}),\n ...(stage.sections?.length ? { sections: stage.sections } : {}),\n })\n }\n rows.push(...aiSitePublishRow(job, phase))\n return rows\n}\n\n/** The row the site's look is designed on (AGL-3660). */\nexport const AI_SITE_LOOK_ROW_LABEL = 'Designing your look'\n\n/** What the look's row says while it runs. */\nexport const AI_SITE_LOOK_HINT =\n 'Choosing a base theme, colors, fonts and the style of your buttons, cards and forms for your kind of site. This takes a few seconds.'\n\n/** What the planning row says while it runs. */\nexport const AI_SITE_PLAN_HINT =\n 'Reading your answers and choosing your pages, what each one says, and the forms and layout they need. This usually takes under a minute.'\n\n/** What the page being written says while it is the active row (AGL-3596). */\nexport const AI_SITE_PAGE_HINT =\n 'Writing this page’s sections from your plan. It is done once every section is in.'\n\n/** What any other part being built says while it is the active row. */\nexport const AI_SITE_ITEM_HINT = 'Building this now. The next step starts when it is done.'\n\n/** The plan's page an item builds: by its title, else by its place among the pages. */\nfunction aiPlanScreenFor(\n plan: AiJobSummary['plan'],\n title: string,\n index: number,\n): NonNullable<AiJobSummary['plan']>['screens'][number] | null {\n const screens = plan?.screens ?? []\n return screens.find((screen) => screen.title === title) ?? (index >= 0 ? (screens[index] ?? null) : null)\n}\n\n/**\n * A guided start ends by putting its pages live (AGL-3596): the last row, so\n * the person sees that step coming and when it is done. Only a site job\n * publishes on its own; a build publishes when asked, and says so in its plan.\n */\nfunction aiSitePublishRow(\n job: Pick<AiJobSummary, 'status'> & Partial<Pick<AiJobSummary, 'kind' | 'sitePublish'>>,\n phase: AiSiteBuildPhase,\n): AiSiteBuildRow[] {\n if (job.kind !== 'site') return []\n const published = (job.sitePublish?.published.length ?? 0) > 0\n return [\n {\n id: 'publish',\n label: 'Publishing your site',\n // A job that stopped or failed publishes nothing, so the row is passed over.\n state: phase === 'done' ? (published ? 'done' : 'skipped') : phase === 'working' ? 'waiting' : 'skipped',\n },\n ]\n}\n\n/**\n * How far the job is, from 0 to 1, by its rows: a finished row counts whole\n * and the active one half. `null` before there is more than one row to count.\n */\nexport function aiSiteBuildFraction(rows: readonly AiSiteBuildRow[]): number | null {\n if (rows.length < 2) return null\n const counted = rows.reduce(\n (sum, row) => sum + (row.state === 'active' ? 0.5 : row.state === 'waiting' || row.state === 'paused' ? 0 : 1),\n 0,\n )\n return Math.min(1, counted / rows.length)\n}\n\n/**\n * The job's one price (AGL-3594), never a plan's on its own: what it has used\n * so far while it works, what it used once done, and, on a failure we gave\n * back, that nothing was charged.\n */\nexport function aiSiteBuildCreditsLine(\n job: Pick<AiJobSummary, 'status' | 'creditsSpent' | 'refundedCredits'> & Partial<Pick<AiJobSummary, 'kind'>>,\n): string | null {\n const spent = Math.max(0, job.creditsSpent ?? 0)\n const refunded = Math.max(0, job.refundedCredits ?? 0)\n const net = Math.max(0, spent - refunded)\n const phase = aiSiteBuildPhase(job)\n // What the job made, by its kind; a job that names none is a site's.\n const subject = !job.kind || job.kind === 'site' ? 'This site' : aiJobKindNoun(job.kind) === 'AI job' ? 'This AI job' : `This ${aiJobKindNoun(job.kind)}`\n // A failure on our side says so in the words every surface uses (AGL-3596),\n // from the job's recorded give-back.\n const refund = phase === 'working' || phase === 'done' ? null : aiJobRefundCopy(job)\n if (refund && refunded > 0) return refund\n if (phase === 'done') return `${subject} used ${net} credits.`\n if (phase === 'working') return `Credits used so far: ${net}`\n if (refund) return `${subject} used ${net} credits. ${refund}`\n return net > 0 ? `${subject} used ${net} credits.` : null\n}\n\n/** The heading and the sentence under it, for one job's page in its current phase. */\nexport interface AiJobPageCopy {\n heading: string\n lede: string\n}\n\n/**\n * What one job's page says at the top (AGL-3594, AGL-3596). A site job reads\n * as \"Building your site\"; any other kind names what it makes — \"Building\n * your page\" — or, for a kind with no noun, the job itself.\n *\n * A guided site start publishes its pages when it finishes, and says so\n * (`sitePublish`): with a page live the heading is \"Your site is live\". A\n * done site job without that record — an older job, or one that was not a\n * guided start — built drafts, and says that instead.\n */\nexport function aiJobPageCopy(\n job: Pick<AiJobSummary, 'kind' | 'status' | 'review' | 'error'> &\n Partial<Pick<AiJobSummary, 'sitePublish' | 'items' | 'outputs'>>,\n brand: string,\n): AiJobPageCopy {\n const phase = aiSiteBuildPhase(job)\n // Paused by the meter (AGL-3660): why, what is already built, and that it\n // carries on — never \"was not built\", which read as a dead end.\n if (job.status === 'needs_input') {\n return {\n heading: aiJobPausedTitle(job.kind),\n lede: [job.error, aiJobBuiltSoFar(job), AI_JOB_PAUSED_NEXT_COPY].filter(Boolean).join(' '),\n }\n }\n // A build is what the person asked for, item by item (AGL-3616).\n if (job.kind === 'build') return aiBuildPageCopy(job, brand)\n const noun = job.kind === 'site' ? 'site' : aiJobKindNoun(job.kind)\n const generic = noun === 'AI job'\n // `products` is the one plural noun.\n const isAre = noun.endsWith('s') ? 'are' : 'is'\n const why = job.review?.message ?? job.error ?? null\n if (phase === 'working') {\n return {\n heading: generic ? 'Your AI job is running' : `Building your ${noun}`,\n lede:\n job.kind === 'site'\n ? `${brand} AI is planning your pages and writing each one. You can leave this page; it keeps going.`\n : `${brand} AI is working on it. You can leave this page; it keeps going.`,\n }\n }\n if (phase === 'done') {\n const published = job.sitePublish?.published.length ?? 0\n // Part of the site was built (AGL-3616): what was, what was not, and its credits.\n const partial = job.kind === 'site' ? aiSitePartialCopy({ items: job.items, status: job.status }) : null\n if (job.kind === 'site' && published > 0) {\n return {\n heading: 'Your site is live',\n lede: [\n partial,\n job.sitePublish?.drafts.length\n ? 'Your pages are published, except the ones listed below, which stayed drafts.'\n : 'Your pages are published, and anyone can visit your site now.',\n ]\n .filter(Boolean)\n .join(' '),\n }\n }\n if (partial) return { heading: 'Your site is ready', lede: `${partial} Your new pages are drafts. Publish them when you’re happy.` }\n if (job.kind === 'site' && job.sitePublish) {\n return {\n heading: 'Your site is ready',\n lede: 'None of your new pages could be published, so they are drafts. The reasons are below.',\n }\n }\n return {\n heading: generic ? 'Your AI job finished' : `Your ${noun} ${isAre} ready`,\n lede:\n job.kind === 'site'\n ? 'Your new pages are drafts. Publish them when you’re happy.'\n : 'Everything it built is an unpublished draft until you publish it.',\n }\n }\n if (phase === 'canceled') {\n return { heading: generic ? 'Your AI job was canceled' : `Your ${noun} was not built`, lede: 'The job was canceled.' }\n }\n return {\n heading: generic ? 'Your AI job stopped' : `Your ${noun} ${noun.endsWith('s') ? 'were' : 'was'} not built`,\n lede: why ?? (generic ? 'Something went wrong running this job.' : `Something went wrong building your ${noun}.`),\n }\n}\n\nconst joinWords = (names: string[]): string =>\n names.length <= 1 ? (names[0] ?? '') : `${names.slice(0, -1).join(', ')} and ${names[names.length - 1]}`\n\n/**\n * What a job has already built, in a sentence (AGL-3660): its look, the header\n * and footer, its form, its pages — from the item ledger where it has one,\n * else from what it has reported as outputs. `null` when it has built nothing.\n */\nexport function aiJobBuiltSoFar(\n job: Pick<AiJobSummary, 'kind'> & Partial<Pick<AiJobSummary, 'items' | 'outputs'>>,\n): string | null {\n const built = (job.items ?? []).filter((row) => row.status === 'succeeded' || row.status === 'degraded')\n const names: string[] = []\n if (job.items?.length) {\n for (const row of built) {\n if (row.op === 'theme') names.push('your look')\n else if (row.op === 'layout') names.push('the header and footer')\n else if (row.op === 'form') names.push(`the form “${row.label}”`)\n else if (row.op === 'page') names.push(`the page “${row.label}”`)\n else if (row.op === 'email') names.push('your welcome email')\n else names.push(`${aiBuildOpNoun(row.op).toLowerCase()} “${row.label}”`)\n }\n } else {\n const outputs = job.outputs ?? []\n const count = (resource: string) => outputs.filter((output) => output.resource === resource).length\n if (count('theme')) names.push('your look')\n if (count('layout')) names.push('the header and footer')\n if (count('form')) names.push(count('form') === 1 ? 'the form' : `${count('form')} forms`)\n const pages = count('screen')\n if (pages) names.push(pages === 1 ? '1 page' : `${pages} pages`)\n }\n return names.length ? `Built so far: ${joinWords(names)}.` : null\n}\n\n/** A build's page heading and sentence (AGL-3616). */\nfunction aiBuildPageCopy(\n job: Pick<AiJobSummary, 'status' | 'review' | 'error'> & Partial<Pick<AiJobSummary, 'items' | 'sitePublish'>>,\n brand: string,\n): AiJobPageCopy {\n const phase = aiSiteBuildPhase(job)\n const outcome = aiBuildOutcomeLine({ items: job.items ?? [], status: job.status })\n if (phase === 'working') {\n return {\n heading: 'Building what you asked for',\n lede: `${brand} AI is building each part in turn. You can leave this page; it keeps going.`,\n }\n }\n if (phase === 'stopped') {\n return {\n heading: job.review?.reason === 'plan' ? 'Your plan is ready' : 'Your build needs you',\n lede: job.review?.message ?? job.error ?? 'It waits for your decision.',\n }\n }\n if (phase === 'done') {\n const failed = (job.items ?? []).some((row) => row.status === 'failed' || row.status === 'skipped')\n const live = job.sitePublish?.published.length ?? 0\n return {\n heading: failed ? 'Most of it is ready' : 'Everything you asked for is ready',\n lede: [\n outcome,\n live ? 'Your new pages are published.' : 'Everything it built is an unpublished draft until you publish it.',\n failed ? 'Try again builds only what failed.' : '',\n ]\n .filter(Boolean)\n .join(' '),\n }\n }\n if (phase === 'canceled') return { heading: 'Your build was canceled', lede: 'The job was canceled.' }\n return {\n heading: 'Nothing could be built',\n lede: [job.error ?? 'Something went wrong building what you asked for.', outcome].filter(Boolean).join(' '),\n }\n}\n"],"names":["aiJobConfirmingOwnPlan","aiJobKindNoun","aiJobRefundCopy","aiBuildItemRows","aiBuildOpNoun","aiBuildOutcomeLine","aiSitePartialCopy","AI_JOB_PAUSED_NEXT_COPY","aiJobPausedTitle","aiSiteBuildPhase","job","status","CREATION_ROWS","kind","resource","label","AI_SITE_LOOK_ROW_LABEL","name","aiSiteBuildRows","rows","aiSiteBuildRowsOf","aiPausedRows","at","findIndex","row","state","index","map","position","hint","startedAt","planStep","built","phase","items","length","site","pages","filter","op","steps","find","step","id","credits","Math","max","floor","creditsSpent","ledger","indexOf","screen","aiPlanScreenFor","plan","finished","net","creditsRefunded","slot","detail","Boolean","join","AI_SITE_PAGE_HINT","AI_SITE_LOOK_HINT","AI_SITE_ITEM_HINT","sections","section","aiSitePublishRow","planDone","stopped","paused","planState","AI_SITE_PLAN_HINT","pagesAsked","siteInputs","round","push","submissions","Map","output","outputs","set","get","take","left","stages","moving","creation","create","entry","look","some","screens","forEach","page","title","buildStartedAt","reached","stage","done","current","published","sitePublish","aiSiteBuildFraction","counted","reduce","sum","min","aiSiteBuildCreditsLine","spent","refunded","refundedCredits","subject","refund","aiJobPageCopy","brand","heading","lede","error","aiJobBuiltSoFar","aiBuildPageCopy","noun","generic","isAre","endsWith","why","review","message","partial","drafts","joinWords","names","slice","toLowerCase","count","outcome","reason","failed","live"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,sBAAsB,EAAEC,aAAa,QAAQ,uBAAmB;AACzE,SAASC,eAAe,QAAQ,2BAAuB;AACvD,SAASC,eAAe,EAAEC,aAAa,EAAEC,kBAAkB,EAAEC,iBAAiB,QAAQ,yBAAqB;AAC3G,SAASC,uBAAuB,EAAEC,gBAAgB,QAAQ,qBAAiB;AAyC3E,OAAO,SAASC,iBACdC,GAAuG;IAEvG,yEAAyE;IACzE,+CAA+C;IAC/C,IAAIV,uBAAuBU,MAAM,OAAO;IACxC,OAAQA,IAAIC,MAAM;QAChB,KAAK;YACH,OAAO;QACT,KAAK;YACH,OAAO;QACT,KAAK;YACH,OAAO;QACT,KAAK;QACL,KAAK;YACH,OAAO;QACT;YACE,OAAO;IACX;AACF;AAEA,kGAAkG,GAClG,MAAMC,gBAAoG;IACxG;QAAEC,MAAM;QAAgBC,UAAU;QAASC,OAAO,IAAMC;IAAuB;IAC/E;QAAEH,MAAM;QAAUC,UAAU;QAAUC,OAAO,CAACE,OAAS,CAAC,gCAAgC,EAAEA,MAAM;IAAC;IACjG;QAAEJ,MAAM;QAAQC,UAAU;QAAQC,OAAO,CAACE,OAAS,CAAC,mBAAmB,EAAEA,MAAM;IAAC;CACjF;AAED;;;;;;;CAOC,GACD,OAAO,SAASC,gBACdR,GAC8E;IAE9E,MAAMS,OAAOC,kBAAkBV;IAC/B,OAAOA,IAAIC,MAAM,KAAK,gBAAgBU,aAAaF,QAAQA;AAC7D;AAEA;;;;;CAKC,GACD,SAASE,aAAaF,IAAsB;IAC1C,MAAMG,KAAKH,KAAKI,SAAS,CAAC,CAACC,MAAQA,IAAIC,KAAK,KAAK,YAAYD,IAAIC,KAAK,KAAK;IAC3E,MAAMC,QAAQJ,MAAM,IAAIA,KAAKH,KAAKI,SAAS,CAAC,CAACC,MAAQA,IAAIC,KAAK,KAAK;IACnE,OAAON,KAAKQ,GAAG,CAAC,CAACH,KAAKI;QACpB,IAAIJ,IAAIC,KAAK,KAAK,YAAYG,aAAaF,OAAO,OAAO,aAAKF;YAAKC,OAAO;YAAWI,MAAM;YAAMC,WAAW;;QAC5G,IAAIF,aAAaF,OAAO,OAAOF;QAC/B,OAAO,aAAKA;YAAKC,OAAO;YAAUI,MAAM;YAAMC,WAAW;;IAC3D;AACF;AAEA,SAASV,kBACPV,GAC8E;cAqFbqB,wBA4BHC;QA5G1DtB,YA4IUA,WAUSA;IAzJvB,MAAMuB,QAAQxB,iBAAiBC;IAC/B,6EAA6E;IAC7E,gDAAgD;IAChD,KAAIA,aAAAA,IAAIwB,KAAK,qBAATxB,WAAWyB,MAAM,EAAE;YAENzB,aAQsCqB;QATrD,MAAMK,OAAO1B,IAAIG,IAAI,KAAK;QAC1B,MAAMwB,QAAQ,EAAC3B,cAAAA,IAAIwB,KAAK,YAATxB,cAAa,EAAE,EAAE4B,MAAM,CAAC,CAACd,MAAQA,IAAIe,EAAE,KAAK;QAC3D,wEAAwE;QACxE,MAAMR,WAAWrB,IAAI8B,KAAK,CAACC,IAAI,CAAC,CAACC,OAASA,KAAKzB,IAAI,KAAK;QACxD,OAAO;YACL;gBACE0B,IAAI;gBACJ5B,OAAOqB,OAAO,wBAAwB;gBACtCX,OAAO;eACHM,WAAW;gBAAEa,SAASC,KAAKC,GAAG,CAAC,GAAGD,KAAKE,KAAK,EAAChB,0BAAAA,SAASiB,YAAY,YAArBjB,0BAAyB;YAAI,IAAI,CAAC;eAElF5B,gBAAgBO,KAAKiB,GAAG,CAAC,CAACH,KAAKE;oBAChBhB,YAkB6BuC,sBAA6BA,yBAavDzB;gBA/BnB,MAAMyB,SAAS,EAACvC,aAAAA,IAAIwB,KAAK,YAATxB,aAAa,EAAE,CAAC,CAACgB,MAAM;gBACvC,mEAAmE;gBACnE,MAAMX,QACJ,CAACqB,QAAQ,CAACa,SACNzB,IAAIT,KAAK,GACTkC,OAAOV,EAAE,KAAK,UACZvB,yBACAiC,OAAOV,EAAE,KAAK,WACZ,CAAC,gCAAgC,EAAEU,OAAOlC,KAAK,EAAE,GACjDkC,OAAOV,EAAE,KAAK,SACZ,CAAC,mBAAmB,EAAEU,OAAOlC,KAAK,EAAE,GACpCkC,OAAOV,EAAE,KAAK,SACZ,CAAC,aAAa,EAAEF,MAAMa,OAAO,CAACD,UAAU,EAAE,IAAI,EAAEZ,MAAMF,MAAM,CAAC,EAAE,EAAEc,OAAOlC,KAAK,EAAE,GAC/EkC,OAAOV,EAAE,KAAK,UACZ,+BACAf,IAAIT,KAAK;gBACzB,MAAMoC,SAASF,CAAAA,0BAAAA,OAAQV,EAAE,MAAK,SAASa,gBAAgB1C,IAAI2C,IAAI,EAAEJ,OAAOlC,KAAK,EAAEsB,MAAMa,OAAO,CAACD,WAAW;gBACxG,MAAMK,WAAW9B,IAAIC,KAAK,KAAK,UAAUD,IAAIC,KAAK,KAAK;gBACvD,MAAM8B,MAAMN,SAASJ,KAAKC,GAAG,CAAC,GAAGD,KAAKE,KAAK,CAAC,EAACE,uBAAAA,OAAOD,YAAY,YAAnBC,uBAAuB,OAAMA,0BAAAA,OAAOO,eAAe,YAAtBP,0BAA0B,OAAO;gBAC3G,OAAO;oBACLN,IAAInB,IAAIiC,IAAI;oBACZ1C;oBACAU,OAAOD,IAAIC,KAAK;oBAChB,kEAAkE;oBAClE,qEAAqE;oBACrEiC,QACElC,IAAIC,KAAK,KAAK,YAAYQ,UAAU,YAChC;wBAACT,IAAIkC,MAAM;wBAAEtB,OAAO,0CAA0C;qBAA2B,CAACE,MAAM,CAACqB,SAASC,IAAI,CAAC,OAC/GpC,IAAIkC,MAAM;mBACZlC,IAAIC,KAAK,KAAK,WACd;oBACEK,SAAS,GAAEN,iBAAAA,IAAIM,SAAS,YAAbN,iBAAiB;oBAC5BK,MACEoB,CAAAA,0BAAAA,OAAQV,EAAE,MAAK,SACXsB,oBACAzB,QAAQa,CAAAA,0BAAAA,OAAQV,EAAE,MAAK,UACrBuB,oBACAC;gBACV,IACA,CAAC,GACDZ,CAAAA,0BAAAA,OAAQa,QAAQ,CAAC7B,MAAM,IAAG;oBAAE6B,UAAUb,OAAOa,QAAQ,CAACrC,GAAG,CAAC,CAACsC,UAAYA,QAAQhD,IAAI;gBAAE,IAAI,CAAC,GAC1FqC,YAAYL,SAAS;oBAAEL,SAASW;gBAAI,IAAI,CAAC;YAEjD;eACGW,iBAAiBxD,KAAKuB;SAC1B;IACH;IACA,MAAMF,WAAWrB,IAAI8B,KAAK,CAACC,IAAI,CAAC,CAACC,OAASA,KAAKzB,IAAI,KAAK;IACxD,MAAMkD,WAAWpC,CAAAA,4BAAAA,SAAUpB,MAAM,MAAK,UAAUgD,QAAQjD,IAAI2C,IAAI;IAChE,MAAMe,UAAUnC,UAAU,YAAYA,UAAU;IAChD,MAAMoC,SAAS3D,IAAIC,MAAM,KAAK;IAC9B,MAAM2D,YAAiCH,WACnC,SACAE,SACE,WACAD,UACE,WACAnC,UAAU,YACR,WACA;IACV,MAAMd,OAAyB;QAC7B;YACEwB,IAAI;YACJ5B,OAAO;YACPU,OAAO6C;YACPxC,SAAS,UAAEC,4BAAAA,SAAUD,SAAS,mBAAI;YAClCD,MAAMyC,cAAc,WAAWC,oBAAoB;WAC/CJ,YAAYpC,WAAW;YAAEa,SAASC,KAAKC,GAAG,CAAC,GAAGD,KAAKE,KAAK,EAAChB,yBAAAA,SAASiB,YAAY,YAArBjB,yBAAyB;QAAI,IAAI,CAAC;KAElG;IACD,wEAAwE;IACxE,uEAAuE;IACvE,6BAA6B;IAC7B,IAAI,CAACrB,IAAI2C,IAAI,IAAI3C,IAAIG,IAAI,KAAK,SAAS;YACXH;QAA1B,MAAM8D,aAAa,SAAO9D,kBAAAA,IAAI+D,UAAU,qBAAd/D,eAAgB,CAAC,QAAQ,MAAK,WAAWmC,KAAK6B,KAAK,CAAChE,IAAI+D,UAAU,CAAC,QAAQ,IAAc;QACnH,IAAI/D,IAAI+D,UAAU,EAAE;YAClB,uEAAuE;YACvEtD,KAAKwD,IAAI,CAAC;gBAAEhC,IAAI;gBAAQ5B,OAAOC;gBAAwBS,OAAO;YAAU;YACxEN,KAAKwD,IAAI,CAAC;gBAAEhC,IAAI;gBAAU5B,OAAO;gBAAkCU,OAAO;YAAU;YACpF,mEAAmE;YACnE,2EAA2E;YAC3E,MAAMmD,cAAclE,IAAI+D,UAAU,CAAC,cAAc;YACjD,IAAIG,gBAAgB,WAAWA,gBAAgB,QAAQ;gBACrDzD,KAAKwD,IAAI,CAAC;oBAAEhC,IAAI;oBAAQ5B,OAAO;oBAA8BU,OAAO;gBAAU;YAChF;YACAN,KAAKwD,IAAI,CAAC;gBACRhC,IAAI;gBACJ5B,OAAOyD,aAAa,IAAI,CAAC,aAAa,EAAEA,WAAW,MAAM,CAAC,GAAG;gBAC7D/C,OAAO;YACT;YACAN,KAAKwD,IAAI,IAAIT,iBAAiBxD,KAAKuB;QACrC;QACA,OAAOd;IACT;IACA,MAAMa,QAAQ,IAAI6C;IAClB,KAAK,MAAMC,UAAUpE,IAAIqE,OAAO,CAAE/C,MAAMgD,GAAG,CAACF,OAAOhE,QAAQ,EAAE,EAACkB,aAAAA,MAAMiD,GAAG,CAACH,OAAOhE,QAAQ,aAAzBkB,aAA8B,KAAK;IACjG,MAAMkD,OAAO,CAACpE;YACCkB;QAAb,MAAMmD,QAAOnD,aAAAA,MAAMiD,GAAG,CAACnE,qBAAVkB,aAAuB;QACpC,IAAImD,QAAQ,GAAG,OAAO;QACtBnD,MAAMgD,GAAG,CAAClE,UAAUqE,OAAO;QAC3B,OAAO;IACT;IACA,MAAMC,SAAqG,EAAE;IAC7G,0EAA0E;IAC1E,0EAA0E;IAC1E,0EAA0E;IAC1E,yEAAyE;IACzE,0EAA0E;IAC1E,2EAA2E;IAC3E,gBAAgB;IAChB,MAAMC,SAAS,AAACpD,CAAAA,UAAU,aAAaoC,MAAK,KAAM3D,IAAIqE,OAAO,CAAC5C,MAAM,KAAK;IACzE,KAAK,MAAMX,OAAOZ,cAAe;YACdF;QAAjB,MAAM4E,YAAW5E,aAAAA,IAAI2C,IAAI,qBAAR3C,WAAU6E,MAAM,CAAC9C,IAAI,CAAC,CAAC+C,QAAUA,MAAM3E,IAAI,KAAKW,IAAIX,IAAI;QACzE,MAAM4E,OAAOjE,IAAIV,QAAQ,KAAK;QAC9B,IACEwE,YACCG,QAAQ/E,IAAIG,IAAI,KAAK,WAAWwE,UAChCI,QAAQ/E,IAAIqE,OAAO,CAACW,IAAI,CAAC,CAACZ,SAAWA,OAAOhE,QAAQ,KAAK,UAC1D;;YACAsE,OAAOT,IAAI,CAAC;gBACVhC,IAAI8C,OAAO,SAASjE,IAAIV,QAAQ;gBAChCC,OAAOS,IAAIT,KAAK,UAACuE,4BAAAA,SAAUrE,IAAI,oBAAI;gBACnCH,UAAUU,IAAIV,QAAQ;eAClB2E,OAAO;gBAAE5D,MAAMiC;YAAkB,IAAI,CAAC;QAE9C;IACF;IACA,MAAMzB,kBAAQ3B,YAAAA,IAAI2C,IAAI,qBAAR3C,UAAUiF,OAAO,oBAAI,EAAE;IACrCtD,MAAMuD,OAAO,CAAC,CAACC,MAAMnE;QACnB0D,OAAOT,IAAI,CAAC;YACVhC,IAAI,CAAC,KAAK,EAAEjB,OAAO;YACnBX,OAAO,CAAC,aAAa,EAAEW,QAAQ,EAAE,IAAI,EAAEW,MAAMF,MAAM,CAAC,EAAE,EAAE0D,KAAKC,KAAK,EAAE;YACpEhF,UAAU;YACVkD,UAAU6B,KAAK7B,QAAQ,CAACrC,GAAG,CAAC,CAACsC,UAAYA,QAAQhD,IAAI;QACvD;IACF;IACA,qEAAqE;IACrE,MAAM8E,2BAAiBrF,kBAAAA,IAAI8B,KAAK,CAACC,IAAI,CAAC,CAACC,OAASA,KAAKzB,IAAI,KAAK,UAAUyB,KAAKZ,SAAS,sBAA/DpB,gBAAkEoB,SAAS,oBAAI;IACtG,IAAIkE,UAAU;IACd,KAAK,MAAMC,SAASb,OAAQ;YAkBpBa;QAjBN,MAAMC,OAAOjE,UAAU,UAAUiD,KAAKe,MAAMnF,QAAQ;QACpD,MAAMqF,UAAU,CAACD,QAAQ,CAACF,WAAW7B;QACrC,IAAIgC,SAASH,UAAU;QACvB,MAAMvE,QAA6ByE,OAC/B,SACAC,WAAW9B,SACT,WACA8B,WAAW/B,UACT,WACA+B,WAAWlE,UAAU,YACnB,WACA;QACVd,KAAKwD,IAAI,CAAC;YACRhC,IAAIsD,MAAMtD,EAAE;YACZ5B,OAAOkF,MAAMlF,KAAK;YAClBU;WACIA,UAAU,YAAYwE,MAAMpE,IAAI,GAAG;YAAEA,MAAMoE,MAAMpE,IAAI;YAAEC,WAAWiE;QAAe,IAAI,CAAC,GACtFE,EAAAA,kBAAAA,MAAMjC,QAAQ,qBAAdiC,gBAAgB9D,MAAM,IAAG;YAAE6B,UAAUiC,MAAMjC,QAAQ;QAAC,IAAI,CAAC;IAEjE;IACA7C,KAAKwD,IAAI,IAAIT,iBAAiBxD,KAAKuB;IACnC,OAAOd;AACT;AAEA,uDAAuD,GACvD,OAAO,MAAMH,yBAAyB,sBAAqB;AAE3D,4CAA4C,GAC5C,OAAO,MAAM8C,oBACX,uIAAsI;AAExI,8CAA8C,GAC9C,OAAO,MAAMS,oBACX,2IAA0I;AAE5I,4EAA4E,GAC5E,OAAO,MAAMV,oBACX,oFAAmF;AAErF,qEAAqE,GACrE,OAAO,MAAME,oBAAoB,2DAA0D;AAE3F,qFAAqF,GACrF,SAASX,gBACPC,IAA0B,EAC1ByC,KAAa,EACbpE,KAAa;cAGNiE,eAAmEA;IAD1E,MAAMA,kBAAUtC,wBAAAA,KAAMsC,OAAO,mBAAI,EAAE;IACnC,QAAOA,gBAAAA,QAAQlD,IAAI,CAAC,CAACU,SAAWA,OAAO2C,KAAK,KAAKA,kBAA1CH,gBAAqDjE,SAAS,KAAKiE,iBAAAA,OAAO,CAACjE,MAAM,YAAdiE,iBAAkB,OAAQ;AACtG;AAEA;;;;CAIC,GACD,SAASzB,iBACPxD,GAAuF,EACvFuB,KAAuB;;QAGJvB;IADnB,IAAIA,IAAIG,IAAI,KAAK,QAAQ,OAAO,EAAE;IAClC,MAAMuF,YAAY,UAAC1F,mBAAAA,IAAI2F,WAAW,qBAAf3F,iBAAiB0F,SAAS,CAACjE,MAAM,mBAAI,KAAK;IAC7D,OAAO;QACL;YACEQ,IAAI;YACJ5B,OAAO;YACP,6EAA6E;YAC7EU,OAAOQ,UAAU,SAAUmE,YAAY,SAAS,YAAanE,UAAU,YAAY,YAAY;QACjG;KACD;AACH;AAEA;;;CAGC,GACD,OAAO,SAASqE,oBAAoBnF,IAA+B;IACjE,IAAIA,KAAKgB,MAAM,GAAG,GAAG,OAAO;IAC5B,MAAMoE,UAAUpF,KAAKqF,MAAM,CACzB,CAACC,KAAKjF,MAAQiF,MAAOjF,CAAAA,IAAIC,KAAK,KAAK,WAAW,MAAMD,IAAIC,KAAK,KAAK,aAAaD,IAAIC,KAAK,KAAK,WAAW,IAAI,CAAA,GAC5G;IAEF,OAAOoB,KAAK6D,GAAG,CAAC,GAAGH,UAAUpF,KAAKgB,MAAM;AAC1C;AAEA;;;;CAIC,GACD,OAAO,SAASwE,uBACdjG,GAA4G;QAElFA,mBACGA;IAD7B,MAAMkG,QAAQ/D,KAAKC,GAAG,CAAC,IAAGpC,oBAAAA,IAAIsC,YAAY,YAAhBtC,oBAAoB;IAC9C,MAAMmG,WAAWhE,KAAKC,GAAG,CAAC,IAAGpC,uBAAAA,IAAIoG,eAAe,YAAnBpG,uBAAuB;IACpD,MAAM6C,MAAMV,KAAKC,GAAG,CAAC,GAAG8D,QAAQC;IAChC,MAAM5E,QAAQxB,iBAAiBC;IAC/B,qEAAqE;IACrE,MAAMqG,UAAU,CAACrG,IAAIG,IAAI,IAAIH,IAAIG,IAAI,KAAK,SAAS,cAAcZ,cAAcS,IAAIG,IAAI,MAAM,WAAW,gBAAgB,CAAC,KAAK,EAAEZ,cAAcS,IAAIG,IAAI,GAAG;IACzJ,4EAA4E;IAC5E,qCAAqC;IACrC,MAAMmG,SAAS/E,UAAU,aAAaA,UAAU,SAAS,OAAO/B,gBAAgBQ;IAChF,IAAIsG,UAAUH,WAAW,GAAG,OAAOG;IACnC,IAAI/E,UAAU,QAAQ,OAAO,GAAG8E,QAAQ,MAAM,EAAExD,IAAI,SAAS,CAAC;IAC9D,IAAItB,UAAU,WAAW,OAAO,CAAC,qBAAqB,EAAEsB,KAAK;IAC7D,IAAIyD,QAAQ,OAAO,GAAGD,QAAQ,MAAM,EAAExD,IAAI,UAAU,EAAEyD,QAAQ;IAC9D,OAAOzD,MAAM,IAAI,GAAGwD,QAAQ,MAAM,EAAExD,IAAI,SAAS,CAAC,GAAG;AACvD;AAQA;;;;;;;;;CASC,GACD,OAAO,SAAS0D,cACdvG,GACkE,EAClEwG,KAAa;QAiBDxG;QAAAA;IAfZ,MAAMuB,QAAQxB,iBAAiBC;IAC/B,0EAA0E;IAC1E,gEAAgE;IAChE,IAAIA,IAAIC,MAAM,KAAK,eAAe;QAChC,OAAO;YACLwG,SAAS3G,iBAAiBE,IAAIG,IAAI;YAClCuG,MAAM;gBAAC1G,IAAI2G,KAAK;gBAAEC,gBAAgB5G;gBAAMH;aAAwB,CAAC+B,MAAM,CAACqB,SAASC,IAAI,CAAC;QACxF;IACF;IACA,iEAAiE;IACjE,IAAIlD,IAAIG,IAAI,KAAK,SAAS,OAAO0G,gBAAgB7G,KAAKwG;IACtD,MAAMM,OAAO9G,IAAIG,IAAI,KAAK,SAAS,SAASZ,cAAcS,IAAIG,IAAI;IAClE,MAAM4G,UAAUD,SAAS;IACzB,qCAAqC;IACrC,MAAME,QAAQF,KAAKG,QAAQ,CAAC,OAAO,QAAQ;IAC3C,MAAMC,OAAMlH,iBAAAA,cAAAA,IAAImH,MAAM,qBAAVnH,YAAYoH,OAAO,oBAAIpH,IAAI2G,KAAK,YAAhC3G,OAAoC;IAChD,IAAIuB,UAAU,WAAW;QACvB,OAAO;YACLkF,SAASM,UAAU,2BAA2B,CAAC,cAAc,EAAED,MAAM;YACrEJ,MACE1G,IAAIG,IAAI,KAAK,SACT,GAAGqG,MAAM,yFAAyF,CAAC,GACnG,GAAGA,MAAM,8DAA8D,CAAC;QAChF;IACF;IACA,IAAIjF,UAAU,QAAQ;;YACFvB;QAAlB,MAAM0F,sBAAY1F,mBAAAA,IAAI2F,WAAW,qBAAf3F,iBAAiB0F,SAAS,CAACjE,MAAM,oBAAI;QACvD,kFAAkF;QAClF,MAAM4F,UAAUrH,IAAIG,IAAI,KAAK,SAASP,kBAAkB;YAAE4B,OAAOxB,IAAIwB,KAAK;YAAEvB,QAAQD,IAAIC,MAAM;QAAC,KAAK;QACpG,IAAID,IAAIG,IAAI,KAAK,UAAUuF,YAAY,GAAG;gBAKpC1F;YAJJ,OAAO;gBACLyG,SAAS;gBACTC,MAAM;oBACJW;oBACArH,EAAAA,oBAAAA,IAAI2F,WAAW,qBAAf3F,kBAAiBsH,MAAM,CAAC7F,MAAM,IAC1B,iFACA;iBACL,CACEG,MAAM,CAACqB,SACPC,IAAI,CAAC;YACV;QACF;QACA,IAAImE,SAAS,OAAO;YAAEZ,SAAS;YAAsBC,MAAM,GAAGW,QAAQ,2DAA2D,CAAC;QAAC;QACnI,IAAIrH,IAAIG,IAAI,KAAK,UAAUH,IAAI2F,WAAW,EAAE;YAC1C,OAAO;gBACLc,SAAS;gBACTC,MAAM;YACR;QACF;QACA,OAAO;YACLD,SAASM,UAAU,yBAAyB,CAAC,KAAK,EAAED,KAAK,CAAC,EAAEE,MAAM,MAAM,CAAC;YACzEN,MACE1G,IAAIG,IAAI,KAAK,SACT,+DACA;QACR;IACF;IACA,IAAIoB,UAAU,YAAY;QACxB,OAAO;YAAEkF,SAASM,UAAU,6BAA6B,CAAC,KAAK,EAAED,KAAK,cAAc,CAAC;YAAEJ,MAAM;QAAwB;IACvH;IACA,OAAO;QACLD,SAASM,UAAU,wBAAwB,CAAC,KAAK,EAAED,KAAK,CAAC,EAAEA,KAAKG,QAAQ,CAAC,OAAO,SAAS,MAAM,UAAU,CAAC;QAC1GP,IAAI,EAAEQ,cAAAA,MAAQH,UAAU,2CAA2C,CAAC,mCAAmC,EAAED,KAAK,CAAC,CAAC;IAClH;AACF;AAEA,MAAMS,YAAY,CAACC;QACIA;WAArBA,MAAM/F,MAAM,IAAI,KAAK+F,UAAAA,KAAK,CAAC,EAAE,YAARA,UAAY,KAAM,GAAGA,MAAMC,KAAK,CAAC,GAAG,CAAC,GAAGvE,IAAI,CAAC,MAAM,KAAK,EAAEsE,KAAK,CAACA,MAAM/F,MAAM,GAAG,EAAE,EAAE;;AAE1G;;;;CAIC,GACD,OAAO,SAASmF,gBACd5G,GAAkF;QAEnEA;QAEXA;IAFJ,MAAMsB,QAAQ,EAACtB,aAAAA,IAAIwB,KAAK,YAATxB,aAAa,EAAE,EAAE4B,MAAM,CAAC,CAACd,MAAQA,IAAIb,MAAM,KAAK,eAAea,IAAIb,MAAM,KAAK;IAC7F,MAAMuH,QAAkB,EAAE;IAC1B,KAAIxH,cAAAA,IAAIwB,KAAK,qBAATxB,YAAWyB,MAAM,EAAE;QACrB,KAAK,MAAMX,OAAOQ,MAAO;YACvB,IAAIR,IAAIe,EAAE,KAAK,SAAS2F,MAAMvD,IAAI,CAAC;iBAC9B,IAAInD,IAAIe,EAAE,KAAK,UAAU2F,MAAMvD,IAAI,CAAC;iBACpC,IAAInD,IAAIe,EAAE,KAAK,QAAQ2F,MAAMvD,IAAI,CAAC,CAAC,UAAU,EAAEnD,IAAIT,KAAK,CAAC,CAAC,CAAC;iBAC3D,IAAIS,IAAIe,EAAE,KAAK,QAAQ2F,MAAMvD,IAAI,CAAC,CAAC,UAAU,EAAEnD,IAAIT,KAAK,CAAC,CAAC,CAAC;iBAC3D,IAAIS,IAAIe,EAAE,KAAK,SAAS2F,MAAMvD,IAAI,CAAC;iBACnCuD,MAAMvD,IAAI,CAAC,GAAGvE,cAAcoB,IAAIe,EAAE,EAAE6F,WAAW,GAAG,EAAE,EAAE5G,IAAIT,KAAK,CAAC,CAAC,CAAC;QACzE;IACF,OAAO;YACWL;QAAhB,MAAMqE,WAAUrE,eAAAA,IAAIqE,OAAO,YAAXrE,eAAe,EAAE;QACjC,MAAM2H,QAAQ,CAACvH,WAAqBiE,QAAQzC,MAAM,CAAC,CAACwC,SAAWA,OAAOhE,QAAQ,KAAKA,UAAUqB,MAAM;QACnG,IAAIkG,MAAM,UAAUH,MAAMvD,IAAI,CAAC;QAC/B,IAAI0D,MAAM,WAAWH,MAAMvD,IAAI,CAAC;QAChC,IAAI0D,MAAM,SAASH,MAAMvD,IAAI,CAAC0D,MAAM,YAAY,IAAI,aAAa,GAAGA,MAAM,QAAQ,MAAM,CAAC;QACzF,MAAMhG,QAAQgG,MAAM;QACpB,IAAIhG,OAAO6F,MAAMvD,IAAI,CAACtC,UAAU,IAAI,WAAW,GAAGA,MAAM,MAAM,CAAC;IACjE;IACA,OAAO6F,MAAM/F,MAAM,GAAG,CAAC,cAAc,EAAE8F,UAAUC,OAAO,CAAC,CAAC,GAAG;AAC/D;AAEA,oDAAoD,GACpD,SAASX,gBACP7G,GAA6G,EAC7GwG,KAAa;QAG+BxG,YA8BnCA;IA/BT,MAAMuB,QAAQxB,iBAAiBC;IAC/B,MAAM4H,UAAUjI,mBAAmB;QAAE6B,KAAK,GAAExB,aAAAA,IAAIwB,KAAK,YAATxB,aAAa,EAAE;QAAEC,QAAQD,IAAIC,MAAM;IAAC;IAChF,IAAIsB,UAAU,WAAW;QACvB,OAAO;YACLkF,SAAS;YACTC,MAAM,GAAGF,MAAM,2EAA2E,CAAC;QAC7F;IACF;IACA,IAAIjF,UAAU,WAAW;YAGfvB;YADGA,aACHA;QAFR,OAAO;YACLyG,SAASzG,EAAAA,cAAAA,IAAImH,MAAM,qBAAVnH,YAAY6H,MAAM,MAAK,SAAS,uBAAuB;YAChEnB,IAAI,GAAE1G,iBAAAA,eAAAA,IAAImH,MAAM,qBAAVnH,aAAYoH,OAAO,oBAAIpH,IAAI2G,KAAK,YAAhC3G,OAAoC;QAC5C;IACF;IACA,IAAIuB,UAAU,QAAQ;YACJvB;YACHA;QADb,MAAM8H,SAAS,EAAC9H,cAAAA,IAAIwB,KAAK,YAATxB,cAAa,EAAE,EAAEgF,IAAI,CAAC,CAAClE,MAAQA,IAAIb,MAAM,KAAK,YAAYa,IAAIb,MAAM,KAAK;QACzF,MAAM8H,iBAAO/H,mBAAAA,IAAI2F,WAAW,qBAAf3F,iBAAiB0F,SAAS,CAACjE,MAAM,oBAAI;QAClD,OAAO;YACLgF,SAASqB,SAAS,wBAAwB;YAC1CpB,MAAM;gBACJkB;gBACAG,OAAO,kCAAkC;gBACzCD,SAAS,uCAAuC;aACjD,CACElG,MAAM,CAACqB,SACPC,IAAI,CAAC;QACV;IACF;IACA,IAAI3B,UAAU,YAAY,OAAO;QAAEkF,SAAS;QAA2BC,MAAM;IAAwB;IACrG,OAAO;QACLD,SAAS;QACTC,MAAM;aAAC1G,aAAAA,IAAI2G,KAAK,YAAT3G,aAAa;YAAqD4H;SAAQ,CAAChG,MAAM,CAACqB,SAASC,IAAI,CAAC;IACzG;AACF"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-site-build-progress.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 { AI_JOB_CANCELED_LEDE, aiJobCanceledCreditsCopy } from './ai-job-cancel-copy'\nimport { aiJobConfirmingOwnPlan, aiJobKindNoun } from './ai-job-activity'\nimport { aiJobRefundCopy } from './ai-job-failure-copy'\nimport { aiBuildItemRows, aiBuildOpNoun, aiBuildOutcomeLine, aiSitePartialCopy } from './ai-build-progress'\nimport { AI_JOB_PAUSED_NEXT_COPY, aiJobPausedTitle } from './ai-job-notice'\nimport type { AiJobSummary } from './ai-jobs.types'\n\n/**\n * What the \"Building your site\" page shows of a site job (AGL-3594): one row\n * a stage — planning the pages, then each page — with its state, the state\n * the whole job is in, and the job's one price.\n *\n * Read off the job summary every surface already follows: the plan step's\n * status, the plan's pages, and the pages the build has reported as outputs.\n * A scaffold builds its units in plan order and reports each page when it is\n * written, so the pages written so far are the first pages of the plan.\n *\n * Pure: no React, no request. The page and its spec read the same rows.\n */\n\n/**\n * `paused` (AGL-3660): the row a job the meter paused stopped at — not\n * running, not failed, and carried on from by Resume.\n */\nexport type AiSiteBuildRowState = 'done' | 'active' | 'waiting' | 'failed' | 'skipped' | 'paused'\n\nexport interface AiSiteBuildRow {\n id: string\n label: string\n state: AiSiteBuildRowState\n /** What the person should know about this row (AGL-3616): a build item's failure, note or refund. */\n detail?: string | null\n /** What the row is doing while it is the active one, in a sentence. */\n hint?: string | null\n /** A page's planned sections, in order, so the person sees what is being written. */\n sections?: string[]\n /** The credits this row's work used, once it has finished; absent while it runs. */\n credits?: number\n /** When this row's work started, for the elapsed time beside the active row. */\n startedAt?: string | null\n}\n\n/** Where the whole job stands, as the page's header and actions read it. */\nexport type AiSiteBuildPhase = 'working' | 'done' | 'stopped' | 'failed' | 'canceled'\n\nexport function aiSiteBuildPhase(\n job: Pick<AiJobSummary, 'status'> & Partial<Pick<AiJobSummary, 'review' | 'autoConfirm' | 'updatedAt'>>,\n): AiSiteBuildPhase {\n // A guided start's plan parked for the instant its confirmation takes is\n // the build going on (AGL-3596), never a stop.\n if (aiJobConfirmingOwnPlan(job)) return 'working'\n switch (job.status) {\n case 'done':\n return 'done'\n case 'failed':\n return 'failed'\n case 'canceled':\n return 'canceled'\n case 'needs_review':\n case 'needs_input':\n return 'stopped'\n default:\n return 'working'\n }\n}\n\n/** The creations a scaffold builds before its pages, in its build order, and what each row says. */\nconst CREATION_ROWS: ReadonlyArray<{ kind: string; resource: string; label: (name: string) => string }> = [\n { kind: 'theme-change', resource: 'theme', label: () => AI_SITE_LOOK_ROW_LABEL },\n { kind: 'layout', resource: 'layout', label: (name) => `Building the header and footer: ${name}` },\n { kind: 'form', resource: 'form', label: (name) => `Building the form: ${name}` },\n]\n\n/**\n * One row a stage: planning, then each creation the plan builds before its\n * pages — the colors, the layout, the form — then each page (AGL-3596). A\n * scaffold builds them in that order and reports each as an output when it\n * is written, so the first row without its output is where the build is, and\n * where a failure stopped it: a layout that broke is the layout's row, never\n * the first page's.\n */\nexport function aiSiteBuildRows(\n job: Pick<AiJobSummary, 'status' | 'steps' | 'plan' | 'outputs' | 'review'> &\n Partial<Pick<AiJobSummary, 'items' | 'kind' | 'siteInputs' | 'sitePublish'>>,\n): AiSiteBuildRow[] {\n const rows = aiSiteBuildRowsOf(job)\n return job.status === 'needs_input' ? aiPausedRows(rows) : rows\n}\n\n/**\n * A job the meter paused (AGL-3660) shows no spinner: the row it stopped at —\n * one still marked running, else the first not yet built — reads `paused`,\n * with no running clock and no \"building now\" hint, and every row after it\n * keeps waiting.\n */\nfunction aiPausedRows(rows: AiSiteBuildRow[]): AiSiteBuildRow[] {\n const at = rows.findIndex((row) => row.state === 'active' || row.state === 'paused')\n const index = at >= 0 ? at : rows.findIndex((row) => row.state === 'waiting')\n return rows.map((row, position) => {\n if (row.state === 'active' && position !== index) return { ...row, state: 'waiting', hint: null, startedAt: null }\n if (position !== index) return row\n return { ...row, state: 'paused', hint: null, startedAt: null }\n })\n}\n\nfunction aiSiteBuildRowsOf(\n job: Pick<AiJobSummary, 'status' | 'steps' | 'plan' | 'outputs' | 'review'> &\n Partial<Pick<AiJobSummary, 'items' | 'kind' | 'siteInputs' | 'sitePublish'>>,\n): AiSiteBuildRow[] {\n const phase = aiSiteBuildPhase(job)\n // A build is read off its item ledger (AGL-3616): one row an item, each with\n // its own state and what became of its credits.\n if (job.items?.length) {\n const site = job.kind !== 'build'\n const pages = (job.items ?? []).filter((row) => row.op === 'page')\n // The plan keeps the credits it used once the items take over the page.\n const planStep = job.steps.find((step) => step.name === 'plan')\n return [\n {\n id: 'plan',\n label: site ? 'Planning your pages' : 'Planning what to build',\n state: 'done',\n ...(planStep ? { credits: Math.max(0, Math.floor(planStep.creditsSpent ?? 0)) } : {}),\n },\n ...aiBuildItemRows(job).map((row, index) => {\n const ledger = (job.items ?? [])[index]\n // A site keeps the scaffold's own words for each stage (AGL-3596).\n const label =\n !site || !ledger\n ? row.label\n : ledger.op === 'theme'\n ? AI_SITE_LOOK_ROW_LABEL\n : ledger.op === 'layout'\n ? `Building the header and footer: ${ledger.label}`\n : ledger.op === 'form'\n ? `Building the form: ${ledger.label}`\n : ledger.op === 'page'\n ? `Writing page ${pages.indexOf(ledger) + 1} of ${pages.length}: ${ledger.label}`\n : ledger.op === 'email'\n ? 'Writing your welcome email'\n : row.label\n const screen = ledger?.op === 'page' ? aiPlanScreenFor(job.plan, ledger.label, pages.indexOf(ledger)) : null\n const finished = row.state === 'done' || row.state === 'failed'\n const net = ledger ? Math.max(0, Math.floor((ledger.creditsSpent ?? 0) - (ledger.creditsRefunded ?? 0))) : 0\n return {\n id: row.slot,\n label,\n state: row.state,\n // A part that failed while the rest is still being built says the\n // build goes on (AGL-3596): read alone, it looked like the site had.\n detail:\n row.state === 'failed' && phase === 'working'\n ? [row.detail, site ? 'The rest of your site keeps building.' : 'The rest keeps building.'].filter(Boolean).join(' ')\n : row.detail,\n ...(row.state === 'active'\n ? {\n startedAt: row.startedAt ?? null,\n hint:\n ledger?.op === 'page'\n ? AI_SITE_PAGE_HINT\n : site && ledger?.op === 'theme'\n ? AI_SITE_LOOK_HINT\n : AI_SITE_ITEM_HINT,\n }\n : {}),\n ...(screen?.sections.length ? { sections: screen.sections.map((section) => section.name) } : {}),\n ...(finished && ledger ? { credits: net } : {}),\n }\n }),\n ...aiSitePublishRow(job, phase),\n ]\n }\n const planStep = job.steps.find((step) => step.name === 'plan')\n const planDone = planStep?.status === 'done' || Boolean(job.plan)\n const stopped = phase === 'failed' || phase === 'stopped'\n const paused = job.status === 'needs_input'\n const planState: AiSiteBuildRowState = planDone\n ? 'done'\n : paused\n ? 'paused'\n : stopped\n ? 'failed'\n : phase === 'working'\n ? 'active'\n : 'waiting'\n const rows: AiSiteBuildRow[] = [\n {\n id: 'plan',\n label: 'Planning your pages',\n state: planState,\n startedAt: planStep?.startedAt ?? null,\n hint: planState === 'active' ? AI_SITE_PLAN_HINT : null,\n ...(planDone && planStep ? { credits: Math.max(0, Math.floor(planStep.creditsSpent ?? 0)) } : {}),\n },\n ]\n // Before there is a plan, the stages a guided start always goes through\n // are named from its answers, so the page shows the whole way from the\n // start rather than one row.\n if (!job.plan && job.kind !== 'build') {\n const pagesAsked = typeof job.siteInputs?.['pages'] === 'number' ? Math.round(job.siteInputs['pages'] as number) : 0\n if (job.siteInputs) {\n // The look is designed first, before the header and footer (AGL-3660).\n rows.push({ id: 'look', label: AI_SITE_LOOK_ROW_LABEL, state: 'waiting' })\n rows.push({ id: 'layout', label: 'Building the header and footer', state: 'waiting' })\n // A guided start that says where its contact form's submissions go\n // plans that form, built after the layout and before the pages (AGL-3596).\n const submissions = job.siteInputs['submissions']\n if (submissions === 'inbox' || submissions === 'lead') {\n rows.push({ id: 'form', label: 'Building your contact form', state: 'waiting' })\n }\n rows.push({\n id: 'pages',\n label: pagesAsked > 1 ? `Writing your ${pagesAsked} pages` : 'Writing your pages',\n state: 'waiting',\n })\n rows.push(...aiSitePublishRow(job, phase))\n }\n return rows\n }\n const built = new Map<string, number>()\n for (const output of job.outputs) built.set(output.resource, (built.get(output.resource) ?? 0) + 1)\n const take = (resource: string): boolean => {\n const left = built.get(resource) ?? 0\n if (left <= 0) return false\n built.set(resource, left - 1)\n return true\n }\n const stages: Array<{ id: string; label: string; resource: string; sections?: string[]; hint?: string }> = []\n // A scaffold designs its look first on every start (AGL-3660), and writes\n // its item ledger only with that first pass's outcome — so while the look\n // is being designed, the job is read here, without a ledger, and the look\n // is the stage it is on: a moving site job that has built nothing yet. A\n // job read without its ledger that has built something and no look is one\n // from before the look had a unit of its own, and has the stage only where\n // it built one.\n const moving = (phase === 'working' || paused) && job.outputs.length === 0\n for (const row of CREATION_ROWS) {\n const creation = job.plan?.create.find((entry) => entry.kind === row.kind)\n const look = row.resource === 'theme'\n if (\n creation ||\n (look && job.kind !== 'build' && moving) ||\n (look && job.outputs.some((output) => output.resource === 'theme'))\n ) {\n stages.push({\n id: look ? 'look' : row.resource,\n label: row.label(creation?.name ?? ''),\n resource: row.resource,\n ...(look ? { hint: AI_SITE_LOOK_HINT } : {}),\n })\n }\n }\n const pages = job.plan?.screens ?? []\n pages.forEach((page, index) => {\n stages.push({\n id: `page-${index}`,\n label: `Writing page ${index + 1} of ${pages.length}: ${page.title}`,\n resource: 'screen',\n sections: page.sections.map((section) => section.name),\n })\n })\n // The build step's start: when the first stage after the plan began.\n const buildStartedAt = job.steps.find((step) => step.name !== 'plan' && step.startedAt)?.startedAt ?? null\n let reached = false\n for (const stage of stages) {\n const done = phase === 'done' || take(stage.resource)\n const current = !done && !reached && planDone\n if (current) reached = true\n const state: AiSiteBuildRowState = done\n ? 'done'\n : current && paused\n ? 'paused'\n : current && stopped\n ? 'failed'\n : current && phase === 'working'\n ? 'active'\n : 'waiting'\n rows.push({\n id: stage.id,\n label: stage.label,\n state,\n ...(state === 'active' && stage.hint ? { hint: stage.hint, startedAt: buildStartedAt } : {}),\n ...(stage.sections?.length ? { sections: stage.sections } : {}),\n })\n }\n rows.push(...aiSitePublishRow(job, phase))\n return rows\n}\n\n/** The row the site's look is designed on (AGL-3660). */\nexport const AI_SITE_LOOK_ROW_LABEL = 'Designing your look'\n\n/** What the look's row says while it runs. */\nexport const AI_SITE_LOOK_HINT =\n 'Choosing a base theme, colors, fonts and the style of your buttons, cards and forms for your kind of site. This takes a few seconds.'\n\n/** What the planning row says while it runs. */\nexport const AI_SITE_PLAN_HINT =\n 'Reading your answers and choosing your pages, what each one says, and the forms and layout they need. This usually takes under a minute.'\n\n/** What the page being written says while it is the active row (AGL-3596). */\nexport const AI_SITE_PAGE_HINT =\n 'Writing this page’s sections from your plan. It is done once every section is in.'\n\n/** What any other part being built says while it is the active row. */\nexport const AI_SITE_ITEM_HINT = 'Building this now. The next step starts when it is done.'\n\n/** The plan's page an item builds: by its title, else by its place among the pages. */\nfunction aiPlanScreenFor(\n plan: AiJobSummary['plan'],\n title: string,\n index: number,\n): NonNullable<AiJobSummary['plan']>['screens'][number] | null {\n const screens = plan?.screens ?? []\n return screens.find((screen) => screen.title === title) ?? (index >= 0 ? (screens[index] ?? null) : null)\n}\n\n/**\n * A guided start ends by putting its pages live (AGL-3596): the last row, so\n * the person sees that step coming and when it is done. Only a site job\n * publishes on its own; a build publishes when asked, and says so in its plan.\n */\nfunction aiSitePublishRow(\n job: Pick<AiJobSummary, 'status'> & Partial<Pick<AiJobSummary, 'kind' | 'sitePublish'>>,\n phase: AiSiteBuildPhase,\n): AiSiteBuildRow[] {\n if (job.kind !== 'site') return []\n const published = (job.sitePublish?.published.length ?? 0) > 0\n return [\n {\n id: 'publish',\n label: 'Publishing your site',\n // A job that stopped or failed publishes nothing, so the row is passed over.\n state: phase === 'done' ? (published ? 'done' : 'skipped') : phase === 'working' ? 'waiting' : 'skipped',\n },\n ]\n}\n\n/**\n * How far the job is, from 0 to 1, by its rows: a finished row counts whole\n * and the active one half. `null` before there is more than one row to count.\n */\nexport function aiSiteBuildFraction(rows: readonly AiSiteBuildRow[]): number | null {\n if (rows.length < 2) return null\n const counted = rows.reduce(\n (sum, row) => sum + (row.state === 'active' ? 0.5 : row.state === 'waiting' || row.state === 'paused' ? 0 : 1),\n 0,\n )\n return Math.min(1, counted / rows.length)\n}\n\n/**\n * The job's one price (AGL-3594), never a plan's on its own: what it has used\n * so far while it works, what it used once done, and, on a failure we gave\n * back, that nothing was charged.\n */\nexport function aiSiteBuildCreditsLine(\n job: Pick<AiJobSummary, 'status' | 'creditsSpent' | 'refundedCredits'> & Partial<Pick<AiJobSummary, 'kind'>>,\n): string | null {\n const spent = Math.max(0, job.creditsSpent ?? 0)\n const refunded = Math.max(0, job.refundedCredits ?? 0)\n const net = Math.max(0, spent - refunded)\n const phase = aiSiteBuildPhase(job)\n // What the job made, by its kind; a job that names none is a site's.\n const subject = !job.kind || job.kind === 'site' ? 'This site' : aiJobKindNoun(job.kind) === 'AI job' ? 'This AI job' : `This ${aiJobKindNoun(job.kind)}`\n // A failure on our side says so in the words every surface uses (AGL-3596),\n // from the job's recorded give-back.\n const refund = phase === 'working' || phase === 'done' ? null : aiJobRefundCopy(job)\n if (refund && refunded > 0) return refund\n // A person's cancel keeps what ran charged, and says how much (AGL-3616).\n if (phase === 'canceled') return aiJobCanceledCreditsCopy(job)\n if (phase === 'done') return `${subject} used ${net} credits.`\n if (phase === 'working') return `Credits used so far: ${net}`\n if (refund) return `${subject} used ${net} credits. ${refund}`\n return net > 0 ? `${subject} used ${net} credits.` : null\n}\n\n/** The heading and the sentence under it, for one job's page in its current phase. */\nexport interface AiJobPageCopy {\n heading: string\n lede: string\n}\n\n/**\n * What one job's page says at the top (AGL-3594, AGL-3596). A site job reads\n * as \"Building your site\"; any other kind names what it makes — \"Building\n * your page\" — or, for a kind with no noun, the job itself.\n *\n * A guided site start publishes its pages when it finishes, and says so\n * (`sitePublish`): with a page live the heading is \"Your site is live\". A\n * done site job without that record — an older job, or one that was not a\n * guided start — built drafts, and says that instead.\n */\nexport function aiJobPageCopy(\n job: Pick<AiJobSummary, 'kind' | 'status' | 'review' | 'error'> &\n Partial<Pick<AiJobSummary, 'sitePublish' | 'items' | 'outputs'>>,\n brand: string,\n): AiJobPageCopy {\n const phase = aiSiteBuildPhase(job)\n // Paused by the meter (AGL-3660): why, what is already built, and that it\n // carries on — never \"was not built\", which read as a dead end.\n if (job.status === 'needs_input') {\n return {\n heading: aiJobPausedTitle(job.kind),\n lede: [job.error, aiJobBuiltSoFar(job), AI_JOB_PAUSED_NEXT_COPY].filter(Boolean).join(' '),\n }\n }\n // A build is what the person asked for, item by item (AGL-3616).\n if (job.kind === 'build') return aiBuildPageCopy(job, brand)\n const noun = job.kind === 'site' ? 'site' : aiJobKindNoun(job.kind)\n const generic = noun === 'AI job'\n // `products` is the one plural noun.\n const isAre = noun.endsWith('s') ? 'are' : 'is'\n const why = job.review?.message ?? job.error ?? null\n if (phase === 'working') {\n return {\n heading: generic ? 'Your AI job is running' : `Building your ${noun}`,\n lede:\n job.kind === 'site'\n ? `${brand} AI is planning your pages and writing each one. You can leave this page; it keeps going.`\n : `${brand} AI is working on it. You can leave this page; it keeps going.`,\n }\n }\n if (phase === 'done') {\n const published = job.sitePublish?.published.length ?? 0\n // Part of the site was built (AGL-3616): what was, what was not, and its credits.\n const partial = job.kind === 'site' ? aiSitePartialCopy({ items: job.items, status: job.status }) : null\n if (job.kind === 'site' && published > 0) {\n return {\n heading: 'Your site is live',\n lede: [\n partial,\n job.sitePublish?.drafts.length\n ? 'Your pages are published, except the ones listed below, which stayed drafts.'\n : 'Your pages are published, and anyone can visit your site now.',\n ]\n .filter(Boolean)\n .join(' '),\n }\n }\n if (partial) return { heading: 'Your site is ready', lede: `${partial} Your new pages are drafts. Publish them when you’re happy.` }\n if (job.kind === 'site' && job.sitePublish) {\n return {\n heading: 'Your site is ready',\n lede: 'None of your new pages could be published, so they are drafts. The reasons are below.',\n }\n }\n return {\n heading: generic ? 'Your AI job finished' : `Your ${noun} ${isAre} ready`,\n lede:\n job.kind === 'site'\n ? 'Your new pages are drafts. Publish them when you’re happy.'\n : 'Everything it built is an unpublished draft until you publish it.',\n }\n }\n if (phase === 'canceled') {\n return { heading: generic ? 'Your AI job was canceled' : `You canceled your ${noun}`, lede: AI_JOB_CANCELED_LEDE }\n }\n return {\n heading: generic ? 'Your AI job stopped' : `Your ${noun} ${noun.endsWith('s') ? 'were' : 'was'} not built`,\n lede: why ?? (generic ? 'Something went wrong running this job.' : `Something went wrong building your ${noun}.`),\n }\n}\n\nconst joinWords = (names: string[]): string =>\n names.length <= 1 ? (names[0] ?? '') : `${names.slice(0, -1).join(', ')} and ${names[names.length - 1]}`\n\n/**\n * What a job has already built, in a sentence (AGL-3660): its look, the header\n * and footer, its form, its pages — from the item ledger where it has one,\n * else from what it has reported as outputs. `null` when it has built nothing.\n */\nexport function aiJobBuiltSoFar(\n job: Pick<AiJobSummary, 'kind'> & Partial<Pick<AiJobSummary, 'items' | 'outputs'>>,\n): string | null {\n const built = (job.items ?? []).filter((row) => row.status === 'succeeded' || row.status === 'degraded')\n const names: string[] = []\n if (job.items?.length) {\n for (const row of built) {\n if (row.op === 'theme') names.push('your look')\n else if (row.op === 'layout') names.push('the header and footer')\n else if (row.op === 'form') names.push(`the form “${row.label}”`)\n else if (row.op === 'page') names.push(`the page “${row.label}”`)\n else if (row.op === 'email') names.push('your welcome email')\n else names.push(`${aiBuildOpNoun(row.op).toLowerCase()} “${row.label}”`)\n }\n } else {\n const outputs = job.outputs ?? []\n const count = (resource: string) => outputs.filter((output) => output.resource === resource).length\n if (count('theme')) names.push('your look')\n if (count('layout')) names.push('the header and footer')\n if (count('form')) names.push(count('form') === 1 ? 'the form' : `${count('form')} forms`)\n const pages = count('screen')\n if (pages) names.push(pages === 1 ? '1 page' : `${pages} pages`)\n }\n return names.length ? `Built so far: ${joinWords(names)}.` : null\n}\n\n/** A build's page heading and sentence (AGL-3616). */\nfunction aiBuildPageCopy(\n job: Pick<AiJobSummary, 'status' | 'review' | 'error'> & Partial<Pick<AiJobSummary, 'items' | 'sitePublish'>>,\n brand: string,\n): AiJobPageCopy {\n const phase = aiSiteBuildPhase(job)\n const outcome = aiBuildOutcomeLine({ items: job.items ?? [], status: job.status })\n if (phase === 'working') {\n return {\n heading: 'Building what you asked for',\n lede: `${brand} AI is building each part in turn. You can leave this page; it keeps going.`,\n }\n }\n if (phase === 'stopped') {\n return {\n heading: job.review?.reason === 'plan' ? 'Your plan is ready' : 'Your build needs you',\n lede: job.review?.message ?? job.error ?? 'It waits for your decision.',\n }\n }\n if (phase === 'done') {\n const failed = (job.items ?? []).some((row) => row.status === 'failed' || row.status === 'skipped')\n const live = job.sitePublish?.published.length ?? 0\n return {\n heading: failed ? 'Most of it is ready' : 'Everything you asked for is ready',\n lede: [\n outcome,\n live ? 'Your new pages are published.' : 'Everything it built is an unpublished draft until you publish it.',\n failed ? 'Try again builds only what failed.' : '',\n ]\n .filter(Boolean)\n .join(' '),\n }\n }\n if (phase === 'canceled') return { heading: 'Your build was canceled', lede: AI_JOB_CANCELED_LEDE }\n return {\n heading: 'Nothing could be built',\n lede: [job.error ?? 'Something went wrong building what you asked for.', outcome].filter(Boolean).join(' '),\n }\n}\n"],"names":["AI_JOB_CANCELED_LEDE","aiJobCanceledCreditsCopy","aiJobConfirmingOwnPlan","aiJobKindNoun","aiJobRefundCopy","aiBuildItemRows","aiBuildOpNoun","aiBuildOutcomeLine","aiSitePartialCopy","AI_JOB_PAUSED_NEXT_COPY","aiJobPausedTitle","aiSiteBuildPhase","job","status","CREATION_ROWS","kind","resource","label","AI_SITE_LOOK_ROW_LABEL","name","aiSiteBuildRows","rows","aiSiteBuildRowsOf","aiPausedRows","at","findIndex","row","state","index","map","position","hint","startedAt","planStep","built","phase","items","length","site","pages","filter","op","steps","find","step","id","credits","Math","max","floor","creditsSpent","ledger","indexOf","screen","aiPlanScreenFor","plan","finished","net","creditsRefunded","slot","detail","Boolean","join","AI_SITE_PAGE_HINT","AI_SITE_LOOK_HINT","AI_SITE_ITEM_HINT","sections","section","aiSitePublishRow","planDone","stopped","paused","planState","AI_SITE_PLAN_HINT","pagesAsked","siteInputs","round","push","submissions","Map","output","outputs","set","get","take","left","stages","moving","creation","create","entry","look","some","screens","forEach","page","title","buildStartedAt","reached","stage","done","current","published","sitePublish","aiSiteBuildFraction","counted","reduce","sum","min","aiSiteBuildCreditsLine","spent","refunded","refundedCredits","subject","refund","aiJobPageCopy","brand","heading","lede","error","aiJobBuiltSoFar","aiBuildPageCopy","noun","generic","isAre","endsWith","why","review","message","partial","drafts","joinWords","names","slice","toLowerCase","count","outcome","reason","failed","live"],"mappings":"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"}
|
|
@@ -172,6 +172,19 @@ export interface AiFreeCreditsLeft {
|
|
|
172
172
|
left: number;
|
|
173
173
|
total: number;
|
|
174
174
|
resetsOn: string;
|
|
175
|
+
/**
|
|
176
|
+
* Both meters `left` is the lesser of, in credits net of give-backs, for the
|
|
177
|
+
* usage strip's live read (AGL-3722): the owner's Free allowance across
|
|
178
|
+
* their Free workspaces (`account`, `null` with no owner) and this
|
|
179
|
+
* workspace's band.
|
|
180
|
+
*/
|
|
181
|
+
used?: {
|
|
182
|
+
account: number | null;
|
|
183
|
+
org: number;
|
|
184
|
+
orgBand: number | null;
|
|
185
|
+
/** The strip's warn state for these figures, worked out where the strip's own rule is not shipped to the browser. */
|
|
186
|
+
state: 'ok' | 'warn' | 'capped';
|
|
187
|
+
};
|
|
175
188
|
}
|
|
176
189
|
/** The first UTC day of the month after `now`, `YYYY-MM-DD`: when the Free credits come back. */
|
|
177
190
|
export declare function aiFreeCreditsResetOn(now: Date): string;
|
|
@@ -517,10 +530,14 @@ export declare function aiSiteWordsSaidAnything(words: AiSiteWords): boolean;
|
|
|
517
530
|
export declare function aiSiteWords(inputs: Readonly<Record<string, unknown>> | null | undefined): AiSiteWords;
|
|
518
531
|
/**
|
|
519
532
|
* What a scaffold builds for itself: the layout its pages render inside, the
|
|
520
|
-
* form they place,
|
|
521
|
-
* section
|
|
533
|
+
* form they place, the palette suggestion a member applies in the Theme
|
|
534
|
+
* section, and the datasets its structured content is kept in (AGL-3616),
|
|
535
|
+
* which the data plugin writes. Anything else a plan asks to create is a job
|
|
536
|
+
* of its own.
|
|
522
537
|
*/
|
|
523
538
|
export declare const AI_SITE_CREATE_KINDS: readonly AiBuildPlanCreateKind[];
|
|
539
|
+
/** The most datasets one site start creates (AGL-3616). */
|
|
540
|
+
export declare const AI_SITE_DATASETS_MAX = 3;
|
|
524
541
|
/** The creations a scaffold cannot build, in the order the plan lists them, each named once. */
|
|
525
542
|
export declare function aiSitePlanPrerequisites(plan: AiBuildPlan): Array<{
|
|
526
543
|
kind: AiBuildPlanCreateKind;
|
|
@@ -548,6 +565,8 @@ export interface AiPlanPassOptions {
|
|
|
548
565
|
welcomeEmail?: boolean;
|
|
549
566
|
/** The creation kinds the job builds itself; a scaffold's when absent. */
|
|
550
567
|
creates?: readonly AiBuildPlanCreateKind[];
|
|
568
|
+
/** Each page and creation written on its own (`aiStandaloneScreenCreditRange`), not as a site start’s (AGL-3722). */
|
|
569
|
+
standalonePages?: boolean;
|
|
551
570
|
}
|
|
552
571
|
/**
|
|
553
572
|
* The passes a plan implies: one per section of every screen, one more per
|
|
@@ -583,17 +602,32 @@ export declare function aiSiteCreditEstimate(pages: number, options?: {
|
|
|
583
602
|
* The measured cost of one creation a plan builds (AGL-3722): a layout's or
|
|
584
603
|
* a form's as measured, a theme's as the site start's look measured, and
|
|
585
604
|
* any other kind at the stand-in a single-answer creation is priced at.
|
|
605
|
+
* `standalone` for one written on its own — a build's, a page job's — rather
|
|
606
|
+
* than inside a site start's warm run.
|
|
586
607
|
*/
|
|
587
|
-
export declare function aiCreationMeasuredCredits(kind: string
|
|
608
|
+
export declare function aiCreationMeasuredCredits(kind: string, options?: {
|
|
609
|
+
standalone?: boolean;
|
|
610
|
+
}): AiMeasuredCredits;
|
|
588
611
|
/** A planned page's range: its sections scaled around the measured page, its ceiling one pass a section and one for its listing. */
|
|
589
612
|
export declare function aiScreenCreditRange(sections: number): AiCreditRange;
|
|
613
|
+
/**
|
|
614
|
+
* A page written on its own — an Assist build's, a page job's — as a range
|
|
615
|
+
* (AGL-3722): a fixed cost and one per section as such pages measured
|
|
616
|
+
* (`AI_MEASURED_STANDALONE_PAGE`), never a site start's later, cheaper
|
|
617
|
+
* pages; the ceiling as `aiScreenCreditRange`'s.
|
|
618
|
+
*/
|
|
619
|
+
export declare function aiStandaloneScreenCreditRange(sections: number): AiCreditRange;
|
|
590
620
|
/**
|
|
591
621
|
* What a plan is likely to cost, its p90 and its ceiling (AGL-3722): the
|
|
592
622
|
* same units `aiPlanCreditEstimate` counts, each at what it measured; the
|
|
593
623
|
* ceiling IS `aiPlanCreditEstimate`.
|
|
594
624
|
*/
|
|
595
625
|
export declare function aiPlanCreditRange(plan: AiBuildPlan, options?: AiPlanPassOptions): AiCreditRange;
|
|
596
|
-
/**
|
|
626
|
+
/**
|
|
627
|
+
* `aiJobPlanCreditEstimate` as a range (AGL-3722): the same kinds, each at
|
|
628
|
+
* what it measured. Only a site start writes its pages in one warm run; every
|
|
629
|
+
* other kind's page is written on its own.
|
|
630
|
+
*/
|
|
597
631
|
export declare function aiJobPlanCreditRange(kind: string, plan: AiBuildPlan): AiCreditRange;
|
|
598
632
|
/**
|
|
599
633
|
* A paid site start's range before its plan (AGL-3722): the plan, its look,
|
|
@@ -15,11 +15,11 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
|
15
15
|
* See the License for the specific language governing permissions and
|
|
16
16
|
* limitations under the License.
|
|
17
17
|
*/ import { aiSiteKind } from "./ai-site-kinds.js";
|
|
18
|
-
import { AI_BUILD_PLAN_CREATION_NOUNS, aiPlanUndeclaredRefs } from "./ai-build-plan.js";
|
|
18
|
+
import { AI_BUILD_PLAN_CREATION_NOUNS, aiPlanFormDatasetNames, aiPlanUndeclaredRefs } from "./ai-build-plan.js";
|
|
19
19
|
import { AI_JOB_CREATE_KINDS } from "./ai-job-creations.js";
|
|
20
20
|
import { AI_PAGE_CREATE_KINDS } from "./ai-page-job.js";
|
|
21
21
|
import { FREE_AI_TASTE_CREDITS_PER_MONTH } from "../plan-entitlements.js";
|
|
22
|
-
import { AI_CREDIT_RANGE_ZERO, AI_MEASURED_PASS_CREDITS, AI_MEASURED_UNIT_CREDITS, aiCreditRangeAdd, aiCreditRangeOf, aiCreditRangeOrdered, aiCreditsPromptFor, aiMeasuredPageCredits } from "./ai-credit-estimate.js";
|
|
22
|
+
import { AI_CREDIT_RANGE_ZERO, AI_MEASURED_PASS_CREDITS, AI_MEASURED_STANDALONE_CREATION_CREDITS, AI_MEASURED_UNIT_CREDITS, aiCreditRangeAdd, aiCreditRangeOf, aiCreditRangeOrdered, aiCreditsPromptFor, aiMeasuredPageCredits, aiMeasuredStandalonePageCredits } from "./ai-credit-estimate.js";
|
|
23
23
|
/**
|
|
24
24
|
* What a site scaffold is (AGL-2911): the inputs a `site` job is admitted
|
|
25
25
|
* with, the plans a scaffold can build, and what one is estimated to cost
|
|
@@ -573,13 +573,17 @@ const ID_CHARS = /^[A-Za-z0-9_-]{1,64}$/;
|
|
|
573
573
|
}
|
|
574
574
|
/**
|
|
575
575
|
* What a scaffold builds for itself: the layout its pages render inside, the
|
|
576
|
-
* form they place,
|
|
577
|
-
* section
|
|
576
|
+
* form they place, the palette suggestion a member applies in the Theme
|
|
577
|
+
* section, and the datasets its structured content is kept in (AGL-3616),
|
|
578
|
+
* which the data plugin writes. Anything else a plan asks to create is a job
|
|
579
|
+
* of its own.
|
|
578
580
|
*/ export const AI_SITE_CREATE_KINDS = [
|
|
579
581
|
'layout',
|
|
580
582
|
'form',
|
|
581
|
-
'theme-change'
|
|
583
|
+
'theme-change',
|
|
584
|
+
'dataset'
|
|
582
585
|
];
|
|
586
|
+
/** The most datasets one site start creates (AGL-3616). */ export const AI_SITE_DATASETS_MAX = 3;
|
|
583
587
|
/** The creations a scaffold cannot build, in the order the plan lists them, each named once. */ export function aiSitePlanPrerequisites(plan) {
|
|
584
588
|
const seen = new Set();
|
|
585
589
|
const prerequisites = [];
|
|
@@ -632,6 +636,10 @@ const ID_CHARS = /^[A-Za-z0-9_-]{1,64}$/;
|
|
|
632
636
|
if (!screens.some((screen)=>screen.nav)) {
|
|
633
637
|
return 'No page in this plan is in the site navigation. Describe the site again.';
|
|
634
638
|
}
|
|
639
|
+
const datasets = plan.create.filter((entry)=>entry.kind === 'dataset').length;
|
|
640
|
+
if (datasets > AI_SITE_DATASETS_MAX) {
|
|
641
|
+
return `This plan creates ${datasets} datasets, and a site scaffold creates at most ${AI_SITE_DATASETS_MAX}. Keep the others' items on their pages.`;
|
|
642
|
+
}
|
|
635
643
|
return null;
|
|
636
644
|
}
|
|
637
645
|
/**
|
|
@@ -646,6 +654,13 @@ const ID_CHARS = /^[A-Za-z0-9_-]{1,64}$/;
|
|
|
646
654
|
const listed = parts.length === 1 ? parts[0] : `${parts.slice(0, -1).join(', ')} and ${parts[parts.length - 1]}`;
|
|
647
655
|
return `This site needs what the workspace does not have yet. Create ${listed}, then describe the site again.`;
|
|
648
656
|
}
|
|
657
|
+
/**
|
|
658
|
+
* Whether a creation is built with no model: a dataset a form of the plan
|
|
659
|
+
* writes to (AGL-3616) starts empty, designed from its planned fields, so it
|
|
660
|
+
* is no pass of the estimate.
|
|
661
|
+
*/ function aiPlanCreationIsModelFree(plan, entry) {
|
|
662
|
+
return entry.kind === 'dataset' && aiPlanFormDatasetNames(plan).has(entry.name.trim().toLowerCase());
|
|
663
|
+
}
|
|
649
664
|
/**
|
|
650
665
|
* The passes a plan implies: one per section of every screen, one more per
|
|
651
666
|
* screen for its search listing and its draft, and one for each thing the
|
|
@@ -654,7 +669,7 @@ const ID_CHARS = /^[A-Za-z0-9_-]{1,64}$/;
|
|
|
654
669
|
var _options_creates;
|
|
655
670
|
const screens = plan.screens.reduce((total, screen)=>total + screen.sections.length + 1, 0);
|
|
656
671
|
const creates = (_options_creates = options.creates) != null ? _options_creates : AI_SITE_CREATE_KINDS;
|
|
657
|
-
const creations = plan.create.filter((entry)=>creates.includes(entry.kind)).length;
|
|
672
|
+
const creations = plan.create.filter((entry)=>creates.includes(entry.kind) && !aiPlanCreationIsModelFree(plan, entry)).length;
|
|
658
673
|
return screens + creations + (options.welcomeEmail ? 1 : 0);
|
|
659
674
|
}
|
|
660
675
|
/**
|
|
@@ -691,8 +706,12 @@ const ID_CHARS = /^[A-Za-z0-9_-]{1,64}$/;
|
|
|
691
706
|
* The measured cost of one creation a plan builds (AGL-3722): a layout's or
|
|
692
707
|
* a form's as measured, a theme's as the site start's look measured, and
|
|
693
708
|
* any other kind at the stand-in a single-answer creation is priced at.
|
|
694
|
-
|
|
695
|
-
|
|
709
|
+
* `standalone` for one written on its own — a build's, a page job's — rather
|
|
710
|
+
* than inside a site start's warm run.
|
|
711
|
+
*/ export function aiCreationMeasuredCredits(kind, options = {}) {
|
|
712
|
+
if (kind === 'layout') {
|
|
713
|
+
return options.standalone ? AI_MEASURED_STANDALONE_CREATION_CREDITS.layout : AI_MEASURED_UNIT_CREDITS.layout;
|
|
714
|
+
}
|
|
696
715
|
if (kind === 'form') return AI_MEASURED_UNIT_CREDITS.form;
|
|
697
716
|
if (kind === 'theme' || kind === 'theme-change') return AI_MEASURED_UNIT_CREDITS.theme;
|
|
698
717
|
return AI_MEASURED_PASS_CREDITS;
|
|
@@ -700,6 +719,14 @@ const ID_CHARS = /^[A-Za-z0-9_-]{1,64}$/;
|
|
|
700
719
|
/** A planned page's range: its sections scaled around the measured page, its ceiling one pass a section and one for its listing. */ export function aiScreenCreditRange(sections) {
|
|
701
720
|
return aiCreditRangeOf(aiMeasuredPageCredits(sections), (Math.max(0, sections) + 1) * AI_SITE_PASS_CREDITS);
|
|
702
721
|
}
|
|
722
|
+
/**
|
|
723
|
+
* A page written on its own — an Assist build's, a page job's — as a range
|
|
724
|
+
* (AGL-3722): a fixed cost and one per section as such pages measured
|
|
725
|
+
* (`AI_MEASURED_STANDALONE_PAGE`), never a site start's later, cheaper
|
|
726
|
+
* pages; the ceiling as `aiScreenCreditRange`'s.
|
|
727
|
+
*/ export function aiStandaloneScreenCreditRange(sections) {
|
|
728
|
+
return aiCreditRangeOf(aiMeasuredStandalonePageCredits(sections), (Math.max(0, sections) + 1) * AI_SITE_PASS_CREDITS);
|
|
729
|
+
}
|
|
703
730
|
/**
|
|
704
731
|
* What a plan is likely to cost, its p90 and its ceiling (AGL-3722): the
|
|
705
732
|
* same units `aiPlanCreditEstimate` counts, each at what it measured; the
|
|
@@ -708,19 +735,28 @@ const ID_CHARS = /^[A-Za-z0-9_-]{1,64}$/;
|
|
|
708
735
|
var _options_creates;
|
|
709
736
|
const creates = (_options_creates = options.creates) != null ? _options_creates : AI_SITE_CREATE_KINDS;
|
|
710
737
|
let range = AI_CREDIT_RANGE_ZERO;
|
|
711
|
-
|
|
738
|
+
const screenRange = options.standalonePages ? aiStandaloneScreenCreditRange : aiScreenCreditRange;
|
|
739
|
+
for (const screen of plan.screens)range = aiCreditRangeAdd(range, screenRange(screen.sections.length));
|
|
712
740
|
for (const entry of plan.create){
|
|
713
|
-
if (!creates.includes(entry.kind)) continue;
|
|
714
|
-
range = aiCreditRangeAdd(range, aiCreditRangeOf(aiCreationMeasuredCredits(entry.kind
|
|
741
|
+
if (!creates.includes(entry.kind) || aiPlanCreationIsModelFree(plan, entry)) continue;
|
|
742
|
+
range = aiCreditRangeAdd(range, aiCreditRangeOf(aiCreationMeasuredCredits(entry.kind, {
|
|
743
|
+
standalone: options.standalonePages
|
|
744
|
+
}), AI_SITE_PASS_CREDITS));
|
|
715
745
|
}
|
|
716
746
|
if (options.welcomeEmail) range = aiCreditRangeAdd(range, aiCreditRangeOf(AI_MEASURED_PASS_CREDITS, AI_SITE_PASS_CREDITS));
|
|
717
747
|
return aiCreditRangeOrdered(range);
|
|
718
748
|
}
|
|
719
|
-
/**
|
|
749
|
+
/**
|
|
750
|
+
* `aiJobPlanCreditEstimate` as a range (AGL-3722): the same kinds, each at
|
|
751
|
+
* what it measured. Only a site start writes its pages in one warm run; every
|
|
752
|
+
* other kind's page is written on its own.
|
|
753
|
+
*/ export function aiJobPlanCreditRange(kind, plan) {
|
|
720
754
|
const creates = kind === 'page' ? AI_PAGE_CREATE_KINDS : AI_JOB_CREATE_KINDS[kind];
|
|
721
|
-
return aiPlanCreditRange(plan, creates ? {
|
|
755
|
+
return aiPlanCreditRange(plan, _extends({}, creates ? {
|
|
722
756
|
creates
|
|
723
|
-
} : {}
|
|
757
|
+
} : {}, {
|
|
758
|
+
standalonePages: kind !== 'site'
|
|
759
|
+
}));
|
|
724
760
|
}
|
|
725
761
|
/**
|
|
726
762
|
* A paid site start's range before its plan (AGL-3722): the plan, its look,
|