@aglyn/plugins-ai 1.0.0-beta.230 → 1.0.0-beta.231
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-assist-provider-on-host.component.d.ts +66 -0
- package/src/lib/components/ai-assist-provider-on-host.component.js +78 -0
- package/src/lib/components/ai-assist-provider-on-host.component.js.map +1 -0
- package/src/lib/components/ai-assist-provider.component.js +9 -10
- package/src/lib/components/ai-assist-provider.component.js.map +1 -1
- package/src/lib/components/ai-job-plan.component.js +3 -1
- package/src/lib/components/ai-job-plan.component.js.map +1 -1
- package/src/lib/components/ai-permissions-on-host.component.d.ts +3 -46
- package/src/lib/components/ai-permissions-on-host.component.js +5 -53
- package/src/lib/components/ai-permissions-on-host.component.js.map +1 -1
- package/src/lib/components/ai-site-build-page.component.js +20 -8
- package/src/lib/components/ai-site-build-page.component.js.map +1 -1
- package/src/lib/jobs/ai-build-unit-outcome.js +4 -2
- package/src/lib/jobs/ai-build-unit-outcome.js.map +1 -1
- package/src/lib/jobs/ai-job-auto-confirm.d.ts +8 -0
- package/src/lib/jobs/ai-job-auto-confirm.js +30 -6
- package/src/lib/jobs/ai-job-auto-confirm.js.map +1 -1
- package/src/lib/jobs/ai-job-build-step.js +11 -0
- package/src/lib/jobs/ai-job-build-step.js.map +1 -1
- package/src/lib/jobs/ai-job-generation.d.ts +8 -0
- package/src/lib/jobs/ai-job-generation.js +13 -2
- package/src/lib/jobs/ai-job-generation.js.map +1 -1
- package/src/lib/jobs/ai-job-insight-step.js +8 -1
- package/src/lib/jobs/ai-job-insight-step.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-language.d.ts +73 -0
- package/src/lib/jobs/ai-job-layout-language.js +194 -0
- package/src/lib/jobs/ai-job-layout-language.js.map +1 -0
- package/src/lib/jobs/ai-job-layout-site-pages.d.ts +13 -2
- package/src/lib/jobs/ai-job-layout-site-pages.js +18 -2
- package/src/lib/jobs/ai-job-layout-site-pages.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-step.d.ts +5 -0
- package/src/lib/jobs/ai-job-layout-step.js +105 -5
- package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
- package/src/lib/jobs/ai-job-page-language.d.ts +149 -0
- package/src/lib/jobs/ai-job-page-language.js +355 -0
- package/src/lib/jobs/ai-job-page-language.js.map +1 -0
- package/src/lib/jobs/ai-job-page-sections.d.ts +37 -1
- package/src/lib/jobs/ai-job-page-sections.js +93 -11
- package/src/lib/jobs/ai-job-page-sections.js.map +1 -1
- package/src/lib/jobs/ai-job-page-step.d.ts +4 -4
- package/src/lib/jobs/ai-job-page-step.js +105 -14
- package/src/lib/jobs/ai-job-page-step.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-step.d.ts +1 -1
- package/src/lib/jobs/ai-job-plan-step.js +8 -2
- package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-step.d.ts +4 -0
- package/src/lib/jobs/ai-job-site-step.js +20 -3
- package/src/lib/jobs/ai-job-site-step.js.map +1 -1
- package/src/lib/jobs/ai-job-workflow-step.js +2 -2
- package/src/lib/jobs/ai-job-workflow-step.js.map +1 -1
- package/src/lib/jobs/ai-jobs.d.ts +1 -1
- package/src/lib/jobs/ai-jobs.js +17 -8
- package/src/lib/jobs/ai-jobs.js.map +1 -1
- package/src/lib/jobs/ai-layout-full-height.d.ts +40 -0
- package/src/lib/jobs/ai-layout-full-height.js +82 -0
- package/src/lib/jobs/ai-layout-full-height.js.map +1 -0
- package/src/lib/jobs/ai-layout-site-facts.d.ts +54 -0
- package/src/lib/jobs/ai-layout-site-facts.js +153 -0
- package/src/lib/jobs/ai-layout-site-facts.js.map +1 -0
- package/src/lib/layout-language/ai-layout-compiler.d.ts +145 -0
- package/src/lib/layout-language/ai-layout-compiler.js +1148 -0
- package/src/lib/layout-language/ai-layout-compiler.js.map +1 -0
- package/src/lib/layout-language/ai-layout-copy.d.ts +73 -0
- package/src/lib/layout-language/ai-layout-copy.js +137 -0
- package/src/lib/layout-language/ai-layout-copy.js.map +1 -0
- package/src/lib/layout-language/ai-layout-frame.d.ts +52 -0
- package/src/lib/layout-language/ai-layout-frame.js +457 -0
- package/src/lib/layout-language/ai-layout-frame.js.map +1 -0
- package/src/lib/layout-language/ai-layout-gaps.d.ts +63 -0
- package/src/lib/layout-language/ai-layout-gaps.js +345 -0
- package/src/lib/layout-language/ai-layout-gaps.js.map +1 -0
- package/src/lib/layout-language/ai-layout-language.d.ts +161 -0
- package/src/lib/layout-language/ai-layout-language.js +446 -0
- package/src/lib/layout-language/ai-layout-language.js.map +1 -0
- package/src/lib/layout-language/ai-layout-links.d.ts +80 -0
- package/src/lib/layout-language/ai-layout-links.js +109 -0
- package/src/lib/layout-language/ai-layout-links.js.map +1 -0
- package/src/lib/layout-language/ai-layout-store.d.ts +39 -0
- package/src/lib/layout-language/ai-layout-store.js +69 -0
- package/src/lib/layout-language/ai-layout-store.js.map +1 -0
- package/src/lib/layout-language/ai-layout-tree.d.ts +59 -0
- package/src/lib/layout-language/ai-layout-tree.js +84 -0
- package/src/lib/layout-language/ai-layout-tree.js.map +1 -0
- package/src/lib/lazy-widget.d.ts +45 -0
- package/src/lib/lazy-widget.js +53 -0
- package/src/lib/lazy-widget.js.map +1 -0
- package/src/lib/model/ai-build-job.js +2 -1
- package/src/lib/model/ai-build-job.js.map +1 -1
- package/src/lib/model/ai-build-progress.d.ts +20 -3
- package/src/lib/model/ai-build-progress.js +48 -6
- package/src/lib/model/ai-build-progress.js.map +1 -1
- package/src/lib/model/ai-host-publish-context.d.ts +51 -0
- package/src/lib/model/ai-host-publish-context.js +104 -0
- package/src/lib/model/ai-host-publish-context.js.map +1 -0
- package/src/lib/model/ai-job-activity.d.ts +22 -0
- package/src/lib/model/ai-job-activity.js +22 -1
- package/src/lib/model/ai-job-activity.js.map +1 -1
- package/src/lib/model/ai-job-failure-copy.d.ts +38 -1
- package/src/lib/model/ai-job-failure-copy.js +56 -1
- package/src/lib/model/ai-job-failure-copy.js.map +1 -1
- package/src/lib/model/ai-job-notice.d.ts +3 -1
- package/src/lib/model/ai-job-notice.js +31 -9
- package/src/lib/model/ai-job-notice.js.map +1 -1
- package/src/lib/model/ai-jobs.types.d.ts +19 -0
- package/src/lib/model/ai-jobs.types.js.map +1 -1
- package/src/lib/model/ai-site-build-progress.d.ts +5 -1
- package/src/lib/model/ai-site-build-progress.js +27 -4
- package/src/lib/model/ai-site-build-progress.js.map +1 -1
- package/src/lib/model/ai-site-job.d.ts +8 -0
- package/src/lib/model/ai-site-job.js +9 -0
- package/src/lib/model/ai-site-job.js.map +1 -1
- package/src/lib/model/ai-site-start-seo.d.ts +1 -1
- package/src/lib/model/ai-site-start.d.ts +5 -2
- package/src/lib/model/ai-site-start.js +5 -2
- package/src/lib/model/ai-site-start.js.map +1 -1
- package/src/lib/plugin.js +79 -47
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/providers/catalog.d.ts +1 -1
- package/src/lib/providers/catalog.js +3 -3
- package/src/lib/providers/catalog.js.map +1 -1
- package/src/lib/runtime/ai-doctrine-validators.d.ts +82 -0
- package/src/lib/runtime/ai-doctrine-validators.js +436 -4
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-doctrine.js +12 -3
- package/src/lib/runtime/ai-doctrine.js.map +1 -1
- package/src/lib/runtime/ai-icon-library.d.ts +257 -0
- package/src/lib/runtime/ai-icon-library.js +254 -0
- package/src/lib/runtime/ai-icon-library.js.map +1 -0
- package/src/lib/runtime/ai-node-tree.d.ts +9 -0
- package/src/lib/runtime/ai-node-tree.js +30 -4
- package/src/lib/runtime/ai-node-tree.js.map +1 -1
- package/src/lib/runtime/ai-palette.d.ts +6 -0
- package/src/lib/runtime/ai-palette.generated.js +70 -0
- package/src/lib/runtime/ai-palette.generated.js.map +1 -1
- package/src/lib/runtime/ai-palette.js.map +1 -1
- package/src/lib/tools/ai-workflow-tool.d.ts +6 -2
- package/src/lib/tools/ai-workflow-tool.js +16 -14
- package/src/lib/tools/ai-workflow-tool.js.map +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-build-step.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { AglynOrgBilling } from '@aglyn/aglyn/foundation/definitions/org-billing.types'\nimport { resolveEffectivePlan } from '@aglyn/aglyn/app-utils/plan-entitlements'\nimport { resolveOrgIdForHost } from '@aglyn/tenant-data-admin/server/organizations'\nimport {\n AI_BUILD_LIMITS,\n AI_BUILD_PUBLISH_INPUT,\n aiArticle,\n aiBuildCreditEstimate,\n aiBuildDegradation,\n aiBuildInitialLedger,\n aiBuildItemDelivered,\n aiBuildItemOpen,\n aiBuildNextUnit,\n aiBuildOrder,\n aiBuildPlacedItemLines,\n aiBuildPlanShapeRefusal,\n aiBuildUnits,\n type AiBuildOps,\n type AiBuildUnit,\n} from '../model/ai-build-job'\nimport { isAiPlanNewRef, type AiBuildPlanCreateKind } from '../model/ai-build-plan'\nimport type {\n AiJob,\n AiJobItemFailure,\n AiJobItemLedger,\n AiJobKind,\n AiJobOutput,\n AiJobPlan,\n} from '../model/ai-jobs.types'\nimport { AI_SITE_MAX_SECTIONS } from '../model/ai-site-job'\nimport { FREE_AI_TASTE_CREDITS_PER_MONTH } from '../plan-entitlements'\nimport { aiModelForStep } from '../providers/routing'\nimport {\n AI_BUILD_UNIT_EMPTY_COPY,\n aiBuildBuiltRefs,\n aiUnitErrorRetryable,\n aiUnitFailure,\n aiUnitSpend,\n} from './ai-build-unit-outcome'\nimport { assistCreditsFromUsd } from '../usage/assist-credits'\nimport { ASSIST_RETURNED_USD_FIELD, assistSpendAfterReturnsUsd } from '../usage/assist-credit-returns'\nimport { freeAccountUsageRef, freeAssistAccount, type AssistMeteredOrg } from '../usage/assist-free-taste'\nimport { aiBuildOps, type AiBuildOpsContext } from './ai-build-capabilities'\nimport {\n aiJobAdmissionRefusal,\n registerAiJobAdmission,\n type AiJobAdmission,\n type AiJobAdmissionContext,\n} from './ai-job-admission'\nimport { aiOriginJobId } from './ai-job-draft-ids'\nimport { readAiDraftNodes } from './ai-job-drafts'\nimport { aiConfirmedPlan, aiUnspentOutcome } from './ai-job-generation'\nimport { AI_JOB_PAGE_STEP_MINIMUM_MS } from './ai-job-page-budget'\nimport {\n aiPluginDraftAdmissionRefusal,\n aiPluginDraftWriter,\n type AiPluginDraftWriterLookup,\n} from './ai-job-plugin-drafts'\nimport { aiSitePageWritten, aiSiteResolvedRef } from './ai-job-site-step'\nimport {\n AI_JOB_BRIEF_MAX_CHARS,\n type AiJobItemOutcome,\n type AiJobStepOutcome,\n type AiJobStepRunner,\n} from './ai-job-text-step'\nimport { aiJobPublishesBuild, aiPublishGuidedSite } from './ai-site-publish'\nimport {\n AI_PLANNED_JOB_KINDS,\n aiJobStepRunMinimumMs,\n aiJobStepRunnerFor,\n registerAiJobStep,\n registerAiJobStepPasses,\n} from './ai-jobs'\n\n/**\n * The build step (AGL-3616): the generation step of a `build` job, run once\n * the member confirmed the plan their request produced.\n *\n * Like the site scaffold it generalizes, it builds nothing itself and asks no\n * model of its own. Each pass takes the next unit in dependency order and\n * hands it to what builds that kind of thing — a page, layout, form,\n * component, email design or template to this plugin's own step, under a job\n * derived for the unit; another plugin's resource to that plugin's writer on\n * the resource-draft seam, with the content the owner's capability derives\n * from the item's arguments. One unit, one pass, one recorded spend.\n *\n * ── Settled item by item ────────────────────────────────────────────────\n *\n * Where a unit stands is read from the job's ITEM LEDGER, never from counting\n * outputs: the machine writes each pass's row in the transaction that\n * records its spend (`recordStep`). A unit that fails is a FAILED ROW, not a\n * failed job: the pass reports it, the machine gives back that item's spend\n * where the failure was ours, and the next pass builds the next unit. What\n * depended on it is built without it, or skipped where it cannot stand\n * without it (`aiBuildDegradation`). The machine ends the job when nothing is\n * left: `done` when anything was delivered, `failed` — its planning given\n * back too — when nothing was.\n *\n * ── Drafts, unless asked and confirmed ───────────────────────────────────\n *\n * Every unit writes the draft its own kind writes, which nothing publishes.\n * Only a build whose request asked to publish AND whose member ticked the\n * plan card's box puts its built pages live, once, on its last pass, through\n * the guided start's publish.\n */\n\n/** What a build answers with no confirmed plan to build. */\nexport const AI_BUILD_NO_PLAN_COPY = 'This build has no confirmed plan to build.'\n\n/** What a unit says when nothing on this site builds its operation any more. */\nexport const aiBuildUnavailableCopy = (noun: string): string =>\n `Not built: ${aiArticle(noun)} cannot be made on this site right now.`\n\n\n/** What a page that was reported built but holds none of its plan says. */\nexport const AI_BUILD_PAGE_NOT_WRITTEN_COPY = 'The page was not written from its plan.'\n\n\n/**\n * The most passes a build's step may take: every unit at a page's worst — its\n * sections and its listing — so a runner that never finishes is still\n * bounded while the largest plan is not. Try again starts the count over.\n */\nexport const AI_BUILD_MAX_PASSES = AI_BUILD_LIMITS.units * (AI_SITE_MAX_SECTIONS + 1)\n\n/** The job kind a creation of the build is built by. */\nconst CREATION_JOB_KINDS: Partial<Record<AiBuildPlanCreateKind, AiJobKind>> = {\n layout: 'layout',\n form: 'form',\n component: 'component',\n email: 'email',\n}\n\n/** The job kind a unit is built by, where an AI runner builds it. */\nexport function aiBuildUnitJobKind(unit: AiBuildUnit, ops: AiBuildOps): AiJobKind | null {\n if (unit.screen) return 'page'\n if (unit.creation) return CREATION_JOB_KINDS[unit.creation.kind] ?? null\n const runnerKind = ops.get(unit.op)?.runnerKind\n return runnerKind ? (runnerKind as AiJobKind) : null\n}\n\n/** The id a unit's job, and so its draft, is named by. */\nexport function aiBuildUnitJobId(job: Pick<AiJob, '$id'>, unit: AiBuildUnit): string {\n return unit.creation?.id ?? unit.screen?.id ?? unit.item?.id ?? `${job.$id}-${unit.slot}`\n}\n\n/**\n * The job a unit is built under (generalizing `aiSiteUnitJob`): this job's\n * org, site, creator and model; the kind that builds the unit; a plan\n * narrowed to the unit, its `new:` references resolved to what was built and\n * the failed ones left out; and a brief that says what the unit is, what it\n * is built without, and which blocks its sections place.\n */\nexport function aiBuildUnitJob(\n job: AiJob,\n unit: AiBuildUnit,\n context: {\n kind: AiJobKind\n units: readonly AiBuildUnit[]\n ledger: readonly AiJobItemLedger[]\n ops: AiBuildOps\n briefLines?: readonly string[]\n },\n): AiJob {\n const plan = job.plan as AiJobPlan\n const built = aiBuildBuiltRefs(context.units, context.ledger, job.outputs ?? [])\n const labels = { ...(plan.labels ?? {}) }\n for (const entry of built.values()) labels[entry.id] = entry.label\n const brief = [job.brief.trim()]\n const unitPlan: AiJobPlan = { ...plan, labels, reuse: [...plan.reuse], create: [], screens: [] }\n delete unitPlan.items\n const placedByScreens = new Set(plan.screens.flatMap((screen) => screen.sections.flatMap((section) => section.uses)))\n if (unit.creation) {\n unitPlan.reuse = plan.reuse.filter((entry) => !placedByScreens.has(entry.id))\n unitPlan.create = [{ ...unit.creation, duplicateOf: aiSiteResolvedRef(unit.creation.duplicateOf, built) }]\n brief.push(`Build the ${unit.creation.kind} “${unit.creation.name}”: ${unit.creation.why}`)\n }\n if (unit.screen) {\n const screen = unit.screen\n for (const entry of built.values()) {\n unitPlan.reuse.push({ kind: entry.kind, id: entry.id, purpose: `the ${entry.kind} this request built` })\n }\n let builtLayout: string | null = null\n for (const entry of built.values()) if (entry.kind === 'layout') builtLayout = builtLayout ?? entry.id\n unitPlan.screens = [\n {\n ...screen,\n // A page whose layout failed renders inside the site's own: the page\n // step falls back to it when the plan names none.\n layout: aiSiteResolvedRef(screen.layout, built) ?? (isAiPlanNewRef(screen.layout) ? builtLayout : null),\n template: aiSiteResolvedRef(screen.template, built),\n duplicateOf: aiSiteResolvedRef(screen.duplicateOf, built),\n record: screen.record\n ? { ...screen.record, dataset: aiSiteResolvedRef(screen.record.dataset, built) ?? screen.record.dataset }\n : null,\n sections: screen.sections.map((section) => ({\n ...section,\n uses: section.uses\n .map((ref) => aiSiteResolvedRef(ref, built))\n .filter((ref): ref is string => !!ref),\n })),\n },\n ]\n brief.push(`Build the page “${screen.title}”, at ${screen.slug}.`)\n brief.push(...aiBuildPlacedItemLines(unit, { units: context.units, ledger: context.ledger, ops: context.ops }))\n }\n const item = unit.item\n if (item) {\n const noun = context.ops.get(item.op)?.noun ?? item.op\n brief.push(`Build the ${noun} “${item.name}”${item.why ? `: ${item.why}` : '.'}`)\n // A planned kind's step builds the plan's first creation of its kind.\n unitPlan.create = AI_PLANNED_JOB_KINDS.includes(context.kind)\n ? [{ kind: context.kind as AiBuildPlanCreateKind, name: item.name, why: item.why, duplicateOf: null, fields: [], ...(item.id ? { id: item.id } : {}) }]\n : []\n }\n brief.push(...(context.briefLines ?? []))\n const inputs: Record<string, unknown> = {\n ...job.inputs,\n ...(item?.args ?? {}),\n originJobId: aiOriginJobId(job),\n }\n delete inputs[AI_BUILD_PUBLISH_INPUT]\n return {\n ...job,\n $id: aiBuildUnitJobId(job, unit),\n kind: context.kind,\n steps: [],\n outputs: [],\n items: null,\n plan: item && !AI_PLANNED_JOB_KINDS.includes(context.kind) ? null : unitPlan,\n inputs,\n brief: brief.join('\\n').slice(0, AI_JOB_BRIEF_MAX_CHARS),\n }\n}\n\n/** The unit's failure, for its row: ours or not, why, in customer-safe words. */\nfunction failed(\n slot: string,\n failure: AiJobItemFailure,\n extra: Partial<AiJobItemOutcome> = {},\n): AiJobItemOutcome {\n return { slot, status: 'failed', failure, ...extra }\n}\n\nexport interface AiJobBuildStepDeps {\n /** The runner registry; specs hand in fakes. */\n runnerFor?: typeof aiJobStepRunnerFor\n /** The draft writer registry; specs hand in fakes. */\n writerFor?: AiPluginDraftWriterLookup\n /** The operations this site may use; the registry and its gates otherwise. */\n opsFor?: (context: AiBuildOpsContext) => Promise<AiBuildOps>\n /** A runner kind's own admission, asked before its item runs; the registry's otherwise. */\n admissionFor?: typeof aiJobAdmissionRefusal\n /** Another plugin's admission for its writer; the seam's otherwise. */\n pluginAdmission?: typeof aiPluginDraftAdmissionRefusal\n /** The draft reader a built page is checked through. */\n readNodes?: typeof readAiDraftNodes\n /** The publish a build asked for and confirmed. */\n publish?: typeof aiPublishGuidedSite\n}\n\n/** Whether a build's workspace spends the Free taste. */\nexport function aiBuildFreeTaste(org: object | null | undefined): boolean {\n return Boolean(org) && resolveEffectivePlan(org as never) === 'free'\n}\n\n/** The site document a gate reads, or `null`. */\nasync function readHost(firestore: FirebaseFirestore.Firestore, hostId: string | null) {\n if (!hostId) return null\n return ((await firestore.collection('hosts').doc(hostId).get()).data() ?? null) as Record<string, unknown> | null\n}\n\nexport function createAiJobBuildStep(deps: AiJobBuildStepDeps = {}): AiJobStepRunner {\n const runnerFor = deps.runnerFor ?? aiJobStepRunnerFor\n const writerFor = deps.writerFor ?? aiPluginDraftWriter\n const opsFor = deps.opsFor ?? ((context) => aiBuildOps(context))\n const admissionFor = deps.admissionFor ?? aiJobAdmissionRefusal\n const pluginAdmission = deps.pluginAdmission ?? aiPluginDraftAdmissionRefusal\n const readNodes = deps.readNodes ?? readAiDraftNodes\n const publish = deps.publish ?? aiPublishGuidedSite\n\n return async (context): Promise<AiJobStepOutcome> => {\n const { job, firestore } = context\n const model = context.modelFor?.('job.plan') ?? aiModelForStep('job.plan')\n const plan = aiConfirmedPlan(job)\n if (!plan) return { ...aiUnspentOutcome(model), failure: AI_BUILD_NO_PLAN_COPY }\n const org = (context.org ?? null) as Partial<AglynOrgBilling> | null\n const units = aiBuildUnits(plan)\n const ordered = aiBuildOrder(units) ?? units\n const starting = !job.items?.length\n const ledger = starting ? aiBuildInitialLedger(units) : (job.items as AiJobItemLedger[])\n const init = starting ? { items: ledger } : {}\n const unit = aiBuildNextUnit(ordered, ledger)\n const rows = new Map(ledger.map((row) => [row.slot, row]))\n const host = await readHost(firestore, job.hostId ?? null)\n const ops = await opsFor({\n orgId: job.orgId,\n hostId: job.hostId ?? null,\n org,\n host,\n freeTaste: aiBuildFreeTaste(org),\n })\n\n /** Whether units other than this one are still to build. */\n const othersOpen = (slot: string) =>\n ordered.some((one) => one.slot !== slot && aiBuildItemOpen(rows.get(one.slot) ?? { status: 'pending' }))\n\n /** The build's last pass puts its pages live, where it was asked and confirmed. */\n const finish = async (outcome: AiJobStepOutcome, extraPages: readonly AiJobOutput[] = []): Promise<AiJobStepOutcome> => {\n if (!aiJobPublishesBuild(job) || job.sitePublish || !job.hostId) return outcome\n const pageIds = new Set(\n units\n .filter((one) => one.screen && aiBuildItemDelivered(rows.get(one.slot) ?? { status: 'pending' }))\n .flatMap((one) => rows.get(one.slot)?.outputs ?? []),\n )\n const pages = [\n ...(job.outputs ?? []).filter((output) => output.resource === 'screen' && pageIds.has(output.id)),\n ...extraPages,\n ]\n if (!pages.length) return outcome\n const sitePublish = await publish(firestore, { job, outputs: pages, now: context.now }).catch((error: unknown) => {\n console.error('ai build publish threw', { orgId: job.orgId, jobId: job.$id, error })\n return null\n })\n return sitePublish ? { ...outcome, sitePublish } : outcome\n }\n\n if (!unit) return finish({ ...aiUnspentOutcome(model), ...init })\n\n const settle = (item: AiJobItemOutcome, spent: AiJobStepOutcome = aiUnspentOutcome(model)): AiJobStepOutcome => ({\n ...spent,\n ...init,\n item,\n ...(item.status === 'running' || othersOpen(unit.slot) ? { continue: true } : {}),\n })\n\n // What its dependencies came to.\n const degradation = aiBuildDegradation(unit, { units, ledger, ops })\n if (degradation.skip) {\n return settle({ slot: unit.slot, status: 'skipped', note: degradation.skip, degradedBy: degradation.degradedBy })\n }\n const capability = ops.get(unit.op)\n const noun = capability?.noun ?? unit.op\n if (!capability) return settle({ slot: unit.slot, status: 'skipped', note: aiBuildUnavailableCopy(noun) })\n const degraded = degradation.degradedBy.length > 0\n const note = degradation.notes.length ? degradation.notes.join(' ') : null\n\n // Another plugin's resource: its writer, with the content its capability derives.\n if (capability.draftResource) {\n const item = unit.item\n const writer = writerFor(capability.draftResource)\n if (!item || !writer || !job.hostId) {\n return settle({ slot: unit.slot, status: 'skipped', note: aiBuildUnavailableCopy(noun) })\n }\n const admission: AiJobAdmissionContext = {\n firestore,\n orgId: job.orgId,\n hostId: job.hostId,\n inputs: {},\n org,\n uid: job.createdBy,\n }\n const refusal = await pluginAdmission(admission, {\n kind: 'build',\n drafts: [{ resource: capability.draftResource, label: capability.noun }],\n writerFor,\n })\n if (refusal) return settle({ slot: unit.slot, status: 'skipped', note: `Not built: ${refusal.error}` })\n const dependencies: Record<string, { op: string; id: string }> = {}\n for (const slot of unit.deps) {\n const dep = units.find((one) => one.slot === slot)\n const row = rows.get(slot)\n if (!dep || !row || !aiBuildItemDelivered(row) || !row.outputs.length) continue\n dependencies[`new:${dep.label}`] = { op: dep.op, id: row.outputs[0] }\n }\n const content = capability.draftContent?.({ name: item.name, args: item.args }, { hostId: job.hostId, dependencies }) ?? item.args\n const checked = writer.check(content, { hostId: job.hostId })\n if (checked.ok === false) {\n return settle(\n failed(unit.slot, { ours: true, reason: 'step-failure', message: checked.problems[0] ?? AI_BUILD_UNIT_EMPTY_COPY }),\n )\n }\n const id = aiBuildUnitJobId(job, unit)\n const written = await writer.write({\n orgId: job.orgId,\n hostId: job.hostId,\n uid: job.createdBy,\n org,\n now: context.now,\n id,\n name: item.name,\n content,\n })\n if (written.ok === false) {\n return settle(failed(unit.slot, { ours: false, reason: 'review', message: written.error }))\n }\n const output: AiJobOutput = {\n resource: 'draft',\n id: written.id,\n versionId: written.versionId,\n hostId: job.hostId,\n label: written.name,\n draftResource: capability.draftResource,\n note: `A draft, in ${capability.where}.`,\n }\n return settle(\n {\n slot: unit.slot,\n status: degraded ? 'degraded' : 'succeeded',\n outputs: [written.id],\n note,\n ...(degraded ? { degradedBy: degradation.degradedBy } : {}),\n },\n { ...aiUnspentOutcome(model), outputs: [output] },\n )\n }\n\n // This plugin's own runner, under a job derived for the unit.\n const kind = aiBuildUnitJobKind(unit, ops)\n const runner = kind ? runnerFor(kind) : null\n if (!kind || !runner) return settle({ slot: unit.slot, status: 'skipped', note: aiBuildUnavailableCopy(noun) })\n const derived = aiBuildUnitJob(job, unit, { kind, units, ledger, ops, briefLines: degradation.briefLines })\n // An item's kind is asked its own admission first (a campaign's plan,\n // a template's subject), once, before its first pass.\n if (unit.item && rows.get(unit.slot)?.status !== 'running') {\n const refusal = await admissionFor(kind, {\n firestore,\n orgId: job.orgId,\n hostId: job.hostId ?? null,\n inputs: derived.inputs,\n org,\n uid: job.createdBy,\n }).catch((error: unknown) => {\n console.error('ai build item admission failed', { orgId: job.orgId, jobId: job.$id, slot: unit.slot, error })\n return { status: 403 as const, error: AI_BUILD_UNIT_EMPTY_COPY }\n })\n if (refusal) return settle({ slot: unit.slot, status: 'skipped', note: `Not built: ${refusal.error}` })\n }\n\n let outcome: AiJobStepOutcome\n try {\n outcome = await runner({ ...context, job: derived })\n } catch (error) {\n // A provider that will answer later is the machine's to retry; anything\n // else is this item's failure, and the build goes on.\n if (aiUnitErrorRetryable(error)) throw error\n console.error('ai build unit threw', { orgId: job.orgId, jobId: job.$id, slot: unit.slot, error })\n return settle(failed(unit.slot, { ours: true, reason: 'provider', message: AI_BUILD_UNIT_EMPTY_COPY }))\n }\n const spent = aiUnitSpend(outcome)\n const ids = outcome.outputs.map((output) => output.id)\n const stopped = aiUnitFailure(unit.slot, outcome)\n if (stopped) return settle(stopped, spent)\n // A page counts as built only when its plan's sections are in it (AGL-3596).\n if (unit.screen && job.hostId) {\n const page = outcome.outputs.find((output) => output.resource === 'screen')\n const written =\n page !== undefined &&\n (await aiSitePageWritten(\n firestore,\n { hostId: job.hostId, unitJobId: derived.$id, screen: unit.screen, draftId: page.id },\n readNodes,\n ))\n if (!written) {\n return settle(\n failed(unit.slot, { ours: true, reason: 'step-failure', message: AI_BUILD_PAGE_NOT_WRITTEN_COPY }),\n { ...spent, outputs: outcome.outputs.filter((output) => output.resource !== 'screen') },\n )\n }\n }\n const done = settle(\n {\n slot: unit.slot,\n status: degraded ? 'degraded' : 'succeeded',\n outputs: ids,\n note,\n ...(degraded ? { degradedBy: degradation.degradedBy } : {}),\n },\n spent,\n )\n if (done.continue) return done\n // The last unit: what was built goes live where it was asked and confirmed.\n rows.set(unit.slot, { ...(rows.get(unit.slot) as AiJobItemLedger), status: degraded ? 'degraded' : 'succeeded', outputs: ids })\n return finish(done, unit.screen ? outcome.outputs.filter((output) => output.resource === 'screen') : [])\n }\n}\n\nexport const runAiJobBuildStep = createAiJobBuildStep()\n\n/**\n * The least time a build's next pass needs: what the step that builds its\n * next unit registers for the job derived for it; a page pass's when the\n * next unit is another plugin's draft, which asks no model.\n */\nexport function aiBuildJobRunMinimumMs(job: AiJob): number {\n const plan = aiConfirmedPlan(job)\n if (!plan) return AI_JOB_PAGE_STEP_MINIMUM_MS\n const units = aiBuildUnits(plan)\n const ordered = aiBuildOrder(units) ?? units\n const ledger = job.items?.length ? job.items : aiBuildInitialLedger(units)\n const unit = aiBuildNextUnit(ordered, ledger)\n if (!unit) return AI_JOB_PAGE_STEP_MINIMUM_MS\n const kind: AiJobKind | null = unit.screen\n ? 'page'\n : unit.creation\n ? (CREATION_JOB_KINDS[unit.creation.kind] ?? null)\n : (AI_BUILD_ITEM_RUNNER_KINDS[unit.op] ?? null)\n if (!kind) return AI_JOB_PAGE_STEP_MINIMUM_MS\n return aiJobStepRunMinimumMs(aiBuildUnitJob(job, unit, { kind, units, ledger, ops: new Map() }))\n}\n\n/** The runner kinds this plugin's own item operations use, for the time a pass needs without the registry. */\nconst AI_BUILD_ITEM_RUNNER_KINDS: Readonly<Record<string, AiJobKind>> = {\n template: 'template',\n campaign: 'campaign',\n workflow: 'workflow',\n}\n\n/**\n * Why a Free build cannot be confirmed: its estimate is more than the owner's\n * Free allowance has left this month. Read off the account month the meter\n * writes, net of give-backs; a read that fails admits, since the reservation\n * still refuses at the wall.\n */\nexport async function aiBuildFreeBalanceRefusal(input: {\n firestore: FirebaseFirestore.Firestore\n org: object | null\n estimate: number\n now: Date\n}): Promise<string | null> {\n const account = freeAssistAccount(input.org as AssistMeteredOrg | null)\n if (!account?.accountUid) return null\n try {\n const month = input.now.toISOString().slice(0, 7)\n const snapshot = await freeAccountUsageRef(input.firestore, account.accountUid, month).get()\n const spent = assistCreditsFromUsd(\n assistSpendAfterReturnsUsd(snapshot.get('estCostUsd'), snapshot.get(ASSIST_RETURNED_USD_FIELD)),\n )\n const left = Math.max(0, FREE_AI_TASTE_CREDITS_PER_MONTH - spent)\n return input.estimate > left\n ? `This plan is estimated at ${input.estimate} credits, and your Free AI credits have ${left} left this month. Ask for less, or upgrade for more.`\n : null\n } catch (error) {\n console.error('ai build free balance read failed', { error })\n return null\n }\n}\n\nexport interface AiBuildAdmissionDeps {\n opsFor?: (context: AiBuildOpsContext) => Promise<AiBuildOps>\n freeBalanceRefusal?: typeof aiBuildFreeBalanceRefusal\n ownerOf?: typeof resolveOrgIdForHost\n now?: () => Date\n}\n\n/**\n * A build is admitted for a site of the job's own org where a page can be\n * built; and confirmed only for a plan it can build here — its shape, its\n * operations as this site has them, and on the Free taste an estimate the\n * month's Free credits still cover.\n */\nexport function createAiBuildJobAdmission(deps: AiBuildAdmissionDeps = {}): AiJobAdmission {\n const opsFor = deps.opsFor ?? ((context) => aiBuildOps(context))\n const freeBalanceRefusal = deps.freeBalanceRefusal ?? aiBuildFreeBalanceRefusal\n const ownerOf = deps.ownerOf ?? resolveOrgIdForHost\n return async (context) => {\n if (!context.hostId) return { status: 400, error: 'Open the site to build on before asking Assist to build' }\n const owner = await ownerOf(context.hostId)\n if (!owner || owner !== context.orgId) return { status: 404, error: 'Unknown site' }\n if (!aiJobStepRunnerFor('page')) return { status: 400, error: 'Building from a request is not available yet.' }\n const plan = context.plan\n if (!plan) return null\n const org = context.org as Partial<AglynOrgBilling> | null\n const freeTaste = aiBuildFreeTaste(org)\n const host = await readHost(context.firestore, context.hostId)\n const ops = await opsFor({ orgId: context.orgId, hostId: context.hostId, org, host, freeTaste })\n const shape = aiBuildPlanShapeRefusal(plan, { freeTaste, ops })\n if (shape) return { status: 400, error: shape }\n if (freeTaste) {\n const refusal = await freeBalanceRefusal({\n firestore: context.firestore,\n org,\n estimate: aiBuildCreditEstimate(plan, { ops }),\n now: deps.now?.() ?? new Date(),\n })\n if (refusal) return { status: 403, error: refusal }\n }\n return null\n }\n}\n\nexport const aiBuildJobAdmission = createAiBuildJobAdmission()\n\n/**\n * Registers the build with the least time a pass needs, the time its next\n * pass needs, the passes its largest plan may take, and its admission.\n */\nexport function registerAiBuildJob(): void {\n registerAiJobStep('build', runAiJobBuildStep, {\n minimumMs: AI_JOB_PAGE_STEP_MINIMUM_MS,\n minimumMsFor: aiBuildJobRunMinimumMs,\n })\n registerAiJobStepPasses('build', AI_BUILD_MAX_PASSES)\n registerAiJobAdmission('build', aiBuildJobAdmission)\n}\n"],"names":["resolveEffectivePlan","resolveOrgIdForHost","AI_BUILD_LIMITS","AI_BUILD_PUBLISH_INPUT","aiArticle","aiBuildCreditEstimate","aiBuildDegradation","aiBuildInitialLedger","aiBuildItemDelivered","aiBuildItemOpen","aiBuildNextUnit","aiBuildOrder","aiBuildPlacedItemLines","aiBuildPlanShapeRefusal","aiBuildUnits","isAiPlanNewRef","AI_SITE_MAX_SECTIONS","FREE_AI_TASTE_CREDITS_PER_MONTH","aiModelForStep","AI_BUILD_UNIT_EMPTY_COPY","aiBuildBuiltRefs","aiUnitErrorRetryable","aiUnitFailure","aiUnitSpend","assistCreditsFromUsd","ASSIST_RETURNED_USD_FIELD","assistSpendAfterReturnsUsd","freeAccountUsageRef","freeAssistAccount","aiBuildOps","aiJobAdmissionRefusal","registerAiJobAdmission","aiOriginJobId","readAiDraftNodes","aiConfirmedPlan","aiUnspentOutcome","AI_JOB_PAGE_STEP_MINIMUM_MS","aiPluginDraftAdmissionRefusal","aiPluginDraftWriter","aiSitePageWritten","aiSiteResolvedRef","AI_JOB_BRIEF_MAX_CHARS","aiJobPublishesBuild","aiPublishGuidedSite","AI_PLANNED_JOB_KINDS","aiJobStepRunMinimumMs","aiJobStepRunnerFor","registerAiJobStep","registerAiJobStepPasses","AI_BUILD_NO_PLAN_COPY","aiBuildUnavailableCopy","noun","AI_BUILD_PAGE_NOT_WRITTEN_COPY","AI_BUILD_MAX_PASSES","units","CREATION_JOB_KINDS","layout","form","component","email","aiBuildUnitJobKind","unit","ops","screen","creation","kind","runnerKind","get","op","aiBuildUnitJobId","job","id","item","$id","slot","aiBuildUnitJob","context","plan","built","ledger","outputs","labels","entry","values","label","brief","trim","unitPlan","reuse","create","screens","items","placedByScreens","Set","flatMap","sections","section","uses","filter","has","duplicateOf","push","name","why","purpose","builtLayout","template","record","dataset","map","ref","title","slug","includes","fields","briefLines","inputs","args","originJobId","steps","join","slice","failed","failure","extra","status","aiBuildFreeTaste","org","Boolean","readHost","firestore","hostId","collection","doc","data","createAiJobBuildStep","deps","runnerFor","writerFor","opsFor","admissionFor","pluginAdmission","readNodes","publish","rows","model","modelFor","ordered","starting","length","init","Map","row","host","orgId","freeTaste","othersOpen","some","one","finish","outcome","extraPages","sitePublish","pageIds","pages","output","resource","now","catch","error","console","jobId","settle","spent","continue","degradation","skip","note","degradedBy","capability","degraded","notes","draftResource","writer","admission","uid","createdBy","refusal","drafts","dependencies","dep","find","content","draftContent","checked","check","ok","ours","reason","message","problems","written","write","versionId","where","runner","derived","ids","stopped","page","undefined","unitJobId","draftId","done","set","runAiJobBuildStep","aiBuildJobRunMinimumMs","AI_BUILD_ITEM_RUNNER_KINDS","campaign","workflow","aiBuildFreeBalanceRefusal","input","account","accountUid","month","toISOString","snapshot","left","Math","max","estimate","createAiBuildJobAdmission","freeBalanceRefusal","ownerOf","owner","shape","Date","aiBuildJobAdmission","registerAiBuildJob","minimumMs","minimumMsFor"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-build-step.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { AglynOrgBilling } from '@aglyn/aglyn/foundation/definitions/org-billing.types'\nimport { resolveEffectivePlan } from '@aglyn/aglyn/app-utils/plan-entitlements'\nimport { resolveOrgIdForHost } from '@aglyn/tenant-data-admin/server/organizations'\nimport {\n AI_BUILD_LIMITS,\n AI_BUILD_PUBLISH_INPUT,\n aiArticle,\n aiBuildCreditEstimate,\n aiBuildDegradation,\n aiBuildInitialLedger,\n aiBuildItemDelivered,\n aiBuildItemOpen,\n aiBuildNextUnit,\n aiBuildOrder,\n aiBuildPlacedItemLines,\n aiBuildPlanShapeRefusal,\n aiBuildUnits,\n type AiBuildOps,\n type AiBuildUnit,\n} from '../model/ai-build-job'\nimport { isAiPlanNewRef, type AiBuildPlanCreateKind } from '../model/ai-build-plan'\nimport type {\n AiJob,\n AiJobItemFailure,\n AiJobItemLedger,\n AiJobKind,\n AiJobOutput,\n AiJobPlan,\n} from '../model/ai-jobs.types'\nimport { AI_SITE_MAX_SECTIONS } from '../model/ai-site-job'\nimport { FREE_AI_TASTE_CREDITS_PER_MONTH } from '../plan-entitlements'\nimport { aiModelForStep } from '../providers/routing'\nimport {\n AI_BUILD_UNIT_EMPTY_COPY,\n aiBuildBuiltRefs,\n aiUnitErrorRetryable,\n aiUnitFailure,\n aiUnitSpend,\n} from './ai-build-unit-outcome'\nimport { assistCreditsFromUsd } from '../usage/assist-credits'\nimport { ASSIST_RETURNED_USD_FIELD, assistSpendAfterReturnsUsd } from '../usage/assist-credit-returns'\nimport { freeAccountUsageRef, freeAssistAccount, type AssistMeteredOrg } from '../usage/assist-free-taste'\nimport { aiBuildOps, type AiBuildOpsContext } from './ai-build-capabilities'\nimport {\n aiJobAdmissionRefusal,\n registerAiJobAdmission,\n type AiJobAdmission,\n type AiJobAdmissionContext,\n} from './ai-job-admission'\nimport { aiOriginJobId } from './ai-job-draft-ids'\nimport { readAiDraftNodes } from './ai-job-drafts'\nimport { aiConfirmedPlan, aiUnspentOutcome } from './ai-job-generation'\nimport { AI_JOB_PAGE_STEP_MINIMUM_MS } from './ai-job-page-budget'\nimport {\n aiPluginDraftAdmissionRefusal,\n aiPluginDraftWriter,\n type AiPluginDraftWriterLookup,\n} from './ai-job-plugin-drafts'\nimport { aiSitePageWritten, aiSiteResolvedRef } from './ai-job-site-step'\nimport { AI_LAYOUT_SITE_PAGES_INPUT, aiLayoutSitePagesOfPlan } from './ai-job-layout-site-pages'\nimport { AI_LAYOUT_FORM_PAGE_INPUT, AI_LAYOUT_LANGUAGE_INPUT, aiLayoutFormPageOfPlan } from './ai-job-page-language'\nimport {\n AI_JOB_BRIEF_MAX_CHARS,\n type AiJobItemOutcome,\n type AiJobStepOutcome,\n type AiJobStepRunner,\n} from './ai-job-text-step'\nimport { aiJobPublishesBuild, aiPublishGuidedSite } from './ai-site-publish'\nimport {\n AI_PLANNED_JOB_KINDS,\n aiJobStepRunMinimumMs,\n aiJobStepRunnerFor,\n registerAiJobStep,\n registerAiJobStepPasses,\n} from './ai-jobs'\n\n/**\n * The build step (AGL-3616): the generation step of a `build` job, run once\n * the member confirmed the plan their request produced.\n *\n * Like the site scaffold it generalizes, it builds nothing itself and asks no\n * model of its own. Each pass takes the next unit in dependency order and\n * hands it to what builds that kind of thing — a page, layout, form,\n * component, email design or template to this plugin's own step, under a job\n * derived for the unit; another plugin's resource to that plugin's writer on\n * the resource-draft seam, with the content the owner's capability derives\n * from the item's arguments. One unit, one pass, one recorded spend.\n *\n * ── Settled item by item ────────────────────────────────────────────────\n *\n * Where a unit stands is read from the job's ITEM LEDGER, never from counting\n * outputs: the machine writes each pass's row in the transaction that\n * records its spend (`recordStep`). A unit that fails is a FAILED ROW, not a\n * failed job: the pass reports it, the machine gives back that item's spend\n * where the failure was ours, and the next pass builds the next unit. What\n * depended on it is built without it, or skipped where it cannot stand\n * without it (`aiBuildDegradation`). The machine ends the job when nothing is\n * left: `done` when anything was delivered, `failed` — its planning given\n * back too — when nothing was.\n *\n * ── Drafts, unless asked and confirmed ───────────────────────────────────\n *\n * Every unit writes the draft its own kind writes, which nothing publishes.\n * Only a build whose request asked to publish AND whose member ticked the\n * plan card's box puts its built pages live, once, on its last pass, through\n * the guided start's publish.\n */\n\n/** What a build answers with no confirmed plan to build. */\nexport const AI_BUILD_NO_PLAN_COPY = 'This build has no confirmed plan to build.'\n\n/** What a unit says when nothing on this site builds its operation any more. */\nexport const aiBuildUnavailableCopy = (noun: string): string =>\n `Not built: ${aiArticle(noun)} cannot be made on this site right now.`\n\n\n/** What a page that was reported built but holds none of its plan says. */\nexport const AI_BUILD_PAGE_NOT_WRITTEN_COPY = 'The page was not written from its plan.'\n\n\n/**\n * The most passes a build's step may take: every unit at a page's worst — its\n * sections and its listing — so a runner that never finishes is still\n * bounded while the largest plan is not. Try again starts the count over.\n */\nexport const AI_BUILD_MAX_PASSES = AI_BUILD_LIMITS.units * (AI_SITE_MAX_SECTIONS + 1)\n\n/** The job kind a creation of the build is built by. */\nconst CREATION_JOB_KINDS: Partial<Record<AiBuildPlanCreateKind, AiJobKind>> = {\n layout: 'layout',\n form: 'form',\n component: 'component',\n email: 'email',\n}\n\n/** The job kind a unit is built by, where an AI runner builds it. */\nexport function aiBuildUnitJobKind(unit: AiBuildUnit, ops: AiBuildOps): AiJobKind | null {\n if (unit.screen) return 'page'\n if (unit.creation) return CREATION_JOB_KINDS[unit.creation.kind] ?? null\n const runnerKind = ops.get(unit.op)?.runnerKind\n return runnerKind ? (runnerKind as AiJobKind) : null\n}\n\n/** The id a unit's job, and so its draft, is named by. */\nexport function aiBuildUnitJobId(job: Pick<AiJob, '$id'>, unit: AiBuildUnit): string {\n return unit.creation?.id ?? unit.screen?.id ?? unit.item?.id ?? `${job.$id}-${unit.slot}`\n}\n\n/**\n * The job a unit is built under (generalizing `aiSiteUnitJob`): this job's\n * org, site, creator and model; the kind that builds the unit; a plan\n * narrowed to the unit, its `new:` references resolved to what was built and\n * the failed ones left out; and a brief that says what the unit is, what it\n * is built without, and which blocks its sections place.\n */\nexport function aiBuildUnitJob(\n job: AiJob,\n unit: AiBuildUnit,\n context: {\n kind: AiJobKind\n units: readonly AiBuildUnit[]\n ledger: readonly AiJobItemLedger[]\n ops: AiBuildOps\n briefLines?: readonly string[]\n },\n): AiJob {\n const plan = job.plan as AiJobPlan\n const built = aiBuildBuiltRefs(context.units, context.ledger, job.outputs ?? [])\n const labels = { ...(plan.labels ?? {}) }\n for (const entry of built.values()) labels[entry.id] = entry.label\n const brief = [job.brief.trim()]\n const unitPlan: AiJobPlan = { ...plan, labels, reuse: [...plan.reuse], create: [], screens: [] }\n delete unitPlan.items\n const placedByScreens = new Set(plan.screens.flatMap((screen) => screen.sections.flatMap((section) => section.uses)))\n if (unit.creation) {\n unitPlan.reuse = plan.reuse.filter((entry) => !placedByScreens.has(entry.id))\n unitPlan.create = [{ ...unit.creation, duplicateOf: aiSiteResolvedRef(unit.creation.duplicateOf, built) }]\n brief.push(`Build the ${unit.creation.kind} “${unit.creation.name}”: ${unit.creation.why}`)\n }\n if (unit.screen) {\n const screen = unit.screen\n for (const entry of built.values()) {\n unitPlan.reuse.push({ kind: entry.kind, id: entry.id, purpose: `the ${entry.kind} this request built` })\n }\n let builtLayout: string | null = null\n for (const entry of built.values()) if (entry.kind === 'layout') builtLayout = builtLayout ?? entry.id\n unitPlan.screens = [\n {\n ...screen,\n // A page whose layout failed renders inside the site's own: the page\n // step falls back to it when the plan names none.\n layout: aiSiteResolvedRef(screen.layout, built) ?? (isAiPlanNewRef(screen.layout) ? builtLayout : null),\n template: aiSiteResolvedRef(screen.template, built),\n duplicateOf: aiSiteResolvedRef(screen.duplicateOf, built),\n record: screen.record\n ? { ...screen.record, dataset: aiSiteResolvedRef(screen.record.dataset, built) ?? screen.record.dataset }\n : null,\n sections: screen.sections.map((section) => ({\n ...section,\n uses: section.uses\n .map((ref) => aiSiteResolvedRef(ref, built))\n .filter((ref): ref is string => !!ref),\n })),\n },\n ]\n brief.push(`Build the page “${screen.title}”, at ${screen.slug}.`)\n brief.push(...aiBuildPlacedItemLines(unit, { units: context.units, ledger: context.ledger, ops: context.ops }))\n }\n const item = unit.item\n if (item) {\n const noun = context.ops.get(item.op)?.noun ?? item.op\n brief.push(`Build the ${noun} “${item.name}”${item.why ? `: ${item.why}` : '.'}`)\n // A planned kind's step builds the plan's first creation of its kind.\n unitPlan.create = AI_PLANNED_JOB_KINDS.includes(context.kind)\n ? [{ kind: context.kind as AiBuildPlanCreateKind, name: item.name, why: item.why, duplicateOf: null, fields: [], ...(item.id ? { id: item.id } : {}) }]\n : []\n }\n brief.push(...(context.briefLines ?? []))\n const inputs: Record<string, unknown> = {\n ...job.inputs,\n ...(item?.args ?? {}),\n originJobId: aiOriginJobId(job),\n }\n delete inputs[AI_BUILD_PUBLISH_INPUT]\n // A build's pages and layouts are designed in the layout language and\n // compiled (AGL-3660), told the pages the build plans and where its form is.\n if (context.kind === 'page' || context.kind === 'layout') {\n inputs[AI_LAYOUT_LANGUAGE_INPUT] = true\n const pages = aiLayoutSitePagesOfPlan(plan.screens)\n if (pages.length) inputs[AI_LAYOUT_SITE_PAGES_INPUT] = pages\n const formPage = aiLayoutFormPageOfPlan(plan)\n if (formPage) inputs[AI_LAYOUT_FORM_PAGE_INPUT] = formPage\n }\n return {\n ...job,\n $id: aiBuildUnitJobId(job, unit),\n kind: context.kind,\n steps: [],\n outputs: [],\n items: null,\n plan: item && !AI_PLANNED_JOB_KINDS.includes(context.kind) ? null : unitPlan,\n inputs,\n brief: brief.join('\\n').slice(0, AI_JOB_BRIEF_MAX_CHARS),\n }\n}\n\n/** The unit's failure, for its row: ours or not, why, in customer-safe words. */\nfunction failed(\n slot: string,\n failure: AiJobItemFailure,\n extra: Partial<AiJobItemOutcome> = {},\n): AiJobItemOutcome {\n return { slot, status: 'failed', failure, ...extra }\n}\n\nexport interface AiJobBuildStepDeps {\n /** The runner registry; specs hand in fakes. */\n runnerFor?: typeof aiJobStepRunnerFor\n /** The draft writer registry; specs hand in fakes. */\n writerFor?: AiPluginDraftWriterLookup\n /** The operations this site may use; the registry and its gates otherwise. */\n opsFor?: (context: AiBuildOpsContext) => Promise<AiBuildOps>\n /** A runner kind's own admission, asked before its item runs; the registry's otherwise. */\n admissionFor?: typeof aiJobAdmissionRefusal\n /** Another plugin's admission for its writer; the seam's otherwise. */\n pluginAdmission?: typeof aiPluginDraftAdmissionRefusal\n /** The draft reader a built page is checked through. */\n readNodes?: typeof readAiDraftNodes\n /** The publish a build asked for and confirmed. */\n publish?: typeof aiPublishGuidedSite\n}\n\n/** Whether a build's workspace spends the Free taste. */\nexport function aiBuildFreeTaste(org: object | null | undefined): boolean {\n return Boolean(org) && resolveEffectivePlan(org as never) === 'free'\n}\n\n/** The site document a gate reads, or `null`. */\nasync function readHost(firestore: FirebaseFirestore.Firestore, hostId: string | null) {\n if (!hostId) return null\n return ((await firestore.collection('hosts').doc(hostId).get()).data() ?? null) as Record<string, unknown> | null\n}\n\nexport function createAiJobBuildStep(deps: AiJobBuildStepDeps = {}): AiJobStepRunner {\n const runnerFor = deps.runnerFor ?? aiJobStepRunnerFor\n const writerFor = deps.writerFor ?? aiPluginDraftWriter\n const opsFor = deps.opsFor ?? ((context) => aiBuildOps(context))\n const admissionFor = deps.admissionFor ?? aiJobAdmissionRefusal\n const pluginAdmission = deps.pluginAdmission ?? aiPluginDraftAdmissionRefusal\n const readNodes = deps.readNodes ?? readAiDraftNodes\n const publish = deps.publish ?? aiPublishGuidedSite\n\n return async (context): Promise<AiJobStepOutcome> => {\n const { job, firestore } = context\n const model = context.modelFor?.('job.plan') ?? aiModelForStep('job.plan')\n const plan = aiConfirmedPlan(job)\n if (!plan) return { ...aiUnspentOutcome(model), failure: AI_BUILD_NO_PLAN_COPY }\n const org = (context.org ?? null) as Partial<AglynOrgBilling> | null\n const units = aiBuildUnits(plan)\n const ordered = aiBuildOrder(units) ?? units\n const starting = !job.items?.length\n const ledger = starting ? aiBuildInitialLedger(units) : (job.items as AiJobItemLedger[])\n const init = starting ? { items: ledger } : {}\n const unit = aiBuildNextUnit(ordered, ledger)\n const rows = new Map(ledger.map((row) => [row.slot, row]))\n const host = await readHost(firestore, job.hostId ?? null)\n const ops = await opsFor({\n orgId: job.orgId,\n hostId: job.hostId ?? null,\n org,\n host,\n freeTaste: aiBuildFreeTaste(org),\n })\n\n /** Whether units other than this one are still to build. */\n const othersOpen = (slot: string) =>\n ordered.some((one) => one.slot !== slot && aiBuildItemOpen(rows.get(one.slot) ?? { status: 'pending' }))\n\n /** The build's last pass puts its pages live, where it was asked and confirmed. */\n const finish = async (outcome: AiJobStepOutcome, extraPages: readonly AiJobOutput[] = []): Promise<AiJobStepOutcome> => {\n if (!aiJobPublishesBuild(job) || job.sitePublish || !job.hostId) return outcome\n const pageIds = new Set(\n units\n .filter((one) => one.screen && aiBuildItemDelivered(rows.get(one.slot) ?? { status: 'pending' }))\n .flatMap((one) => rows.get(one.slot)?.outputs ?? []),\n )\n const pages = [\n ...(job.outputs ?? []).filter((output) => output.resource === 'screen' && pageIds.has(output.id)),\n ...extraPages,\n ]\n if (!pages.length) return outcome\n const sitePublish = await publish(firestore, { job, outputs: pages, now: context.now }).catch((error: unknown) => {\n console.error('ai build publish threw', { orgId: job.orgId, jobId: job.$id, error })\n return null\n })\n return sitePublish ? { ...outcome, sitePublish } : outcome\n }\n\n if (!unit) return finish({ ...aiUnspentOutcome(model), ...init })\n\n const settle = (item: AiJobItemOutcome, spent: AiJobStepOutcome = aiUnspentOutcome(model)): AiJobStepOutcome => ({\n ...spent,\n ...init,\n item,\n ...(item.status === 'running' || othersOpen(unit.slot) ? { continue: true } : {}),\n })\n\n // What its dependencies came to.\n const degradation = aiBuildDegradation(unit, { units, ledger, ops })\n if (degradation.skip) {\n return settle({ slot: unit.slot, status: 'skipped', note: degradation.skip, degradedBy: degradation.degradedBy })\n }\n const capability = ops.get(unit.op)\n const noun = capability?.noun ?? unit.op\n if (!capability) return settle({ slot: unit.slot, status: 'skipped', note: aiBuildUnavailableCopy(noun) })\n const degraded = degradation.degradedBy.length > 0\n const note = degradation.notes.length ? degradation.notes.join(' ') : null\n\n // Another plugin's resource: its writer, with the content its capability derives.\n if (capability.draftResource) {\n const item = unit.item\n const writer = writerFor(capability.draftResource)\n if (!item || !writer || !job.hostId) {\n return settle({ slot: unit.slot, status: 'skipped', note: aiBuildUnavailableCopy(noun) })\n }\n const admission: AiJobAdmissionContext = {\n firestore,\n orgId: job.orgId,\n hostId: job.hostId,\n inputs: {},\n org,\n uid: job.createdBy,\n }\n const refusal = await pluginAdmission(admission, {\n kind: 'build',\n drafts: [{ resource: capability.draftResource, label: capability.noun }],\n writerFor,\n })\n if (refusal) return settle({ slot: unit.slot, status: 'skipped', note: `Not built: ${refusal.error}` })\n const dependencies: Record<string, { op: string; id: string }> = {}\n for (const slot of unit.deps) {\n const dep = units.find((one) => one.slot === slot)\n const row = rows.get(slot)\n if (!dep || !row || !aiBuildItemDelivered(row) || !row.outputs.length) continue\n dependencies[`new:${dep.label}`] = { op: dep.op, id: row.outputs[0] }\n }\n const content = capability.draftContent?.({ name: item.name, args: item.args }, { hostId: job.hostId, dependencies }) ?? item.args\n const checked = writer.check(content, { hostId: job.hostId })\n if (checked.ok === false) {\n return settle(\n failed(unit.slot, { ours: true, reason: 'step-failure', message: checked.problems[0] ?? AI_BUILD_UNIT_EMPTY_COPY }),\n )\n }\n const id = aiBuildUnitJobId(job, unit)\n const written = await writer.write({\n orgId: job.orgId,\n hostId: job.hostId,\n uid: job.createdBy,\n org,\n now: context.now,\n id,\n name: item.name,\n content,\n })\n if (written.ok === false) {\n return settle(failed(unit.slot, { ours: false, reason: 'review', message: written.error }))\n }\n const output: AiJobOutput = {\n resource: 'draft',\n id: written.id,\n versionId: written.versionId,\n hostId: job.hostId,\n label: written.name,\n draftResource: capability.draftResource,\n note: `A draft, in ${capability.where}.`,\n }\n return settle(\n {\n slot: unit.slot,\n status: degraded ? 'degraded' : 'succeeded',\n outputs: [written.id],\n note,\n ...(degraded ? { degradedBy: degradation.degradedBy } : {}),\n },\n { ...aiUnspentOutcome(model), outputs: [output] },\n )\n }\n\n // This plugin's own runner, under a job derived for the unit.\n const kind = aiBuildUnitJobKind(unit, ops)\n const runner = kind ? runnerFor(kind) : null\n if (!kind || !runner) return settle({ slot: unit.slot, status: 'skipped', note: aiBuildUnavailableCopy(noun) })\n const derived = aiBuildUnitJob(job, unit, { kind, units, ledger, ops, briefLines: degradation.briefLines })\n // An item's kind is asked its own admission first (a campaign's plan,\n // a template's subject), once, before its first pass.\n if (unit.item && rows.get(unit.slot)?.status !== 'running') {\n const refusal = await admissionFor(kind, {\n firestore,\n orgId: job.orgId,\n hostId: job.hostId ?? null,\n inputs: derived.inputs,\n org,\n uid: job.createdBy,\n }).catch((error: unknown) => {\n console.error('ai build item admission failed', { orgId: job.orgId, jobId: job.$id, slot: unit.slot, error })\n return { status: 403 as const, error: AI_BUILD_UNIT_EMPTY_COPY }\n })\n if (refusal) return settle({ slot: unit.slot, status: 'skipped', note: `Not built: ${refusal.error}` })\n }\n\n let outcome: AiJobStepOutcome\n try {\n outcome = await runner({ ...context, job: derived })\n } catch (error) {\n // A provider that will answer later is the machine's to retry; anything\n // else is this item's failure, and the build goes on.\n if (aiUnitErrorRetryable(error)) throw error\n console.error('ai build unit threw', { orgId: job.orgId, jobId: job.$id, slot: unit.slot, error })\n return settle(failed(unit.slot, { ours: true, reason: 'provider', message: AI_BUILD_UNIT_EMPTY_COPY }))\n }\n const spent = aiUnitSpend(outcome)\n const ids = outcome.outputs.map((output) => output.id)\n const stopped = aiUnitFailure(unit.slot, outcome)\n if (stopped) return settle(stopped, spent)\n // A page counts as built only when its plan's sections are in it (AGL-3596).\n if (unit.screen && job.hostId) {\n const page = outcome.outputs.find((output) => output.resource === 'screen')\n const written =\n page !== undefined &&\n (await aiSitePageWritten(\n firestore,\n { hostId: job.hostId, unitJobId: derived.$id, screen: unit.screen, draftId: page.id },\n readNodes,\n ))\n if (!written) {\n return settle(\n failed(unit.slot, { ours: true, reason: 'step-failure', message: AI_BUILD_PAGE_NOT_WRITTEN_COPY }),\n { ...spent, outputs: outcome.outputs.filter((output) => output.resource !== 'screen') },\n )\n }\n }\n const done = settle(\n {\n slot: unit.slot,\n status: degraded ? 'degraded' : 'succeeded',\n outputs: ids,\n note,\n ...(degraded ? { degradedBy: degradation.degradedBy } : {}),\n },\n spent,\n )\n if (done.continue) return done\n // The last unit: what was built goes live where it was asked and confirmed.\n rows.set(unit.slot, { ...(rows.get(unit.slot) as AiJobItemLedger), status: degraded ? 'degraded' : 'succeeded', outputs: ids })\n return finish(done, unit.screen ? outcome.outputs.filter((output) => output.resource === 'screen') : [])\n }\n}\n\nexport const runAiJobBuildStep = createAiJobBuildStep()\n\n/**\n * The least time a build's next pass needs: what the step that builds its\n * next unit registers for the job derived for it; a page pass's when the\n * next unit is another plugin's draft, which asks no model.\n */\nexport function aiBuildJobRunMinimumMs(job: AiJob): number {\n const plan = aiConfirmedPlan(job)\n if (!plan) return AI_JOB_PAGE_STEP_MINIMUM_MS\n const units = aiBuildUnits(plan)\n const ordered = aiBuildOrder(units) ?? units\n const ledger = job.items?.length ? job.items : aiBuildInitialLedger(units)\n const unit = aiBuildNextUnit(ordered, ledger)\n if (!unit) return AI_JOB_PAGE_STEP_MINIMUM_MS\n const kind: AiJobKind | null = unit.screen\n ? 'page'\n : unit.creation\n ? (CREATION_JOB_KINDS[unit.creation.kind] ?? null)\n : (AI_BUILD_ITEM_RUNNER_KINDS[unit.op] ?? null)\n if (!kind) return AI_JOB_PAGE_STEP_MINIMUM_MS\n return aiJobStepRunMinimumMs(aiBuildUnitJob(job, unit, { kind, units, ledger, ops: new Map() }))\n}\n\n/** The runner kinds this plugin's own item operations use, for the time a pass needs without the registry. */\nconst AI_BUILD_ITEM_RUNNER_KINDS: Readonly<Record<string, AiJobKind>> = {\n template: 'template',\n campaign: 'campaign',\n workflow: 'workflow',\n}\n\n/**\n * Why a Free build cannot be confirmed: its estimate is more than the owner's\n * Free allowance has left this month. Read off the account month the meter\n * writes, net of give-backs; a read that fails admits, since the reservation\n * still refuses at the wall.\n */\nexport async function aiBuildFreeBalanceRefusal(input: {\n firestore: FirebaseFirestore.Firestore\n org: object | null\n estimate: number\n now: Date\n}): Promise<string | null> {\n const account = freeAssistAccount(input.org as AssistMeteredOrg | null)\n if (!account?.accountUid) return null\n try {\n const month = input.now.toISOString().slice(0, 7)\n const snapshot = await freeAccountUsageRef(input.firestore, account.accountUid, month).get()\n const spent = assistCreditsFromUsd(\n assistSpendAfterReturnsUsd(snapshot.get('estCostUsd'), snapshot.get(ASSIST_RETURNED_USD_FIELD)),\n )\n const left = Math.max(0, FREE_AI_TASTE_CREDITS_PER_MONTH - spent)\n return input.estimate > left\n ? `This plan is estimated at ${input.estimate} credits, and your Free AI credits have ${left} left this month. Ask for less, or upgrade for more.`\n : null\n } catch (error) {\n console.error('ai build free balance read failed', { error })\n return null\n }\n}\n\nexport interface AiBuildAdmissionDeps {\n opsFor?: (context: AiBuildOpsContext) => Promise<AiBuildOps>\n freeBalanceRefusal?: typeof aiBuildFreeBalanceRefusal\n ownerOf?: typeof resolveOrgIdForHost\n now?: () => Date\n}\n\n/**\n * A build is admitted for a site of the job's own org where a page can be\n * built; and confirmed only for a plan it can build here — its shape, its\n * operations as this site has them, and on the Free taste an estimate the\n * month's Free credits still cover.\n */\nexport function createAiBuildJobAdmission(deps: AiBuildAdmissionDeps = {}): AiJobAdmission {\n const opsFor = deps.opsFor ?? ((context) => aiBuildOps(context))\n const freeBalanceRefusal = deps.freeBalanceRefusal ?? aiBuildFreeBalanceRefusal\n const ownerOf = deps.ownerOf ?? resolveOrgIdForHost\n return async (context) => {\n if (!context.hostId) return { status: 400, error: 'Open the site to build on before asking Assist to build' }\n const owner = await ownerOf(context.hostId)\n if (!owner || owner !== context.orgId) return { status: 404, error: 'Unknown site' }\n if (!aiJobStepRunnerFor('page')) return { status: 400, error: 'Building from a request is not available yet.' }\n const plan = context.plan\n if (!plan) return null\n const org = context.org as Partial<AglynOrgBilling> | null\n const freeTaste = aiBuildFreeTaste(org)\n const host = await readHost(context.firestore, context.hostId)\n const ops = await opsFor({ orgId: context.orgId, hostId: context.hostId, org, host, freeTaste })\n const shape = aiBuildPlanShapeRefusal(plan, { freeTaste, ops })\n if (shape) return { status: 400, error: shape }\n if (freeTaste) {\n const refusal = await freeBalanceRefusal({\n firestore: context.firestore,\n org,\n estimate: aiBuildCreditEstimate(plan, { ops }),\n now: deps.now?.() ?? new Date(),\n })\n if (refusal) return { status: 403, error: refusal }\n }\n return null\n }\n}\n\nexport const aiBuildJobAdmission = createAiBuildJobAdmission()\n\n/**\n * Registers the build with the least time a pass needs, the time its next\n * pass needs, the passes its largest plan may take, and its admission.\n */\nexport function registerAiBuildJob(): void {\n registerAiJobStep('build', runAiJobBuildStep, {\n minimumMs: AI_JOB_PAGE_STEP_MINIMUM_MS,\n minimumMsFor: aiBuildJobRunMinimumMs,\n })\n registerAiJobStepPasses('build', AI_BUILD_MAX_PASSES)\n registerAiJobAdmission('build', aiBuildJobAdmission)\n}\n"],"names":["resolveEffectivePlan","resolveOrgIdForHost","AI_BUILD_LIMITS","AI_BUILD_PUBLISH_INPUT","aiArticle","aiBuildCreditEstimate","aiBuildDegradation","aiBuildInitialLedger","aiBuildItemDelivered","aiBuildItemOpen","aiBuildNextUnit","aiBuildOrder","aiBuildPlacedItemLines","aiBuildPlanShapeRefusal","aiBuildUnits","isAiPlanNewRef","AI_SITE_MAX_SECTIONS","FREE_AI_TASTE_CREDITS_PER_MONTH","aiModelForStep","AI_BUILD_UNIT_EMPTY_COPY","aiBuildBuiltRefs","aiUnitErrorRetryable","aiUnitFailure","aiUnitSpend","assistCreditsFromUsd","ASSIST_RETURNED_USD_FIELD","assistSpendAfterReturnsUsd","freeAccountUsageRef","freeAssistAccount","aiBuildOps","aiJobAdmissionRefusal","registerAiJobAdmission","aiOriginJobId","readAiDraftNodes","aiConfirmedPlan","aiUnspentOutcome","AI_JOB_PAGE_STEP_MINIMUM_MS","aiPluginDraftAdmissionRefusal","aiPluginDraftWriter","aiSitePageWritten","aiSiteResolvedRef","AI_LAYOUT_SITE_PAGES_INPUT","aiLayoutSitePagesOfPlan","AI_LAYOUT_FORM_PAGE_INPUT","AI_LAYOUT_LANGUAGE_INPUT","aiLayoutFormPageOfPlan","AI_JOB_BRIEF_MAX_CHARS","aiJobPublishesBuild","aiPublishGuidedSite","AI_PLANNED_JOB_KINDS","aiJobStepRunMinimumMs","aiJobStepRunnerFor","registerAiJobStep","registerAiJobStepPasses","AI_BUILD_NO_PLAN_COPY","aiBuildUnavailableCopy","noun","AI_BUILD_PAGE_NOT_WRITTEN_COPY","AI_BUILD_MAX_PASSES","units","CREATION_JOB_KINDS","layout","form","component","email","aiBuildUnitJobKind","unit","ops","screen","creation","kind","runnerKind","get","op","aiBuildUnitJobId","job","id","item","$id","slot","aiBuildUnitJob","context","plan","built","ledger","outputs","labels","entry","values","label","brief","trim","unitPlan","reuse","create","screens","items","placedByScreens","Set","flatMap","sections","section","uses","filter","has","duplicateOf","push","name","why","purpose","builtLayout","template","record","dataset","map","ref","title","slug","includes","fields","briefLines","inputs","args","originJobId","pages","length","formPage","steps","join","slice","failed","failure","extra","status","aiBuildFreeTaste","org","Boolean","readHost","firestore","hostId","collection","doc","data","createAiJobBuildStep","deps","runnerFor","writerFor","opsFor","admissionFor","pluginAdmission","readNodes","publish","rows","model","modelFor","ordered","starting","init","Map","row","host","orgId","freeTaste","othersOpen","some","one","finish","outcome","extraPages","sitePublish","pageIds","output","resource","now","catch","error","console","jobId","settle","spent","continue","degradation","skip","note","degradedBy","capability","degraded","notes","draftResource","writer","admission","uid","createdBy","refusal","drafts","dependencies","dep","find","content","draftContent","checked","check","ok","ours","reason","message","problems","written","write","versionId","where","runner","derived","ids","stopped","page","undefined","unitJobId","draftId","done","set","runAiJobBuildStep","aiBuildJobRunMinimumMs","AI_BUILD_ITEM_RUNNER_KINDS","campaign","workflow","aiBuildFreeBalanceRefusal","input","account","accountUid","month","toISOString","snapshot","left","Math","max","estimate","createAiBuildJobAdmission","freeBalanceRefusal","ownerOf","owner","shape","Date","aiBuildJobAdmission","registerAiBuildJob","minimumMs","minimumMsFor"],"mappings":"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"}
|
|
@@ -100,12 +100,20 @@ export declare function aiDoctrineReviewOutline(answer: Record<string, unknown>
|
|
|
100
100
|
* entries it names, and an outline of those parts of the answer it was found
|
|
101
101
|
* in (AGL-3078), so why a job stopped can be read back from the job once the
|
|
102
102
|
* answer itself is gone.
|
|
103
|
+
*
|
|
104
|
+
* The doctrine's own sentence ("Rule 12 (Responsive by the theme's
|
|
105
|
+
* breakpoints): ...") is never what the customer reads (AGL-3596): it rides
|
|
106
|
+
* as `detail`, for staff, and `message` is the plain refusal, a page's when
|
|
107
|
+
* `page` says the refused thing is one, followed by `then` when given.
|
|
103
108
|
*/
|
|
104
109
|
export declare function aiDoctrineReview(result: {
|
|
105
110
|
message: string;
|
|
106
111
|
violations: readonly AiDoctrineViolation[];
|
|
107
112
|
/** The refused answer, whose nodes the findings name by id; outlined, never kept. */
|
|
108
113
|
answer?: Record<string, unknown> | null;
|
|
114
|
+
}, options?: {
|
|
115
|
+
page?: boolean;
|
|
116
|
+
then?: string;
|
|
109
117
|
}): AiJobReview;
|
|
110
118
|
/** The review a step stops its job for when the site cannot take the draft. */
|
|
111
119
|
export declare function aiLimitReview(message: string): AiJobReview;
|
|
@@ -17,6 +17,7 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
|
17
17
|
*/ import { REUSABLE_INSTANCE_COMPONENT_ID, REUSABLE_INSTANCE_PROP_VALUES_KEY } from "@aglyn/aglyn/app-utils/reusable-component-keys";
|
|
18
18
|
import { CANVAS_ROOT_ELEMENT_ID } from "@aglyn/aglyn/foundation/constants/canvas";
|
|
19
19
|
import { AI_BUILD_PLAN_EMBED_HOST_NAMES, AI_PLAN_NEW_REF_PREFIX, isAiPlanNewRef } from "../model/ai-build-plan.js";
|
|
20
|
+
import { AI_DOCTRINE_REFUSED_COPY, AI_DOCTRINE_REFUSED_PAGE_COPY, aiIsDoctrineRuleText } from "../model/ai-job-failure-copy.js";
|
|
20
21
|
import { isAiUnreadToken } from "../model/ai-template-subjects.js";
|
|
21
22
|
import { aiAnswerTree } from "../runtime/ai-doctrine.js";
|
|
22
23
|
import { aiBracketedFacts, aiTreeCopy, walkTree } from "../runtime/ai-doctrine-validators.js";
|
|
@@ -327,11 +328,21 @@ function elementOf(node) {
|
|
|
327
328
|
* entries it names, and an outline of those parts of the answer it was found
|
|
328
329
|
* in (AGL-3078), so why a job stopped can be read back from the job once the
|
|
329
330
|
* answer itself is gone.
|
|
330
|
-
|
|
331
|
+
*
|
|
332
|
+
* The doctrine's own sentence ("Rule 12 (Responsive by the theme's
|
|
333
|
+
* breakpoints): ...") is never what the customer reads (AGL-3596): it rides
|
|
334
|
+
* as `detail`, for staff, and `message` is the plain refusal, a page's when
|
|
335
|
+
* `page` says the refused thing is one, followed by `then` when given.
|
|
336
|
+
*/ export function aiDoctrineReview(result, options = {}) {
|
|
331
337
|
const outline = aiDoctrineReviewOutline(result.answer, result.violations);
|
|
338
|
+
const ruleText = aiIsDoctrineRuleText(result.message);
|
|
339
|
+
const plain = ruleText ? options.page ? AI_DOCTRINE_REFUSED_PAGE_COPY : AI_DOCTRINE_REFUSED_COPY : result.message;
|
|
332
340
|
return _extends({
|
|
333
341
|
reason: 'doctrine',
|
|
334
|
-
message:
|
|
342
|
+
message: options.then ? `${plain} ${options.then}` : plain
|
|
343
|
+
}, ruleText ? {
|
|
344
|
+
detail: result.message
|
|
345
|
+
} : {}, {
|
|
335
346
|
findings: result.violations.map(({ rule, code, message, nodeIds, paths })=>_extends({
|
|
336
347
|
rule,
|
|
337
348
|
code,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-generation.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 REUSABLE_INSTANCE_COMPONENT_ID,\n REUSABLE_INSTANCE_PROP_VALUES_KEY,\n} from '@aglyn/aglyn/app-utils/reusable-component-keys'\nimport { CANVAS_ROOT_ELEMENT_ID } from '@aglyn/aglyn/foundation/constants/canvas'\nimport {\n AI_BUILD_PLAN_EMBED_HOST_NAMES,\n AI_PLAN_NEW_REF_PREFIX,\n isAiPlanNewRef,\n type AiBuildPlanCreate,\n type AiBuildPlanCreateKind,\n} from '../model/ai-build-plan'\nimport type {\n AiJob,\n AiJobOutput,\n AiJobPlan,\n AiJobReview,\n AiJobReviewOutlineNode,\n} from '../model/ai-jobs.types'\nimport type { AiSiteInventory } from '../model/ai-site-inventory'\nimport { isAiUnreadToken } from '../model/ai-template-subjects'\nimport { aiAnswerTree, type AiGenerationSpend, type AiValidatedTree } from '../runtime/ai-doctrine'\nimport {\n aiBracketedFacts,\n aiTreeCopy,\n walkTree,\n type AiDoctrineNode,\n type AiDoctrineTree,\n type AiDoctrineViolation,\n} from '../runtime/ai-doctrine-validators'\nimport { AI_INSTANCE_REF_PROP } from '../runtime/ai-node-tree'\nimport type { AssistTokenUsage } from '../usage/assist-usage'\nimport { AI_JOB_BRIEF_MAX_CHARS, type AiJobStepOutcome } from './ai-job-text-step'\n\n/**\n * What the generation steps share (AGL-2909): reading the plan a member\n * confirmed, stating it to the model as references, and turning what a\n * generation spent or refused into the outcome the machine records.\n *\n * A generation step runs after its job's plan step, once a member confirmed\n * the plan, so the plan is part of what it executes: the records it reuses,\n * and the creation it builds. The plan travels as references — ids, names,\n * reasons — never as a document (AGL-2937).\n */\n\nexport const AI_JOB_ZERO_USAGE: AssistTokenUsage = {\n inputTokens: 0,\n outputTokens: 0,\n cacheReadTokens: 0,\n cacheWriteTokens: 0,\n}\n\n/** The job's plan once a member confirmed it; `null` before that, or for a job that never planned. */\nexport function aiConfirmedPlan(job: Pick<AiJob, 'plan'>): AiJobPlan | null {\n return job.plan?.status === 'confirmed' ? job.plan : null\n}\n\n/** The creation of one kind the plan names; the first when it names several. */\nexport function aiPlanCreation(\n plan: AiJobPlan | null,\n kind: AiBuildPlanCreateKind,\n): AiBuildPlanCreate | null {\n return plan?.create.find((entry) => entry.kind === kind) ?? null\n}\n\n/** A plan reference as the model reads it: the id with the name it was planned under. */\nfunction referenceOf(plan: AiJobPlan, ref: string): string {\n const label = plan.labels?.[ref]\n return label ? `${ref} (\"${label}\")` : ref\n}\n\n/** The user turn's opening: the brief, cut at the ceiling every job step keeps. */\nexport function aiJobBriefLine(job: Pick<AiJob, 'brief'>): string {\n return `Brief: ${job.brief.slice(0, AI_JOB_BRIEF_MAX_CHARS)}`\n}\n\n/** The confirmed plan as reference lines; none when there is no confirmed plan. */\nexport function aiPlanReferenceLines(plan: AiJobPlan | null): string[] {\n if (!plan) return []\n const lines = ['Confirmed plan:']\n for (const entry of plan.reuse) {\n lines.push(`- reuse the ${entry.kind === 'screen' ? 'page' : entry.kind} ${referenceOf(plan, entry.id)}: ${entry.purpose}`)\n }\n for (const entry of plan.create) {\n const from = entry.duplicateOf ? `, from a copy of ${referenceOf(plan, entry.duplicateOf)}` : ''\n // A template's slug or timestamp is filled, never shown, so a build told\n // it is a field prints it (AGL-3143 §16).\n const shown = entry.kind === 'template' ? entry.fields.filter((field) => !isAiUnreadToken(field)) : entry.fields\n const fields = shown.length ? ` Fields: ${shown.join(', ')}.` : ''\n lines.push(`- create the ${entry.kind} \"${entry.name}\"${from}: ${entry.why}${fields}`)\n }\n for (const screen of plan.screens) {\n const sections = screen.sections.map((section) => section.name).join(', ')\n lines.push(`- the page \"${screen.title}\" at ${screen.slug}${sections ? `: ${sections}` : ''}`)\n }\n // The one place a Video embed is admitted (AGL-3433): where the plan lists it, at the brief's link or none.\n for (const embed of plan.embeds ?? []) {\n const where = isAiPlanNewRef(embed.where)\n ? `the component \"${embed.where.slice(AI_PLAN_NEW_REF_PREFIX.length)}\"`\n : embed.where\n const link = embed.url ? `, url ${embed.url}` : ', url left empty for the site owner to paste'\n lines.push(`- embed a ${AI_BUILD_PLAN_EMBED_HOST_NAMES[embed.host]} player (videoEmbed) on ${where}${link}`)\n }\n return lines.length > 1 ? lines : []\n}\n\n/**\n * The most of a source's tree a build is shown, as JSON characters: about\n * 3,000 tokens, a whole layout or page template, never a site (AGL-3143 §14).\n */\nexport const AI_COPY_SOURCE_MAX_CHARS = 12_000\n\n/**\n * The record a plan's copy starts from, as the build that replaces the copy\n * is shown it (AGL-3143 §14): its tree in the node-map shape the tool takes,\n * with the one instruction that makes it a start rather than an example.\n * Empty when there is no tree to show or it is too large to show whole, and\n * the build then starts from the brief and the plan.\n *\n * A copy whose source cannot keep what the plan promised is never minted,\n * because the review after it would refuse it with certainty and the member\n * could only try the same copy again. Measured on 2026-09-22 (job\n * `Pq3pgvMH0D`): \"copy the interior layout and add a sidebar\" could only end\n * in review. Building from the source keeps what the plan does not change —\n * the firm's header, its footer, their copy — which a build from the brief\n * alone would draw again from nothing.\n */\nexport function aiCopySourceLines(noun: string, label: string, nodes: Readonly<Record<string, unknown>> | null): string[] {\n if (!nodes || !isRecordNode(nodes[CANVAS_ROOT_ELEMENT_ID])) return []\n const shaped: Record<string, Record<string, unknown>> = {}\n for (const [id, node] of Object.entries(nodes)) {\n if (!isRecordNode(node)) continue\n const { componentId, props, sx, nodes: children } = node\n shaped[id] = {\n componentId,\n ...(isRecordNode(props) && Object.keys(props).length ? { props } : {}),\n ...(isRecordNode(sx) && Object.keys(sx).length ? { sx } : {}),\n ...(Array.isArray(children) && children.length ? { nodes: children } : {}),\n }\n }\n const tree = JSON.stringify({ rootId: CANVAS_ROOT_ELEMENT_ID, nodes: shaped })\n if (tree.length > AI_COPY_SOURCE_MAX_CHARS) return []\n return [\n `The plan starts from a copy of the ${noun} \"${label}\", which lacks part of what the plan gives it. Build from it: keep every part the plan does not change, and add what the plan adds. Its tree, in the node-map shape the tool takes:`,\n tree,\n ]\n}\n\nfunction isRecordNode(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/** The reusable components a tree places, by the id each instance names. */\nexport function aiPlacedComponentIds(tree: Pick<AiValidatedTree, 'rootId' | 'nodes'>): Set<string> {\n const placed = new Set<string>()\n const nodes = tree.nodes as unknown as Record<string, AiDoctrineNode>\n for (const { node } of walkTree({ rootId: tree.rootId, nodes })) {\n const ref = node.props?.[AI_INSTANCE_REF_PROP]\n if (node.componentId === REUSABLE_INSTANCE_COMPONENT_ID && typeof ref === 'string') {\n placed.add(ref)\n }\n }\n return placed\n}\n\n/**\n * Rule 7 on a generated tree: every component the confirmed plan reuses is\n * placed. `noun` names what the tree builds, in the finding.\n */\nexport function aiPlanReuseViolations(\n inventory: AiSiteInventory | null,\n plan: AiJobPlan | null,\n placed: ReadonlySet<string>,\n noun: string,\n): AiDoctrineViolation[] {\n const names = new Map((inventory?.components ?? []).map((component) => [component.id, component.name]))\n const reused = [\n ...new Set(\n (plan?.reuse ?? []).filter((entry) => entry.kind === 'component').map((entry) => entry.id),\n ),\n ]\n const missing = reused.filter((id) => !placed.has(id))\n if (!missing.length) return []\n const listed = missing.map((id) => `\"${names.get(id) ?? id}\"`).join(', ')\n return [\n {\n rule: 7,\n code: 'plan-reuse-not-placed',\n message: `The confirmed plan reuses ${listed}, and the ${noun} does not place ${\n missing.length === 1 ? 'it' : 'them'\n }. Place every component the plan reuses.`,\n },\n ]\n}\n\n/** What a generation spent, as the step's outcome carries it for the meter. */\nexport function aiGenerationSpent(\n spend: AiGenerationSpend,\n): Pick<AiJobStepOutcome, 'outputs' | 'usage' | 'estCostUsd' | 'model' | 'stopReason'> {\n return {\n outputs: [],\n usage: spend.usage,\n estCostUsd: spend.estCostUsd,\n model: spend.model,\n stopReason: spend.stopReason,\n }\n}\n\n/** The most node ids, or plan paths, one finding keeps on its review (AGL-3078). */\nexport const AI_JOB_REVIEW_REFS_MAX = 24\n/** The longest node id or plan path a review keeps, in characters. */\nexport const AI_JOB_REVIEW_REF_MAX_CHARS = 64\n/** How far below a node a finding names its outline goes: the node, its children and theirs. */\nexport const AI_JOB_REVIEW_OUTLINE_MAX_DEPTH = 2\n/** The most nodes a review outlines, across all its findings. */\nexport const AI_JOB_REVIEW_OUTLINE_MAX_NODES = 40\n/** The most names an outlined node lists of its props, of its sx keys, or of its children. */\nexport const AI_JOB_REVIEW_OUTLINE_MAX_NAMES = 16\n/** The most bytes a review's outline takes as JSON, whatever the counts above allow. */\nexport const AI_JOB_REVIEW_OUTLINE_MAX_BYTES = 4_096\n\n/** A Grid's layout props, whose written values an outline keeps. */\nconst AI_JOB_REVIEW_GRID_PROPS = [\n 'container',\n 'size',\n 'offset',\n 'direction',\n 'wrap',\n 'spacing',\n 'rowSpacing',\n 'columnSpacing',\n 'columns',\n] as const\n\n/** A node id or plan path a review keeps: no whitespace, slash or quote, so never copy or an address. */\nconst REVIEW_REF = /^[^\\s/\\\\\"'<>]+$/\n/** An element id an outline names. */\nconst OUTLINE_ELEMENT = /^[\\w.-]{1,64}$/\n/** A prop or sx name an outline lists. */\nconst OUTLINE_NAME = /^[A-Za-z_$][\\w$-]{0,39}$/\n/** The longest layout value an outline keeps as written. */\nconst OUTLINE_VALUE_MAX_CHARS = 40\n/**\n * The words a Grid's layout is written in, besides numbers: breakpoints, span\n * words, switch words, directions and wraps. Nothing else is kept, so no copy is.\n */\nconst OUTLINE_LAYOUT_WORDS = new Set([\n 'xs',\n 'sm',\n 'md',\n 'lg',\n 'xl',\n 'auto',\n 'grow',\n 'true',\n 'false',\n 'yes',\n 'no',\n 'on',\n 'off',\n 'row',\n 'row-reverse',\n 'column',\n 'column-reverse',\n 'wrap',\n 'nowrap',\n 'wrap-reverse',\n])\n/** A number a layout value is written in, with a CSS length unit where it has one. */\nconst OUTLINE_LAYOUT_NUMBER = /^[-+]?(?:\\d+(?:\\.\\d+)?|\\.\\d+)(?:px|rem|em|%)?$/i\n/** What stands between the words of a layout value, as in `xs:12 md:4` or `{ xs: 12, md: 4 }`. */\nconst OUTLINE_LAYOUT_SEPARATORS = /[\\s,:;=/{}()[\\]\"']+/\n/** A binding token, which a component's Grid may carry for a layout value. */\nconst OUTLINE_BINDING = /^\\{\\{\\s*[\\w.-]+\\s*\\}\\}$/\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/** Whether text is a layout value: a binding token, or only layout words and numbers. */\nfunction isLayoutText(value: string): boolean {\n if (value.length > OUTLINE_VALUE_MAX_CHARS) return false\n if (OUTLINE_BINDING.test(value.trim())) return true\n return value\n .split(OUTLINE_LAYOUT_SEPARATORS)\n .every((word) => word === '' || OUTLINE_LAYOUT_WORDS.has(word.toLowerCase()) || OUTLINE_LAYOUT_NUMBER.test(word))\n}\n\n/** An id or a path as a review keeps it: cut to its ceiling, and `?` for one it cannot show. */\nfunction reviewRef(ref: string): string {\n const cut = ref.slice(0, AI_JOB_REVIEW_REF_MAX_CHARS)\n return REVIEW_REF.test(cut) ? cut : '?'\n}\n\nfunction reviewRefs(refs: readonly string[]): string[] {\n return refs.slice(0, AI_JOB_REVIEW_REFS_MAX).map(reviewRef)\n}\n\n/** A written layout value as an outline keeps it; text that is no layout value is kept as a marker. */\nfunction outlineValue(value: unknown): string | number | boolean | null {\n if (value === null) return null\n if (typeof value === 'boolean') return value\n if (typeof value === 'number') return Number.isFinite(value) ? value : null\n if (typeof value === 'string') return isLayoutText(value) ? value : '<text>'\n return Array.isArray(value) ? '<list>' : '<object>'\n}\n\nfunction childIdsOf(node: Record<string, unknown>): string[] {\n return Array.isArray(node['nodes']) ? node['nodes'].filter((child): child is string => typeof child === 'string') : []\n}\n\nfunction elementOf(node: unknown): string {\n const componentId = isRecord(node) ? node['componentId'] : undefined\n return typeof componentId === 'string' && OUTLINE_ELEMENT.test(componentId) ? componentId : '?'\n}\n\n/** One node of an outline: where it sits, what it is and the names of what it sets, never a copy value. */\nfunction outlineNodeOf(\n id: string,\n depth: number,\n node: Record<string, unknown>,\n nodes: Readonly<Record<string, unknown>>,\n): AiJobReviewOutlineNode {\n const props = isRecord(node['props']) ? node['props'] : {}\n const sx = isRecord(node['sx']) ? node['sx'] : isRecord(props['sx']) ? props['sx'] : {}\n const names = (keys: string[]) =>\n keys.filter((key) => OUTLINE_NAME.test(key)).slice(0, AI_JOB_REVIEW_OUTLINE_MAX_NAMES)\n const componentId = elementOf(node)\n const grid =\n componentId === 'muiGrid'\n ? Object.fromEntries(\n AI_JOB_REVIEW_GRID_PROPS.filter((name) => props[name] !== undefined).map((name) => [\n name,\n outlineValue(props[name]),\n ]),\n )\n : {}\n const sxNames = names(Object.keys(sx))\n return {\n id: reviewRef(id),\n depth,\n componentId,\n props: names(Object.keys(props).filter((key) => key !== 'sx')),\n ...(sxNames.length ? { sx: sxNames } : {}),\n ...(Object.keys(grid).length ? { grid } : {}),\n children: childIdsOf(node)\n .filter((child) => isRecord(nodes[child]))\n .slice(0, AI_JOB_REVIEW_OUTLINE_MAX_NAMES)\n .map((child) => elementOf(nodes[child])),\n }\n}\n\n/**\n * The parts of a refused answer its findings name, as a review keeps them\n * (AGL-3078): each node a finding names by id, with the nodes below it to\n * `AI_JOB_REVIEW_OUTLINE_MAX_DEPTH`, in document order and each once. A node\n * keeps its id, its element, the names of its props and sx keys and its\n * children's elements, and a Grid the values of its layout props as written;\n * no copy, and no address, is kept. The outline stops at\n * `AI_JOB_REVIEW_OUTLINE_MAX_NODES` nodes or `AI_JOB_REVIEW_OUTLINE_MAX_BYTES`\n * bytes, whichever comes first. An answer that is no node map, and findings\n * that name no node, outline nothing.\n */\nexport function aiDoctrineReviewOutline(\n answer: Record<string, unknown> | null | undefined,\n violations: readonly AiDoctrineViolation[],\n): AiJobReviewOutlineNode[] {\n const outline: AiJobReviewOutlineNode[] = []\n const tree = answer ? aiAnswerTree(answer) : null\n const nodes = isRecord(tree) && isRecord(tree['nodes']) ? tree['nodes'] : null\n if (!nodes) return outline\n const encoder = new TextEncoder()\n const seen = new Set<string>()\n let bytes = 2\n // False once the outline is full, which ends the walk.\n const add = (id: string, depth: number): boolean => {\n const node = nodes[id]\n if (seen.has(id) || !isRecord(node)) return true\n seen.add(id)\n const entry = outlineNodeOf(id, depth, node, nodes)\n const size = encoder.encode(JSON.stringify(entry)).length + 1\n if (outline.length === AI_JOB_REVIEW_OUTLINE_MAX_NODES || bytes + size > AI_JOB_REVIEW_OUTLINE_MAX_BYTES) {\n return false\n }\n outline.push(entry)\n bytes += size\n return depth === AI_JOB_REVIEW_OUTLINE_MAX_DEPTH || childIdsOf(node).every((child) => add(child, depth + 1))\n }\n const named = violations.flatMap((violation) => (violation.nodeIds ?? []).slice(0, AI_JOB_REVIEW_REFS_MAX))\n for (const id of new Set(named)) {\n if (!add(id, 0)) break\n }\n return outline\n}\n\n/**\n * The review a generation stops its job for when the re-ask still broke a\n * rule: the sentence a person reads, each finding with the nodes or plan\n * entries it names, and an outline of those parts of the answer it was found\n * in (AGL-3078), so why a job stopped can be read back from the job once the\n * answer itself is gone.\n */\nexport function aiDoctrineReview(result: {\n message: string\n violations: readonly AiDoctrineViolation[]\n /** The refused answer, whose nodes the findings name by id; outlined, never kept. */\n answer?: Record<string, unknown> | null\n}): AiJobReview {\n const outline = aiDoctrineReviewOutline(result.answer, result.violations)\n return {\n reason: 'doctrine',\n message: result.message,\n findings: result.violations.map(({ rule, code, message, nodeIds, paths }) => ({\n rule,\n code,\n message,\n ...(nodeIds?.length ? { nodeIds: reviewRefs(nodeIds) } : {}),\n ...(paths?.length ? { paths: reviewRefs(paths) } : {}),\n })),\n ...(outline.length ? { outline } : {}),\n }\n}\n\n/** The review a step stops its job for when the site cannot take the draft. */\nexport function aiLimitReview(message: string): AiJobReview {\n return { reason: 'limit', message, findings: [] }\n}\n\n/**\n * An outcome that reached no model: a draft an earlier run already wrote, a\n * copy the plan asked for, or a person asked before anything was spent.\n */\nexport function aiUnspentOutcome(\n model: string,\n rest: { outputs?: AiJobOutput[]; review?: AiJobReview } = {},\n): AiJobStepOutcome {\n return {\n outputs: rest.outputs ?? [],\n usage: AI_JOB_ZERO_USAGE,\n estCostUsd: 0,\n model,\n stopReason: null,\n ...(rest.review ? { review: rest.review } : {}),\n }\n}\n\n/** Node ids as the MODEL wrote them, from the minted ids a check reads. */\nexport function aiModelNodeIds(\n ids: readonly string[],\n sourceIds: Readonly<Record<string, string>>,\n): string[] {\n return ids.map((id) => sourceIds[id] ?? id)\n}\n\n/** The most facts a note names before it counts the rest. */\nconst AI_NOTE_MAX_FACTS = 8\n\n/** Facts as a sentence lists them: \"a, b and c\", the ones past the cap counted. */\nfunction factList(facts: readonly string[]): string {\n const shown = facts.slice(0, AI_NOTE_MAX_FACTS)\n const items = facts.length > shown.length ? [...shown, `${facts.length - shown.length} more`] : shown\n return items.length <= 1 ? items.join('') : `${items.slice(0, -1).join(', ')} and ${items[items.length - 1]}`\n}\n\n/**\n * What a draft asks the member to fill in before it is published (AGL-3056),\n * in the words the job's output note shows: the facts in square brackets its\n * copy holds, which the brief did not give (rule 14), each once. A page's\n * placed components show their own defaults wherever the page sets no value\n * of its own, so a component whose defaults hold such facts is named once,\n * however often the page places it. `copy` is what the draft shows beside its\n * tree, such as a component's defaults. `null` when there is nothing to fill.\n */\nexport function aiBracketedFactsNote(input: {\n tree: AiDoctrineTree\n inventory: AiSiteInventory | null\n copy?: readonly string[]\n}): string | null {\n const components = input.inventory?.components ?? []\n const componentProps = Object.fromEntries(components.map((component) => [component.id, component.props]))\n const facts = aiBracketedFacts([\n ...aiTreeCopy(input.tree, { componentProps }).map((sample) => sample.text),\n ...(input.copy ?? []),\n ])\n const listed = new Set(facts.map((fact) => fact.toLowerCase()))\n const byComponent = new Map<string, string[]>()\n for (const { node } of walkTree(input.tree)) {\n const component = components.find((row) => row.id === node.props?.[AI_INSTANCE_REF_PROP])\n if (node.componentId !== REUSABLE_INSTANCE_COMPONENT_ID || !component?.bracketedDefaults) continue\n const values = node.props?.[REUSABLE_INSTANCE_PROP_VALUES_KEY]\n const set = (name: string): boolean => {\n const value = values && typeof values === 'object' ? (values as Record<string, unknown>)[name] : undefined\n return value !== undefined && value !== null && String(value).trim() !== ''\n }\n const shown = byComponent.get(component.name) ?? []\n for (const [name, defaults] of Object.entries(component.bracketedDefaults)) {\n if (set(name)) continue\n for (const fact of defaults) {\n if (!listed.has(fact.toLowerCase()) && !shown.some((known) => known.toLowerCase() === fact.toLowerCase())) {\n shown.push(fact)\n }\n }\n }\n if (shown.length) byComponent.set(component.name, shown)\n }\n const sentences = [\n ...(facts.length\n ? [`Before you publish, replace the facts in square brackets, which the brief did not give: ${factList(facts)}.`]\n : []),\n ...[...byComponent.entries()].map(\n ([name, shown]) =>\n `The \"${name}\" component on this page shows ${factList(shown)} until you replace ${shown.length === 1 ? 'it' : 'them'}.`,\n ),\n ]\n return sentences.length ? sentences.join(' ') : null\n}\n"],"names":["REUSABLE_INSTANCE_COMPONENT_ID","REUSABLE_INSTANCE_PROP_VALUES_KEY","CANVAS_ROOT_ELEMENT_ID","AI_BUILD_PLAN_EMBED_HOST_NAMES","AI_PLAN_NEW_REF_PREFIX","isAiPlanNewRef","isAiUnreadToken","aiAnswerTree","aiBracketedFacts","aiTreeCopy","walkTree","AI_INSTANCE_REF_PROP","AI_JOB_BRIEF_MAX_CHARS","AI_JOB_ZERO_USAGE","inputTokens","outputTokens","cacheReadTokens","cacheWriteTokens","aiConfirmedPlan","job","plan","status","aiPlanCreation","kind","create","find","entry","referenceOf","ref","label","labels","aiJobBriefLine","brief","slice","aiPlanReferenceLines","lines","reuse","push","id","purpose","from","duplicateOf","shown","fields","filter","field","length","join","name","why","screen","screens","sections","map","section","title","slug","embed","embeds","where","link","url","host","AI_COPY_SOURCE_MAX_CHARS","aiCopySourceLines","noun","nodes","isRecordNode","shaped","node","Object","entries","componentId","props","sx","children","keys","Array","isArray","tree","JSON","stringify","rootId","value","aiPlacedComponentIds","placed","Set","add","aiPlanReuseViolations","inventory","names","Map","components","component","reused","missing","has","listed","get","rule","code","message","aiGenerationSpent","spend","outputs","usage","estCostUsd","model","stopReason","AI_JOB_REVIEW_REFS_MAX","AI_JOB_REVIEW_REF_MAX_CHARS","AI_JOB_REVIEW_OUTLINE_MAX_DEPTH","AI_JOB_REVIEW_OUTLINE_MAX_NODES","AI_JOB_REVIEW_OUTLINE_MAX_NAMES","AI_JOB_REVIEW_OUTLINE_MAX_BYTES","AI_JOB_REVIEW_GRID_PROPS","REVIEW_REF","OUTLINE_ELEMENT","OUTLINE_NAME","OUTLINE_VALUE_MAX_CHARS","OUTLINE_LAYOUT_WORDS","OUTLINE_LAYOUT_NUMBER","OUTLINE_LAYOUT_SEPARATORS","OUTLINE_BINDING","isRecord","isLayoutText","test","trim","split","every","word","toLowerCase","reviewRef","cut","reviewRefs","refs","outlineValue","Number","isFinite","childIdsOf","child","elementOf","undefined","outlineNodeOf","depth","key","grid","fromEntries","sxNames","aiDoctrineReviewOutline","answer","violations","outline","encoder","TextEncoder","seen","bytes","size","encode","named","flatMap","violation","nodeIds","aiDoctrineReview","result","reason","findings","paths","aiLimitReview","aiUnspentOutcome","rest","review","aiModelNodeIds","ids","sourceIds","AI_NOTE_MAX_FACTS","factList","facts","items","aiBracketedFactsNote","input","componentProps","sample","text","copy","fact","byComponent","row","bracketedDefaults","values","set","String","defaults","some","known","sentences"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-generation.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 REUSABLE_INSTANCE_COMPONENT_ID,\n REUSABLE_INSTANCE_PROP_VALUES_KEY,\n} from '@aglyn/aglyn/app-utils/reusable-component-keys'\nimport { CANVAS_ROOT_ELEMENT_ID } from '@aglyn/aglyn/foundation/constants/canvas'\nimport {\n AI_BUILD_PLAN_EMBED_HOST_NAMES,\n AI_PLAN_NEW_REF_PREFIX,\n isAiPlanNewRef,\n type AiBuildPlanCreate,\n type AiBuildPlanCreateKind,\n} from '../model/ai-build-plan'\nimport type {\n AiJob,\n AiJobOutput,\n AiJobPlan,\n AiJobReview,\n AiJobReviewOutlineNode,\n} from '../model/ai-jobs.types'\nimport { AI_DOCTRINE_REFUSED_COPY, AI_DOCTRINE_REFUSED_PAGE_COPY, aiIsDoctrineRuleText } from '../model/ai-job-failure-copy'\nimport type { AiSiteInventory } from '../model/ai-site-inventory'\nimport { isAiUnreadToken } from '../model/ai-template-subjects'\nimport { aiAnswerTree, type AiGenerationSpend, type AiValidatedTree } from '../runtime/ai-doctrine'\nimport {\n aiBracketedFacts,\n aiTreeCopy,\n walkTree,\n type AiDoctrineNode,\n type AiDoctrineTree,\n type AiDoctrineViolation,\n} from '../runtime/ai-doctrine-validators'\nimport { AI_INSTANCE_REF_PROP } from '../runtime/ai-node-tree'\nimport type { AssistTokenUsage } from '../usage/assist-usage'\nimport { AI_JOB_BRIEF_MAX_CHARS, type AiJobStepOutcome } from './ai-job-text-step'\n\n/**\n * What the generation steps share (AGL-2909): reading the plan a member\n * confirmed, stating it to the model as references, and turning what a\n * generation spent or refused into the outcome the machine records.\n *\n * A generation step runs after its job's plan step, once a member confirmed\n * the plan, so the plan is part of what it executes: the records it reuses,\n * and the creation it builds. The plan travels as references — ids, names,\n * reasons — never as a document (AGL-2937).\n */\n\nexport const AI_JOB_ZERO_USAGE: AssistTokenUsage = {\n inputTokens: 0,\n outputTokens: 0,\n cacheReadTokens: 0,\n cacheWriteTokens: 0,\n}\n\n/** The job's plan once a member confirmed it; `null` before that, or for a job that never planned. */\nexport function aiConfirmedPlan(job: Pick<AiJob, 'plan'>): AiJobPlan | null {\n return job.plan?.status === 'confirmed' ? job.plan : null\n}\n\n/** The creation of one kind the plan names; the first when it names several. */\nexport function aiPlanCreation(\n plan: AiJobPlan | null,\n kind: AiBuildPlanCreateKind,\n): AiBuildPlanCreate | null {\n return plan?.create.find((entry) => entry.kind === kind) ?? null\n}\n\n/** A plan reference as the model reads it: the id with the name it was planned under. */\nfunction referenceOf(plan: AiJobPlan, ref: string): string {\n const label = plan.labels?.[ref]\n return label ? `${ref} (\"${label}\")` : ref\n}\n\n/** The user turn's opening: the brief, cut at the ceiling every job step keeps. */\nexport function aiJobBriefLine(job: Pick<AiJob, 'brief'>): string {\n return `Brief: ${job.brief.slice(0, AI_JOB_BRIEF_MAX_CHARS)}`\n}\n\n/** The confirmed plan as reference lines; none when there is no confirmed plan. */\nexport function aiPlanReferenceLines(plan: AiJobPlan | null): string[] {\n if (!plan) return []\n const lines = ['Confirmed plan:']\n for (const entry of plan.reuse) {\n lines.push(`- reuse the ${entry.kind === 'screen' ? 'page' : entry.kind} ${referenceOf(plan, entry.id)}: ${entry.purpose}`)\n }\n for (const entry of plan.create) {\n const from = entry.duplicateOf ? `, from a copy of ${referenceOf(plan, entry.duplicateOf)}` : ''\n // A template's slug or timestamp is filled, never shown, so a build told\n // it is a field prints it (AGL-3143 §16).\n const shown = entry.kind === 'template' ? entry.fields.filter((field) => !isAiUnreadToken(field)) : entry.fields\n const fields = shown.length ? ` Fields: ${shown.join(', ')}.` : ''\n lines.push(`- create the ${entry.kind} \"${entry.name}\"${from}: ${entry.why}${fields}`)\n }\n for (const screen of plan.screens) {\n const sections = screen.sections.map((section) => section.name).join(', ')\n lines.push(`- the page \"${screen.title}\" at ${screen.slug}${sections ? `: ${sections}` : ''}`)\n }\n // The one place a Video embed is admitted (AGL-3433): where the plan lists it, at the brief's link or none.\n for (const embed of plan.embeds ?? []) {\n const where = isAiPlanNewRef(embed.where)\n ? `the component \"${embed.where.slice(AI_PLAN_NEW_REF_PREFIX.length)}\"`\n : embed.where\n const link = embed.url ? `, url ${embed.url}` : ', url left empty for the site owner to paste'\n lines.push(`- embed a ${AI_BUILD_PLAN_EMBED_HOST_NAMES[embed.host]} player (videoEmbed) on ${where}${link}`)\n }\n return lines.length > 1 ? lines : []\n}\n\n/**\n * The most of a source's tree a build is shown, as JSON characters: about\n * 3,000 tokens, a whole layout or page template, never a site (AGL-3143 §14).\n */\nexport const AI_COPY_SOURCE_MAX_CHARS = 12_000\n\n/**\n * The record a plan's copy starts from, as the build that replaces the copy\n * is shown it (AGL-3143 §14): its tree in the node-map shape the tool takes,\n * with the one instruction that makes it a start rather than an example.\n * Empty when there is no tree to show or it is too large to show whole, and\n * the build then starts from the brief and the plan.\n *\n * A copy whose source cannot keep what the plan promised is never minted,\n * because the review after it would refuse it with certainty and the member\n * could only try the same copy again. Measured on 2026-09-22 (job\n * `Pq3pgvMH0D`): \"copy the interior layout and add a sidebar\" could only end\n * in review. Building from the source keeps what the plan does not change —\n * the firm's header, its footer, their copy — which a build from the brief\n * alone would draw again from nothing.\n */\nexport function aiCopySourceLines(noun: string, label: string, nodes: Readonly<Record<string, unknown>> | null): string[] {\n if (!nodes || !isRecordNode(nodes[CANVAS_ROOT_ELEMENT_ID])) return []\n const shaped: Record<string, Record<string, unknown>> = {}\n for (const [id, node] of Object.entries(nodes)) {\n if (!isRecordNode(node)) continue\n const { componentId, props, sx, nodes: children } = node\n shaped[id] = {\n componentId,\n ...(isRecordNode(props) && Object.keys(props).length ? { props } : {}),\n ...(isRecordNode(sx) && Object.keys(sx).length ? { sx } : {}),\n ...(Array.isArray(children) && children.length ? { nodes: children } : {}),\n }\n }\n const tree = JSON.stringify({ rootId: CANVAS_ROOT_ELEMENT_ID, nodes: shaped })\n if (tree.length > AI_COPY_SOURCE_MAX_CHARS) return []\n return [\n `The plan starts from a copy of the ${noun} \"${label}\", which lacks part of what the plan gives it. Build from it: keep every part the plan does not change, and add what the plan adds. Its tree, in the node-map shape the tool takes:`,\n tree,\n ]\n}\n\nfunction isRecordNode(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/** The reusable components a tree places, by the id each instance names. */\nexport function aiPlacedComponentIds(tree: Pick<AiValidatedTree, 'rootId' | 'nodes'>): Set<string> {\n const placed = new Set<string>()\n const nodes = tree.nodes as unknown as Record<string, AiDoctrineNode>\n for (const { node } of walkTree({ rootId: tree.rootId, nodes })) {\n const ref = node.props?.[AI_INSTANCE_REF_PROP]\n if (node.componentId === REUSABLE_INSTANCE_COMPONENT_ID && typeof ref === 'string') {\n placed.add(ref)\n }\n }\n return placed\n}\n\n/**\n * Rule 7 on a generated tree: every component the confirmed plan reuses is\n * placed. `noun` names what the tree builds, in the finding.\n */\nexport function aiPlanReuseViolations(\n inventory: AiSiteInventory | null,\n plan: AiJobPlan | null,\n placed: ReadonlySet<string>,\n noun: string,\n): AiDoctrineViolation[] {\n const names = new Map((inventory?.components ?? []).map((component) => [component.id, component.name]))\n const reused = [\n ...new Set(\n (plan?.reuse ?? []).filter((entry) => entry.kind === 'component').map((entry) => entry.id),\n ),\n ]\n const missing = reused.filter((id) => !placed.has(id))\n if (!missing.length) return []\n const listed = missing.map((id) => `\"${names.get(id) ?? id}\"`).join(', ')\n return [\n {\n rule: 7,\n code: 'plan-reuse-not-placed',\n message: `The confirmed plan reuses ${listed}, and the ${noun} does not place ${\n missing.length === 1 ? 'it' : 'them'\n }. Place every component the plan reuses.`,\n },\n ]\n}\n\n/** What a generation spent, as the step's outcome carries it for the meter. */\nexport function aiGenerationSpent(\n spend: AiGenerationSpend,\n): Pick<AiJobStepOutcome, 'outputs' | 'usage' | 'estCostUsd' | 'model' | 'stopReason'> {\n return {\n outputs: [],\n usage: spend.usage,\n estCostUsd: spend.estCostUsd,\n model: spend.model,\n stopReason: spend.stopReason,\n }\n}\n\n/** The most node ids, or plan paths, one finding keeps on its review (AGL-3078). */\nexport const AI_JOB_REVIEW_REFS_MAX = 24\n/** The longest node id or plan path a review keeps, in characters. */\nexport const AI_JOB_REVIEW_REF_MAX_CHARS = 64\n/** How far below a node a finding names its outline goes: the node, its children and theirs. */\nexport const AI_JOB_REVIEW_OUTLINE_MAX_DEPTH = 2\n/** The most nodes a review outlines, across all its findings. */\nexport const AI_JOB_REVIEW_OUTLINE_MAX_NODES = 40\n/** The most names an outlined node lists of its props, of its sx keys, or of its children. */\nexport const AI_JOB_REVIEW_OUTLINE_MAX_NAMES = 16\n/** The most bytes a review's outline takes as JSON, whatever the counts above allow. */\nexport const AI_JOB_REVIEW_OUTLINE_MAX_BYTES = 4_096\n\n/** A Grid's layout props, whose written values an outline keeps. */\nconst AI_JOB_REVIEW_GRID_PROPS = [\n 'container',\n 'size',\n 'offset',\n 'direction',\n 'wrap',\n 'spacing',\n 'rowSpacing',\n 'columnSpacing',\n 'columns',\n] as const\n\n/** A node id or plan path a review keeps: no whitespace, slash or quote, so never copy or an address. */\nconst REVIEW_REF = /^[^\\s/\\\\\"'<>]+$/\n/** An element id an outline names. */\nconst OUTLINE_ELEMENT = /^[\\w.-]{1,64}$/\n/** A prop or sx name an outline lists. */\nconst OUTLINE_NAME = /^[A-Za-z_$][\\w$-]{0,39}$/\n/** The longest layout value an outline keeps as written. */\nconst OUTLINE_VALUE_MAX_CHARS = 40\n/**\n * The words a Grid's layout is written in, besides numbers: breakpoints, span\n * words, switch words, directions and wraps. Nothing else is kept, so no copy is.\n */\nconst OUTLINE_LAYOUT_WORDS = new Set([\n 'xs',\n 'sm',\n 'md',\n 'lg',\n 'xl',\n 'auto',\n 'grow',\n 'true',\n 'false',\n 'yes',\n 'no',\n 'on',\n 'off',\n 'row',\n 'row-reverse',\n 'column',\n 'column-reverse',\n 'wrap',\n 'nowrap',\n 'wrap-reverse',\n])\n/** A number a layout value is written in, with a CSS length unit where it has one. */\nconst OUTLINE_LAYOUT_NUMBER = /^[-+]?(?:\\d+(?:\\.\\d+)?|\\.\\d+)(?:px|rem|em|%)?$/i\n/** What stands between the words of a layout value, as in `xs:12 md:4` or `{ xs: 12, md: 4 }`. */\nconst OUTLINE_LAYOUT_SEPARATORS = /[\\s,:;=/{}()[\\]\"']+/\n/** A binding token, which a component's Grid may carry for a layout value. */\nconst OUTLINE_BINDING = /^\\{\\{\\s*[\\w.-]+\\s*\\}\\}$/\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/** Whether text is a layout value: a binding token, or only layout words and numbers. */\nfunction isLayoutText(value: string): boolean {\n if (value.length > OUTLINE_VALUE_MAX_CHARS) return false\n if (OUTLINE_BINDING.test(value.trim())) return true\n return value\n .split(OUTLINE_LAYOUT_SEPARATORS)\n .every((word) => word === '' || OUTLINE_LAYOUT_WORDS.has(word.toLowerCase()) || OUTLINE_LAYOUT_NUMBER.test(word))\n}\n\n/** An id or a path as a review keeps it: cut to its ceiling, and `?` for one it cannot show. */\nfunction reviewRef(ref: string): string {\n const cut = ref.slice(0, AI_JOB_REVIEW_REF_MAX_CHARS)\n return REVIEW_REF.test(cut) ? cut : '?'\n}\n\nfunction reviewRefs(refs: readonly string[]): string[] {\n return refs.slice(0, AI_JOB_REVIEW_REFS_MAX).map(reviewRef)\n}\n\n/** A written layout value as an outline keeps it; text that is no layout value is kept as a marker. */\nfunction outlineValue(value: unknown): string | number | boolean | null {\n if (value === null) return null\n if (typeof value === 'boolean') return value\n if (typeof value === 'number') return Number.isFinite(value) ? value : null\n if (typeof value === 'string') return isLayoutText(value) ? value : '<text>'\n return Array.isArray(value) ? '<list>' : '<object>'\n}\n\nfunction childIdsOf(node: Record<string, unknown>): string[] {\n return Array.isArray(node['nodes']) ? node['nodes'].filter((child): child is string => typeof child === 'string') : []\n}\n\nfunction elementOf(node: unknown): string {\n const componentId = isRecord(node) ? node['componentId'] : undefined\n return typeof componentId === 'string' && OUTLINE_ELEMENT.test(componentId) ? componentId : '?'\n}\n\n/** One node of an outline: where it sits, what it is and the names of what it sets, never a copy value. */\nfunction outlineNodeOf(\n id: string,\n depth: number,\n node: Record<string, unknown>,\n nodes: Readonly<Record<string, unknown>>,\n): AiJobReviewOutlineNode {\n const props = isRecord(node['props']) ? node['props'] : {}\n const sx = isRecord(node['sx']) ? node['sx'] : isRecord(props['sx']) ? props['sx'] : {}\n const names = (keys: string[]) =>\n keys.filter((key) => OUTLINE_NAME.test(key)).slice(0, AI_JOB_REVIEW_OUTLINE_MAX_NAMES)\n const componentId = elementOf(node)\n const grid =\n componentId === 'muiGrid'\n ? Object.fromEntries(\n AI_JOB_REVIEW_GRID_PROPS.filter((name) => props[name] !== undefined).map((name) => [\n name,\n outlineValue(props[name]),\n ]),\n )\n : {}\n const sxNames = names(Object.keys(sx))\n return {\n id: reviewRef(id),\n depth,\n componentId,\n props: names(Object.keys(props).filter((key) => key !== 'sx')),\n ...(sxNames.length ? { sx: sxNames } : {}),\n ...(Object.keys(grid).length ? { grid } : {}),\n children: childIdsOf(node)\n .filter((child) => isRecord(nodes[child]))\n .slice(0, AI_JOB_REVIEW_OUTLINE_MAX_NAMES)\n .map((child) => elementOf(nodes[child])),\n }\n}\n\n/**\n * The parts of a refused answer its findings name, as a review keeps them\n * (AGL-3078): each node a finding names by id, with the nodes below it to\n * `AI_JOB_REVIEW_OUTLINE_MAX_DEPTH`, in document order and each once. A node\n * keeps its id, its element, the names of its props and sx keys and its\n * children's elements, and a Grid the values of its layout props as written;\n * no copy, and no address, is kept. The outline stops at\n * `AI_JOB_REVIEW_OUTLINE_MAX_NODES` nodes or `AI_JOB_REVIEW_OUTLINE_MAX_BYTES`\n * bytes, whichever comes first. An answer that is no node map, and findings\n * that name no node, outline nothing.\n */\nexport function aiDoctrineReviewOutline(\n answer: Record<string, unknown> | null | undefined,\n violations: readonly AiDoctrineViolation[],\n): AiJobReviewOutlineNode[] {\n const outline: AiJobReviewOutlineNode[] = []\n const tree = answer ? aiAnswerTree(answer) : null\n const nodes = isRecord(tree) && isRecord(tree['nodes']) ? tree['nodes'] : null\n if (!nodes) return outline\n const encoder = new TextEncoder()\n const seen = new Set<string>()\n let bytes = 2\n // False once the outline is full, which ends the walk.\n const add = (id: string, depth: number): boolean => {\n const node = nodes[id]\n if (seen.has(id) || !isRecord(node)) return true\n seen.add(id)\n const entry = outlineNodeOf(id, depth, node, nodes)\n const size = encoder.encode(JSON.stringify(entry)).length + 1\n if (outline.length === AI_JOB_REVIEW_OUTLINE_MAX_NODES || bytes + size > AI_JOB_REVIEW_OUTLINE_MAX_BYTES) {\n return false\n }\n outline.push(entry)\n bytes += size\n return depth === AI_JOB_REVIEW_OUTLINE_MAX_DEPTH || childIdsOf(node).every((child) => add(child, depth + 1))\n }\n const named = violations.flatMap((violation) => (violation.nodeIds ?? []).slice(0, AI_JOB_REVIEW_REFS_MAX))\n for (const id of new Set(named)) {\n if (!add(id, 0)) break\n }\n return outline\n}\n\n/**\n * The review a generation stops its job for when the re-ask still broke a\n * rule: the sentence a person reads, each finding with the nodes or plan\n * entries it names, and an outline of those parts of the answer it was found\n * in (AGL-3078), so why a job stopped can be read back from the job once the\n * answer itself is gone.\n *\n * The doctrine's own sentence (\"Rule 12 (Responsive by the theme's\n * breakpoints): ...\") is never what the customer reads (AGL-3596): it rides\n * as `detail`, for staff, and `message` is the plain refusal, a page's when\n * `page` says the refused thing is one, followed by `then` when given.\n */\nexport function aiDoctrineReview(\n result: {\n message: string\n violations: readonly AiDoctrineViolation[]\n /** The refused answer, whose nodes the findings name by id; outlined, never kept. */\n answer?: Record<string, unknown> | null\n },\n options: { page?: boolean; then?: string } = {},\n): AiJobReview {\n const outline = aiDoctrineReviewOutline(result.answer, result.violations)\n const ruleText = aiIsDoctrineRuleText(result.message)\n const plain = ruleText ? (options.page ? AI_DOCTRINE_REFUSED_PAGE_COPY : AI_DOCTRINE_REFUSED_COPY) : result.message\n return {\n reason: 'doctrine',\n message: options.then ? `${plain} ${options.then}` : plain,\n ...(ruleText ? { detail: result.message } : {}),\n findings: result.violations.map(({ rule, code, message, nodeIds, paths }) => ({\n rule,\n code,\n message,\n ...(nodeIds?.length ? { nodeIds: reviewRefs(nodeIds) } : {}),\n ...(paths?.length ? { paths: reviewRefs(paths) } : {}),\n })),\n ...(outline.length ? { outline } : {}),\n }\n}\n\n/** The review a step stops its job for when the site cannot take the draft. */\nexport function aiLimitReview(message: string): AiJobReview {\n return { reason: 'limit', message, findings: [] }\n}\n\n/**\n * An outcome that reached no model: a draft an earlier run already wrote, a\n * copy the plan asked for, or a person asked before anything was spent.\n */\nexport function aiUnspentOutcome(\n model: string,\n rest: { outputs?: AiJobOutput[]; review?: AiJobReview } = {},\n): AiJobStepOutcome {\n return {\n outputs: rest.outputs ?? [],\n usage: AI_JOB_ZERO_USAGE,\n estCostUsd: 0,\n model,\n stopReason: null,\n ...(rest.review ? { review: rest.review } : {}),\n }\n}\n\n/** Node ids as the MODEL wrote them, from the minted ids a check reads. */\nexport function aiModelNodeIds(\n ids: readonly string[],\n sourceIds: Readonly<Record<string, string>>,\n): string[] {\n return ids.map((id) => sourceIds[id] ?? id)\n}\n\n/** The most facts a note names before it counts the rest. */\nconst AI_NOTE_MAX_FACTS = 8\n\n/** Facts as a sentence lists them: \"a, b and c\", the ones past the cap counted. */\nfunction factList(facts: readonly string[]): string {\n const shown = facts.slice(0, AI_NOTE_MAX_FACTS)\n const items = facts.length > shown.length ? [...shown, `${facts.length - shown.length} more`] : shown\n return items.length <= 1 ? items.join('') : `${items.slice(0, -1).join(', ')} and ${items[items.length - 1]}`\n}\n\n/**\n * What a draft asks the member to fill in before it is published (AGL-3056),\n * in the words the job's output note shows: the facts in square brackets its\n * copy holds, which the brief did not give (rule 14), each once. A page's\n * placed components show their own defaults wherever the page sets no value\n * of its own, so a component whose defaults hold such facts is named once,\n * however often the page places it. `copy` is what the draft shows beside its\n * tree, such as a component's defaults. `null` when there is nothing to fill.\n */\nexport function aiBracketedFactsNote(input: {\n tree: AiDoctrineTree\n inventory: AiSiteInventory | null\n copy?: readonly string[]\n}): string | null {\n const components = input.inventory?.components ?? []\n const componentProps = Object.fromEntries(components.map((component) => [component.id, component.props]))\n const facts = aiBracketedFacts([\n ...aiTreeCopy(input.tree, { componentProps }).map((sample) => sample.text),\n ...(input.copy ?? []),\n ])\n const listed = new Set(facts.map((fact) => fact.toLowerCase()))\n const byComponent = new Map<string, string[]>()\n for (const { node } of walkTree(input.tree)) {\n const component = components.find((row) => row.id === node.props?.[AI_INSTANCE_REF_PROP])\n if (node.componentId !== REUSABLE_INSTANCE_COMPONENT_ID || !component?.bracketedDefaults) continue\n const values = node.props?.[REUSABLE_INSTANCE_PROP_VALUES_KEY]\n const set = (name: string): boolean => {\n const value = values && typeof values === 'object' ? (values as Record<string, unknown>)[name] : undefined\n return value !== undefined && value !== null && String(value).trim() !== ''\n }\n const shown = byComponent.get(component.name) ?? []\n for (const [name, defaults] of Object.entries(component.bracketedDefaults)) {\n if (set(name)) continue\n for (const fact of defaults) {\n if (!listed.has(fact.toLowerCase()) && !shown.some((known) => known.toLowerCase() === fact.toLowerCase())) {\n shown.push(fact)\n }\n }\n }\n if (shown.length) byComponent.set(component.name, shown)\n }\n const sentences = [\n ...(facts.length\n ? [`Before you publish, replace the facts in square brackets, which the brief did not give: ${factList(facts)}.`]\n : []),\n ...[...byComponent.entries()].map(\n ([name, shown]) =>\n `The \"${name}\" component on this page shows ${factList(shown)} until you replace ${shown.length === 1 ? 'it' : 'them'}.`,\n ),\n ]\n return sentences.length ? sentences.join(' ') : null\n}\n"],"names":["REUSABLE_INSTANCE_COMPONENT_ID","REUSABLE_INSTANCE_PROP_VALUES_KEY","CANVAS_ROOT_ELEMENT_ID","AI_BUILD_PLAN_EMBED_HOST_NAMES","AI_PLAN_NEW_REF_PREFIX","isAiPlanNewRef","AI_DOCTRINE_REFUSED_COPY","AI_DOCTRINE_REFUSED_PAGE_COPY","aiIsDoctrineRuleText","isAiUnreadToken","aiAnswerTree","aiBracketedFacts","aiTreeCopy","walkTree","AI_INSTANCE_REF_PROP","AI_JOB_BRIEF_MAX_CHARS","AI_JOB_ZERO_USAGE","inputTokens","outputTokens","cacheReadTokens","cacheWriteTokens","aiConfirmedPlan","job","plan","status","aiPlanCreation","kind","create","find","entry","referenceOf","ref","label","labels","aiJobBriefLine","brief","slice","aiPlanReferenceLines","lines","reuse","push","id","purpose","from","duplicateOf","shown","fields","filter","field","length","join","name","why","screen","screens","sections","map","section","title","slug","embed","embeds","where","link","url","host","AI_COPY_SOURCE_MAX_CHARS","aiCopySourceLines","noun","nodes","isRecordNode","shaped","node","Object","entries","componentId","props","sx","children","keys","Array","isArray","tree","JSON","stringify","rootId","value","aiPlacedComponentIds","placed","Set","add","aiPlanReuseViolations","inventory","names","Map","components","component","reused","missing","has","listed","get","rule","code","message","aiGenerationSpent","spend","outputs","usage","estCostUsd","model","stopReason","AI_JOB_REVIEW_REFS_MAX","AI_JOB_REVIEW_REF_MAX_CHARS","AI_JOB_REVIEW_OUTLINE_MAX_DEPTH","AI_JOB_REVIEW_OUTLINE_MAX_NODES","AI_JOB_REVIEW_OUTLINE_MAX_NAMES","AI_JOB_REVIEW_OUTLINE_MAX_BYTES","AI_JOB_REVIEW_GRID_PROPS","REVIEW_REF","OUTLINE_ELEMENT","OUTLINE_NAME","OUTLINE_VALUE_MAX_CHARS","OUTLINE_LAYOUT_WORDS","OUTLINE_LAYOUT_NUMBER","OUTLINE_LAYOUT_SEPARATORS","OUTLINE_BINDING","isRecord","isLayoutText","test","trim","split","every","word","toLowerCase","reviewRef","cut","reviewRefs","refs","outlineValue","Number","isFinite","childIdsOf","child","elementOf","undefined","outlineNodeOf","depth","key","grid","fromEntries","sxNames","aiDoctrineReviewOutline","answer","violations","outline","encoder","TextEncoder","seen","bytes","size","encode","named","flatMap","violation","nodeIds","aiDoctrineReview","result","options","ruleText","plain","page","reason","then","detail","findings","paths","aiLimitReview","aiUnspentOutcome","rest","review","aiModelNodeIds","ids","sourceIds","AI_NOTE_MAX_FACTS","factList","facts","items","aiBracketedFactsNote","input","componentProps","sample","text","copy","fact","byComponent","row","bracketedDefaults","values","set","String","defaults","some","known","sentences"],"mappings":"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"}
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
17
17
|
import { AI_INSIGHT_ASK_SURFACES, AI_INSIGHT_DIGEST_DAYS, AI_INSIGHT_DIGEST_READERS, AI_INSIGHT_QUESTION_MAX_CHARS, AI_INSIGHTS_COLLECTION, aiInsightDays, aiInsightOutput, aiInsightSurface, aiInsightSurfaceNeedsSite } from "../model/ai-insight.js";
|
|
18
|
+
import { aiHostPublishContext, aiSiteStatusTable } from "../model/ai-host-publish-context.js";
|
|
18
19
|
import { AI_ROUTING_TABLE, aiModelForStep } from "../providers/routing.js";
|
|
19
20
|
import { AI_ACCEPTABLE_USE_BLOCK, runAiRequest } from "../runtime/ai-runtime.js";
|
|
20
21
|
import { AI_GENERATION_MAX_ATTEMPTS } from "../runtime/ai-generation-bounds.js";
|
|
@@ -45,6 +46,7 @@ export const AI_INSIGHT_UNREADABLE_COPY = 'The AI did not answer with insights.
|
|
|
45
46
|
'- Name nobody, and write no email address or phone number.',
|
|
46
47
|
'- Plain sentences: no markdown, no headings, no lists.',
|
|
47
48
|
'- When the tables cannot answer part of the question, say so in gap in one sentence with no numbers; otherwise gap is null.',
|
|
49
|
+
'- Whether the site is published, and its address, come only from the Site status table. Page views and visitors never show whether a site is live: never say or suggest a site is not live, not launched or not published because of its traffic.',
|
|
48
50
|
'- Write in the language of the question.'
|
|
49
51
|
].join('\n');
|
|
50
52
|
/** The system blocks both calls send: the rules and the platform's acceptable-use rules. */ export const AI_JOB_INSIGHT_SYSTEM = [
|
|
@@ -234,7 +236,12 @@ export const runAiJobInsightStep = async (context)=>{
|
|
|
234
236
|
refusals: read.refusals
|
|
235
237
|
});
|
|
236
238
|
if (!read.tables.length) return stop(AI_INSIGHT_NO_TABLES_COPY);
|
|
237
|
-
|
|
239
|
+
// A site's question is told whether the site is live, so traffic is never
|
|
240
|
+
// read as launch status. Appended, so the readers' refs stay as they were.
|
|
241
|
+
const tables = !digest && site.host ? [
|
|
242
|
+
...read.tables,
|
|
243
|
+
aiSiteStatusTable(aiHostPublishContext(site.host), `t${read.tables.length + 1}`)
|
|
244
|
+
] : read.tables;
|
|
238
245
|
// 2. The answer, and at most one re-ask.
|
|
239
246
|
let messages = [
|
|
240
247
|
{
|