@aglyn/plugins-ai 1.0.0-beta.237 → 1.0.0-beta.238

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.
Files changed (128) hide show
  1. package/package.json +17 -17
  2. package/src/lib/components/ai-credits-prompt.component.d.ts +11 -4
  3. package/src/lib/components/ai-credits-prompt.component.js +84 -34
  4. package/src/lib/components/ai-credits-prompt.component.js.map +1 -1
  5. package/src/lib/components/ai-job-cancel.component.d.ts +38 -0
  6. package/src/lib/components/ai-job-cancel.component.js +156 -0
  7. package/src/lib/components/ai-job-cancel.component.js.map +1 -0
  8. package/src/lib/components/ai-job-plan.component.js +12 -1
  9. package/src/lib/components/ai-job-plan.component.js.map +1 -1
  10. package/src/lib/components/ai-jobs-list-page.component.js +141 -72
  11. package/src/lib/components/ai-jobs-list-page.component.js.map +1 -1
  12. package/src/lib/components/ai-site-build-page.component.js +22 -2
  13. package/src/lib/components/ai-site-build-page.component.js.map +1 -1
  14. package/src/lib/components/ai-usage-strip.component.d.ts +19 -2
  15. package/src/lib/components/ai-usage-strip.component.js +40 -9
  16. package/src/lib/components/ai-usage-strip.component.js.map +1 -1
  17. package/src/lib/components/assist-jobs-drawer.component.js +12 -16
  18. package/src/lib/components/assist-jobs-drawer.component.js.map +1 -1
  19. package/src/lib/components/assist-panel.component.js +87 -51
  20. package/src/lib/components/assist-panel.component.js.map +1 -1
  21. package/src/lib/jobs/ai-job-build-step.js +4 -1
  22. package/src/lib/jobs/ai-job-build-step.js.map +1 -1
  23. package/src/lib/jobs/ai-job-cancel.d.ts +71 -0
  24. package/src/lib/jobs/ai-job-cancel.js +102 -0
  25. package/src/lib/jobs/ai-job-cancel.js.map +1 -0
  26. package/src/lib/jobs/ai-job-component-step.js +4 -1
  27. package/src/lib/jobs/ai-job-component-step.js.map +1 -1
  28. package/src/lib/jobs/ai-job-drafts.js +2 -0
  29. package/src/lib/jobs/ai-job-drafts.js.map +1 -1
  30. package/src/lib/jobs/ai-job-form-step.js +4 -1
  31. package/src/lib/jobs/ai-job-form-step.js.map +1 -1
  32. package/src/lib/jobs/ai-job-layout-language.d.ts +2 -0
  33. package/src/lib/jobs/ai-job-layout-language.js +13 -1
  34. package/src/lib/jobs/ai-job-layout-language.js.map +1 -1
  35. package/src/lib/jobs/ai-job-layout-step.js +6 -1
  36. package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
  37. package/src/lib/jobs/ai-job-page-language.js +8 -0
  38. package/src/lib/jobs/ai-job-page-language.js.map +1 -1
  39. package/src/lib/jobs/ai-job-page-step.js +12 -1
  40. package/src/lib/jobs/ai-job-page-step.js.map +1 -1
  41. package/src/lib/jobs/ai-job-plan-step.d.ts +20 -1
  42. package/src/lib/jobs/ai-job-plan-step.js +49 -9
  43. package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
  44. package/src/lib/jobs/ai-job-site-content.d.ts +15 -0
  45. package/src/lib/jobs/ai-job-site-content.js +66 -7
  46. package/src/lib/jobs/ai-job-site-content.js.map +1 -1
  47. package/src/lib/jobs/ai-job-site-step.js +4 -1
  48. package/src/lib/jobs/ai-job-site-step.js.map +1 -1
  49. package/src/lib/jobs/ai-job-template-step.js +4 -1
  50. package/src/lib/jobs/ai-job-template-step.js.map +1 -1
  51. package/src/lib/jobs/ai-jobs.d.ts +37 -12
  52. package/src/lib/jobs/ai-jobs.js +184 -28
  53. package/src/lib/jobs/ai-jobs.js.map +1 -1
  54. package/src/lib/jobs/ai-layout-stock-photos.d.ts +143 -70
  55. package/src/lib/jobs/ai-layout-stock-photos.js +649 -176
  56. package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
  57. package/src/lib/jobs/fixtures/ai-ceramics-portfolio-site-plan.d.ts +30 -0
  58. package/src/lib/jobs/fixtures/ai-ceramics-portfolio-site-plan.js +220 -0
  59. package/src/lib/jobs/fixtures/ai-ceramics-portfolio-site-plan.js.map +1 -0
  60. package/src/lib/jobs/fixtures/ai-stock-photo-pixabay-recordings.d.ts +27 -0
  61. package/src/lib/jobs/fixtures/ai-stock-photo-pixabay-recordings.js +151 -0
  62. package/src/lib/jobs/fixtures/ai-stock-photo-pixabay-recordings.js.map +1 -0
  63. package/src/lib/layout-language/ai-layout-compiler.d.ts +6 -0
  64. package/src/lib/layout-language/ai-layout-compiler.js +304 -8
  65. package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
  66. package/src/lib/layout-language/ai-layout-design.d.ts +6 -3
  67. package/src/lib/layout-language/ai-layout-design.js +5 -3
  68. package/src/lib/layout-language/ai-layout-design.js.map +1 -1
  69. package/src/lib/layout-language/ai-layout-frame.d.ts +12 -0
  70. package/src/lib/layout-language/ai-layout-frame.js +58 -3
  71. package/src/lib/layout-language/ai-layout-frame.js.map +1 -1
  72. package/src/lib/layout-language/ai-layout-listings.d.ts +13 -1
  73. package/src/lib/layout-language/ai-layout-listings.js +61 -46
  74. package/src/lib/layout-language/ai-layout-listings.js.map +1 -1
  75. package/src/lib/layout-language/ai-layout-pictures.d.ts +8 -0
  76. package/src/lib/layout-language/ai-layout-pictures.js.map +1 -1
  77. package/src/lib/layout-language/ai-layout-storefront.d.ts +82 -0
  78. package/src/lib/layout-language/ai-layout-storefront.js +223 -0
  79. package/src/lib/layout-language/ai-layout-storefront.js.map +1 -0
  80. package/src/lib/model/ai-build-job.d.ts +9 -1
  81. package/src/lib/model/ai-build-job.js +19 -5
  82. package/src/lib/model/ai-build-job.js.map +1 -1
  83. package/src/lib/model/ai-build-progress.d.ts +2 -2
  84. package/src/lib/model/ai-build-progress.js +6 -4
  85. package/src/lib/model/ai-build-progress.js.map +1 -1
  86. package/src/lib/model/ai-credit-estimate.d.ts +69 -2
  87. package/src/lib/model/ai-credit-estimate.js +91 -3
  88. package/src/lib/model/ai-credit-estimate.js.map +1 -1
  89. package/src/lib/model/ai-job-cancel-copy.d.ts +44 -0
  90. package/src/lib/model/ai-job-cancel-copy.js +53 -0
  91. package/src/lib/model/ai-job-cancel-copy.js.map +1 -0
  92. package/src/lib/model/ai-job-failure-copy.d.ts +4 -1
  93. package/src/lib/model/ai-job-failure-copy.js +8 -3
  94. package/src/lib/model/ai-job-failure-copy.js.map +1 -1
  95. package/src/lib/model/ai-jobs.types.d.ts +20 -0
  96. package/src/lib/model/ai-jobs.types.js.map +1 -1
  97. package/src/lib/model/ai-site-build-progress.js +7 -4
  98. package/src/lib/model/ai-site-build-progress.js.map +1 -1
  99. package/src/lib/model/ai-site-job.d.ts +19 -2
  100. package/src/lib/model/ai-site-job.js +29 -8
  101. package/src/lib/model/ai-site-job.js.map +1 -1
  102. package/src/lib/model/ai-site-kinds.d.ts +4 -1
  103. package/src/lib/model/ai-site-kinds.js +34 -6
  104. package/src/lib/model/ai-site-kinds.js.map +1 -1
  105. package/src/lib/runtime/ai-doctrine-validators.d.ts +37 -1
  106. package/src/lib/runtime/ai-doctrine-validators.js +151 -6
  107. package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
  108. package/src/lib/runtime/ai-doctrine.js +4 -3
  109. package/src/lib/runtime/ai-doctrine.js.map +1 -1
  110. package/src/lib/runtime/ai-palette.generated.js +53 -7
  111. package/src/lib/runtime/ai-palette.generated.js.map +1 -1
  112. package/src/lib/server/ai-jobs-cancel.d.ts +13 -5
  113. package/src/lib/server/ai-jobs-cancel.js +27 -9
  114. package/src/lib/server/ai-jobs-cancel.js.map +1 -1
  115. package/src/lib/server/ai-jobs-gate.d.ts +12 -2
  116. package/src/lib/server/ai-jobs-gate.js +20 -9
  117. package/src/lib/server/ai-jobs-gate.js.map +1 -1
  118. package/src/lib/usage/ai-usage-meter.d.ts +2 -1
  119. package/src/lib/usage/ai-usage-meter.js +17 -1
  120. package/src/lib/usage/ai-usage-meter.js.map +1 -1
  121. package/src/lib/usage/ai-usage-wire.d.ts +6 -0
  122. package/src/lib/usage/ai-usage-wire.js.map +1 -1
  123. package/src/lib/usage/assist-usage.d.ts +7 -0
  124. package/src/lib/usage/assist-usage.js +14 -4
  125. package/src/lib/usage/assist-usage.js.map +1 -1
  126. package/src/lib/usage/free-ai-credits-left.d.ts +7 -0
  127. package/src/lib/usage/free-ai-credits-left.js +4 -2
  128. package/src/lib/usage/free-ai-credits-left.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 aiBuildCreditRange,\n aiBuildDegradation,\n aiBuildInitialLedger,\n aiBuildItemDelivered,\n aiBuildItemOpen,\n aiBuildNextUnit,\n aiBuildOrder,\n aiBuildPlacedItemLines,\n aiBuildPlanShapeRefusal,\n aiBuildSmaller,\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 {\n AI_CREDITS_CONFIRM_CODE,\n aiCreditsPromptFor,\n aiCreditsPromptText,\n type AiCreditRange,\n type AiCreditsPrompt,\n type AiCreditsSmaller,\n} from '../model/ai-credit-estimate'\nimport { aiFreeCreditsNoneLeftText } from '../model/ai-site-job'\nimport { readFreeAiCreditsLeft } from '../usage/free-ai-credits-left'\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 { aiBuildOps, type AiBuildOpsContext } from './ai-build-capabilities'\nimport {\n aiJobAdmissionRefusal,\n registerAiJobAdmission,\n type AiJobAdmission,\n type AiJobAdmissionContext,\n type AiJobAdmissionRefusal,\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 function: 'logic',\n edit: 'edit',\n}\n\n/**\n * What a Free build's credits say before it runs (AGL-3722), on its measured\n * p90 rather than its ceiling: `null` when what is left this month covers the\n * p90 — or when nothing is known about what is left, since each pass's\n * reservation still decides — and `null` too for a build the person chose\n * to build as far as it goes (`confirmed`), whose prompt `onConfirmed` is\n * told so the door records it. Otherwise a 409 carrying the prompt: the\n * range, what is left, and the smaller first build. Nothing left at all is a\n * 429. Read off the same two walls the reservation refuses at\n * (`readFreeAiCreditsLeft`), net of give-backs.\n */\nexport async function aiBuildFreeCreditsRefusal(input: {\n firestore: FirebaseFirestore.Firestore\n orgId: string\n org: object | null\n range: AiCreditRange\n smaller?: AiCreditsSmaller | null\n confirmed?: boolean\n onConfirmed?: (prompt: AiCreditsPrompt) => void\n now: Date\n freeCreditsLeft?: typeof readFreeAiCreditsLeft\n}): Promise<AiJobAdmissionRefusal | null> {\n const read = input.freeCreditsLeft ?? readFreeAiCreditsLeft\n const credits = await read(input.firestore, { orgId: input.orgId, org: input.org, now: input.now })\n if (!credits) return null\n if (credits.left <= 0) return { status: 429, error: aiFreeCreditsNoneLeftText(credits.resetsOn) }\n const prompt = aiCreditsPromptFor(input.range, credits, input.smaller ?? null)\n if (!prompt) return null\n if (!input.confirmed) {\n return { status: 409, error: aiCreditsPromptText(prompt, 'build'), code: AI_CREDITS_CONFIRM_CODE, credits: prompt }\n }\n input.onConfirmed?.(prompt)\n return null\n}\n\nexport interface AiBuildAdmissionDeps {\n opsFor?: (context: AiBuildOpsContext) => Promise<AiBuildOps>\n freeCreditsLeft?: typeof readFreeAiCreditsLeft\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 a measured p90\n * the month's Free credits still cover — or the person's go-ahead to build\n * what fits (AGL-3722).\n */\nexport function createAiBuildJobAdmission(deps: AiBuildAdmissionDeps = {}): AiJobAdmission {\n const opsFor = deps.opsFor ?? ((context) => aiBuildOps(context))\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 aiBuildFreeCreditsRefusal({\n firestore: context.firestore,\n orgId: context.orgId,\n org,\n range: aiBuildCreditRange(plan, { ops }),\n smaller: aiBuildSmaller(plan, ops),\n confirmed: context.creditsConfirmed === true,\n onConfirmed: context.onCreditsConfirmed,\n now: deps.now?.() ?? new Date(),\n freeCreditsLeft: deps.freeCreditsLeft,\n })\n if (refusal) return 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","aiBuildCreditRange","aiBuildDegradation","aiBuildInitialLedger","aiBuildItemDelivered","aiBuildItemOpen","aiBuildNextUnit","aiBuildOrder","aiBuildPlacedItemLines","aiBuildPlanShapeRefusal","aiBuildSmaller","aiBuildUnits","isAiPlanNewRef","AI_SITE_MAX_SECTIONS","AI_CREDITS_CONFIRM_CODE","aiCreditsPromptFor","aiCreditsPromptText","aiFreeCreditsNoneLeftText","readFreeAiCreditsLeft","aiModelForStep","AI_BUILD_UNIT_EMPTY_COPY","aiBuildBuiltRefs","aiUnitErrorRetryable","aiUnitFailure","aiUnitSpend","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","function","edit","aiBuildFreeCreditsRefusal","input","read","freeCreditsLeft","credits","left","resetsOn","prompt","range","smaller","confirmed","code","onConfirmed","createAiBuildJobAdmission","ownerOf","owner","shape","creditsConfirmed","onCreditsConfirmed","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 { aiJobCancelAsked } from './ai-job-cancel'\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 aiBuildCreditRange,\n aiBuildDegradation,\n aiBuildInitialLedger,\n aiBuildItemDelivered,\n aiBuildItemOpen,\n aiBuildNextUnit,\n aiBuildOrder,\n aiBuildPlacedItemLines,\n aiBuildPlanShapeRefusal,\n aiBuildSmaller,\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 {\n AI_CREDITS_CONFIRM_CODE,\n aiCreditsPromptFor,\n aiCreditsPromptText,\n type AiCreditRange,\n type AiCreditsPrompt,\n type AiCreditsSmaller,\n} from '../model/ai-credit-estimate'\nimport { aiFreeCreditsNoneLeftText } from '../model/ai-site-job'\nimport { readFreeAiCreditsLeft } from '../usage/free-ai-credits-left'\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 { aiBuildOps, type AiBuildOpsContext } from './ai-build-capabilities'\nimport {\n aiJobAdmissionRefusal,\n registerAiJobAdmission,\n type AiJobAdmission,\n type AiJobAdmissionContext,\n type AiJobAdmissionRefusal,\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 // A canceled build never puts anything live (AGL-3616): its pages stay drafts.\n if (await aiJobCancelAsked(firestore, job, context.signal)) 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 function: 'logic',\n edit: 'edit',\n}\n\n/**\n * What a Free build's credits say before it runs (AGL-3722), on its measured\n * p90 rather than its ceiling: `null` when what is left this month covers the\n * p90 — or when nothing is known about what is left, since each pass's\n * reservation still decides — and `null` too for a build the person chose\n * to build as far as it goes (`confirmed`), whose prompt `onConfirmed` is\n * told so the door records it. Otherwise a 409 carrying the prompt: the\n * range, what is left, and the smaller first build. Nothing left at all is a\n * 429. Read off the same two walls the reservation refuses at\n * (`readFreeAiCreditsLeft`), net of give-backs.\n */\nexport async function aiBuildFreeCreditsRefusal(input: {\n firestore: FirebaseFirestore.Firestore\n orgId: string\n org: object | null\n range: AiCreditRange\n smaller?: AiCreditsSmaller | null\n confirmed?: boolean\n onConfirmed?: (prompt: AiCreditsPrompt) => void\n now: Date\n freeCreditsLeft?: typeof readFreeAiCreditsLeft\n}): Promise<AiJobAdmissionRefusal | null> {\n const read = input.freeCreditsLeft ?? readFreeAiCreditsLeft\n const credits = await read(input.firestore, { orgId: input.orgId, org: input.org, now: input.now })\n if (!credits) return null\n if (credits.left <= 0) return { status: 429, error: aiFreeCreditsNoneLeftText(credits.resetsOn) }\n const prompt = aiCreditsPromptFor(input.range, credits, input.smaller ?? null)\n if (!prompt) return null\n if (!input.confirmed) {\n return { status: 409, error: aiCreditsPromptText(prompt, 'build'), code: AI_CREDITS_CONFIRM_CODE, credits: prompt }\n }\n input.onConfirmed?.(prompt)\n return null\n}\n\nexport interface AiBuildAdmissionDeps {\n opsFor?: (context: AiBuildOpsContext) => Promise<AiBuildOps>\n freeCreditsLeft?: typeof readFreeAiCreditsLeft\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 a measured p90\n * the month's Free credits still cover — or the person's go-ahead to build\n * what fits (AGL-3722).\n */\nexport function createAiBuildJobAdmission(deps: AiBuildAdmissionDeps = {}): AiJobAdmission {\n const opsFor = deps.opsFor ?? ((context) => aiBuildOps(context))\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 aiBuildFreeCreditsRefusal({\n firestore: context.firestore,\n orgId: context.orgId,\n org,\n range: aiBuildCreditRange(plan, { ops }),\n smaller: aiBuildSmaller(plan, ops),\n confirmed: context.creditsConfirmed === true,\n onConfirmed: context.onCreditsConfirmed,\n now: deps.now?.() ?? new Date(),\n freeCreditsLeft: deps.freeCreditsLeft,\n })\n if (refusal) return 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":["aiJobCancelAsked","resolveEffectivePlan","resolveOrgIdForHost","AI_BUILD_LIMITS","AI_BUILD_PUBLISH_INPUT","aiArticle","aiBuildCreditRange","aiBuildDegradation","aiBuildInitialLedger","aiBuildItemDelivered","aiBuildItemOpen","aiBuildNextUnit","aiBuildOrder","aiBuildPlacedItemLines","aiBuildPlanShapeRefusal","aiBuildSmaller","aiBuildUnits","isAiPlanNewRef","AI_SITE_MAX_SECTIONS","AI_CREDITS_CONFIRM_CODE","aiCreditsPromptFor","aiCreditsPromptText","aiFreeCreditsNoneLeftText","readFreeAiCreditsLeft","aiModelForStep","AI_BUILD_UNIT_EMPTY_COPY","aiBuildBuiltRefs","aiUnitErrorRetryable","aiUnitFailure","aiUnitSpend","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","signal","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","function","edit","aiBuildFreeCreditsRefusal","input","read","freeCreditsLeft","credits","left","resetsOn","prompt","range","smaller","confirmed","code","onConfirmed","createAiBuildJobAdmission","ownerOf","owner","shape","creditsConfirmed","onCreditsConfirmed","Date","aiBuildJobAdmission","registerAiBuildJob","minimumMs","minimumMsFor"],"mappings":"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"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ import { type AiJob } from '../model/ai-jobs.types';
18
+ /**
19
+ * Canceling a job mid-step (AGL-3616).
20
+ *
21
+ * The cancel door writes `cancelRequested` in a transaction. A job no step
22
+ * holds ends `canceled` in that same write. A job whose step holds a live
23
+ * lease is stopped by the runner that holds it: the machine watches the job
24
+ * while the step runs ({@link watchAiJobCancel}) and aborts the step's
25
+ * signal with an {@link AiJobCanceledError}. Every provider call already
26
+ * takes that signal, so a call in flight ends and a call not yet started
27
+ * never starts; a step about to write a draft or publish a site asks
28
+ * {@link throwIfAiJobCanceled} first. The machine's next write then ends the
29
+ * job `canceled`, never `failed`.
30
+ */
31
+ /** How often a running step re-reads its job for a cancel. */
32
+ export declare const AI_JOB_CANCEL_POLL_MS = 3000;
33
+ /** What an item a cancel stopped before says on its row. */
34
+ export declare const AI_JOB_CANCELED_ITEM_NOTE = "Not built: the job was canceled before it reached this.";
35
+ /**
36
+ * The reason a canceled step's signal is aborted with. Named `AbortError`,
37
+ * as `fetch` names an abort, so every path that already treats an abort as
38
+ * "the provider was never heard from" treats a cancel the same way; the
39
+ * machine tells the two apart by {@link isAiJobCanceledError}.
40
+ */
41
+ export declare class AiJobCanceledError extends Error {
42
+ readonly name = "AbortError";
43
+ readonly aiJobCanceled = true;
44
+ constructor();
45
+ }
46
+ export declare function isAiJobCanceledError(error: unknown): boolean;
47
+ /**
48
+ * Stops a step before a write once its job was canceled: throws the cancel
49
+ * when the step's signal was aborted by one. A budget that ran out is not a
50
+ * cancel and lets the write through, as it always has.
51
+ */
52
+ export declare function throwIfAiJobCanceled(signal?: AbortSignal | null): void;
53
+ /** A cancel was asked of a job that has not ended yet. */
54
+ export declare function aiJobCancelPending(job: Pick<AiJob, 'status' | 'cancelRequested'>): boolean;
55
+ /**
56
+ * Aborts `controller` with an {@link AiJobCanceledError} once `read` finds
57
+ * the job canceled or asked to cancel, polling every `pollMs`. Returns the
58
+ * stop for the caller's `finally`. A read that fails is skipped: the watch
59
+ * is a way to stop sooner, never a reason for a step to fail.
60
+ */
61
+ export declare function watchAiJobCancel(read: () => Promise<Pick<AiJob, 'status' | 'cancelRequested'> | null>, controller: AbortController, pollMs?: number): () => void;
62
+ /** Whether a step's signal was aborted by its job's cancel (not by its budget). */
63
+ export declare function aiJobCanceledBy(signal?: AbortSignal | null): boolean;
64
+ /**
65
+ * Whether a person has canceled the job, read fresh (AGL-3616): asked right
66
+ * before a step does something it cannot take back — putting a site live —
67
+ * because the running step's watch only looks every few seconds. A read
68
+ * that fails answers no: the cancel is still caught by the machine's next
69
+ * write.
70
+ */
71
+ export declare function aiJobCancelAsked(firestore: FirebaseFirestore.Firestore, job: Pick<AiJob, '$id' | 'orgId'>, signal?: AbortSignal | null): Promise<boolean>;
@@ -0,0 +1,102 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */ import { AI_JOB_TERMINAL_STATUSES } from "../model/ai-jobs.types.js";
17
+ /**
18
+ * Canceling a job mid-step (AGL-3616).
19
+ *
20
+ * The cancel door writes `cancelRequested` in a transaction. A job no step
21
+ * holds ends `canceled` in that same write. A job whose step holds a live
22
+ * lease is stopped by the runner that holds it: the machine watches the job
23
+ * while the step runs ({@link watchAiJobCancel}) and aborts the step's
24
+ * signal with an {@link AiJobCanceledError}. Every provider call already
25
+ * takes that signal, so a call in flight ends and a call not yet started
26
+ * never starts; a step about to write a draft or publish a site asks
27
+ * {@link throwIfAiJobCanceled} first. The machine's next write then ends the
28
+ * job `canceled`, never `failed`.
29
+ */ /** How often a running step re-reads its job for a cancel. */ export const AI_JOB_CANCEL_POLL_MS = 3000;
30
+ /** What an item a cancel stopped before says on its row. */ export const AI_JOB_CANCELED_ITEM_NOTE = 'Not built: the job was canceled before it reached this.';
31
+ /**
32
+ * The reason a canceled step's signal is aborted with. Named `AbortError`,
33
+ * as `fetch` names an abort, so every path that already treats an abort as
34
+ * "the provider was never heard from" treats a cancel the same way; the
35
+ * machine tells the two apart by {@link isAiJobCanceledError}.
36
+ */ export class AiJobCanceledError extends Error {
37
+ constructor(){
38
+ super('The AI job was canceled.'), this.name = 'AbortError', this.aiJobCanceled = true;
39
+ }
40
+ }
41
+ export function isAiJobCanceledError(error) {
42
+ return (error == null ? void 0 : error.aiJobCanceled) === true;
43
+ }
44
+ /**
45
+ * Stops a step before a write once its job was canceled: throws the cancel
46
+ * when the step's signal was aborted by one. A budget that ran out is not a
47
+ * cancel and lets the write through, as it always has.
48
+ */ export function throwIfAiJobCanceled(signal) {
49
+ if ((signal == null ? void 0 : signal.aborted) && isAiJobCanceledError(signal.reason)) throw signal.reason;
50
+ }
51
+ /** A cancel was asked of a job that has not ended yet. */ export function aiJobCancelPending(job) {
52
+ return Boolean(job.cancelRequested) && !AI_JOB_TERMINAL_STATUSES.includes(job.status);
53
+ }
54
+ /**
55
+ * Aborts `controller` with an {@link AiJobCanceledError} once `read` finds
56
+ * the job canceled or asked to cancel, polling every `pollMs`. Returns the
57
+ * stop for the caller's `finally`. A read that fails is skipped: the watch
58
+ * is a way to stop sooner, never a reason for a step to fail.
59
+ */ export function watchAiJobCancel(read, controller, pollMs = AI_JOB_CANCEL_POLL_MS) {
60
+ let stopped = false;
61
+ let timer = null;
62
+ const tick = async ()=>{
63
+ if (stopped || controller.signal.aborted) return;
64
+ const job = await read().catch(()=>null);
65
+ if (stopped || controller.signal.aborted) return;
66
+ if (job && (job.status === 'canceled' || aiJobCancelPending(job))) {
67
+ controller.abort(new AiJobCanceledError());
68
+ return;
69
+ }
70
+ schedule();
71
+ };
72
+ const schedule = ()=>{
73
+ timer = setTimeout(()=>void tick(), pollMs);
74
+ timer.unref == null ? void 0 : timer.unref.call(timer);
75
+ };
76
+ schedule();
77
+ return ()=>{
78
+ stopped = true;
79
+ if (timer) clearTimeout(timer);
80
+ };
81
+ }
82
+ /** Whether a step's signal was aborted by its job's cancel (not by its budget). */ export function aiJobCanceledBy(signal) {
83
+ return Boolean((signal == null ? void 0 : signal.aborted) && isAiJobCanceledError(signal.reason));
84
+ }
85
+ /**
86
+ * Whether a person has canceled the job, read fresh (AGL-3616): asked right
87
+ * before a step does something it cannot take back — putting a site live —
88
+ * because the running step's watch only looks every few seconds. A read
89
+ * that fails answers no: the cancel is still caught by the machine's next
90
+ * write.
91
+ */ export async function aiJobCancelAsked(firestore, job, signal) {
92
+ if (aiJobCanceledBy(signal)) return true;
93
+ try {
94
+ const snapshot = await firestore.collection('orgs').doc(job.orgId).collection('aiJobs').doc(job.$id).get();
95
+ const stored = snapshot.exists ? snapshot.data() : null;
96
+ return Boolean(stored && (stored.status === 'canceled' || aiJobCancelPending(stored)));
97
+ } catch (unused) {
98
+ return false;
99
+ }
100
+ }
101
+
102
+ //# sourceMappingURL=ai-job-cancel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-cancel.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { AI_JOB_TERMINAL_STATUSES, type AiJob } from '../model/ai-jobs.types'\n\n/**\n * Canceling a job mid-step (AGL-3616).\n *\n * The cancel door writes `cancelRequested` in a transaction. A job no step\n * holds ends `canceled` in that same write. A job whose step holds a live\n * lease is stopped by the runner that holds it: the machine watches the job\n * while the step runs ({@link watchAiJobCancel}) and aborts the step's\n * signal with an {@link AiJobCanceledError}. Every provider call already\n * takes that signal, so a call in flight ends and a call not yet started\n * never starts; a step about to write a draft or publish a site asks\n * {@link throwIfAiJobCanceled} first. The machine's next write then ends the\n * job `canceled`, never `failed`.\n */\n\n/** How often a running step re-reads its job for a cancel. */\nexport const AI_JOB_CANCEL_POLL_MS = 3_000\n\n/** What an item a cancel stopped before says on its row. */\nexport const AI_JOB_CANCELED_ITEM_NOTE = 'Not built: the job was canceled before it reached this.'\n\n/**\n * The reason a canceled step's signal is aborted with. Named `AbortError`,\n * as `fetch` names an abort, so every path that already treats an abort as\n * \"the provider was never heard from\" treats a cancel the same way; the\n * machine tells the two apart by {@link isAiJobCanceledError}.\n */\nexport class AiJobCanceledError extends Error {\n override readonly name = 'AbortError'\n readonly aiJobCanceled = true\n constructor() {\n super('The AI job was canceled.')\n }\n}\n\nexport function isAiJobCanceledError(error: unknown): boolean {\n return (error as { aiJobCanceled?: unknown } | null)?.aiJobCanceled === true\n}\n\n/**\n * Stops a step before a write once its job was canceled: throws the cancel\n * when the step's signal was aborted by one. A budget that ran out is not a\n * cancel and lets the write through, as it always has.\n */\nexport function throwIfAiJobCanceled(signal?: AbortSignal | null): void {\n if (signal?.aborted && isAiJobCanceledError(signal.reason)) throw signal.reason\n}\n\n/** A cancel was asked of a job that has not ended yet. */\nexport function aiJobCancelPending(job: Pick<AiJob, 'status' | 'cancelRequested'>): boolean {\n return Boolean(job.cancelRequested) && !AI_JOB_TERMINAL_STATUSES.includes(job.status)\n}\n\n/**\n * Aborts `controller` with an {@link AiJobCanceledError} once `read` finds\n * the job canceled or asked to cancel, polling every `pollMs`. Returns the\n * stop for the caller's `finally`. A read that fails is skipped: the watch\n * is a way to stop sooner, never a reason for a step to fail.\n */\nexport function watchAiJobCancel(\n read: () => Promise<Pick<AiJob, 'status' | 'cancelRequested'> | null>,\n controller: AbortController,\n pollMs = AI_JOB_CANCEL_POLL_MS,\n): () => void {\n let stopped = false\n let timer: ReturnType<typeof setTimeout> | null = null\n const tick = async () => {\n if (stopped || controller.signal.aborted) return\n const job = await read().catch(() => null)\n if (stopped || controller.signal.aborted) return\n if (job && (job.status === 'canceled' || aiJobCancelPending(job))) {\n controller.abort(new AiJobCanceledError())\n return\n }\n schedule()\n }\n const schedule = () => {\n timer = setTimeout(() => void tick(), pollMs)\n ;(timer as { unref?: () => void }).unref?.()\n }\n schedule()\n return () => {\n stopped = true\n if (timer) clearTimeout(timer)\n }\n}\n\n/** Whether a step's signal was aborted by its job's cancel (not by its budget). */\nexport function aiJobCanceledBy(signal?: AbortSignal | null): boolean {\n return Boolean(signal?.aborted && isAiJobCanceledError(signal.reason))\n}\n\n/**\n * Whether a person has canceled the job, read fresh (AGL-3616): asked right\n * before a step does something it cannot take back — putting a site live —\n * because the running step's watch only looks every few seconds. A read\n * that fails answers no: the cancel is still caught by the machine's next\n * write.\n */\nexport async function aiJobCancelAsked(\n firestore: FirebaseFirestore.Firestore,\n job: Pick<AiJob, '$id' | 'orgId'>,\n signal?: AbortSignal | null,\n): Promise<boolean> {\n if (aiJobCanceledBy(signal)) return true\n try {\n const snapshot = await firestore.collection('orgs').doc(job.orgId).collection('aiJobs').doc(job.$id).get()\n const stored = snapshot.exists ? (snapshot.data() as Pick<AiJob, 'status' | 'cancelRequested'>) : null\n return Boolean(stored && (stored.status === 'canceled' || aiJobCancelPending(stored)))\n } catch {\n return false\n }\n}\n"],"names":["AI_JOB_TERMINAL_STATUSES","AI_JOB_CANCEL_POLL_MS","AI_JOB_CANCELED_ITEM_NOTE","AiJobCanceledError","Error","name","aiJobCanceled","isAiJobCanceledError","error","throwIfAiJobCanceled","signal","aborted","reason","aiJobCancelPending","job","Boolean","cancelRequested","includes","status","watchAiJobCancel","read","controller","pollMs","stopped","timer","tick","catch","abort","schedule","setTimeout","unref","clearTimeout","aiJobCanceledBy","aiJobCancelAsked","firestore","snapshot","collection","doc","orgId","$id","get","stored","exists","data"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,wBAAwB,QAAoB,4BAAwB;AAE7E;;;;;;;;;;;;CAYC,GAED,4DAA4D,GAC5D,OAAO,MAAMC,wBAAwB,KAAK;AAE1C,0DAA0D,GAC1D,OAAO,MAAMC,4BAA4B,0DAAyD;AAElG;;;;;CAKC,GACD,OAAO,MAAMC,2BAA2BC;IAGtC,aAAc;QACZ,KAAK,CAAC,kCAHUC,OAAO,mBAChBC,gBAAgB;IAGzB;AACF;AAEA,OAAO,SAASC,qBAAqBC,KAAc;IACjD,OAAO,CAACA,yBAAD,AAACA,MAA8CF,aAAa,MAAK;AAC1E;AAEA;;;;CAIC,GACD,OAAO,SAASG,qBAAqBC,MAA2B;IAC9D,IAAIA,CAAAA,0BAAAA,OAAQC,OAAO,KAAIJ,qBAAqBG,OAAOE,MAAM,GAAG,MAAMF,OAAOE,MAAM;AACjF;AAEA,wDAAwD,GACxD,OAAO,SAASC,mBAAmBC,GAA8C;IAC/E,OAAOC,QAAQD,IAAIE,eAAe,KAAK,CAAChB,yBAAyBiB,QAAQ,CAACH,IAAII,MAAM;AACtF;AAEA;;;;;CAKC,GACD,OAAO,SAASC,iBACdC,IAAqE,EACrEC,UAA2B,EAC3BC,SAASrB,qBAAqB;IAE9B,IAAIsB,UAAU;IACd,IAAIC,QAA8C;IAClD,MAAMC,OAAO;QACX,IAAIF,WAAWF,WAAWX,MAAM,CAACC,OAAO,EAAE;QAC1C,MAAMG,MAAM,MAAMM,OAAOM,KAAK,CAAC,IAAM;QACrC,IAAIH,WAAWF,WAAWX,MAAM,CAACC,OAAO,EAAE;QAC1C,IAAIG,OAAQA,CAAAA,IAAII,MAAM,KAAK,cAAcL,mBAAmBC,IAAG,GAAI;YACjEO,WAAWM,KAAK,CAAC,IAAIxB;YACrB;QACF;QACAyB;IACF;IACA,MAAMA,WAAW;QACfJ,QAAQK,WAAW,IAAM,KAAKJ,QAAQH;QACpCE,MAAiCM,KAAK,oBAAvC,AAACN,MAAiCM,KAAK,MAAtCN;IACJ;IACAI;IACA,OAAO;QACLL,UAAU;QACV,IAAIC,OAAOO,aAAaP;IAC1B;AACF;AAEA,iFAAiF,GACjF,OAAO,SAASQ,gBAAgBtB,MAA2B;IACzD,OAAOK,QAAQL,CAAAA,0BAAAA,OAAQC,OAAO,KAAIJ,qBAAqBG,OAAOE,MAAM;AACtE;AAEA;;;;;;CAMC,GACD,OAAO,eAAeqB,iBACpBC,SAAsC,EACtCpB,GAAiC,EACjCJ,MAA2B;IAE3B,IAAIsB,gBAAgBtB,SAAS,OAAO;IACpC,IAAI;QACF,MAAMyB,WAAW,MAAMD,UAAUE,UAAU,CAAC,QAAQC,GAAG,CAACvB,IAAIwB,KAAK,EAAEF,UAAU,CAAC,UAAUC,GAAG,CAACvB,IAAIyB,GAAG,EAAEC,GAAG;QACxG,MAAMC,SAASN,SAASO,MAAM,GAAIP,SAASQ,IAAI,KAAmD;QAClG,OAAO5B,QAAQ0B,UAAWA,CAAAA,OAAOvB,MAAM,KAAK,cAAcL,mBAAmB4B,OAAM;IACrF,EAAE,eAAM;QACN,OAAO;IACT;AACF"}
@@ -1,3 +1,4 @@
1
+ import { _ as _extends } from "@swc/helpers/_/_extends";
1
2
  /**
2
3
  * @license
3
4
  * Copyright 2026 Aglyn LLC
@@ -13,7 +14,7 @@
13
14
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
15
  * See the License for the specific language governing permissions and
15
16
  * limitations under the License.
16
- */ import { _ as _extends } from "@swc/helpers/_/_extends";
17
+ */ import { throwIfAiJobCanceled } from "./ai-job-cancel.js";
17
18
  import { duplicateResource } from "@aglyn/tenant-data-admin/server/duplicate-resource";
18
19
  import { aiPlanEmbedsFor } from "../model/ai-build-plan.js";
19
20
  import { AI_STEP_TIERS } from "../providers/catalog.js";
@@ -304,6 +305,8 @@ export function createAiJobComponentStep(deps = {}) {
304
305
  if (result.status === 'needs_input') return _extends({}, spent, {
305
306
  review: aiDoctrineReview(result)
306
307
  });
308
+ // Never written once the job is canceled (AGL-3616).
309
+ throwIfAiJobCanceled(signal);
307
310
  const draft = await writeAiDraft(firestore, {
308
311
  kind: 'component',
309
312
  hostId,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-component-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 type { ReusableComponentProp } from '@aglyn/aglyn/foundation/definitions/platform.types'\nimport { duplicateResource } from '@aglyn/tenant-data-admin/server/duplicate-resource'\nimport { aiPlanEmbedsFor } from '../model/ai-build-plan'\nimport type { AiJob, AiJobOutput, AiJobPlan, AiJobReview } from '../model/ai-jobs.types'\nimport { AI_STEP_TIERS } from '../providers/catalog'\nimport { AI_ROUTING_TABLE, aiModelForStep } from '../providers/routing'\nimport { runValidatedGeneration } from '../runtime/ai-doctrine'\nimport type { AiDoctrineTree } from '../runtime/ai-doctrine-validators'\nimport type { AiLoadEstimate } from '../runtime/ai-palette'\nimport type { AiSystemBlock } from '../runtime/ai-runtime'\nimport { readSiteInventory } from '../runtime/site-inventory'\nimport {\n AI_COMPONENT_TOOL_NAME,\n aiComponentPropKindWords,\n aiComponentTool,\n} from '../tools/ai-component-tool'\nimport { registerAiJobAdmission, type AiJobAdmission } from './ai-job-admission'\nimport {\n aiBuildingPlanCreations,\n aiJobPlanCreationsRefusal,\n aiPlanCreationsRunMinimumMs,\n} from './ai-job-plan-creations'\nimport { aiJobStepBudget } from './ai-job-budget'\nimport { aiComponentCheck, aiPlannedComponentProps } from './ai-job-component-checks'\nimport { aiJobDraftId } from './ai-job-draft-ids'\nimport {\n aiDraftAdmissionRefusal,\n aiDraftAllowanceRefusal,\n aiSiteSubdomain,\n readAiDraft,\n readAiDraftComponentProps,\n writeAiDraft,\n type AiDraftRecord,\n} from './ai-job-drafts'\nimport {\n aiBracketedFactsNote,\n aiConfirmedPlan,\n aiDoctrineReview,\n aiGenerationSpent,\n aiJobBriefLine,\n aiLimitReview,\n aiPlanCreation,\n aiPlanReferenceLines,\n aiUnspentOutcome,\n} from './ai-job-generation'\nimport type { AiJobStepRunner } from './ai-job-text-step'\nimport { registerAiJobStep } from './ai-jobs'\n\n/**\n * The component step (AGL-2908): the generation step of a `component` job,\n * run once a member confirmed the job's plan.\n *\n * It builds ONE reusable component from the brief — its tree, and the typed\n * properties each page that places it fills in — through\n * `runValidatedGeneration('component', …)`, so every building rule holds on\n * the tree before it is kept. The tree binds each property with\n * `{{prop.<name>}}`; the palette validator keeps those tokens as written\n * (`definesComponent`), and the step's own check, through `extend`, holds\n * each binding to what the Properties dialog and the Attributes panel allow\n * (`ai-job-component-checks.ts`).\n *\n * The component lands as a new draft (`ai-job-drafts.ts`) that no page\n * places, and gets its first version when a member opens it, as every\n * component created outside the besigner does. A plan that starts from a copy\n * of a component the site has gets that copy, through the platform's\n * duplicate module, and nothing generated.\n *\n * A generation step runs on the job beat, never at an inline door: it\n * registers the least time its lookup rounds, its answer and its re-ask need\n * (AGL-3035), runs without extended thinking, and asks for no more of the\n * ceiling the layout and template steps keep than fits that time on the model\n * it runs. The step's spec measures the request and the answer against it.\n */\n\n/** The longest answer a component may run to; the tree is held to the component budget either way. */\nexport const AI_JOB_COMPONENT_MAX_TOKENS = AI_ROUTING_TABLE['job.component'].maxTokens\n\n/**\n * The component step's time (AGL-3035, AGL-3036): its two inventory-lookup\n * rounds, its answer and its re-ask at the routing table's ceiling on the tier\n * `job.component` is served from, fitted to what a beat can give a step.\n */\nexport const AI_JOB_COMPONENT_STEP_BUDGET = aiJobStepBudget({\n tier: AI_STEP_TIERS['job.component'],\n maxTokens: AI_JOB_COMPONENT_MAX_TOKENS,\n})\n\n/** The least time one component step needs before it starts. */\nexport const AI_JOB_COMPONENT_STEP_MINIMUM_MS = AI_JOB_COMPONENT_STEP_BUDGET.minimumMs\n\n/** A component's name when the plan names none. */\nexport const AI_JOB_COMPONENT_DEFAULT_NAME = 'Component'\n\n/** The component step's own instructions, cached after the doctrine and before the catalog. */\nexport const AI_JOB_COMPONENT_INSTRUCTIONS: readonly AiSystemBlock[] = [\n {\n text: [\n `You build one reusable component for a website: a block the site places on many pages, each placement filling in its own values. Answer with ${AI_COMPONENT_TOOL_NAME}: the component’s tree as one flat node map, and the properties it declares.`,\n 'The root is the document wrapper holding the component. A component is placed inside a page section, which opens with its own h2: it carries no main landmark, its headings are h3 or below, and its colors, spacing and type come from the theme.',\n 'Every value that should differ from one placement to the next is a property, bound in the tree with its token {{prop.<name>}}. Declare every property the tree binds, and bind every property you declare.',\n `Property kinds, as type (the name a page reads): ${aiComponentPropKindWords()}.`,\n 'A property binds only to a field that holds its kind of value:',\n '- text, richText and number: copy, such as a Typography’s text, an Image’s alt or a Button’s label, as the whole value or inside a sentence.',\n '- image: an Image’s src, as the whole value.',\n '- href: a screenId or an href, as the whole value.',\n '- icon: an Icon element’s iconId, as the whole value.',\n '- boolean: a switch setting such as a Button’s fullWidth, or hideIf on the part it hides, as the whole value.',\n '- choice: a setting with fixed options such as a Button’s variant, as the whole value, with every answer’s value one that setting lists.',\n 'An optional part, such as a photo, a badge or a second button, gets a boolean property labeled \"Hide <the part>\" whose default is \"false\", and the part’s own element carries \"hideIf\": \"{{prop.<name>}}\". Never hide the whole component.',\n 'A default is what the component shows until a page sets it: copy in the site’s voice from the brief, with a fact the brief does not give in square brackets; for href a page id from the site inventory, or \"\"; for image \"\", for an upload; for icon \"\", for the site owner to pick; for choice one of its answers’ values.',\n ].join('\\n'),\n },\n]\n\n/** The component job as the generation's user turn: the name, the brief, the confirmed plan. */\nexport function aiJobComponentPrompt(\n job: Pick<AiJob, 'brief'>,\n plan: AiJobPlan | null,\n name: string,\n): string {\n return [`Component name: ${name}`, aiJobBriefLine(job), ...aiPlanReferenceLines(plan)].join('\\n')\n}\n\n/**\n * The props the confirmed plan gives the component, against the component a\n * COPY actually produced (AGL-3024); `null` when the copy keeps the plan, or\n * when the plan promised no prop to keep.\n *\n * The third kind to need this, after the layout (`aiCopiedLayoutReview`) and\n * the page (`aiCopiedPageReview`), and found the same way: a duplicate branch\n * generates nothing, so it is the one place a plan's promise can go unmet\n * with no model to answer for it.\n *\n * MEASURED 2026-09-21. A plan read \"the existing card has no link target; the\n * brief needs one\", and `practice-area-link-card` came out BYTE-IDENTICAL to\n * `practice-area-card` — the same seven nodes under the same ids, the same\n * three props, no link anywhere. Its job reported the copy as built. So did\n * `practice-area-link-card-v2` the following pass. A component job can\n * therefore charge a member for a duplicate of something they already had.\n *\n * A copy that cannot be read back settles nothing, and a promise this step\n * cannot settle is reviewed by a person rather than reported as kept.\n */\nexport async function aiCopiedComponentReview(\n firestore: FirebaseFirestore.Firestore,\n input: { hostId: string; id: string; name: string; plan: AiJobPlan | null },\n): Promise<AiJobReview | null> {\n const promised = aiPlannedComponentProps(input.plan)\n if (!promised.length) return null\n const declared = await readAiDraftComponentProps(firestore, { hostId: input.hostId, id: input.id })\n if (declared === null) {\n return {\n reason: 'doctrine',\n message: `\"${input.name}\" was copied from the component the plan names, and this job could not read the copy back to check it has the properties the plan gives it. Open the component and check it before you place it.`,\n findings: [],\n }\n }\n const missing = promised.filter((name) => !declared.includes(name))\n if (!missing.length) return null\n const one = missing.length === 1\n return aiDoctrineReview({\n message: `\"${input.name}\" is a copy of the component the plan names, and a copy is all it is: the plan gives it ${\n one ? 'a property' : 'properties'\n } the copy does not have.`,\n violations: [\n {\n rule: null,\n code: 'plan-props-missing',\n message: `The confirmed plan says this component has ${missing.join(', ')}, and the copy has ${\n declared.length ? declared.join(', ') : 'none of them'\n }. Add ${one ? 'it' : 'each of them'}, and use ${\n one ? 'it' : 'them'\n } in the component, or the copy is the component it was copied from.`,\n },\n ],\n })\n}\n\n/**\n * A component job is admitted for a site of the job's own org whose plan\n * includes reusable components, and confirmed only for a plan whose other\n * creations it builds (AGL-3143 §15).\n */\nexport const aiComponentJobAdmission: AiJobAdmission = (context) =>\n aiDraftAdmissionRefusal(context.firestore, {\n orgId: context.orgId,\n hostId: context.hostId,\n kind: 'component',\n org: context.org,\n ownCheck: (hostId) => aiJobPlanCreationsRefusal('component', context, hostId),\n })\n\nexport interface AiJobComponentStepDeps {\n /** The inventory reader; specs hand in a fake. */\n readInventory?: typeof readSiteInventory\n /** The platform's duplicate module, for a plan that starts from a copy. */\n duplicate?: typeof duplicateResource\n}\n\nexport function createAiJobComponentStep(deps: AiJobComponentStepDeps = {}): AiJobStepRunner {\n const readInventory = deps.readInventory ?? readSiteInventory\n const duplicate = deps.duplicate ?? duplicateResource\n return async ({ job, now, signal, firestore, modelFor }) => {\n const hostId = job.hostId\n if (!hostId) throw new Error('a component job names no site, and its admission refuses one')\n const output = (draft: AiDraftRecord, load?: AiLoadEstimate | null, note?: string | null): AiJobOutput => ({\n resource: 'reusableComponent',\n id: draft.id,\n versionId: draft.versionId,\n hostId,\n hostSubdomain: draft.hostSubdomain,\n label: draft.name,\n ...(load ? { load } : {}),\n ...(note ? { note } : {}),\n })\n // The switch's answer for this job, else the routing table's (AGL-2942).\n const model = modelFor?.('job.component') ?? aiModelForStep('job.component')\n\n // A run cut off after its draft was written reports that draft, found by the id the job recorded.\n const draftId = aiJobDraftId(job, 'component')\n const written = await readAiDraft(firestore, { kind: 'component', hostId, id: draftId })\n if (written) return aiUnspentOutcome(model, { outputs: [output(written)] })\n\n const [inventory, orgSnapshot] = await Promise.all([\n readInventory(job.orgId, hostId, { firestore }),\n firestore.collection('orgs').doc(job.orgId).get(),\n ])\n const org = (orgSnapshot.data() ?? null) as Partial<AglynOrgBilling> | null\n const plan = aiConfirmedPlan(job)\n const creation = aiPlanCreation(plan, 'component')\n const name = creation?.name || AI_JOB_COMPONENT_DEFAULT_NAME\n\n // The plan starts from a copy of a component the site has (rule 15): the\n // copy is the draft, and nothing is generated. A source gone since the\n // plan was made is built from the brief instead, and one that lacks a\n // property the plan gives the component is built from the plan\n // (AGL-3143 §14): copying it could only end in the review below. A source\n // this step cannot read still copies.\n const source = creation?.duplicateOf\n ? inventory.components.find((component) => component.id === creation.duplicateOf)\n : undefined\n const sourceReview = source\n ? await aiCopiedComponentReview(firestore, { hostId, id: source.id, name: source.name, plan })\n : null\n if (creation?.duplicateOf && source && !sourceReview?.findings.length) {\n const copy = await duplicate('component', {\n orgId: job.orgId,\n hostId,\n sourceId: creation.duplicateOf,\n name,\n uid: job.createdBy,\n org: org as Record<string, unknown> | null,\n // The same claim the page step makes (AGL-3024): a copy core mints is\n // written under core's own id, never `draftId`, so a step that runs\n // again — a pass resumed, a run cut off after the copy — would mint a\n // second. The job's recorded id makes core replay the one it made.\n attemptKey: draftId,\n })\n if (copy.ok) {\n const hostSubdomain = await aiSiteSubdomain(firestore, hostId)\n // The copy is reported with what the plan promised of it read back\n // (AGL-3024): this branch generates nothing, so without this a plan\n // reading \"duplicate it and add a link\" reports Done on a duplicate\n // that added nothing.\n const review = await aiCopiedComponentReview(firestore, {\n hostId,\n id: copy.id,\n name: copy.name,\n plan,\n })\n if (review) return aiUnspentOutcome(model, { review })\n return aiUnspentOutcome(model, {\n outputs: [\n output({ id: copy.id, versionId: copy.versionId ?? null, name: copy.name, hostSubdomain }),\n ],\n })\n }\n if (copy.ok === false && copy.status === 403) {\n return aiUnspentOutcome(model, { review: aiLimitReview(copy.error) })\n }\n }\n\n const allowance = await aiDraftAllowanceRefusal(firestore, { kind: 'component', hostId, org })\n if (allowance) return aiUnspentOutcome(model, { review: aiLimitReview(allowance) })\n\n let props: ReusableComponentProp[] = []\n const result = await runValidatedGeneration('component', {\n step: 'job.component',\n model,\n instructions: AI_JOB_COMPONENT_INSTRUCTIONS,\n inventory,\n messages: [{ role: 'user', content: aiJobComponentPrompt(job, plan, name) }],\n tool: aiComponentTool(),\n maxTokens: AI_JOB_COMPONENT_STEP_BUDGET.maxTokens(model),\n ...(AI_ROUTING_TABLE['job.component'].thinking\n ? { thinking: AI_ROUTING_TABLE['job.component'].thinking }\n : {}),\n ...(AI_ROUTING_TABLE['job.component'].effort\n ? { effort: AI_ROUTING_TABLE['job.component'].effort }\n : {}),\n // A third-party player the confirmed plan lists for this component is the only one it may embed (AGL-3433).\n context: {\n definesComponent: true,\n ...(creation ? { plannedEmbeds: aiPlanEmbedsFor(plan, { create: creation.name }) } : {}),\n },\n extend: aiComponentCheck({\n inventory,\n plan,\n onProps: (declared) => {\n props = declared\n },\n }),\n ...(signal ? { signal } : {}),\n })\n const spent = aiGenerationSpent(result)\n if (result.status === 'refused') return { ...spent, refused: true }\n if (result.status === 'needs_input') return { ...spent, review: aiDoctrineReview(result) }\n\n const draft = await writeAiDraft(firestore, {\n kind: 'component',\n hostId,\n id: draftId,\n uid: job.createdBy,\n org,\n name,\n nodes: result.value.nodes,\n rootId: result.value.rootId,\n props,\n now,\n })\n if (draft.ok === false) {\n if (draft.status === 404) throw new Error(`site ${hostId} vanished while its component was generated`)\n return { ...spent, review: aiLimitReview(draft.error) }\n }\n // The facts the brief did not give, in the tree or the defaults a page shows (AGL-3056).\n const note = aiBracketedFactsNote({\n tree: { rootId: result.value.rootId, nodes: result.value.nodes as unknown as AiDoctrineTree['nodes'] },\n inventory,\n copy: props.map((prop) => (typeof prop.defaultValue === 'string' ? prop.defaultValue : '')),\n })\n return { ...spent, outputs: [output(draft, result.value.load, note)] }\n }\n}\n\n/** The component step, building the forms and other components its plan creates first (AGL-3143 §15). */\nexport const runAiJobComponentStep = aiBuildingPlanCreations(\n 'component',\n 'job.component',\n createAiJobComponentStep(),\n)\n\n/** Registers the component step and the check a component job passes before it is created or resumed. */\nexport function registerAiComponentJob(): void {\n registerAiJobStep('component', runAiJobComponentStep, {\n minimumMs: AI_JOB_COMPONENT_STEP_MINIMUM_MS,\n minimumMsFor: aiPlanCreationsRunMinimumMs('component'),\n })\n registerAiJobAdmission('component', aiComponentJobAdmission)\n}\n"],"names":["duplicateResource","aiPlanEmbedsFor","AI_STEP_TIERS","AI_ROUTING_TABLE","aiModelForStep","runValidatedGeneration","readSiteInventory","AI_COMPONENT_TOOL_NAME","aiComponentPropKindWords","aiComponentTool","registerAiJobAdmission","aiBuildingPlanCreations","aiJobPlanCreationsRefusal","aiPlanCreationsRunMinimumMs","aiJobStepBudget","aiComponentCheck","aiPlannedComponentProps","aiJobDraftId","aiDraftAdmissionRefusal","aiDraftAllowanceRefusal","aiSiteSubdomain","readAiDraft","readAiDraftComponentProps","writeAiDraft","aiBracketedFactsNote","aiConfirmedPlan","aiDoctrineReview","aiGenerationSpent","aiJobBriefLine","aiLimitReview","aiPlanCreation","aiPlanReferenceLines","aiUnspentOutcome","registerAiJobStep","AI_JOB_COMPONENT_MAX_TOKENS","maxTokens","AI_JOB_COMPONENT_STEP_BUDGET","tier","AI_JOB_COMPONENT_STEP_MINIMUM_MS","minimumMs","AI_JOB_COMPONENT_DEFAULT_NAME","AI_JOB_COMPONENT_INSTRUCTIONS","text","join","aiJobComponentPrompt","job","plan","name","aiCopiedComponentReview","firestore","input","promised","length","declared","hostId","id","reason","message","findings","missing","filter","includes","one","violations","rule","code","aiComponentJobAdmission","context","orgId","kind","org","ownCheck","createAiJobComponentStep","deps","readInventory","duplicate","now","signal","modelFor","orgSnapshot","Error","output","draft","load","note","resource","versionId","hostSubdomain","label","model","draftId","written","outputs","inventory","Promise","all","collection","doc","get","data","creation","source","duplicateOf","components","find","component","undefined","sourceReview","copy","sourceId","uid","createdBy","attemptKey","ok","review","status","error","allowance","props","result","step","instructions","messages","role","content","tool","thinking","effort","definesComponent","plannedEmbeds","create","extend","onProps","spent","refused","nodes","value","rootId","tree","map","prop","defaultValue","runAiJobComponentStep","registerAiComponentJob","minimumMsFor"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC;AAID,SAASA,iBAAiB,QAAQ,qDAAoD;AACtF,SAASC,eAAe,QAAQ,4BAAwB;AAExD,SAASC,aAAa,QAAQ,0BAAsB;AACpD,SAASC,gBAAgB,EAAEC,cAAc,QAAQ,0BAAsB;AACvE,SAASC,sBAAsB,QAAQ,4BAAwB;AAI/D,SAASC,iBAAiB,QAAQ,+BAA2B;AAC7D,SACEC,sBAAsB,EACtBC,wBAAwB,EACxBC,eAAe,QACV,gCAA4B;AACnC,SAASC,sBAAsB,QAA6B,wBAAoB;AAChF,SACEC,uBAAuB,EACvBC,yBAAyB,EACzBC,2BAA2B,QACtB,6BAAyB;AAChC,SAASC,eAAe,QAAQ,qBAAiB;AACjD,SAASC,gBAAgB,EAAEC,uBAAuB,QAAQ,+BAA2B;AACrF,SAASC,YAAY,QAAQ,wBAAoB;AACjD,SACEC,uBAAuB,EACvBC,uBAAuB,EACvBC,eAAe,EACfC,WAAW,EACXC,yBAAyB,EACzBC,YAAY,QAEP,qBAAiB;AACxB,SACEC,oBAAoB,EACpBC,eAAe,EACfC,gBAAgB,EAChBC,iBAAiB,EACjBC,cAAc,EACdC,aAAa,EACbC,cAAc,EACdC,oBAAoB,EACpBC,gBAAgB,QACX,yBAAqB;AAE5B,SAASC,iBAAiB,QAAQ,eAAW;AAE7C;;;;;;;;;;;;;;;;;;;;;;;;CAwBC,GAED,oGAAoG,GACpG,OAAO,MAAMC,8BAA8B/B,gBAAgB,CAAC,gBAAgB,CAACgC,SAAS,CAAA;AAEtF;;;;CAIC,GACD,OAAO,MAAMC,+BAA+BtB,gBAAgB;IAC1DuB,MAAMnC,aAAa,CAAC,gBAAgB;IACpCiC,WAAWD;AACb,GAAE;AAEF,8DAA8D,GAC9D,OAAO,MAAMI,mCAAmCF,6BAA6BG,SAAS,CAAA;AAEtF,iDAAiD,GACjD,OAAO,MAAMC,gCAAgC,YAAW;AAExD,6FAA6F,GAC7F,OAAO,MAAMC,gCAA0D;IACrE;QACEC,MAAM;YACJ,CAAC,6IAA6I,EAAEnC,uBAAuB,4EAA4E,CAAC;YACpP;YACA;YACA,CAAC,iDAAiD,EAAEC,2BAA2B,CAAC,CAAC;YACjF;YACA;YACA;YACA;YACA;YACA;YACA;YACA;YACA;SACD,CAACmC,IAAI,CAAC;IACT;CACD,CAAA;AAED,8FAA8F,GAC9F,OAAO,SAASC,qBACdC,GAAyB,EACzBC,IAAsB,EACtBC,IAAY;IAEZ,OAAO;QAAC,CAAC,gBAAgB,EAAEA,MAAM;QAAEnB,eAAeiB;WAASd,qBAAqBe;KAAM,CAACH,IAAI,CAAC;AAC9F;AAEA;;;;;;;;;;;;;;;;;;;CAmBC,GACD,OAAO,eAAeK,wBACpBC,SAAsC,EACtCC,KAA2E;IAE3E,MAAMC,WAAWnC,wBAAwBkC,MAAMJ,IAAI;IACnD,IAAI,CAACK,SAASC,MAAM,EAAE,OAAO;IAC7B,MAAMC,WAAW,MAAM/B,0BAA0B2B,WAAW;QAAEK,QAAQJ,MAAMI,MAAM;QAAEC,IAAIL,MAAMK,EAAE;IAAC;IACjG,IAAIF,aAAa,MAAM;QACrB,OAAO;YACLG,QAAQ;YACRC,SAAS,CAAC,CAAC,EAAEP,MAAMH,IAAI,CAAC,gMAAgM,CAAC;YACzNW,UAAU,EAAE;QACd;IACF;IACA,MAAMC,UAAUR,SAASS,MAAM,CAAC,CAACb,OAAS,CAACM,SAASQ,QAAQ,CAACd;IAC7D,IAAI,CAACY,QAAQP,MAAM,EAAE,OAAO;IAC5B,MAAMU,MAAMH,QAAQP,MAAM,KAAK;IAC/B,OAAO1B,iBAAiB;QACtB+B,SAAS,CAAC,CAAC,EAAEP,MAAMH,IAAI,CAAC,wFAAwF,EAC9Ge,MAAM,eAAe,aACtB,wBAAwB,CAAC;QAC1BC,YAAY;YACV;gBACEC,MAAM;gBACNC,MAAM;gBACNR,SAAS,CAAC,2CAA2C,EAAEE,QAAQhB,IAAI,CAAC,MAAM,mBAAmB,EAC3FU,SAASD,MAAM,GAAGC,SAASV,IAAI,CAAC,QAAQ,eACzC,MAAM,EAAEmB,MAAM,OAAO,eAAe,UAAU,EAC7CA,MAAM,OAAO,OACd,mEAAmE,CAAC;YACvE;SACD;IACH;AACF;AAEA;;;;CAIC,GACD,OAAO,MAAMI,0BAA0C,CAACC,UACtDjD,wBAAwBiD,QAAQlB,SAAS,EAAE;QACzCmB,OAAOD,QAAQC,KAAK;QACpBd,QAAQa,QAAQb,MAAM;QACtBe,MAAM;QACNC,KAAKH,QAAQG,GAAG;QAChBC,UAAU,CAACjB,SAAW1C,0BAA0B,aAAauD,SAASb;IACxE,GAAE;AASJ,OAAO,SAASkB,yBAAyBC,OAA+B,CAAC,CAAC;QAClDA,qBACJA;IADlB,MAAMC,iBAAgBD,sBAAAA,KAAKC,aAAa,YAAlBD,sBAAsBnE;IAC5C,MAAMqE,aAAYF,kBAAAA,KAAKE,SAAS,YAAdF,kBAAkBzE;IACpC,OAAO,OAAO,EAAE6C,GAAG,EAAE+B,GAAG,EAAEC,MAAM,EAAE5B,SAAS,EAAE6B,QAAQ,EAAE;kBAyBxCC;QAxBb,MAAMzB,SAAST,IAAIS,MAAM;QACzB,IAAI,CAACA,QAAQ,MAAM,IAAI0B,MAAM;QAC7B,MAAMC,SAAS,CAACC,OAAsBC,MAA8BC,OAAuC;gBACzGC,UAAU;gBACV9B,IAAI2B,MAAM3B,EAAE;gBACZ+B,WAAWJ,MAAMI,SAAS;gBAC1BhC;gBACAiC,eAAeL,MAAMK,aAAa;gBAClCC,OAAON,MAAMnC,IAAI;eACboC,OAAO;gBAAEA;YAAK,IAAI,CAAC,GACnBC,OAAO;gBAAEA;YAAK,IAAI,CAAC;QAEzB,yEAAyE;QACzE,MAAMK,gBAAQX,4BAAAA,SAAW,mCAAoB1E,eAAe;QAE5D,kGAAkG;QAClG,MAAMsF,UAAUzE,aAAa4B,KAAK;QAClC,MAAM8C,UAAU,MAAMtE,YAAY4B,WAAW;YAAEoB,MAAM;YAAaf;YAAQC,IAAImC;QAAQ;QACtF,IAAIC,SAAS,OAAO3D,iBAAiByD,OAAO;YAAEG,SAAS;gBAACX,OAAOU;aAAS;QAAC;QAEzE,MAAM,CAACE,WAAWd,YAAY,GAAG,MAAMe,QAAQC,GAAG,CAAC;YACjDrB,cAAc7B,IAAIuB,KAAK,EAAEd,QAAQ;gBAAEL;YAAU;YAC7CA,UAAU+C,UAAU,CAAC,QAAQC,GAAG,CAACpD,IAAIuB,KAAK,EAAE8B,GAAG;SAChD;QACD,MAAM5B,OAAOS,oBAAAA,YAAYoB,IAAI,cAAhBpB,oBAAsB;QACnC,MAAMjC,OAAOrB,gBAAgBoB;QAC7B,MAAMuD,WAAWtE,eAAegB,MAAM;QACtC,MAAMC,OAAOqD,CAAAA,4BAAAA,SAAUrD,IAAI,KAAIP;QAE/B,yEAAyE;QACzE,uEAAuE;QACvE,sEAAsE;QACtE,+DAA+D;QAC/D,0EAA0E;QAC1E,sCAAsC;QACtC,MAAM6D,SAASD,CAAAA,4BAAAA,SAAUE,WAAW,IAChCT,UAAUU,UAAU,CAACC,IAAI,CAAC,CAACC,YAAcA,UAAUlD,EAAE,KAAK6C,SAASE,WAAW,IAC9EI;QACJ,MAAMC,eAAeN,SACjB,MAAMrD,wBAAwBC,WAAW;YAAEK;YAAQC,IAAI8C,OAAO9C,EAAE;YAAER,MAAMsD,OAAOtD,IAAI;YAAED;QAAK,KAC1F;QACJ,IAAIsD,CAAAA,4BAAAA,SAAUE,WAAW,KAAID,UAAU,EAACM,gCAAAA,aAAcjD,QAAQ,CAACN,MAAM,GAAE;YACrE,MAAMwD,OAAO,MAAMjC,UAAU,aAAa;gBACxCP,OAAOvB,IAAIuB,KAAK;gBAChBd;gBACAuD,UAAUT,SAASE,WAAW;gBAC9BvD;gBACA+D,KAAKjE,IAAIkE,SAAS;gBAClBzC,KAAKA;gBACL,sEAAsE;gBACtE,oEAAoE;gBACpE,sEAAsE;gBACtE,mEAAmE;gBACnE0C,YAAYtB;YACd;YACA,IAAIkB,KAAKK,EAAE,EAAE;oBAe0BL;gBAdrC,MAAMrB,gBAAgB,MAAMnE,gBAAgB6B,WAAWK;gBACvD,mEAAmE;gBACnE,oEAAoE;gBACpE,oEAAoE;gBACpE,sBAAsB;gBACtB,MAAM4D,SAAS,MAAMlE,wBAAwBC,WAAW;oBACtDK;oBACAC,IAAIqD,KAAKrD,EAAE;oBACXR,MAAM6D,KAAK7D,IAAI;oBACfD;gBACF;gBACA,IAAIoE,QAAQ,OAAOlF,iBAAiByD,OAAO;oBAAEyB;gBAAO;gBACpD,OAAOlF,iBAAiByD,OAAO;oBAC7BG,SAAS;wBACPX,OAAO;4BAAE1B,IAAIqD,KAAKrD,EAAE;4BAAE+B,SAAS,GAAEsB,kBAAAA,KAAKtB,SAAS,YAAdsB,kBAAkB;4BAAM7D,MAAM6D,KAAK7D,IAAI;4BAAEwC;wBAAc;qBACzF;gBACH;YACF;YACA,IAAIqB,KAAKK,EAAE,KAAK,SAASL,KAAKO,MAAM,KAAK,KAAK;gBAC5C,OAAOnF,iBAAiByD,OAAO;oBAAEyB,QAAQrF,cAAc+E,KAAKQ,KAAK;gBAAE;YACrE;QACF;QAEA,MAAMC,YAAY,MAAMlG,wBAAwB8B,WAAW;YAAEoB,MAAM;YAAaf;YAAQgB;QAAI;QAC5F,IAAI+C,WAAW,OAAOrF,iBAAiByD,OAAO;YAAEyB,QAAQrF,cAAcwF;QAAW;QAEjF,IAAIC,QAAiC,EAAE;QACvC,MAAMC,SAAS,MAAMlH,uBAAuB,aAAa;YACvDmH,MAAM;YACN/B;YACAgC,cAAchF;YACdoD;YACA6B,UAAU;gBAAC;oBAAEC,MAAM;oBAAQC,SAAShF,qBAAqBC,KAAKC,MAAMC;gBAAM;aAAE;YAC5E8E,MAAMpH;YACN0B,WAAWC,6BAA6BD,SAAS,CAACsD;WAC9CtF,gBAAgB,CAAC,gBAAgB,CAAC2H,QAAQ,GAC1C;YAAEA,UAAU3H,gBAAgB,CAAC,gBAAgB,CAAC2H,QAAQ;QAAC,IACvD,CAAC,GACD3H,gBAAgB,CAAC,gBAAgB,CAAC4H,MAAM,GACxC;YAAEA,QAAQ5H,gBAAgB,CAAC,gBAAgB,CAAC4H,MAAM;QAAC,IACnD,CAAC;YACL,4GAA4G;YAC5G5D,SAAS;gBACP6D,kBAAkB;eACd5B,WAAW;gBAAE6B,eAAehI,gBAAgB6C,MAAM;oBAAEoF,QAAQ9B,SAASrD,IAAI;gBAAC;YAAG,IAAI,CAAC;YAExFoF,QAAQpH,iBAAiB;gBACvB8E;gBACA/C;gBACAsF,SAAS,CAAC/E;oBACRiE,QAAQjE;gBACV;YACF;WACIwB,SAAS;YAAEA;QAAO,IAAI,CAAC;QAE7B,MAAMwD,QAAQ1G,kBAAkB4F;QAChC,IAAIA,OAAOJ,MAAM,KAAK,WAAW,OAAO,aAAKkB;YAAOC,SAAS;;QAC7D,IAAIf,OAAOJ,MAAM,KAAK,eAAe,OAAO,aAAKkB;YAAOnB,QAAQxF,iBAAiB6F;;QAEjF,MAAMrC,QAAQ,MAAM3D,aAAa0B,WAAW;YAC1CoB,MAAM;YACNf;YACAC,IAAImC;YACJoB,KAAKjE,IAAIkE,SAAS;YAClBzC;YACAvB;YACAwF,OAAOhB,OAAOiB,KAAK,CAACD,KAAK;YACzBE,QAAQlB,OAAOiB,KAAK,CAACC,MAAM;YAC3BnB;YACA1C;QACF;QACA,IAAIM,MAAM+B,EAAE,KAAK,OAAO;YACtB,IAAI/B,MAAMiC,MAAM,KAAK,KAAK,MAAM,IAAInC,MAAM,CAAC,KAAK,EAAE1B,OAAO,2CAA2C,CAAC;YACrG,OAAO,aAAK+E;gBAAOnB,QAAQrF,cAAcqD,MAAMkC,KAAK;;QACtD;QACA,yFAAyF;QACzF,MAAMhC,OAAO5D,qBAAqB;YAChCkH,MAAM;gBAAED,QAAQlB,OAAOiB,KAAK,CAACC,MAAM;gBAAEF,OAAOhB,OAAOiB,KAAK,CAACD,KAAK;YAAuC;YACrG1C;YACAe,MAAMU,MAAMqB,GAAG,CAAC,CAACC,OAAU,OAAOA,KAAKC,YAAY,KAAK,WAAWD,KAAKC,YAAY,GAAG;QACzF;QACA,OAAO,aAAKR;YAAOzC,SAAS;gBAACX,OAAOC,OAAOqC,OAAOiB,KAAK,CAACrD,IAAI,EAAEC;aAAM;;IACtE;AACF;AAEA,uGAAuG,GACvG,OAAO,MAAM0D,wBAAwBnI,wBACnC,aACA,iBACA6D,4BACD;AAED,uGAAuG,GACvG,OAAO,SAASuE;IACd9G,kBAAkB,aAAa6G,uBAAuB;QACpDvG,WAAWD;QACX0G,cAAcnI,4BAA4B;IAC5C;IACAH,uBAAuB,aAAawD;AACtC"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-component-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 { throwIfAiJobCanceled } from './ai-job-cancel'\nimport type { AglynOrgBilling } from '@aglyn/aglyn/foundation/definitions/org-billing.types'\nimport type { ReusableComponentProp } from '@aglyn/aglyn/foundation/definitions/platform.types'\nimport { duplicateResource } from '@aglyn/tenant-data-admin/server/duplicate-resource'\nimport { aiPlanEmbedsFor } from '../model/ai-build-plan'\nimport type { AiJob, AiJobOutput, AiJobPlan, AiJobReview } from '../model/ai-jobs.types'\nimport { AI_STEP_TIERS } from '../providers/catalog'\nimport { AI_ROUTING_TABLE, aiModelForStep } from '../providers/routing'\nimport { runValidatedGeneration } from '../runtime/ai-doctrine'\nimport type { AiDoctrineTree } from '../runtime/ai-doctrine-validators'\nimport type { AiLoadEstimate } from '../runtime/ai-palette'\nimport type { AiSystemBlock } from '../runtime/ai-runtime'\nimport { readSiteInventory } from '../runtime/site-inventory'\nimport {\n AI_COMPONENT_TOOL_NAME,\n aiComponentPropKindWords,\n aiComponentTool,\n} from '../tools/ai-component-tool'\nimport { registerAiJobAdmission, type AiJobAdmission } from './ai-job-admission'\nimport {\n aiBuildingPlanCreations,\n aiJobPlanCreationsRefusal,\n aiPlanCreationsRunMinimumMs,\n} from './ai-job-plan-creations'\nimport { aiJobStepBudget } from './ai-job-budget'\nimport { aiComponentCheck, aiPlannedComponentProps } from './ai-job-component-checks'\nimport { aiJobDraftId } from './ai-job-draft-ids'\nimport {\n aiDraftAdmissionRefusal,\n aiDraftAllowanceRefusal,\n aiSiteSubdomain,\n readAiDraft,\n readAiDraftComponentProps,\n writeAiDraft,\n type AiDraftRecord,\n} from './ai-job-drafts'\nimport {\n aiBracketedFactsNote,\n aiConfirmedPlan,\n aiDoctrineReview,\n aiGenerationSpent,\n aiJobBriefLine,\n aiLimitReview,\n aiPlanCreation,\n aiPlanReferenceLines,\n aiUnspentOutcome,\n} from './ai-job-generation'\nimport type { AiJobStepRunner } from './ai-job-text-step'\nimport { registerAiJobStep } from './ai-jobs'\n\n/**\n * The component step (AGL-2908): the generation step of a `component` job,\n * run once a member confirmed the job's plan.\n *\n * It builds ONE reusable component from the brief — its tree, and the typed\n * properties each page that places it fills in — through\n * `runValidatedGeneration('component', …)`, so every building rule holds on\n * the tree before it is kept. The tree binds each property with\n * `{{prop.<name>}}`; the palette validator keeps those tokens as written\n * (`definesComponent`), and the step's own check, through `extend`, holds\n * each binding to what the Properties dialog and the Attributes panel allow\n * (`ai-job-component-checks.ts`).\n *\n * The component lands as a new draft (`ai-job-drafts.ts`) that no page\n * places, and gets its first version when a member opens it, as every\n * component created outside the besigner does. A plan that starts from a copy\n * of a component the site has gets that copy, through the platform's\n * duplicate module, and nothing generated.\n *\n * A generation step runs on the job beat, never at an inline door: it\n * registers the least time its lookup rounds, its answer and its re-ask need\n * (AGL-3035), runs without extended thinking, and asks for no more of the\n * ceiling the layout and template steps keep than fits that time on the model\n * it runs. The step's spec measures the request and the answer against it.\n */\n\n/** The longest answer a component may run to; the tree is held to the component budget either way. */\nexport const AI_JOB_COMPONENT_MAX_TOKENS = AI_ROUTING_TABLE['job.component'].maxTokens\n\n/**\n * The component step's time (AGL-3035, AGL-3036): its two inventory-lookup\n * rounds, its answer and its re-ask at the routing table's ceiling on the tier\n * `job.component` is served from, fitted to what a beat can give a step.\n */\nexport const AI_JOB_COMPONENT_STEP_BUDGET = aiJobStepBudget({\n tier: AI_STEP_TIERS['job.component'],\n maxTokens: AI_JOB_COMPONENT_MAX_TOKENS,\n})\n\n/** The least time one component step needs before it starts. */\nexport const AI_JOB_COMPONENT_STEP_MINIMUM_MS = AI_JOB_COMPONENT_STEP_BUDGET.minimumMs\n\n/** A component's name when the plan names none. */\nexport const AI_JOB_COMPONENT_DEFAULT_NAME = 'Component'\n\n/** The component step's own instructions, cached after the doctrine and before the catalog. */\nexport const AI_JOB_COMPONENT_INSTRUCTIONS: readonly AiSystemBlock[] = [\n {\n text: [\n `You build one reusable component for a website: a block the site places on many pages, each placement filling in its own values. Answer with ${AI_COMPONENT_TOOL_NAME}: the component’s tree as one flat node map, and the properties it declares.`,\n 'The root is the document wrapper holding the component. A component is placed inside a page section, which opens with its own h2: it carries no main landmark, its headings are h3 or below, and its colors, spacing and type come from the theme.',\n 'Every value that should differ from one placement to the next is a property, bound in the tree with its token {{prop.<name>}}. Declare every property the tree binds, and bind every property you declare.',\n `Property kinds, as type (the name a page reads): ${aiComponentPropKindWords()}.`,\n 'A property binds only to a field that holds its kind of value:',\n '- text, richText and number: copy, such as a Typography’s text, an Image’s alt or a Button’s label, as the whole value or inside a sentence.',\n '- image: an Image’s src, as the whole value.',\n '- href: a screenId or an href, as the whole value.',\n '- icon: an Icon element’s iconId, as the whole value.',\n '- boolean: a switch setting such as a Button’s fullWidth, or hideIf on the part it hides, as the whole value.',\n '- choice: a setting with fixed options such as a Button’s variant, as the whole value, with every answer’s value one that setting lists.',\n 'An optional part, such as a photo, a badge or a second button, gets a boolean property labeled \"Hide <the part>\" whose default is \"false\", and the part’s own element carries \"hideIf\": \"{{prop.<name>}}\". Never hide the whole component.',\n 'A default is what the component shows until a page sets it: copy in the site’s voice from the brief, with a fact the brief does not give in square brackets; for href a page id from the site inventory, or \"\"; for image \"\", for an upload; for icon \"\", for the site owner to pick; for choice one of its answers’ values.',\n ].join('\\n'),\n },\n]\n\n/** The component job as the generation's user turn: the name, the brief, the confirmed plan. */\nexport function aiJobComponentPrompt(\n job: Pick<AiJob, 'brief'>,\n plan: AiJobPlan | null,\n name: string,\n): string {\n return [`Component name: ${name}`, aiJobBriefLine(job), ...aiPlanReferenceLines(plan)].join('\\n')\n}\n\n/**\n * The props the confirmed plan gives the component, against the component a\n * COPY actually produced (AGL-3024); `null` when the copy keeps the plan, or\n * when the plan promised no prop to keep.\n *\n * The third kind to need this, after the layout (`aiCopiedLayoutReview`) and\n * the page (`aiCopiedPageReview`), and found the same way: a duplicate branch\n * generates nothing, so it is the one place a plan's promise can go unmet\n * with no model to answer for it.\n *\n * MEASURED 2026-09-21. A plan read \"the existing card has no link target; the\n * brief needs one\", and `practice-area-link-card` came out BYTE-IDENTICAL to\n * `practice-area-card` — the same seven nodes under the same ids, the same\n * three props, no link anywhere. Its job reported the copy as built. So did\n * `practice-area-link-card-v2` the following pass. A component job can\n * therefore charge a member for a duplicate of something they already had.\n *\n * A copy that cannot be read back settles nothing, and a promise this step\n * cannot settle is reviewed by a person rather than reported as kept.\n */\nexport async function aiCopiedComponentReview(\n firestore: FirebaseFirestore.Firestore,\n input: { hostId: string; id: string; name: string; plan: AiJobPlan | null },\n): Promise<AiJobReview | null> {\n const promised = aiPlannedComponentProps(input.plan)\n if (!promised.length) return null\n const declared = await readAiDraftComponentProps(firestore, { hostId: input.hostId, id: input.id })\n if (declared === null) {\n return {\n reason: 'doctrine',\n message: `\"${input.name}\" was copied from the component the plan names, and this job could not read the copy back to check it has the properties the plan gives it. Open the component and check it before you place it.`,\n findings: [],\n }\n }\n const missing = promised.filter((name) => !declared.includes(name))\n if (!missing.length) return null\n const one = missing.length === 1\n return aiDoctrineReview({\n message: `\"${input.name}\" is a copy of the component the plan names, and a copy is all it is: the plan gives it ${\n one ? 'a property' : 'properties'\n } the copy does not have.`,\n violations: [\n {\n rule: null,\n code: 'plan-props-missing',\n message: `The confirmed plan says this component has ${missing.join(', ')}, and the copy has ${\n declared.length ? declared.join(', ') : 'none of them'\n }. Add ${one ? 'it' : 'each of them'}, and use ${\n one ? 'it' : 'them'\n } in the component, or the copy is the component it was copied from.`,\n },\n ],\n })\n}\n\n/**\n * A component job is admitted for a site of the job's own org whose plan\n * includes reusable components, and confirmed only for a plan whose other\n * creations it builds (AGL-3143 §15).\n */\nexport const aiComponentJobAdmission: AiJobAdmission = (context) =>\n aiDraftAdmissionRefusal(context.firestore, {\n orgId: context.orgId,\n hostId: context.hostId,\n kind: 'component',\n org: context.org,\n ownCheck: (hostId) => aiJobPlanCreationsRefusal('component', context, hostId),\n })\n\nexport interface AiJobComponentStepDeps {\n /** The inventory reader; specs hand in a fake. */\n readInventory?: typeof readSiteInventory\n /** The platform's duplicate module, for a plan that starts from a copy. */\n duplicate?: typeof duplicateResource\n}\n\nexport function createAiJobComponentStep(deps: AiJobComponentStepDeps = {}): AiJobStepRunner {\n const readInventory = deps.readInventory ?? readSiteInventory\n const duplicate = deps.duplicate ?? duplicateResource\n return async ({ job, now, signal, firestore, modelFor }) => {\n const hostId = job.hostId\n if (!hostId) throw new Error('a component job names no site, and its admission refuses one')\n const output = (draft: AiDraftRecord, load?: AiLoadEstimate | null, note?: string | null): AiJobOutput => ({\n resource: 'reusableComponent',\n id: draft.id,\n versionId: draft.versionId,\n hostId,\n hostSubdomain: draft.hostSubdomain,\n label: draft.name,\n ...(load ? { load } : {}),\n ...(note ? { note } : {}),\n })\n // The switch's answer for this job, else the routing table's (AGL-2942).\n const model = modelFor?.('job.component') ?? aiModelForStep('job.component')\n\n // A run cut off after its draft was written reports that draft, found by the id the job recorded.\n const draftId = aiJobDraftId(job, 'component')\n const written = await readAiDraft(firestore, { kind: 'component', hostId, id: draftId })\n if (written) return aiUnspentOutcome(model, { outputs: [output(written)] })\n\n const [inventory, orgSnapshot] = await Promise.all([\n readInventory(job.orgId, hostId, { firestore }),\n firestore.collection('orgs').doc(job.orgId).get(),\n ])\n const org = (orgSnapshot.data() ?? null) as Partial<AglynOrgBilling> | null\n const plan = aiConfirmedPlan(job)\n const creation = aiPlanCreation(plan, 'component')\n const name = creation?.name || AI_JOB_COMPONENT_DEFAULT_NAME\n\n // The plan starts from a copy of a component the site has (rule 15): the\n // copy is the draft, and nothing is generated. A source gone since the\n // plan was made is built from the brief instead, and one that lacks a\n // property the plan gives the component is built from the plan\n // (AGL-3143 §14): copying it could only end in the review below. A source\n // this step cannot read still copies.\n const source = creation?.duplicateOf\n ? inventory.components.find((component) => component.id === creation.duplicateOf)\n : undefined\n const sourceReview = source\n ? await aiCopiedComponentReview(firestore, { hostId, id: source.id, name: source.name, plan })\n : null\n if (creation?.duplicateOf && source && !sourceReview?.findings.length) {\n const copy = await duplicate('component', {\n orgId: job.orgId,\n hostId,\n sourceId: creation.duplicateOf,\n name,\n uid: job.createdBy,\n org: org as Record<string, unknown> | null,\n // The same claim the page step makes (AGL-3024): a copy core mints is\n // written under core's own id, never `draftId`, so a step that runs\n // again — a pass resumed, a run cut off after the copy — would mint a\n // second. The job's recorded id makes core replay the one it made.\n attemptKey: draftId,\n })\n if (copy.ok) {\n const hostSubdomain = await aiSiteSubdomain(firestore, hostId)\n // The copy is reported with what the plan promised of it read back\n // (AGL-3024): this branch generates nothing, so without this a plan\n // reading \"duplicate it and add a link\" reports Done on a duplicate\n // that added nothing.\n const review = await aiCopiedComponentReview(firestore, {\n hostId,\n id: copy.id,\n name: copy.name,\n plan,\n })\n if (review) return aiUnspentOutcome(model, { review })\n return aiUnspentOutcome(model, {\n outputs: [\n output({ id: copy.id, versionId: copy.versionId ?? null, name: copy.name, hostSubdomain }),\n ],\n })\n }\n if (copy.ok === false && copy.status === 403) {\n return aiUnspentOutcome(model, { review: aiLimitReview(copy.error) })\n }\n }\n\n const allowance = await aiDraftAllowanceRefusal(firestore, { kind: 'component', hostId, org })\n if (allowance) return aiUnspentOutcome(model, { review: aiLimitReview(allowance) })\n\n let props: ReusableComponentProp[] = []\n const result = await runValidatedGeneration('component', {\n step: 'job.component',\n model,\n instructions: AI_JOB_COMPONENT_INSTRUCTIONS,\n inventory,\n messages: [{ role: 'user', content: aiJobComponentPrompt(job, plan, name) }],\n tool: aiComponentTool(),\n maxTokens: AI_JOB_COMPONENT_STEP_BUDGET.maxTokens(model),\n ...(AI_ROUTING_TABLE['job.component'].thinking\n ? { thinking: AI_ROUTING_TABLE['job.component'].thinking }\n : {}),\n ...(AI_ROUTING_TABLE['job.component'].effort\n ? { effort: AI_ROUTING_TABLE['job.component'].effort }\n : {}),\n // A third-party player the confirmed plan lists for this component is the only one it may embed (AGL-3433).\n context: {\n definesComponent: true,\n ...(creation ? { plannedEmbeds: aiPlanEmbedsFor(plan, { create: creation.name }) } : {}),\n },\n extend: aiComponentCheck({\n inventory,\n plan,\n onProps: (declared) => {\n props = declared\n },\n }),\n ...(signal ? { signal } : {}),\n })\n const spent = aiGenerationSpent(result)\n if (result.status === 'refused') return { ...spent, refused: true }\n if (result.status === 'needs_input') return { ...spent, review: aiDoctrineReview(result) }\n\n // Never written once the job is canceled (AGL-3616).\n throwIfAiJobCanceled(signal)\n const draft = await writeAiDraft(firestore, {\n kind: 'component',\n hostId,\n id: draftId,\n uid: job.createdBy,\n org,\n name,\n nodes: result.value.nodes,\n rootId: result.value.rootId,\n props,\n now,\n })\n if (draft.ok === false) {\n if (draft.status === 404) throw new Error(`site ${hostId} vanished while its component was generated`)\n return { ...spent, review: aiLimitReview(draft.error) }\n }\n // The facts the brief did not give, in the tree or the defaults a page shows (AGL-3056).\n const note = aiBracketedFactsNote({\n tree: { rootId: result.value.rootId, nodes: result.value.nodes as unknown as AiDoctrineTree['nodes'] },\n inventory,\n copy: props.map((prop) => (typeof prop.defaultValue === 'string' ? prop.defaultValue : '')),\n })\n return { ...spent, outputs: [output(draft, result.value.load, note)] }\n }\n}\n\n/** The component step, building the forms and other components its plan creates first (AGL-3143 §15). */\nexport const runAiJobComponentStep = aiBuildingPlanCreations(\n 'component',\n 'job.component',\n createAiJobComponentStep(),\n)\n\n/** Registers the component step and the check a component job passes before it is created or resumed. */\nexport function registerAiComponentJob(): void {\n registerAiJobStep('component', runAiJobComponentStep, {\n minimumMs: AI_JOB_COMPONENT_STEP_MINIMUM_MS,\n minimumMsFor: aiPlanCreationsRunMinimumMs('component'),\n })\n registerAiJobAdmission('component', aiComponentJobAdmission)\n}\n"],"names":["throwIfAiJobCanceled","duplicateResource","aiPlanEmbedsFor","AI_STEP_TIERS","AI_ROUTING_TABLE","aiModelForStep","runValidatedGeneration","readSiteInventory","AI_COMPONENT_TOOL_NAME","aiComponentPropKindWords","aiComponentTool","registerAiJobAdmission","aiBuildingPlanCreations","aiJobPlanCreationsRefusal","aiPlanCreationsRunMinimumMs","aiJobStepBudget","aiComponentCheck","aiPlannedComponentProps","aiJobDraftId","aiDraftAdmissionRefusal","aiDraftAllowanceRefusal","aiSiteSubdomain","readAiDraft","readAiDraftComponentProps","writeAiDraft","aiBracketedFactsNote","aiConfirmedPlan","aiDoctrineReview","aiGenerationSpent","aiJobBriefLine","aiLimitReview","aiPlanCreation","aiPlanReferenceLines","aiUnspentOutcome","registerAiJobStep","AI_JOB_COMPONENT_MAX_TOKENS","maxTokens","AI_JOB_COMPONENT_STEP_BUDGET","tier","AI_JOB_COMPONENT_STEP_MINIMUM_MS","minimumMs","AI_JOB_COMPONENT_DEFAULT_NAME","AI_JOB_COMPONENT_INSTRUCTIONS","text","join","aiJobComponentPrompt","job","plan","name","aiCopiedComponentReview","firestore","input","promised","length","declared","hostId","id","reason","message","findings","missing","filter","includes","one","violations","rule","code","aiComponentJobAdmission","context","orgId","kind","org","ownCheck","createAiJobComponentStep","deps","readInventory","duplicate","now","signal","modelFor","orgSnapshot","Error","output","draft","load","note","resource","versionId","hostSubdomain","label","model","draftId","written","outputs","inventory","Promise","all","collection","doc","get","data","creation","source","duplicateOf","components","find","component","undefined","sourceReview","copy","sourceId","uid","createdBy","attemptKey","ok","review","status","error","allowance","props","result","step","instructions","messages","role","content","tool","thinking","effort","definesComponent","plannedEmbeds","create","extend","onProps","spent","refused","nodes","value","rootId","tree","map","prop","defaultValue","runAiJobComponentStep","registerAiComponentJob","minimumMsFor"],"mappings":"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"}