@aglyn/plugins-ai 1.0.0-beta.227 → 1.0.0-beta.228
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/package.json +17 -17
- package/src/lib/components/ai-brief-dialog.component.js +1 -1
- package/src/lib/components/ai-brief-dialog.component.js.map +1 -1
- package/src/lib/components/ai-describe-automation.component.js +7 -2
- package/src/lib/components/ai-describe-automation.component.js.map +1 -1
- package/src/lib/components/ai-describe-button.component.js +6 -1
- package/src/lib/components/ai-describe-button.component.js.map +1 -1
- package/src/lib/components/ai-describe-page.component.d.ts +1 -1
- package/src/lib/components/ai-describe-page.component.js +1 -1
- package/src/lib/components/ai-describe-page.component.js.map +1 -1
- package/src/lib/components/ai-job-events.d.ts +7 -1
- package/src/lib/components/ai-job-events.js +4 -2
- package/src/lib/components/ai-job-events.js.map +1 -1
- package/src/lib/components/ai-job-site.d.ts +22 -0
- package/src/lib/components/ai-job-site.js +73 -0
- package/src/lib/components/ai-job-site.js.map +1 -0
- package/src/lib/components/ai-jobs-list-page.component.d.ts +23 -0
- package/src/lib/components/ai-jobs-list-page.component.js +303 -0
- package/src/lib/components/ai-jobs-list-page.component.js.map +1 -0
- package/src/lib/components/ai-jobs-page.component.d.ts +7 -0
- package/src/lib/components/ai-jobs-page.component.js +30 -0
- package/src/lib/components/ai-jobs-page.component.js.map +1 -0
- package/src/lib/components/ai-page-brief-dialog.component.d.ts +1 -1
- package/src/lib/components/ai-page-brief-dialog.component.js.map +1 -1
- package/src/lib/components/ai-site-build-page.component.d.ts +30 -2
- package/src/lib/components/ai-site-build-page.component.js +359 -241
- package/src/lib/components/ai-site-build-page.component.js.map +1 -1
- package/src/lib/components/ai-site-start-card.component.d.ts +8 -0
- package/src/lib/components/ai-site-start-card.component.js +20 -11
- package/src/lib/components/ai-site-start-card.component.js.map +1 -1
- package/src/lib/components/assist-panel.component.js +9 -4
- package/src/lib/components/assist-panel.component.js.map +1 -1
- package/src/lib/jobs/ai-job-draft-ids.d.ts +6 -0
- package/src/lib/jobs/ai-job-draft-ids.js +9 -0
- package/src/lib/jobs/ai-job-draft-ids.js.map +1 -1
- package/src/lib/jobs/ai-job-drafts.d.ts +7 -0
- package/src/lib/jobs/ai-job-drafts.js +16 -7
- package/src/lib/jobs/ai-job-drafts.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-step.js +2 -1
- package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
- package/src/lib/jobs/ai-job-page-sections.d.ts +6 -5
- package/src/lib/jobs/ai-job-page-sections.js +3 -3
- package/src/lib/jobs/ai-job-page-sections.js.map +1 -1
- package/src/lib/jobs/ai-job-page-step.d.ts +19 -1
- package/src/lib/jobs/ai-job-page-step.js +50 -8
- package/src/lib/jobs/ai-job-page-step.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-step.js +7 -2
- package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-step.d.ts +21 -0
- package/src/lib/jobs/ai-job-site-step.js +96 -17
- package/src/lib/jobs/ai-job-site-step.js.map +1 -1
- package/src/lib/jobs/ai-job-text-step.d.ts +7 -1
- package/src/lib/jobs/ai-job-text-step.js.map +1 -1
- package/src/lib/jobs/ai-jobs.d.ts +9 -1
- package/src/lib/jobs/ai-jobs.js +9 -2
- package/src/lib/jobs/ai-jobs.js.map +1 -1
- package/src/lib/jobs/ai-site-publish.d.ts +98 -0
- package/src/lib/jobs/ai-site-publish.js +372 -0
- package/src/lib/jobs/ai-site-publish.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-page-briefs.d.ts +7 -6
- package/src/lib/jobs/fixtures/ai-page-briefs.js +23 -56
- package/src/lib/jobs/fixtures/ai-page-briefs.js.map +1 -1
- package/src/lib/model/ai-job-notice.d.ts +3 -1
- package/src/lib/model/ai-job-notice.js +18 -1
- package/src/lib/model/ai-job-notice.js.map +1 -1
- package/src/lib/model/ai-jobs.types.d.ts +27 -0
- package/src/lib/model/ai-jobs.types.js.map +1 -1
- package/src/lib/model/ai-plan-capabilities.d.ts +25 -10
- package/src/lib/model/ai-plan-capabilities.js +15 -2
- package/src/lib/model/ai-plan-capabilities.js.map +1 -1
- package/src/lib/model/ai-site-build-progress.d.ts +17 -1
- package/src/lib/model/ai-site-build-progress.js +64 -4
- package/src/lib/model/ai-site-build-progress.js.map +1 -1
- package/src/lib/model/ai-site-start-seo.d.ts +19 -0
- package/src/lib/model/ai-site-start-seo.js +91 -3
- package/src/lib/model/ai-site-start-seo.js.map +1 -1
- package/src/lib/plugin.js +18 -10
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/runtime/ai-doctrine-validators.d.ts +16 -12
- package/src/lib/runtime/ai-doctrine-validators.js +19 -36
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-doctrine.js +1 -1
- package/src/lib/runtime/ai-doctrine.js.map +1 -1
- package/src/lib/runtime/ai-eval-live.d.ts +1 -1
- package/src/lib/runtime/ai-eval-live.js +1 -1
- package/src/lib/runtime/ai-eval-live.js.map +1 -1
- package/src/lib/server/ai-jobs-live-sites.d.ts +26 -0
- package/src/lib/server/ai-jobs-live-sites.js +58 -0
- package/src/lib/server/ai-jobs-live-sites.js.map +1 -0
- package/src/lib/server/ai-jobs-route.js +22 -3
- package/src/lib/server/ai-jobs-route.js.map +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-page-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 { buildPageMarkdown } from '@aglyn/aglyn/app-utils/page-markdown'\nimport { checkEntitlement } from '@aglyn/aglyn/app-utils/plan-entitlements'\nimport { SCREEN_SEO_TEXT_GUIDANCE } from '@aglyn/aglyn/app-utils/screen-seo-fields'\nimport { CANVAS_ROOT_ELEMENT_ID } from '@aglyn/aglyn/foundation/constants/canvas'\nimport type { AglynOrgBilling } from '@aglyn/aglyn/foundation/definitions/org-billing.types'\nimport { duplicateResource } from '@aglyn/tenant-data-admin/server/duplicate-resource'\nimport { AI_BUILD_PLAN_LIMITS, aiPlanEmbedsFor, type AiBuildPlanScreen } from '../model/ai-build-plan'\nimport {\n AI_PAGE_CREATE_KINDS,\n aiPageCreationRefusal,\n aiPagePlanRefusal,\n parseAiPageJobInputs,\n} from '../model/ai-page-job'\nimport { aiPlanCapabilitiesForJob, aiPlanUncreatable } from '../model/ai-plan-capabilities'\nimport { aiSiteWords } from '../model/ai-site-job'\nimport type { AiJob, AiJobOutput, AiJobPlan, AiJobReview } from '../model/ai-jobs.types'\nimport { aiModelForStep } from '../providers/routing'\nimport { aiDoctrineNeedsInputMessage, runValidatedGeneration } from '../runtime/ai-doctrine'\nimport { validateAiDoctrineTree, type AiDoctrineTree } from '../runtime/ai-doctrine-validators'\nimport type { AiLoadEstimate } from '../runtime/ai-palette'\nimport { AI_SEO_FIELDS_MAX_TOKENS, generateSeoFields } from '../runtime/seo-fields'\nimport { readSiteInventory } from '../runtime/site-inventory'\nimport { registerAiJobAdmission, type AiJobAdmission } from './ai-job-admission'\nimport { aiGenerationWorstCaseMs } from './ai-job-budget'\nimport { aiJobDraftId } from './ai-job-draft-ids'\nimport {\n aiDraftAdmissionRefusal,\n aiDraftAllowanceRefusal,\n aiSiteSubdomain,\n readAiDraft,\n readAiDraftNodes,\n readAiPlanCapabilities,\n updateAiDraftNodes,\n writeAiDraft,\n writeAiDraftScreenSeo,\n type AiDraftRecord,\n} from './ai-job-drafts'\nimport {\n aiBracketedFactsNote,\n aiConfirmedPlan,\n aiDoctrineReview,\n aiGenerationSpent,\n aiLimitReview,\n aiModelNodeIds,\n aiUnspentOutcome,\n} from './ai-job-generation'\nimport {\n AI_JOB_PAGE_SECTION_MAX_TOKENS,\n AI_JOB_PAGE_SECTION_TOKENS,\n AI_JOB_PAGE_STEP_BUDGET,\n AI_JOB_PAGE_STEP_MINIMUM_MS,\n aiJobPageSectionMaxTokens,\n} from './ai-job-page-budget'\nimport {\n AI_JOB_PAGE_INSTRUCTIONS,\n AI_PAGE_SECTION_TOOL,\n aiEmptyPage,\n aiPageCheckContext,\n aiPageRecordNote,\n aiPageRecordTemplate,\n aiPageRecordTokens,\n aiPageSectionCheck,\n aiPageSectionNodeId,\n aiPageSectionPrompt,\n aiPageSectionSmaller,\n aiPageWithSection,\n type AiPageSection,\n} from './ai-job-page-sections'\nimport { AI_PLAN_ITEMS_MIN, aiPlanCopiedPageViolations } from './ai-job-plan-conformance'\nimport {\n aiCreationUnit,\n aiRunJobUnit,\n aiSiteBuiltRefs,\n aiSitePendingUnits,\n aiSiteUnitJob,\n type AiSiteUnit,\n} from './ai-job-site-step'\nimport type { AiJobStepOutcome, AiJobStepRunner } from './ai-job-text-step'\nimport {\n aiJobStepRunMinimumMs,\n aiJobStepRunnerFor,\n registerAiJobStep,\n registerAiJobStepPasses,\n} from './ai-jobs'\n\n/**\n * The page step (AGL-2907): the generation step of a `page` job, run once a\n * member confirmed the job's plan. It builds ONE screen, section by section,\n * from what the site already has, and leaves it an unpublished draft.\n *\n * ── Why a page is built in passes ────────────────────────────────────────\n *\n * A page is far larger than one answer the job beat can wait for: the beat\n * gives a step what is left of its budget, and a whole page, answered and re-asked,\n * takes several times that. The confirmed plan already names the page's\n * sections, so the step builds one section per pass and asks the machine to\n * continue. Every pass is one reservation and one generation — the section's\n * lookup rounds, its answer and, when it breaks a rule, its one re-ask — sized\n * so that worst case fits `AI_JOB_PAGE_STEP_MINIMUM_MS`, which the step\n * registers as the least time a section pass needs before it starts.\n *\n * - The first pass builds the top section, which holds the page's h1, and\n * writes the draft screen and its first version.\n * - Each later pass builds the next section and adds it to that version.\n * - The last pass generates no tree: it holds the whole page to the\n * doctrine, writes the search title and description, and reports the\n * draft with its measured weight and a navigation proposal.\n *\n * How a section is asked for and held to the rules as part of the page is\n * `ai-job-page-sections.ts`.\n *\n * ── What the plan creates comes first (AGL-3031) ─────────────────────────\n *\n * A plan that creates a layout, forms or components is built in the same job,\n * before its page, one creation a pass, through the site scaffold's unit\n * machinery: each creation is handed to the step that builds that kind, under\n * a job derived from this one, and lands as the draft that step writes. Where\n * the job stands is read from its outputs, so nothing is kept anywhere else.\n * Once every creation is built, the page's passes run on the plan with each\n * `new:<name>` resolved to the record that was built, which the site's\n * inventory now lists — so the page places the new component and binds the new\n * form by id, and renders inside the new layout, exactly as it would one the\n * site always had. A pass that builds a creation needs the time the step that\n * builds it registers, not a section's (`aiPageJobRunMinimumMs`, AGL-3035), so\n * neither the beat nor a door starts it with only a section's time left.\n *\n * ── Never published ──────────────────────────────────────────────────────\n *\n * The draft is written the way `/api/hosts/resources` and\n * `/api/hosts/versions` write a screen and its first version, and nothing\n * else is touched: not the host document, whose routing map is what the live\n * site serves, and no collection, store, layout or navigation pointer\n * (`ai-job-drafts.ts`). A screen the routing map does not name serves nothing\n * until a member publishes it.\n */\n\n/**\n * A section pass's time, and the ceiling each pass asks on each model, are\n * declared apart from the step (`ai-job-page-budget.ts`): the site scaffold\n * builds its pages through this step and reads them without loading it.\n */\nexport {\n AI_JOB_PAGE_SECTION_MAX_TOKENS,\n AI_JOB_PAGE_SECTION_TOKENS,\n AI_JOB_PAGE_STEP_BUDGET,\n AI_JOB_PAGE_STEP_MINIMUM_MS,\n aiJobPageSectionMaxTokens,\n}\n\n/**\n * Tokens one element takes in a section's answer, ESTIMATED: the tree as JSON\n * text inside the tool call, at four characters a token. Measured on the ten\n * golden pages at a median of 38 and a most of 43 over their 39 sections, and\n * `ai-job-page-evals.spec.ts` holds every golden section under it.\n */\nexport const AI_JOB_PAGE_TOKENS_PER_ELEMENT = 45\n\n/**\n * Real tokens to every token estimated at four characters (AGL-3042). A\n * section's ceiling is counted by the provider in its own tokens, which run\n * above that estimate. MEASURED on the first live document run on a Free\n * workspace (AGL-3024, test-org, 2026-09-16), as the cache read of a request\n * over the estimate of the same cached prefix: 4,059 over 2,825 for the page\n * plan and 6,649 over 4,310 for the layout, the higher of the two, 1.5427 —\n * the ratio `ai-job-free-page.spec.ts` prices a Free page at. It is measured on\n * prompt text; an answer's own is read off a recorded pass, whose output\n * tokens the step's record keeps (`lastRuns`), beside the section it stored.\n */\nexport const AI_JOB_PAGE_REAL_TOKENS_PER_ESTIMATED = Math.max(4_059 / 2_825, 6_649 / 4_310)\n\n/**\n * Real tokens one element takes in a section's answer: the estimate at its\n * golden most, in real tokens, rounded up — 45 × 1.5427 → 70 (AGL-3042).\n */\nexport const AI_JOB_PAGE_REAL_TOKENS_PER_ELEMENT = Math.ceil(\n AI_JOB_PAGE_TOKENS_PER_ELEMENT * AI_JOB_PAGE_REAL_TOKENS_PER_ESTIMATED,\n)\n\n/**\n * The most elements a section's request asks it to keep under at an answer\n * ceiling: 15 at the balanced tier's 1,050 (AGL-3042). Counted in real\n * tokens, because the ceiling is: counted in the estimate, the budget is 23\n * elements, which a section of the goldens' largest elements fills at 1,597\n * real tokens — half as much again as the ceiling that cuts it off.\n */\nexport function aiJobPageSectionMaxElements(maxTokens: number): number {\n return Math.max(1, Math.floor(maxTokens / AI_JOB_PAGE_REAL_TOKENS_PER_ELEMENT))\n}\n\n/** A page's name when the plan names none. */\nexport const AI_JOB_PAGE_DEFAULT_NAME = 'New page'\n\nexport const AI_JOB_PAGE_NO_PLAN_COPY = 'This page job has no confirmed plan to build.'\nexport const AI_JOB_PAGE_DELETED_COPY = 'The draft page was deleted while it was being built.'\n\n/** A creation the plan names that this deployment has no step to build. */\nexport const AI_JOB_PAGE_CREATION_UNAVAILABLE_COPY =\n 'This page needs something that cannot be built here yet. Describe the page again.'\n\n/** A creation that finished without reporting what it built; the page cannot place it. */\nexport const AI_JOB_PAGE_CREATION_EMPTY_COPY =\n 'Part of this page could not be built. Describe the page again.'\n\n/**\n * What a page refused on its last pass tells the member beside the rules it\n * broke (AGL-3143). The draft is reported with the review, and mending what\n * the findings name in it is what lets the next pass finish the job.\n */\nexport const AI_JOB_PAGE_REFUSED_DRAFT_COPY =\n 'The draft is yours to open: mend what these name in it, then try again.'\n\n/**\n * The most passes a page job may take (AGL-3031): every creation the plan\n * limits admit, every section they admit, and the last pass that reports the\n * draft. A creation's step builds it in one pass, and a page one section a\n * pass, so a runner that never finishes is still bounded while a real page is\n * not.\n */\nexport const AI_JOB_PAGE_MAX_PASSES = AI_BUILD_PLAN_LIMITS.create + AI_BUILD_PLAN_LIMITS.sections + 1\n\n/**\n * The units a plan's creations are built as, in the order a scaffold builds\n * them: the layout, then the forms, then the components. Each is addressed by\n * its place in the plan, so a unit run again finds its own draft.\n */\nexport function aiPageJobUnits(plan: Pick<AiJobPlan, 'create'>): AiSiteUnit[] {\n return AI_PAGE_CREATE_KINDS.flatMap((kind) =>\n plan.create.flatMap((creation, index) => {\n if (creation.kind !== kind) return []\n const unit = aiCreationUnit(creation, `c${index}`)\n return unit ? [unit] : []\n }),\n )\n}\n\n/**\n * The least time this page job's next pass needs (AGL-3035). A pass that\n * builds a creation hands it to the step registered for that kind, under the\n * job derived for it, so it needs what that step registers for that job — a\n * layout's time, not a section's. Every other pass — a section, the last one —\n * needs a section pass's.\n */\nexport function aiPageJobRunMinimumMs(job: AiJob): number {\n const confirmed = aiConfirmedPlan(job)\n if (!confirmed) return AI_JOB_PAGE_STEP_MINIMUM_MS\n const units = aiPageJobUnits(confirmed)\n const outputs = job.outputs ?? []\n const [unit] = aiSitePendingUnits(units, outputs)\n if (!unit) return AI_JOB_PAGE_STEP_MINIMUM_MS\n return aiJobStepRunMinimumMs(aiSiteUnitJob(job, unit, aiSiteBuiltRefs(units, outputs)))\n}\n\n/** The one-segment address a draft asks for, from the plan's slug. */\nexport function aiPageDraftSlug(screen: Pick<AiBuildPlanScreen, 'slug'>): string {\n const segments = screen.slug.split('/').map((part) => part.trim()).filter(Boolean)\n return segments[segments.length - 1] ?? ''\n}\n\n/** A plan's listing value held to the length the SEO editor guides it to, cut at a word. */\nfunction clip(value: string, max: number): string {\n const text = value.trim()\n if (text.length <= max) return text\n const cut = text.slice(0, max + 1)\n const space = cut.lastIndexOf(' ')\n return (space > max / 2 ? cut.slice(0, space) : text.slice(0, max)).trim()\n}\n\n/** The site's name as its visitors see it, read the way the SEO step reads it. */\nfunction siteNameOf(host: FirebaseFirestore.DocumentSnapshot): string {\n const text = (value: unknown) => (typeof value === 'string' ? value.trim() : '')\n return text(host.get('seo.title')) || text(host.get('displayName')) || text(host.get('subdomain'))\n}\n\n/**\n * The counts the confirmed plan's sections promised, against the page a COPY\n * actually produced (AGL-3024); `null` when the copy keeps the plan, or when\n * the plan promised no count to keep.\n *\n * The same hole the layout copy had (`aiCopiedLayoutReview`), on the kind the\n * defect was measured on. This branch generates nothing, so no model ever\n * answers for the plan: a plan reading \"start from the Services page, one\n * card per practice area\" gets the Services page and whatever IT had, and\n * until now reported `Done` — the one report that tells a customer there is\n * no reason to re-read the page.\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 aiCopiedPageReview(\n firestore: FirebaseFirestore.Firestore,\n input: { hostId: string; id: string; name: string; screen: Pick<AiBuildPlanScreen, 'sections'> },\n): Promise<AiJobReview | null> {\n if (!input.screen.sections.some((section) => section.items >= AI_PLAN_ITEMS_MIN)) return null\n const stored = await readAiDraftNodes(firestore, { kind: 'screen', hostId: input.hostId, id: input.id })\n const nodes = (stored?.nodes ?? {}) as unknown as AiDoctrineTree['nodes']\n if (!nodes[CANVAS_ROOT_ELEMENT_ID]) {\n return {\n reason: 'doctrine',\n message: `\"${input.name}\" was copied from the page the plan names, and this job could not read the copy back to check it shows what the plan says it shows. Open the page and check it before you put it in front of anyone.`,\n findings: [],\n }\n }\n const violations = aiPlanCopiedPageViolations(input.screen, { rootId: CANVAS_ROOT_ELEMENT_ID, nodes })\n if (!violations.length) return null\n return aiDoctrineReview({\n message: `\"${input.name}\" is a copy of the page the plan names, and a copy is all it is: ${violations[0].message}`,\n violations,\n })\n}\n\n/**\n * A page job is admitted with inputs that read, for a site of the job's own\n * org with a screen to spare; and confirmed only for a plan it can build —\n * one screen, whose creations are ones a page job builds, this deployment has\n * a step for, and this workspace may still make on this site (AGL-3031).\n */\nexport const aiPageJobAdmission: AiJobAdmission = async (context) => {\n const inputs = parseAiPageJobInputs(context.inputs)\n if (typeof inputs === 'string') return { status: 400, error: inputs }\n const plan = context.plan\n if (plan) {\n const refusal = aiPagePlanRefusal(plan)\n if (refusal) return { status: 400, error: refusal }\n if (aiPageJobUnits(plan).some((unit) => !aiJobStepRunnerFor(unit.jobKind))) {\n return { status: 400, error: AI_JOB_PAGE_CREATION_UNAVAILABLE_COPY }\n }\n }\n const org = context.org as Partial<AglynOrgBilling> | null\n return aiDraftAdmissionRefusal(context.firestore, {\n orgId: context.orgId,\n hostId: context.hostId,\n kind: 'screen',\n noun: 'page',\n org,\n // What the workspace may still make here, read once the site is known to\n // be the org's: the site may have changed since the plan was told.\n ...(plan?.create.length\n ? {\n ownCheck: async (hostId: string) => {\n const capabilities = aiPlanCapabilitiesForJob(\n await readAiPlanCapabilities(context.firestore, { hostId, org }),\n { noun: 'a page job', creates: AI_PAGE_CREATE_KINDS },\n )\n const refusal = aiPageCreationRefusal(aiPlanUncreatable(plan, capabilities))\n return refusal ? { status: 403 as const, error: refusal } : null\n },\n }\n : {}),\n })\n}\n\nexport interface AiJobPageStepDeps {\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 /** The listing generator (AGL-2910). */\n seoFields?: typeof generateSeoFields\n /** The runners a plan's creations are built by (AGL-3031); the registry's otherwise. */\n runnerFor?: typeof aiJobStepRunnerFor\n}\n\nexport function createAiJobPageStep(deps: AiJobPageStepDeps = {}): AiJobStepRunner {\n const readInventory = deps.readInventory ?? readSiteInventory\n const duplicate = deps.duplicate ?? duplicateResource\n const seoFields = deps.seoFields ?? generateSeoFields\n const runnerFor = deps.runnerFor ?? aiJobStepRunnerFor\n return async (stepContext): Promise<AiJobStepOutcome> => {\n const { job, now, signal, firestore, modelFor } = stepContext\n const hostId = job.hostId\n if (!hostId) throw new Error('a page job names no site, and its admission refuses one')\n // The switch's answer for this job, else the routing table's (AGL-2942).\n const model = modelFor?.('job.page') ?? aiModelForStep('job.page')\n const confirmed = aiConfirmedPlan(job)\n if (!confirmed) return { ...aiUnspentOutcome(model), failure: AI_JOB_PAGE_NO_PLAN_COPY }\n // The resume door refuses such a plan at confirmation; one confirmed\n // before a site changed still stops here, before any spend.\n const refusal = aiPagePlanRefusal(confirmed)\n if (refusal) return { ...aiUnspentOutcome(model), failure: refusal }\n\n // ── The creations first (AGL-3031): one a pass, by the steps that own them ──\n const units = aiPageJobUnits(confirmed)\n const outputs = job.outputs ?? []\n const [unit] = aiSitePendingUnits(units, outputs)\n if (unit) {\n const runner = runnerFor(unit.jobKind)\n if (!runner) return { ...aiUnspentOutcome(model), failure: AI_JOB_PAGE_CREATION_UNAVAILABLE_COPY }\n const pass = await aiRunJobUnit(stepContext, {\n unit,\n units,\n runner,\n emptyCopy: AI_JOB_PAGE_CREATION_EMPTY_COPY,\n })\n // A built creation is followed by the next, or by the page's first section.\n return pass.built ? { ...pass.outcome, continue: true } : pass.outcome\n }\n // The page's plan with every creation resolved to the record it built, as\n // a scaffold's page is planned against what the scaffold built.\n const plan: AiJobPlan = units.length\n ? (aiSiteUnitJob(\n job,\n {\n kind: 'page',\n jobKind: 'page',\n resource: 'screen',\n slot: 'page',\n screen: confirmed.screens[0],\n label: confirmed.screens[0].title,\n },\n aiSiteBuiltRefs(units, outputs),\n ).plan as AiJobPlan)\n : confirmed\n const screen = plan.screens[0] as AiBuildPlanScreen\n const name = screen.title || AI_JOB_PAGE_DEFAULT_NAME\n const slug = aiPageDraftSlug(screen)\n const sectionIds = screen.sections.map((_, index) => aiPageSectionNodeId(job.$id, index))\n // The draft screen, by the id the job recorded for it: every pass writes and finds the same one.\n const draftId = aiJobDraftId(job, 'screen')\n\n const output = (\n draft: Pick<AiDraftRecord, 'id' | 'versionId' | 'name' | 'hostSubdomain'>,\n load?: AiLoadEstimate | null,\n note?: string | null,\n ): AiJobOutput => ({\n resource: 'screen',\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 // Rule 10: a navigation entry travels with the page as a proposal, for\n // the member to add once the page is live; no menu is written.\n ...(screen.nav ? { proposal: { navigation: { label: name, slug } } } : {}),\n })\n\n const [inventory, orgSnapshot, written] = await Promise.all([\n readInventory(job.orgId, hostId, { firestore }),\n firestore.collection('orgs').doc(job.orgId).get(),\n readAiDraft(firestore, { kind: 'screen', hostId, id: draftId }),\n ])\n const org = (orgSnapshot.data() ?? null) as Partial<AglynOrgBilling> | null\n if (written?.deleted) return { ...aiUnspentOutcome(model), failure: AI_JOB_PAGE_DELETED_COPY }\n // A record template's copy binds its dataset's fields, and its draft says\n // where the member saves the binding (AGL-3475).\n const record = aiPageRecordTemplate(screen.record, inventory, plan)\n const recordNote = record ? aiPageRecordNote(record) : null\n\n // The plan starts from a copy of a screen the site has (rule 15): the copy\n // is the draft — unrouted, as every copy is — and nothing is generated.\n // Only a page is a page's start: a collection entry template's copy stays\n // a template, so a plan naming one builds the page instead.\n // A copy shows what its SOURCE shows, and this branch generates nothing.\n // So if the source cannot already show what the plan promised, copying it\n // can only produce a page that breaks the promise — the refusal below\n // would be certain before the copy was ever minted. Read the source and\n // build instead, rather than leave the member a page they must throw away\n // and a Try again that can only copy the same thing (AGL-3024).\n // Only a source we could READ and found short diverts: one we cannot read\n // is one we cannot judge, and the review after the copy still answers for\n // that. A short source carries findings; an unreadable one carries none.\n const sourceReview =\n !written && screen.duplicateOf\n ? await aiCopiedPageReview(firestore, { hostId, id: screen.duplicateOf, name, screen })\n : null\n const sourceShort = Boolean(sourceReview?.findings.length)\n if (\n !written &&\n !sourceShort &&\n screen.duplicateOf &&\n inventory.screens.some((row) => row.id === screen.duplicateOf && !row.template)\n ) {\n const copy = await duplicate('screen', {\n orgId: job.orgId,\n hostId,\n sourceId: screen.duplicateOf,\n name,\n uid: job.createdBy,\n org: org as Record<string, unknown> | null,\n // A copy core mints is written under core's id, never `draftId`, so\n // `written` above cannot find it and this branch would fire on every\n // pass — and the pass after a refused copy is the common one, since\n // the review below offers to try again. The job's recorded id is the\n // claim, so a second pass replays the one copy instead of leaving\n // another orphan screen behind it (AGL-3024).\n attemptKey: draftId,\n })\n if (copy.ok) {\n // A copy is not a build (AGL-3024). Nothing is generated here, so\n // whatever the plan promised BEYOND the source is in the draft only\n // if the source already had it, and no model is left to answer for\n // the difference — the copy itself has to.\n const review = await aiCopiedPageReview(firestore, { hostId, id: copy.id, name, screen })\n if (review) return aiUnspentOutcome(model, { review })\n const hostSubdomain = await aiSiteSubdomain(firestore, hostId)\n return aiUnspentOutcome(model, {\n outputs: [output({ id: copy.id, versionId: copy.versionId, name: copy.name, hostSubdomain }, null, recordNote)],\n })\n }\n if (copy.ok === false && copy.status === 403) {\n return aiUnspentOutcome(model, { review: aiLimitReview(copy.error) })\n }\n }\n\n const stored = written ? await readAiDraftNodes(firestore, { kind: 'screen', hostId, id: draftId }) : null\n if (written && !stored) return { ...aiUnspentOutcome(model), failure: AI_JOB_PAGE_DELETED_COPY }\n const page = stored?.nodes ?? aiEmptyPage()\n const index = sectionIds.findIndex((id) => !(id in page))\n // A workspace that keeps no reusable components or saved forms builds its\n // page inline, and every pass is held to the rules that way (AGL-3030).\n const reusableComponents = checkEntitlement(org, 'reusableComponents')\n // A link may take a visitor to a section of this page, by its name in the plan (AGL-3097).\n const sections = screen.sections.map((section) => section.name)\n // A third-party player the confirmed plan lists for this page is the only one it may embed (AGL-3433).\n const embeds = aiPlanEmbedsFor(plan, { slug: screen.slug })\n const context = aiPageCheckContext(inventory, {\n reusableComponents,\n sections,\n embeds,\n recordTokens: aiPageRecordTokens(record),\n })\n\n // ── The last pass: the whole page, its listing, and the draft reported ──\n if (index === -1 && written) {\n // A link the page carries to one of its sections goes there, now every section is built.\n const report = validateAiDoctrineTree({ rootId: CANVAS_ROOT_ELEMENT_ID, nodes: page }, 'page', {\n ...context,\n scrollTargetIds: sectionIds,\n })\n // The facts the brief did not give, on the page or in the defaults its components show (AGL-3056).\n const note =\n [\n aiBracketedFactsNote({\n tree: { rootId: CANVAS_ROOT_ELEMENT_ID, nodes: page as unknown as AiDoctrineTree['nodes'] },\n inventory,\n }),\n recordNote,\n ]\n .filter(Boolean)\n .join(' ') || null\n // The page's own draft, reported once however many times the last pass\n // runs (AGL-3143): a refused pass reports it and a later one that\n // passes does not report it twice.\n const reported = outputs.some((entry) => entry.resource === 'screen' && entry.id === draftId)\n const reports = reported ? [] : [output(written, report.load, note)]\n if (report.violations.length) {\n // The check mints its own ids; the review names the nodes by the ids the draft stores them under.\n const storedIds = report.tree?.sourceIds ?? {}\n const violations = report.violations.map((violation) =>\n violation.nodeIds ? { ...violation, nodeIds: aiModelNodeIds(violation.nodeIds, storedIds) } : violation,\n )\n // THE DRAFT TRAVELS WITH THE REFUSAL (AGL-3143). Every other review\n // parks on a model's answer, so trying again asks the model again and\n // can come back different. This one reads the STORED page and spends\n // nothing, so trying again refuses the same page for the same reasons\n // forever — and a job parked here having reported no output names no\n // draft at all, which leaves the member nowhere to mend the nodes the\n // findings name and no way to finish the job from the console. The\n // draft is reported now instead, and the job still parks: what the\n // review names is mended in the draft, and the next pass then passes.\n return aiUnspentOutcome(model, {\n outputs: reports,\n review: aiDoctrineReview({\n message: `${aiDoctrineNeedsInputMessage(violations)} ${AI_JOB_PAGE_REFUSED_DRAFT_COPY}`,\n violations,\n answer: { tree: { rootId: CANVAS_ROOT_ELEMENT_ID, nodes: page } },\n }),\n })\n }\n let spent: AiJobStepOutcome = aiUnspentOutcome(model)\n if (!written.seo?.title?.trim() || !written.seo?.description?.trim()) {\n const seoModel = modelFor?.('job.seo') ?? aiModelForStep('job.seo')\n // The plan's own listing, held to the editor's guidance, when the\n // listing cannot be written inside this pass's time on the model the\n // job runs.\n let values = {\n title: clip(screen.seoTitle, SCREEN_SEO_TEXT_GUIDANCE.title),\n description: clip(screen.seoDescription, SCREEN_SEO_TEXT_GUIDANCE.description),\n }\n if (\n // A listing is written from the page's own text, with no inventory\n // and so no lookup round.\n aiGenerationWorstCaseMs({ maxTokens: AI_SEO_FIELDS_MAX_TOKENS, model: seoModel, lookups: 0 }) <=\n AI_JOB_PAGE_STEP_MINIMUM_MS\n ) {\n const host = await firestore.collection('hosts').doc(hostId).get()\n const result = await seoFields({\n subject: { kind: 'screen', name, path: `/${slug}` },\n brand: siteNameOf(host),\n // What the site is and who it is for (AGL-2918). The page's own\n // Markdown says what the page is about and cannot say who it was\n // written for, so the listing borrows the two answers the job was\n // started from — the scaffold's derived page job carries them,\n // and a page job started from the Screens page carries neither.\n site: aiSiteWords(job.inputs),\n text: buildPageMarkdown({ nodes: page as never, rootId: CANVAS_ROOT_ELEMENT_ID }),\n fields: ['title', 'description'],\n // Other screens by name only: their own listings are their content.\n otherTitles: inventory.screens.map((row) => row.name),\n model: seoModel,\n ...(signal ? { signal } : {}),\n })\n spent = { ...spent, ...aiGenerationSpent(result) }\n if (result.status === 'ok') {\n values = {\n title: result.value.title?.trim() || values.title,\n description: result.value.description?.trim() || values.description,\n }\n }\n }\n await writeAiDraftScreenSeo(firestore, { hostId, id: draftId, seo: values, now })\n }\n return { ...spent, outputs: reports }\n }\n\n // ── A section pass ──\n if (!written) {\n const allowance = await aiDraftAllowanceRefusal(firestore, { kind: 'screen', hostId, org })\n if (allowance) return aiUnspentOutcome(model, { review: aiLimitReview(allowance) })\n }\n const maxTokens = aiJobPageSectionMaxTokens(model)\n const maxElements = aiJobPageSectionMaxElements(maxTokens)\n const result = await runValidatedGeneration<AiPageSection>('page-section', {\n step: 'job.page',\n model,\n instructions: AI_JOB_PAGE_INSTRUCTIONS,\n inventory,\n messages: [\n {\n role: 'user',\n content: aiPageSectionPrompt({ job, plan, screen, index, maxElements, reusableComponents, record }),\n },\n ],\n tool: AI_PAGE_SECTION_TOOL,\n maxTokens,\n // A section cut off at its ceiling is asked for smaller, and a member\n // whose section still does not fit reads that it was too large (AGL-3042).\n cutOff: { noun: 'section', smaller: aiPageSectionSmaller({ maxElements, reusableComponents }) },\n thinking: 'off',\n check: aiPageSectionCheck({\n page,\n sectionIds,\n index,\n context,\n section: screen.sections[index],\n inventory,\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 if (!written) {\n const draft = await writeAiDraft(firestore, {\n kind: 'screen',\n hostId,\n id: draftId,\n uid: job.createdBy,\n org,\n name,\n nodes: aiPageWithSection(aiEmptyPage(), result.value, sectionIds),\n slug,\n layoutId:\n screen.layout && inventory.layouts.some((row) => row.id === screen.layout) ? screen.layout : null,\n now,\n })\n if (draft.ok === false) {\n if (draft.status === 404) throw new Error(`site ${hostId} vanished while its page was generated`)\n return { ...spent, review: aiLimitReview(draft.error) }\n }\n } else {\n const update = await updateAiDraftNodes(firestore, {\n kind: 'screen',\n hostId,\n id: draftId,\n now,\n // A pass run again after its write finds its section and changes nothing.\n update: (nodes) =>\n result.value.rootId in nodes ? null : aiPageWithSection(nodes, result.value, sectionIds),\n })\n if (update.ok === false) return { ...spent, failure: AI_JOB_PAGE_DELETED_COPY }\n }\n return { ...spent, continue: true }\n }\n}\n\nexport const runAiJobPageStep = createAiJobPageStep()\n\n/**\n * Registers the page step with the least time a section pass needs and the\n * time its next pass needs, the passes a page with its creations may take, and\n * the check a page job passes when it is created or its plan is confirmed.\n */\nexport function registerAiPageJob(): void {\n registerAiJobStep('page', runAiJobPageStep, {\n minimumMs: AI_JOB_PAGE_STEP_MINIMUM_MS,\n minimumMsFor: aiPageJobRunMinimumMs,\n })\n registerAiJobStepPasses('page', AI_JOB_PAGE_MAX_PASSES)\n registerAiJobAdmission('page', aiPageJobAdmission)\n}\n"],"names":["buildPageMarkdown","checkEntitlement","SCREEN_SEO_TEXT_GUIDANCE","CANVAS_ROOT_ELEMENT_ID","duplicateResource","AI_BUILD_PLAN_LIMITS","aiPlanEmbedsFor","AI_PAGE_CREATE_KINDS","aiPageCreationRefusal","aiPagePlanRefusal","parseAiPageJobInputs","aiPlanCapabilitiesForJob","aiPlanUncreatable","aiSiteWords","aiModelForStep","aiDoctrineNeedsInputMessage","runValidatedGeneration","validateAiDoctrineTree","AI_SEO_FIELDS_MAX_TOKENS","generateSeoFields","readSiteInventory","registerAiJobAdmission","aiGenerationWorstCaseMs","aiJobDraftId","aiDraftAdmissionRefusal","aiDraftAllowanceRefusal","aiSiteSubdomain","readAiDraft","readAiDraftNodes","readAiPlanCapabilities","updateAiDraftNodes","writeAiDraft","writeAiDraftScreenSeo","aiBracketedFactsNote","aiConfirmedPlan","aiDoctrineReview","aiGenerationSpent","aiLimitReview","aiModelNodeIds","aiUnspentOutcome","AI_JOB_PAGE_SECTION_MAX_TOKENS","AI_JOB_PAGE_SECTION_TOKENS","AI_JOB_PAGE_STEP_BUDGET","AI_JOB_PAGE_STEP_MINIMUM_MS","aiJobPageSectionMaxTokens","AI_JOB_PAGE_INSTRUCTIONS","AI_PAGE_SECTION_TOOL","aiEmptyPage","aiPageCheckContext","aiPageRecordNote","aiPageRecordTemplate","aiPageRecordTokens","aiPageSectionCheck","aiPageSectionNodeId","aiPageSectionPrompt","aiPageSectionSmaller","aiPageWithSection","AI_PLAN_ITEMS_MIN","aiPlanCopiedPageViolations","aiCreationUnit","aiRunJobUnit","aiSiteBuiltRefs","aiSitePendingUnits","aiSiteUnitJob","aiJobStepRunMinimumMs","aiJobStepRunnerFor","registerAiJobStep","registerAiJobStepPasses","AI_JOB_PAGE_TOKENS_PER_ELEMENT","AI_JOB_PAGE_REAL_TOKENS_PER_ESTIMATED","Math","max","AI_JOB_PAGE_REAL_TOKENS_PER_ELEMENT","ceil","aiJobPageSectionMaxElements","maxTokens","floor","AI_JOB_PAGE_DEFAULT_NAME","AI_JOB_PAGE_NO_PLAN_COPY","AI_JOB_PAGE_DELETED_COPY","AI_JOB_PAGE_CREATION_UNAVAILABLE_COPY","AI_JOB_PAGE_CREATION_EMPTY_COPY","AI_JOB_PAGE_REFUSED_DRAFT_COPY","AI_JOB_PAGE_MAX_PASSES","create","sections","aiPageJobUnits","plan","flatMap","kind","creation","index","unit","aiPageJobRunMinimumMs","job","confirmed","units","outputs","aiPageDraftSlug","screen","segments","slug","split","map","part","trim","filter","Boolean","length","clip","value","text","cut","slice","space","lastIndexOf","siteNameOf","host","get","aiCopiedPageReview","firestore","input","some","section","items","stored","hostId","id","nodes","reason","message","name","findings","violations","rootId","aiPageJobAdmission","context","inputs","status","error","refusal","jobKind","org","orgId","noun","ownCheck","capabilities","creates","createAiJobPageStep","deps","readInventory","duplicate","seoFields","runnerFor","stepContext","orgSnapshot","now","signal","modelFor","Error","model","failure","runner","pass","emptyCopy","built","outcome","continue","resource","slot","screens","label","title","sectionIds","_","$id","draftId","output","draft","load","note","versionId","hostSubdomain","nav","proposal","navigation","inventory","written","Promise","all","collection","doc","data","deleted","record","recordNote","sourceReview","duplicateOf","sourceShort","row","template","copy","sourceId","uid","createdBy","attemptKey","ok","review","page","findIndex","reusableComponents","embeds","recordTokens","report","scrollTargetIds","tree","join","reported","entry","reports","storedIds","sourceIds","violation","nodeIds","answer","spent","seo","description","seoModel","values","seoTitle","seoDescription","lookups","result","subject","path","brand","site","fields","otherTitles","allowance","maxElements","step","instructions","messages","role","content","tool","cutOff","smaller","thinking","check","refused","layoutId","layout","layouts","update","runAiJobPageStep","registerAiPageJob","minimumMs","minimumMsFor"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-page-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 { buildPageMarkdown } from '@aglyn/aglyn/app-utils/page-markdown'\nimport { checkEntitlement } from '@aglyn/aglyn/app-utils/plan-entitlements'\nimport { SCREEN_SEO_TEXT_GUIDANCE } from '@aglyn/aglyn/app-utils/screen-seo-fields'\nimport { SCREEN_ROOT_PATH, screenRoutePathToUrl } from '@aglyn/aglyn/app-utils/screen-route'\nimport { CANVAS_ROOT_ELEMENT_ID } from '@aglyn/aglyn/foundation/constants/canvas'\nimport type { AglynOrgBilling } from '@aglyn/aglyn/foundation/definitions/org-billing.types'\nimport { duplicateResource } from '@aglyn/tenant-data-admin/server/duplicate-resource'\nimport { AI_BUILD_PLAN_LIMITS, aiPlanEmbedsFor, type AiBuildPlanScreen } from '../model/ai-build-plan'\nimport {\n AI_PAGE_CREATE_KINDS,\n aiPageCreationRefusal,\n aiPagePlanRefusal,\n parseAiPageJobInputs,\n} from '../model/ai-page-job'\nimport { aiPlanCapabilitiesForJob, aiPlanUncreatable } from '../model/ai-plan-capabilities'\nimport { aiSiteWords } from '../model/ai-site-job'\nimport type { AiJob, AiJobOutput, AiJobPlan, AiJobReview } from '../model/ai-jobs.types'\nimport { aiModelForStep } from '../providers/routing'\nimport { aiDoctrineNeedsInputMessage, runValidatedGeneration } from '../runtime/ai-doctrine'\nimport { validateAiDoctrineTree, type AiDoctrineTree } from '../runtime/ai-doctrine-validators'\nimport type { AiLoadEstimate } from '../runtime/ai-palette'\nimport { AI_SEO_FIELDS_MAX_TOKENS, generateSeoFields } from '../runtime/seo-fields'\nimport { readSiteInventory } from '../runtime/site-inventory'\nimport type { AiSiteInventory } from '../model/ai-site-inventory'\nimport { registerAiJobAdmission, type AiJobAdmission } from './ai-job-admission'\nimport { aiGenerationWorstCaseMs } from './ai-job-budget'\nimport { aiJobDraftId, aiOriginJobId } from './ai-job-draft-ids'\nimport {\n aiDraftAdmissionRefusal,\n aiDraftAllowanceRefusal,\n aiSiteSubdomain,\n readAiDraft,\n readAiDraftNodes,\n readAiPlanCapabilities,\n updateAiDraftNodes,\n writeAiDraft,\n writeAiDraftScreenSeo,\n type AiDraftRecord,\n} from './ai-job-drafts'\nimport {\n aiBracketedFactsNote,\n aiConfirmedPlan,\n aiDoctrineReview,\n aiGenerationSpent,\n aiLimitReview,\n aiModelNodeIds,\n aiUnspentOutcome,\n} from './ai-job-generation'\nimport {\n AI_JOB_PAGE_SECTION_MAX_TOKENS,\n AI_JOB_PAGE_SECTION_TOKENS,\n AI_JOB_PAGE_STEP_BUDGET,\n AI_JOB_PAGE_STEP_MINIMUM_MS,\n aiJobPageSectionMaxTokens,\n} from './ai-job-page-budget'\nimport {\n AI_JOB_PAGE_INSTRUCTIONS,\n AI_PAGE_SECTION_TOOL,\n aiEmptyPage,\n aiPageCheckContext,\n aiPageRecordNote,\n aiPageRecordTemplate,\n aiPageRecordTokens,\n aiPageSectionCheck,\n aiPageSectionNodeId,\n aiPageSectionPrompt,\n aiPageSectionSmaller,\n aiPageWithSection,\n type AiPageSection,\n} from './ai-job-page-sections'\nimport { AI_PLAN_ITEMS_MIN, aiPlanCopiedPageViolations } from './ai-job-plan-conformance'\nimport {\n aiCreationUnit,\n aiRunJobUnit,\n aiSiteBuiltRefs,\n aiSitePendingUnits,\n aiSiteUnitJob,\n type AiSiteUnit,\n} from './ai-job-site-step'\nimport type { AiJobStepOutcome, AiJobStepRunner } from './ai-job-text-step'\nimport {\n aiJobStepRunMinimumMs,\n aiJobStepRunnerFor,\n registerAiJobStep,\n registerAiJobStepPasses,\n} from './ai-jobs'\n\n/**\n * The page step (AGL-2907): the generation step of a `page` job, run once a\n * member confirmed the job's plan. It builds ONE screen, section by section,\n * from what the site already has, and leaves it an unpublished draft.\n *\n * ── Why a page is built in passes ────────────────────────────────────────\n *\n * A page is far larger than one answer the job beat can wait for: the beat\n * gives a step what is left of its budget, and a whole page, answered and re-asked,\n * takes several times that. The confirmed plan already names the page's\n * sections, so the step builds one section per pass and asks the machine to\n * continue. Every pass is one reservation and one generation — the section's\n * lookup rounds, its answer and, when it breaks a rule, its one re-ask — sized\n * so that worst case fits `AI_JOB_PAGE_STEP_MINIMUM_MS`, which the step\n * registers as the least time a section pass needs before it starts.\n *\n * - The first pass builds the top section, which holds the page's h1, and\n * writes the draft screen and its first version.\n * - Each later pass builds the next section and adds it to that version.\n * - The last pass generates no tree: it holds the whole page to the\n * doctrine, writes the search title and description, and reports the\n * draft with its measured weight and a navigation proposal.\n *\n * How a section is asked for and held to the rules as part of the page is\n * `ai-job-page-sections.ts`.\n *\n * ── What the plan creates comes first (AGL-3031) ─────────────────────────\n *\n * A plan that creates a layout, forms or components is built in the same job,\n * before its page, one creation a pass, through the site scaffold's unit\n * machinery: each creation is handed to the step that builds that kind, under\n * a job derived from this one, and lands as the draft that step writes. Where\n * the job stands is read from its outputs, so nothing is kept anywhere else.\n * Once every creation is built, the page's passes run on the plan with each\n * `new:<name>` resolved to the record that was built, which the site's\n * inventory now lists — so the page places the new component and binds the new\n * form by id, and renders inside the new layout, exactly as it would one the\n * site always had. A pass that builds a creation needs the time the step that\n * builds it registers, not a section's (`aiPageJobRunMinimumMs`, AGL-3035), so\n * neither the beat nor a door starts it with only a section's time left.\n *\n * ── Never published ──────────────────────────────────────────────────────\n *\n * The draft is written the way `/api/hosts/resources` and\n * `/api/hosts/versions` write a screen and its first version, and nothing\n * else is touched: not the host document, whose routing map is what the live\n * site serves, and no collection, store, layout or navigation pointer\n * (`ai-job-drafts.ts`). A screen the routing map does not name serves nothing\n * until a member publishes it.\n */\n\n/**\n * A section pass's time, and the ceiling each pass asks on each model, are\n * declared apart from the step (`ai-job-page-budget.ts`): the site scaffold\n * builds its pages through this step and reads them without loading it.\n */\nexport {\n AI_JOB_PAGE_SECTION_MAX_TOKENS,\n AI_JOB_PAGE_SECTION_TOKENS,\n AI_JOB_PAGE_STEP_BUDGET,\n AI_JOB_PAGE_STEP_MINIMUM_MS,\n aiJobPageSectionMaxTokens,\n}\n\n/**\n * Tokens one element takes in a section's answer, ESTIMATED: the tree as JSON\n * text inside the tool call, at four characters a token. Measured on the ten\n * golden pages at a median of 38 and a most of 43 over their 39 sections, and\n * `ai-job-page-evals.spec.ts` holds every golden section under it.\n */\nexport const AI_JOB_PAGE_TOKENS_PER_ELEMENT = 45\n\n/**\n * Real tokens to every token estimated at four characters (AGL-3042). A\n * section's ceiling is counted by the provider in its own tokens, which run\n * above that estimate. MEASURED on the first live document run on a Free\n * workspace (AGL-3024, test-org, 2026-09-16), as the cache read of a request\n * over the estimate of the same cached prefix: 4,059 over 2,825 for the page\n * plan and 6,649 over 4,310 for the layout, the higher of the two, 1.5427 —\n * the ratio `ai-job-free-page.spec.ts` prices a Free page at. It is measured on\n * prompt text; an answer's own is read off a recorded pass, whose output\n * tokens the step's record keeps (`lastRuns`), beside the section it stored.\n */\nexport const AI_JOB_PAGE_REAL_TOKENS_PER_ESTIMATED = Math.max(4_059 / 2_825, 6_649 / 4_310)\n\n/**\n * Real tokens one element takes in a section's answer: the estimate at its\n * golden most, in real tokens, rounded up — 45 × 1.5427 → 70 (AGL-3042).\n */\nexport const AI_JOB_PAGE_REAL_TOKENS_PER_ELEMENT = Math.ceil(\n AI_JOB_PAGE_TOKENS_PER_ELEMENT * AI_JOB_PAGE_REAL_TOKENS_PER_ESTIMATED,\n)\n\n/**\n * The most elements a section's request asks it to keep under at an answer\n * ceiling: 15 at the balanced tier's 1,050 (AGL-3042). Counted in real\n * tokens, because the ceiling is: counted in the estimate, the budget is 23\n * elements, which a section of the goldens' largest elements fills at 1,597\n * real tokens — half as much again as the ceiling that cuts it off.\n */\nexport function aiJobPageSectionMaxElements(maxTokens: number): number {\n return Math.max(1, Math.floor(maxTokens / AI_JOB_PAGE_REAL_TOKENS_PER_ELEMENT))\n}\n\n/** A page's name when the plan names none. */\nexport const AI_JOB_PAGE_DEFAULT_NAME = 'New page'\n\nexport const AI_JOB_PAGE_NO_PLAN_COPY = 'This page job has no confirmed plan to build.'\nexport const AI_JOB_PAGE_DELETED_COPY = 'The draft page was deleted while it was being built.'\n\n/** A creation the plan names that this deployment has no step to build. */\nexport const AI_JOB_PAGE_CREATION_UNAVAILABLE_COPY =\n 'This page needs something that cannot be built here yet. Describe the page again.'\n\n/** A creation that finished without reporting what it built; the page cannot place it. */\nexport const AI_JOB_PAGE_CREATION_EMPTY_COPY =\n 'Part of this page could not be built. Describe the page again.'\n\n/**\n * What a page refused on its last pass tells the member beside the rules it\n * broke (AGL-3143). The draft is reported with the review, and mending what\n * the findings name in it is what lets the next pass finish the job.\n */\nexport const AI_JOB_PAGE_REFUSED_DRAFT_COPY =\n 'The draft is yours to open: mend what these name in it, then try again.'\n\n/**\n * The most passes a page job may take (AGL-3031): every creation the plan\n * limits admit, every section they admit, and the last pass that reports the\n * draft. A creation's step builds it in one pass, and a page one section a\n * pass, so a runner that never finishes is still bounded while a real page is\n * not.\n */\nexport const AI_JOB_PAGE_MAX_PASSES = AI_BUILD_PLAN_LIMITS.create + AI_BUILD_PLAN_LIMITS.sections + 1\n\n/**\n * The units a plan's creations are built as, in the order a scaffold builds\n * them: the layout, then the forms, then the components. Each is addressed by\n * its place in the plan, so a unit run again finds its own draft.\n */\nexport function aiPageJobUnits(plan: Pick<AiJobPlan, 'create'>): AiSiteUnit[] {\n return AI_PAGE_CREATE_KINDS.flatMap((kind) =>\n plan.create.flatMap((creation, index) => {\n if (creation.kind !== kind) return []\n const unit = aiCreationUnit(creation, `c${index}`)\n return unit ? [unit] : []\n }),\n )\n}\n\n/**\n * The least time this page job's next pass needs (AGL-3035). A pass that\n * builds a creation hands it to the step registered for that kind, under the\n * job derived for it, so it needs what that step registers for that job — a\n * layout's time, not a section's. Every other pass — a section, the last one —\n * needs a section pass's.\n */\nexport function aiPageJobRunMinimumMs(job: AiJob): number {\n const confirmed = aiConfirmedPlan(job)\n if (!confirmed) return AI_JOB_PAGE_STEP_MINIMUM_MS\n const units = aiPageJobUnits(confirmed)\n const outputs = job.outputs ?? []\n const [unit] = aiSitePendingUnits(units, outputs)\n if (!unit) return AI_JOB_PAGE_STEP_MINIMUM_MS\n return aiJobStepRunMinimumMs(aiSiteUnitJob(job, unit, aiSiteBuiltRefs(units, outputs)))\n}\n\n/**\n * The one-segment address a draft asks for, from the plan's slug; `/` for a\n * planned home page, which asks for the root rather than for an address made\n * from its name (AGL-3596).\n */\nexport function aiPageDraftSlug(screen: Pick<AiBuildPlanScreen, 'slug'>): string {\n const segments = screen.slug.split('/').map((part) => part.trim()).filter(Boolean)\n if (!segments.length && screen.slug.trim() === SCREEN_ROOT_PATH) return SCREEN_ROOT_PATH\n return segments[segments.length - 1] ?? ''\n}\n\n/**\n * The layout a site's pages render inside (AGL-3596): the one its home page\n * uses, else the one the most pages use, else the site's only layout. `null`\n * for a site with no layout, or several and no page to choose between them.\n */\nexport function aiSiteDefaultLayoutId(\n inventory: Pick<AiSiteInventory, 'layouts' | 'screens'>,\n): string | null {\n const layouts = new Set(inventory.layouts.map((row) => row.id))\n if (!layouts.size) return null\n const pages = inventory.screens.filter(\n (row) => !row.template && row.layoutId && layouts.has(row.layoutId),\n )\n const home = pages.find((row) => row.slug.trim() === SCREEN_ROOT_PATH)\n if (home?.layoutId) return home.layoutId\n const counts = new Map<string, number>()\n for (const row of pages) counts.set(row.layoutId as string, (counts.get(row.layoutId as string) ?? 0) + 1)\n const [mostUsed] = [...counts.entries()].sort((a, b) => b[1] - a[1])\n if (mostUsed) return mostUsed[0]\n return layouts.size === 1 ? [...layouts][0] : null\n}\n\n/**\n * The layout a page draft renders inside: the plan's, where the site has it,\n * else the site's own (AGL-3596). A page the plan named no layout for used to\n * be written with none, and so with no header and no footer, beside pages\n * that had them.\n */\nexport function aiPageDraftLayoutId(\n screen: Pick<AiBuildPlanScreen, 'layout'>,\n inventory: Pick<AiSiteInventory, 'layouts' | 'screens'>,\n): string | null {\n if (screen.layout && inventory.layouts.some((row) => row.id === screen.layout)) return screen.layout\n return aiSiteDefaultLayoutId(inventory)\n}\n\n/** A plan's listing value held to the length the SEO editor guides it to, cut at a word. */\nfunction clip(value: string, max: number): string {\n const text = value.trim()\n if (text.length <= max) return text\n const cut = text.slice(0, max + 1)\n const space = cut.lastIndexOf(' ')\n return (space > max / 2 ? cut.slice(0, space) : text.slice(0, max)).trim()\n}\n\n/** The site's name as its visitors see it, read the way the SEO step reads it. */\nfunction siteNameOf(host: FirebaseFirestore.DocumentSnapshot): string {\n const text = (value: unknown) => (typeof value === 'string' ? value.trim() : '')\n return text(host.get('seo.title')) || text(host.get('displayName')) || text(host.get('subdomain'))\n}\n\n/**\n * The counts the confirmed plan's sections promised, against the page a COPY\n * actually produced (AGL-3024); `null` when the copy keeps the plan, or when\n * the plan promised no count to keep.\n *\n * The same hole the layout copy had (`aiCopiedLayoutReview`), on the kind the\n * defect was measured on. This branch generates nothing, so no model ever\n * answers for the plan: a plan reading \"start from the Services page, one\n * card per practice area\" gets the Services page and whatever IT had, and\n * until now reported `Done` — the one report that tells a customer there is\n * no reason to re-read the page.\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 aiCopiedPageReview(\n firestore: FirebaseFirestore.Firestore,\n input: { hostId: string; id: string; name: string; screen: Pick<AiBuildPlanScreen, 'sections'> },\n): Promise<AiJobReview | null> {\n if (!input.screen.sections.some((section) => section.items >= AI_PLAN_ITEMS_MIN)) return null\n const stored = await readAiDraftNodes(firestore, { kind: 'screen', hostId: input.hostId, id: input.id })\n const nodes = (stored?.nodes ?? {}) as unknown as AiDoctrineTree['nodes']\n if (!nodes[CANVAS_ROOT_ELEMENT_ID]) {\n return {\n reason: 'doctrine',\n message: `\"${input.name}\" was copied from the page the plan names, and this job could not read the copy back to check it shows what the plan says it shows. Open the page and check it before you put it in front of anyone.`,\n findings: [],\n }\n }\n const violations = aiPlanCopiedPageViolations(input.screen, { rootId: CANVAS_ROOT_ELEMENT_ID, nodes })\n if (!violations.length) return null\n return aiDoctrineReview({\n message: `\"${input.name}\" is a copy of the page the plan names, and a copy is all it is: ${violations[0].message}`,\n violations,\n })\n}\n\n/**\n * A page job is admitted with inputs that read, for a site of the job's own\n * org with a screen to spare; and confirmed only for a plan it can build —\n * one screen, whose creations are ones a page job builds, this deployment has\n * a step for, and this workspace may still make on this site (AGL-3031).\n */\nexport const aiPageJobAdmission: AiJobAdmission = async (context) => {\n const inputs = parseAiPageJobInputs(context.inputs)\n if (typeof inputs === 'string') return { status: 400, error: inputs }\n const plan = context.plan\n if (plan) {\n const refusal = aiPagePlanRefusal(plan)\n if (refusal) return { status: 400, error: refusal }\n if (aiPageJobUnits(plan).some((unit) => !aiJobStepRunnerFor(unit.jobKind))) {\n return { status: 400, error: AI_JOB_PAGE_CREATION_UNAVAILABLE_COPY }\n }\n }\n const org = context.org as Partial<AglynOrgBilling> | null\n return aiDraftAdmissionRefusal(context.firestore, {\n orgId: context.orgId,\n hostId: context.hostId,\n kind: 'screen',\n noun: 'page',\n org,\n // What the workspace may still make here, read once the site is known to\n // be the org's: the site may have changed since the plan was told.\n ...(plan?.create.length\n ? {\n ownCheck: async (hostId: string) => {\n const capabilities = aiPlanCapabilitiesForJob(\n await readAiPlanCapabilities(context.firestore, { hostId, org }),\n { noun: 'a page job', creates: AI_PAGE_CREATE_KINDS },\n )\n const refusal = aiPageCreationRefusal(aiPlanUncreatable(plan, capabilities))\n return refusal ? { status: 403 as const, error: refusal } : null\n },\n }\n : {}),\n })\n}\n\nexport interface AiJobPageStepDeps {\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 /** The listing generator (AGL-2910). */\n seoFields?: typeof generateSeoFields\n /** The runners a plan's creations are built by (AGL-3031); the registry's otherwise. */\n runnerFor?: typeof aiJobStepRunnerFor\n}\n\nexport function createAiJobPageStep(deps: AiJobPageStepDeps = {}): AiJobStepRunner {\n const readInventory = deps.readInventory ?? readSiteInventory\n const duplicate = deps.duplicate ?? duplicateResource\n const seoFields = deps.seoFields ?? generateSeoFields\n const runnerFor = deps.runnerFor ?? aiJobStepRunnerFor\n return async (stepContext): Promise<AiJobStepOutcome> => {\n const { job, now, signal, firestore, modelFor } = stepContext\n const hostId = job.hostId\n if (!hostId) throw new Error('a page job names no site, and its admission refuses one')\n // The switch's answer for this job, else the routing table's (AGL-2942).\n const model = modelFor?.('job.page') ?? aiModelForStep('job.page')\n const confirmed = aiConfirmedPlan(job)\n if (!confirmed) return { ...aiUnspentOutcome(model), failure: AI_JOB_PAGE_NO_PLAN_COPY }\n // The resume door refuses such a plan at confirmation; one confirmed\n // before a site changed still stops here, before any spend.\n const refusal = aiPagePlanRefusal(confirmed)\n if (refusal) return { ...aiUnspentOutcome(model), failure: refusal }\n\n // ── The creations first (AGL-3031): one a pass, by the steps that own them ──\n const units = aiPageJobUnits(confirmed)\n const outputs = job.outputs ?? []\n const [unit] = aiSitePendingUnits(units, outputs)\n if (unit) {\n const runner = runnerFor(unit.jobKind)\n if (!runner) return { ...aiUnspentOutcome(model), failure: AI_JOB_PAGE_CREATION_UNAVAILABLE_COPY }\n const pass = await aiRunJobUnit(stepContext, {\n unit,\n units,\n runner,\n emptyCopy: AI_JOB_PAGE_CREATION_EMPTY_COPY,\n })\n // A built creation is followed by the next, or by the page's first section.\n return pass.built ? { ...pass.outcome, continue: true } : pass.outcome\n }\n // The page's plan with every creation resolved to the record it built, as\n // a scaffold's page is planned against what the scaffold built.\n const plan: AiJobPlan = units.length\n ? (aiSiteUnitJob(\n job,\n {\n kind: 'page',\n jobKind: 'page',\n resource: 'screen',\n slot: 'page',\n screen: confirmed.screens[0],\n label: confirmed.screens[0].title,\n },\n aiSiteBuiltRefs(units, outputs),\n ).plan as AiJobPlan)\n : confirmed\n const screen = plan.screens[0] as AiBuildPlanScreen\n const name = screen.title || AI_JOB_PAGE_DEFAULT_NAME\n const slug = aiPageDraftSlug(screen)\n const sectionIds = screen.sections.map((_, index) => aiPageSectionNodeId(job.$id, index))\n // The draft screen, by the id the job recorded for it: every pass writes and finds the same one.\n const draftId = aiJobDraftId(job, 'screen')\n\n const output = (\n draft: Pick<AiDraftRecord, 'id' | 'versionId' | 'name' | 'hostSubdomain'>,\n load?: AiLoadEstimate | null,\n note?: string | null,\n ): AiJobOutput => ({\n resource: 'screen',\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 // Rule 10: a navigation entry travels with the page as a proposal, for\n // the member to add once the page is live; no menu is written.\n ...(screen.nav ? { proposal: { navigation: { label: name, slug } } } : {}),\n })\n\n const [inventory, orgSnapshot, written] = await Promise.all([\n readInventory(job.orgId, hostId, { firestore }),\n firestore.collection('orgs').doc(job.orgId).get(),\n readAiDraft(firestore, { kind: 'screen', hostId, id: draftId }),\n ])\n const org = (orgSnapshot.data() ?? null) as Partial<AglynOrgBilling> | null\n if (written?.deleted) return { ...aiUnspentOutcome(model), failure: AI_JOB_PAGE_DELETED_COPY }\n // A record template's copy binds its dataset's fields, and its draft says\n // where the member saves the binding (AGL-3475).\n const record = aiPageRecordTemplate(screen.record, inventory, plan)\n const recordNote = record ? aiPageRecordNote(record) : null\n\n // The plan starts from a copy of a screen the site has (rule 15): the copy\n // is the draft — unrouted, as every copy is — and nothing is generated.\n // Only a page is a page's start: a collection entry template's copy stays\n // a template, so a plan naming one builds the page instead.\n // A copy shows what its SOURCE shows, and this branch generates nothing.\n // So if the source cannot already show what the plan promised, copying it\n // can only produce a page that breaks the promise — the refusal below\n // would be certain before the copy was ever minted. Read the source and\n // build instead, rather than leave the member a page they must throw away\n // and a Try again that can only copy the same thing (AGL-3024).\n // Only a source we could READ and found short diverts: one we cannot read\n // is one we cannot judge, and the review after the copy still answers for\n // that. A short source carries findings; an unreadable one carries none.\n // A copy of the starter home page is never a page the job built\n // (AGL-3596): the site's own placeholder is replaced by a page generated\n // from the plan, so a plan that names it as a start builds instead.\n const copiesStarter = Boolean(\n screen.duplicateOf &&\n inventory.screens.some((row) => row.id === screen.duplicateOf && row.replaceable),\n )\n const sourceReview =\n !written && screen.duplicateOf && !copiesStarter\n ? await aiCopiedPageReview(firestore, { hostId, id: screen.duplicateOf, name, screen })\n : null\n const sourceShort = Boolean(sourceReview?.findings.length)\n if (\n !written &&\n !sourceShort &&\n !copiesStarter &&\n screen.duplicateOf &&\n inventory.screens.some((row) => row.id === screen.duplicateOf && !row.template)\n ) {\n const copy = await duplicate('screen', {\n orgId: job.orgId,\n hostId,\n sourceId: screen.duplicateOf,\n name,\n uid: job.createdBy,\n org: org as Record<string, unknown> | null,\n // A copy core mints is written under core's id, never `draftId`, so\n // `written` above cannot find it and this branch would fire on every\n // pass — and the pass after a refused copy is the common one, since\n // the review below offers to try again. The job's recorded id is the\n // claim, so a second pass replays the one copy instead of leaving\n // another orphan screen behind it (AGL-3024).\n attemptKey: draftId,\n })\n if (copy.ok) {\n // A copy is not a build (AGL-3024). Nothing is generated here, so\n // whatever the plan promised BEYOND the source is in the draft only\n // if the source already had it, and no model is left to answer for\n // the difference — the copy itself has to.\n const review = await aiCopiedPageReview(firestore, { hostId, id: copy.id, name, screen })\n if (review) return aiUnspentOutcome(model, { review })\n const hostSubdomain = await aiSiteSubdomain(firestore, hostId)\n return aiUnspentOutcome(model, {\n outputs: [output({ id: copy.id, versionId: copy.versionId, name: copy.name, hostSubdomain }, null, recordNote)],\n })\n }\n if (copy.ok === false && copy.status === 403) {\n return aiUnspentOutcome(model, { review: aiLimitReview(copy.error) })\n }\n }\n\n const stored = written ? await readAiDraftNodes(firestore, { kind: 'screen', hostId, id: draftId }) : null\n if (written && !stored) return { ...aiUnspentOutcome(model), failure: AI_JOB_PAGE_DELETED_COPY }\n const page = stored?.nodes ?? aiEmptyPage()\n const index = sectionIds.findIndex((id) => !(id in page))\n // A workspace that keeps no reusable components draws its repeats inline,\n // and every pass is held to the rules that way (AGL-3030). A form is a\n // saved one placed by id on every plan, or none (AGL-3596).\n const reusableComponents = checkEntitlement(org, 'reusableComponents')\n // A link may take a visitor to a section of this page, by its name in the plan (AGL-3097).\n const sections = screen.sections.map((section) => section.name)\n // A third-party player the confirmed plan lists for this page is the only one it may embed (AGL-3433).\n const embeds = aiPlanEmbedsFor(plan, { slug: screen.slug })\n const context = aiPageCheckContext(inventory, {\n reusableComponents,\n sections,\n embeds,\n recordTokens: aiPageRecordTokens(record),\n })\n\n // ── The last pass: the whole page, its listing, and the draft reported ──\n if (index === -1 && written) {\n // A link the page carries to one of its sections goes there, now every section is built.\n const report = validateAiDoctrineTree({ rootId: CANVAS_ROOT_ELEMENT_ID, nodes: page }, 'page', {\n ...context,\n scrollTargetIds: sectionIds,\n })\n // The facts the brief did not give, on the page or in the defaults its components show (AGL-3056).\n const note =\n [\n aiBracketedFactsNote({\n tree: { rootId: CANVAS_ROOT_ELEMENT_ID, nodes: page as unknown as AiDoctrineTree['nodes'] },\n inventory,\n }),\n recordNote,\n ]\n .filter(Boolean)\n .join(' ') || null\n // The page's own draft, reported once however many times the last pass\n // runs (AGL-3143): a refused pass reports it and a later one that\n // passes does not report it twice.\n const reported = outputs.some((entry) => entry.resource === 'screen' && entry.id === draftId)\n const reports = reported ? [] : [output(written, report.load, note)]\n if (report.violations.length) {\n // The check mints its own ids; the review names the nodes by the ids the draft stores them under.\n const storedIds = report.tree?.sourceIds ?? {}\n const violations = report.violations.map((violation) =>\n violation.nodeIds ? { ...violation, nodeIds: aiModelNodeIds(violation.nodeIds, storedIds) } : violation,\n )\n // THE DRAFT TRAVELS WITH THE REFUSAL (AGL-3143). Every other review\n // parks on a model's answer, so trying again asks the model again and\n // can come back different. This one reads the STORED page and spends\n // nothing, so trying again refuses the same page for the same reasons\n // forever — and a job parked here having reported no output names no\n // draft at all, which leaves the member nowhere to mend the nodes the\n // findings name and no way to finish the job from the console. The\n // draft is reported now instead, and the job still parks: what the\n // review names is mended in the draft, and the next pass then passes.\n return aiUnspentOutcome(model, {\n outputs: reports,\n review: aiDoctrineReview({\n message: `${aiDoctrineNeedsInputMessage(violations)} ${AI_JOB_PAGE_REFUSED_DRAFT_COPY}`,\n violations,\n answer: { tree: { rootId: CANVAS_ROOT_ELEMENT_ID, nodes: page } },\n }),\n })\n }\n let spent: AiJobStepOutcome = aiUnspentOutcome(model)\n if (!written.seo?.title?.trim() || !written.seo?.description?.trim()) {\n const seoModel = modelFor?.('job.seo') ?? aiModelForStep('job.seo')\n // The plan's own listing, held to the editor's guidance, when the\n // listing cannot be written inside this pass's time on the model the\n // job runs.\n let values = {\n title: clip(screen.seoTitle, SCREEN_SEO_TEXT_GUIDANCE.title),\n description: clip(screen.seoDescription, SCREEN_SEO_TEXT_GUIDANCE.description),\n }\n if (\n // A listing is written from the page's own text, with no inventory\n // and so no lookup round.\n aiGenerationWorstCaseMs({ maxTokens: AI_SEO_FIELDS_MAX_TOKENS, model: seoModel, lookups: 0 }) <=\n AI_JOB_PAGE_STEP_MINIMUM_MS\n ) {\n const host = await firestore.collection('hosts').doc(hostId).get()\n const result = await seoFields({\n subject: { kind: 'screen', name, path: screenRoutePathToUrl(slug || SCREEN_ROOT_PATH) },\n brand: siteNameOf(host),\n // What the site is and who it is for (AGL-2918). The page's own\n // Markdown says what the page is about and cannot say who it was\n // written for, so the listing borrows the two answers the job was\n // started from — the scaffold's derived page job carries them,\n // and a page job started from the Screens page carries neither.\n site: aiSiteWords(job.inputs),\n text: buildPageMarkdown({ nodes: page as never, rootId: CANVAS_ROOT_ELEMENT_ID }),\n fields: ['title', 'description'],\n // Other screens by name only: their own listings are their content.\n otherTitles: inventory.screens.map((row) => row.name),\n model: seoModel,\n ...(signal ? { signal } : {}),\n })\n spent = { ...spent, ...aiGenerationSpent(result) }\n if (result.status === 'ok') {\n values = {\n title: result.value.title?.trim() || values.title,\n description: result.value.description?.trim() || values.description,\n }\n }\n }\n await writeAiDraftScreenSeo(firestore, { hostId, id: draftId, seo: values, now })\n }\n return { ...spent, outputs: reports }\n }\n\n // ── A section pass ──\n if (!written) {\n const allowance = await aiDraftAllowanceRefusal(firestore, { kind: 'screen', hostId, org })\n if (allowance) return aiUnspentOutcome(model, { review: aiLimitReview(allowance) })\n }\n const maxTokens = aiJobPageSectionMaxTokens(model)\n const maxElements = aiJobPageSectionMaxElements(maxTokens)\n const result = await runValidatedGeneration<AiPageSection>('page-section', {\n step: 'job.page',\n model,\n instructions: AI_JOB_PAGE_INSTRUCTIONS,\n inventory,\n messages: [\n {\n role: 'user',\n content: aiPageSectionPrompt({ job, plan, screen, index, maxElements, reusableComponents, record }),\n },\n ],\n tool: AI_PAGE_SECTION_TOOL,\n maxTokens,\n // A section cut off at its ceiling is asked for smaller, and a member\n // whose section still does not fit reads that it was too large (AGL-3042).\n cutOff: { noun: 'section', smaller: aiPageSectionSmaller({ maxElements, reusableComponents }) },\n thinking: 'off',\n check: aiPageSectionCheck({\n page,\n sectionIds,\n index,\n context,\n section: screen.sections[index],\n inventory,\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 if (!written) {\n const draft = await writeAiDraft(firestore, {\n kind: 'screen',\n hostId,\n id: draftId,\n uid: job.createdBy,\n org,\n name,\n nodes: aiPageWithSection(aiEmptyPage(), result.value, sectionIds),\n slug,\n layoutId: aiPageDraftLayoutId(screen, inventory),\n aiJobId: aiOriginJobId(job),\n now,\n })\n if (draft.ok === false) {\n if (draft.status === 404) throw new Error(`site ${hostId} vanished while its page was generated`)\n return { ...spent, review: aiLimitReview(draft.error) }\n }\n } else {\n const update = await updateAiDraftNodes(firestore, {\n kind: 'screen',\n hostId,\n id: draftId,\n now,\n // A pass run again after its write finds its section and changes nothing.\n update: (nodes) =>\n result.value.rootId in nodes ? null : aiPageWithSection(nodes, result.value, sectionIds),\n })\n if (update.ok === false) return { ...spent, failure: AI_JOB_PAGE_DELETED_COPY }\n }\n return { ...spent, continue: true }\n }\n}\n\nexport const runAiJobPageStep = createAiJobPageStep()\n\n/**\n * Registers the page step with the least time a section pass needs and the\n * time its next pass needs, the passes a page with its creations may take, and\n * the check a page job passes when it is created or its plan is confirmed.\n */\nexport function registerAiPageJob(): void {\n registerAiJobStep('page', runAiJobPageStep, {\n minimumMs: AI_JOB_PAGE_STEP_MINIMUM_MS,\n minimumMsFor: aiPageJobRunMinimumMs,\n })\n registerAiJobStepPasses('page', AI_JOB_PAGE_MAX_PASSES)\n registerAiJobAdmission('page', aiPageJobAdmission)\n}\n"],"names":["buildPageMarkdown","checkEntitlement","SCREEN_SEO_TEXT_GUIDANCE","SCREEN_ROOT_PATH","screenRoutePathToUrl","CANVAS_ROOT_ELEMENT_ID","duplicateResource","AI_BUILD_PLAN_LIMITS","aiPlanEmbedsFor","AI_PAGE_CREATE_KINDS","aiPageCreationRefusal","aiPagePlanRefusal","parseAiPageJobInputs","aiPlanCapabilitiesForJob","aiPlanUncreatable","aiSiteWords","aiModelForStep","aiDoctrineNeedsInputMessage","runValidatedGeneration","validateAiDoctrineTree","AI_SEO_FIELDS_MAX_TOKENS","generateSeoFields","readSiteInventory","registerAiJobAdmission","aiGenerationWorstCaseMs","aiJobDraftId","aiOriginJobId","aiDraftAdmissionRefusal","aiDraftAllowanceRefusal","aiSiteSubdomain","readAiDraft","readAiDraftNodes","readAiPlanCapabilities","updateAiDraftNodes","writeAiDraft","writeAiDraftScreenSeo","aiBracketedFactsNote","aiConfirmedPlan","aiDoctrineReview","aiGenerationSpent","aiLimitReview","aiModelNodeIds","aiUnspentOutcome","AI_JOB_PAGE_SECTION_MAX_TOKENS","AI_JOB_PAGE_SECTION_TOKENS","AI_JOB_PAGE_STEP_BUDGET","AI_JOB_PAGE_STEP_MINIMUM_MS","aiJobPageSectionMaxTokens","AI_JOB_PAGE_INSTRUCTIONS","AI_PAGE_SECTION_TOOL","aiEmptyPage","aiPageCheckContext","aiPageRecordNote","aiPageRecordTemplate","aiPageRecordTokens","aiPageSectionCheck","aiPageSectionNodeId","aiPageSectionPrompt","aiPageSectionSmaller","aiPageWithSection","AI_PLAN_ITEMS_MIN","aiPlanCopiedPageViolations","aiCreationUnit","aiRunJobUnit","aiSiteBuiltRefs","aiSitePendingUnits","aiSiteUnitJob","aiJobStepRunMinimumMs","aiJobStepRunnerFor","registerAiJobStep","registerAiJobStepPasses","AI_JOB_PAGE_TOKENS_PER_ELEMENT","AI_JOB_PAGE_REAL_TOKENS_PER_ESTIMATED","Math","max","AI_JOB_PAGE_REAL_TOKENS_PER_ELEMENT","ceil","aiJobPageSectionMaxElements","maxTokens","floor","AI_JOB_PAGE_DEFAULT_NAME","AI_JOB_PAGE_NO_PLAN_COPY","AI_JOB_PAGE_DELETED_COPY","AI_JOB_PAGE_CREATION_UNAVAILABLE_COPY","AI_JOB_PAGE_CREATION_EMPTY_COPY","AI_JOB_PAGE_REFUSED_DRAFT_COPY","AI_JOB_PAGE_MAX_PASSES","create","sections","aiPageJobUnits","plan","flatMap","kind","creation","index","unit","aiPageJobRunMinimumMs","job","confirmed","units","outputs","aiPageDraftSlug","screen","segments","slug","split","map","part","trim","filter","Boolean","length","aiSiteDefaultLayoutId","inventory","counts","layouts","Set","row","id","size","pages","screens","template","layoutId","has","home","find","Map","set","get","mostUsed","entries","sort","a","b","aiPageDraftLayoutId","layout","some","clip","value","text","cut","slice","space","lastIndexOf","siteNameOf","host","aiCopiedPageReview","firestore","input","section","items","stored","hostId","nodes","reason","message","name","findings","violations","rootId","aiPageJobAdmission","context","inputs","status","error","refusal","jobKind","org","orgId","noun","ownCheck","capabilities","creates","createAiJobPageStep","deps","readInventory","duplicate","seoFields","runnerFor","stepContext","orgSnapshot","now","signal","modelFor","Error","model","failure","runner","pass","emptyCopy","built","outcome","continue","resource","slot","label","title","sectionIds","_","$id","draftId","output","draft","load","note","versionId","hostSubdomain","nav","proposal","navigation","written","Promise","all","collection","doc","data","deleted","record","recordNote","copiesStarter","duplicateOf","replaceable","sourceReview","sourceShort","copy","sourceId","uid","createdBy","attemptKey","ok","review","page","findIndex","reusableComponents","embeds","recordTokens","report","scrollTargetIds","tree","join","reported","entry","reports","storedIds","sourceIds","violation","nodeIds","answer","spent","seo","description","seoModel","values","seoTitle","seoDescription","lookups","result","subject","path","brand","site","fields","otherTitles","allowance","maxElements","step","instructions","messages","role","content","tool","cutOff","smaller","thinking","check","refused","aiJobId","update","runAiJobPageStep","registerAiPageJob","minimumMs","minimumMsFor"],"mappings":"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"}
|
|
@@ -19,7 +19,7 @@ import { AI_BUILD_PLAN_TOOL, aiBuildPlanToolFor, isAiPlanNewRef } from "../model
|
|
|
19
19
|
import { AI_TEMPLATE_SUBJECT_DEFINITIONS, aiTemplateShownTokens } from "../model/ai-template-subjects.js";
|
|
20
20
|
import { AI_JOB_CREATE_KINDS, AI_JOB_CREATE_NOUNS } from "../model/ai-job-creations.js";
|
|
21
21
|
import { AI_PAGE_CREATE_KINDS, aiPagePlanShapeRefusal } from "../model/ai-page-job.js";
|
|
22
|
-
import { aiPlanCapabilitiesForJob, aiPlanCapabilityLines } from "../model/ai-plan-capabilities.js";
|
|
22
|
+
import { AI_PLAN_NO_FORM_SENTENCE, aiPlanCanPlaceForm, aiPlanCapabilitiesForJob, aiPlanCapabilityLines } from "../model/ai-plan-capabilities.js";
|
|
23
23
|
import { AI_FREE_SITE_WORST_CASE_CREDITS, AI_SITE_CREATE_KINDS, AI_SITE_FREE_PAGES, AI_SITE_PLAN_MAX_TOKENS, aiFreeSiteSectionsWithin, aiSitePlanShapeRefusal } from "../model/ai-site-job.js";
|
|
24
24
|
import { aiPlanFailureCopy, aiPlanRetryRefusal } from "../model/ai-job-failure-copy.js";
|
|
25
25
|
import { FREE_AI_TASTE_CREDITS_PER_MONTH } from "../plan-entitlements.js";
|
|
@@ -112,6 +112,11 @@ import { AI_JOBS_COLLECTION, registerAiJobPlanStep } from "./ai-jobs.js";
|
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
114
|
if (capabilities) lines.push(...aiPlanCapabilityLines(capabilities));
|
|
115
|
+
// A form is a saved form or none (AGL-3596): said where the site read has
|
|
116
|
+
// none to place and the job may create none.
|
|
117
|
+
if (capabilities && inventory && !aiPlanCanPlaceForm(inventory, capabilities)) {
|
|
118
|
+
lines.push(AI_PLAN_NO_FORM_SENTENCE);
|
|
119
|
+
}
|
|
115
120
|
lines.push(...aiPlanTemplateTokenLines(job));
|
|
116
121
|
lines.push(...aiPlanSiteLines(job, inventory, capabilities));
|
|
117
122
|
return lines.join('\n');
|
|
@@ -148,7 +153,7 @@ import { AI_JOBS_COLLECTION, registerAiJobPlanStep } from "./ai-jobs.js";
|
|
|
148
153
|
layouts: ((_ref1 = inventory == null ? void 0 : inventory.layouts.length) != null ? _ref1 : 0) ? 0 : 1,
|
|
149
154
|
pages: cap
|
|
150
155
|
}, FREE_AI_TASTE_CREDITS_PER_MONTH);
|
|
151
|
-
lines.push(`This is a Free workspace: plan at most ${cap} ${cap === 1 ? 'page' : 'pages'} — the home page at / and the one page the brief most needs, such as services, booking or contact — with at most ${sections} sections across them
|
|
156
|
+
lines.push(`This is a Free workspace: plan at most ${cap} ${cap === 1 ? 'page' : 'pages'} — the home page at / and the one page the brief most needs, such as services, booking or contact — with at most ${sections} sections across them.${aiPlanCanPlaceForm(inventory, capabilities) ? ' Put the contact form on one of them.' : ''}`);
|
|
152
157
|
}
|
|
153
158
|
lines.push("Keep the plan an outline: each page's title, address, a short search title and description, and its sections named in a few words. The build writes the copy.");
|
|
154
159
|
return lines;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-plan-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 { createHash } from 'node:crypto'\nimport type { AglynOrgBilling } from '@aglyn/aglyn/foundation/definitions/org-billing.types'\nimport {\n AI_BUILD_PLAN_TOOL,\n aiBuildPlanToolFor,\n isAiPlanNewRef,\n type AiBuildPlan,\n} from '../model/ai-build-plan'\nimport type { AiJob, AiJobKind, AiJobPlan, AiJobStatus } from '../model/ai-jobs.types'\nimport {\n AI_TEMPLATE_SUBJECT_DEFINITIONS,\n aiTemplateShownTokens,\n type AiTemplateSubject,\n} from '../model/ai-template-subjects'\nimport { AI_JOB_CREATE_KINDS, AI_JOB_CREATE_NOUNS } from '../model/ai-job-creations'\nimport { AI_PAGE_CREATE_KINDS, aiPagePlanShapeRefusal } from '../model/ai-page-job'\nimport {\n aiPlanCapabilitiesForJob,\n aiPlanCapabilityLines,\n type AiPlanCapabilities,\n type AiPlanJobScope,\n} from '../model/ai-plan-capabilities'\nimport type { AiSiteInventory } from '../model/ai-site-inventory'\nimport {\n AI_FREE_SITE_WORST_CASE_CREDITS,\n AI_SITE_CREATE_KINDS,\n AI_SITE_FREE_PAGES,\n AI_SITE_PLAN_MAX_TOKENS,\n aiFreeSiteSectionsWithin,\n aiSitePlanShapeRefusal,\n} from '../model/ai-site-job'\nimport { aiPlanFailureCopy, aiPlanRetryRefusal } from '../model/ai-job-failure-copy'\nimport { FREE_AI_TASTE_CREDITS_PER_MONTH } from '../plan-entitlements'\nimport { aiDoctrineSystemBlocks, runValidatedGeneration } from '../runtime/ai-doctrine'\nimport { AI_STEP_TIERS, aiCatalogEntry, aiDefaultModelFor } from '../providers/catalog'\nimport {\n AI_FREE_PAGE_WORST_CASE_CREDITS,\n aiPlanEmbedBriefViolations,\n type AiDoctrineViolation,\n} from '../runtime/ai-doctrine-validators'\nimport { assistCreditsFromUsd } from '../usage/assist-credits'\nimport { ASSIST_RETURNED_USD_FIELD, assistSpendAfterReturnsUsd } from '../usage/assist-credit-returns'\nimport { freeAccountUsageRef, freeAssistAccount, type AssistMeteredOrg } from '../usage/assist-free-taste'\nimport { AI_ROUTING_TABLE, aiModelForStep } from '../providers/routing'\nimport type { AiTool } from '../providers/contract'\nimport type { AiSystemBlock } from '../runtime/ai-runtime'\nimport { readSiteInventory } from '../runtime/site-inventory'\nimport { aiJobAdmissionRefusal } from './ai-job-admission'\nimport { readAiPlanCapabilities } from './ai-job-drafts'\nimport {\n AI_JOB_BRIEF_MAX_CHARS,\n type AiJobStepOutcome,\n type AiJobStepRunner,\n} from './ai-job-text-step'\nimport { aiJobStepBudget } from './ai-job-budget'\nimport { aiDoctrineReview, aiUnspentOutcome } from './ai-job-generation'\nimport { aiPlanWithDraftIds } from './ai-job-draft-ids'\nimport { AI_JOBS_COLLECTION, registerAiJobPlanStep } from './ai-jobs'\n\n/**\n * The plan step (AGL-2935): the first step of every job that builds site\n * structure. Before a node is generated, the model answers with a typed plan\n * — what the site already has that the job reuses, what it creates and why,\n * and the screens it builds from them — held to the doctrine's plan rules\n * against the site's inventory and re-asked once when it breaks one.\n *\n * A plan that passes is kept on the job, and the job stops for review: the\n * member reads what it will build before a credit is spent building it, and\n * confirms through the resume door. A plan that still breaks a rule on its\n * re-ask stops the job the same way, with the rules named. Either way the\n * step writes nothing itself; the machine records the plan, the spend and\n * the stop, as it records every step.\n *\n * ── Told what it may create, refused what it cannot build (AGL-3030) ─────\n *\n * Before it asks, the step reads what the job may create on its site: the\n * workspace's plan, the site's counts against its limits, and — for a kind\n * that builds only some plans — what the job builds itself. The user turn\n * states it, the plan rules refuse a creation outside it with the one re-ask,\n * and where the workspace keeps no reusable components they accept the page\n * built inline. A plan the confirm door would still refuse — the kind's own\n * admission, handed the plan, as the resume door hands it — is refused HERE,\n * before a member is shown a Confirm: the job fails with the door's sentence,\n * its plan is not kept, and it holds nothing.\n */\n\n/** The plan step's own instructions, cached after the doctrine. */\nexport const AI_JOB_PLAN_INSTRUCTIONS: readonly AiSystemBlock[] = [\n {\n text:\n 'You plan what a website builder job will build, before anything is built. You are given the kind of job and the brief from the person who owns the site. ' +\n 'Answer with submit_build_plan: what the site inventory already has that the job reuses, what it must create and why nothing listed will do, and each page it builds (the screens list) with its layout, template, slug, search title, search description and sections from top to bottom. ' +\n 'Refer to inventory records by id and to what the plan creates as new:<name>. Plan only what the brief asks for: a component, layout, template, form or email job plans no pages unless the brief asks for them, and a page job plans exactly one page.',\n },\n]\n\n/**\n * The plan step's time (AGL-3026, AGL-3036): its two inventory-lookup rounds,\n * its answer and its re-ask at the routing table's ceiling for `job.plan`, on\n * the tier that step kind is served from, with the step's reads and writes,\n * at the rates `ai-job-budget.ts` assumes — fitted to what a beat can give a\n * step, so the served tier asks a little under the routing ceiling.\n */\nexport const AI_JOB_PLAN_STEP_BUDGET = aiJobStepBudget({\n tier: AI_STEP_TIERS['job.plan'],\n maxTokens: AI_ROUTING_TABLE['job.plan'].maxTokens,\n})\n\n/**\n * The least time a plan needs before it starts. Registered with the step, so\n * an inline door never starts a plan. A plan thinks before it answers and\n * routinely runs past an inline door's budget, and a provider call that\n * budget cuts off is still generated and billed upstream while the meter\n * records nothing. The beat starts a plan only with this much of its own\n * budget left, and a spec holds it inside that budget.\n */\nexport const AI_JOB_PLAN_STEP_MINIMUM_MS = AI_JOB_PLAN_STEP_BUDGET.minimumMs\n\n/**\n * The plan's answer ceiling on the model a job runs: the most whose worst\n * case fits the least time the step registered, and never more than the\n * routing table's. A model the catalog does not know is planned at the\n * slowest.\n */\nexport function aiJobPlanMaxTokens(model: string): number {\n return AI_JOB_PLAN_STEP_BUDGET.maxTokens(model)\n}\n\n/** What the member reads while a plan waits for them. */\nexport const AI_JOB_PLAN_REVIEW_COPY =\n 'The plan is ready. Review what the job will reuse and create, then confirm it to build.'\n\n/**\n * The job as the plan step's user turn: its kind, its brief, its scalar\n * inputs and, where the job read them, what it may create on its site\n * (AGL-3030). Per workspace and per site, which is why they ride here and\n * never in a system block the platform's cache entry is keyed on.\n */\nexport function aiJobPlanPrompt(\n job: Pick<AiJob, 'kind' | 'brief' | 'inputs'>,\n capabilities: AiPlanCapabilities | null = null,\n inventory: AiSiteInventory | null = null,\n): string {\n const lines = [`Job kind: ${job.kind}`, `Brief: ${job.brief.slice(0, AI_JOB_BRIEF_MAX_CHARS)}`]\n for (const [key, value] of Object.entries(job.inputs ?? {})) {\n if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {\n lines.push(`${key}: ${String(value)}`)\n }\n }\n if (capabilities) lines.push(...aiPlanCapabilityLines(capabilities))\n lines.push(...aiPlanTemplateTokenLines(job))\n lines.push(...aiPlanSiteLines(job, inventory, capabilities))\n return lines.join('\\n')\n}\n\n/**\n * What a SITE job's plan is told beside the inventory (AGL-3594): the pages\n * the site already has and that their addresses are taken, which one — the\n * untouched starter home — a planned home page at `/` replaces, the Free page\n * cap and its guidance, and that a plan is an outline.\n *\n * A site job only. A new site is born with a published home page at `/`\n * (AGL-3497), and a plan that did not know it could replace it planned its own\n * home there and was refused for the clash on every new site. Kept off a page\n * job's turn, whose length the Free page's wall is proven at.\n */\nexport function aiPlanSiteLines(\n job: Pick<AiJob, 'kind'>,\n inventory: AiSiteInventory | null,\n capabilities: AiPlanCapabilities | null,\n): string[] {\n if (job.kind !== 'site') return []\n const lines: string[] = []\n const pages = (inventory?.screens ?? []).filter((screen) => !screen.template)\n const starter = pages.find((screen) => screen.replaceable)\n if (pages.length) {\n lines.push('Pages the site already has; their addresses are taken unless a line says otherwise:')\n for (const page of pages) {\n lines.push(\n page.replaceable\n ? `- ${page.name} at ${page.slug}: the starter home page the site was created with. Plan this site's home page at / and it replaces this one.`\n : `- ${page.name} at ${page.slug}`,\n )\n }\n if (!starter && pages.some((page) => page.slug.trim() === '/' || page.slug.trim() === '')) {\n lines.push(\"The home page at / is the owner's own and stays: give every page you plan another address.\")\n }\n }\n const cap = capabilities?.freeSitePages\n if (cap !== undefined) {\n const sections = aiFreeSiteSectionsWithin(\n { layouts: (inventory?.layouts.length ?? 0) ? 0 : 1, pages: cap },\n FREE_AI_TASTE_CREDITS_PER_MONTH,\n )\n lines.push(\n `This is a Free workspace: plan at most ${cap} ${cap === 1 ? 'page' : 'pages'} — the home page at / and the one page the brief most needs, such as services, booking or contact — with at most ${sections} sections across them. Put the contact form on one of them.`,\n )\n }\n lines.push(\n \"Keep the plan an outline: each page's title, address, a short search title and description, and its sections named in a few words. The build writes the copy.\",\n )\n return lines\n}\n\n/**\n * What a TEMPLATE job's plan is told its creation's `fields` are (AGL-3143\n * §11): the subject's binding tokens, listed so the planner promises only\n * what the page can fill and the build can be held to it\n * (`aiPlanTemplateTokenViolations`).\n *\n * ⛔ It rides the job's own turn and NOT the `fields` description in the plan\n * tool, which every kind's request caches. Written there it read better and\n * cost 40 tokens of the shared prefix — 2,980 to 3,020 — which is one credit\n * of the Free page's 300-credit wall, taking the room it keeps for a re-asked\n * section from 45 credits to 44 and breaking `ai-job-free-page.spec.ts`. A\n * page job must not pay for a sentence about templates.\n *\n * The list is the tokens a reader sees, never the whole catalog (AGL-3143\n * §16). Offered a slug and the publish timestamp, a live plan promised them,\n * and its build kept the promise the only way a slug can be kept: it printed\n * \"entry slug: {{entry.slug}}\" on every article.\n */\nexport function aiPlanTemplateTokenLines(job: Pick<AiJob, 'kind' | 'inputs'>): string[] {\n if (job.kind !== 'template') return []\n const subject = (job.inputs ?? {})['subject']\n const definition =\n typeof subject === 'string'\n ? AI_TEMPLATE_SUBJECT_DEFINITIONS[subject as AiTemplateSubject]\n : undefined\n if (!definition) return []\n return [\n `The template's fields are the binding tokens its page shows a reader, each one of: ${aiTemplateShownTokens(\n definition,\n ).join(', ')}. A link or picture token is shown by the link or picture holding it. Promise only what the page fills.`,\n ]\n}\n\n/** A kind that builds only some plans: the creations it makes, and the shapes it refuses. */\nexport interface AiJobPlanScope extends AiPlanJobScope {\n /** Why a job of the kind cannot build a plan of this shape; `null` when it can. Pure. */\n shapeRefusal: (plan: AiBuildPlan, options?: { freeTaste?: boolean }) => string | null\n}\n\n/**\n * The kinds whose confirm door builds only some plans (AGL-3030). A page job\n * builds one screen and a scaffold four to eight; each builds only the\n * creations its list names. A template, layout, component, form or email job\n * builds its own record and what that record places (AGL-3143 §15,\n * `AI_JOB_CREATE_KINDS`), and holds no plan to a shape.\n */\nexport const AI_JOB_PLAN_SCOPES: Readonly<Partial<Record<AiJobKind, AiJobPlanScope>>> = {\n page: { noun: 'a page job', creates: AI_PAGE_CREATE_KINDS, shapeRefusal: aiPagePlanShapeRefusal },\n site: { noun: 'a site scaffold', creates: AI_SITE_CREATE_KINDS, shapeRefusal: aiSitePlanShapeRefusal },\n ...Object.fromEntries(\n Object.entries(AI_JOB_CREATE_KINDS).map(([kind, creates]) => [\n kind,\n { noun: AI_JOB_CREATE_NOUNS[kind as AiJobKind] as string, creates, shapeRefusal: () => null },\n ]),\n ),\n}\n\n/**\n * What the plan step holds beyond the doctrine's plan rules, as the\n * violations the plan's one re-ask names: the players the plan lists against\n * the brief it answers (AGL-3433), which only this step reads beside the plan,\n * and a kind's shape refusal.\n */\nfunction planViolations(\n scope: AiJobPlanScope | null,\n brief: string,\n freeTaste = false,\n): (plan: AiBuildPlan) => AiDoctrineViolation[] {\n return (plan) => {\n const message = scope?.shapeRefusal(plan, { freeTaste }) ?? null\n return [\n ...aiPlanEmbedBriefViolations(plan, brief),\n ...(message ? [{ rule: null, code: 'plan-job-shape', message }] : []),\n ]\n }\n}\n\n/**\n * The inventory names of every id the plan references, so the proposal reads\n * \"the Services layout\" rather than an id — kept on the plan because the\n * inventory is not read again when the member opens it.\n */\nexport function aiPlanLabels(\n plan: AiBuildPlan,\n inventory: AiSiteInventory | null,\n): Record<string, string> {\n const names = new Map<string, string>()\n for (const rows of [\n inventory?.components,\n inventory?.layouts,\n inventory?.templates,\n inventory?.forms,\n inventory?.datasets,\n inventory?.collections,\n inventory?.screens,\n ]) {\n for (const row of rows ?? []) names.set(row.id, row.name)\n }\n const refs = [\n ...plan.reuse.map((entry) => entry.id),\n ...plan.create.map((entry) => entry.duplicateOf),\n ...plan.screens.flatMap((screen) => [\n screen.layout,\n screen.template,\n screen.duplicateOf,\n ...screen.sections.flatMap((section) => section.uses),\n ]),\n ]\n const labels: Record<string, string> = {}\n for (const ref of refs) {\n if (!ref || isAiPlanNewRef(ref)) continue\n const name = names.get(ref)\n if (name) labels[ref] = name\n }\n return labels\n}\n\n/* ------------------------------------------------------------------------ *\n * Reusing an identical brief's plan\n * ------------------------------------------------------------------------ */\n\n/**\n * How long a plan may be reused for (AGL-2937).\n *\n * The window is what makes reuse safe rather than merely cheap. The key\n * already covers everything the request said — the brief, the inputs, the\n * model and the prompt as rendered, site inventory included — so a plan is\n * reused only when nothing the model was shown has changed. What the key\n * cannot see is the world outside the request: a page published, a component\n * renamed after the inventory was read, a member who meant something\n * different the second time. Fifteen minutes is short enough that the answer\n * is still the one the site would give, and long enough to cover what reuse\n * is actually for — the same brief run twice in a sitting, a job resubmitted\n * after a refused reservation, two members starting the same work.\n */\nexport const AI_PLAN_REUSE_WINDOW_MS = 15 * 60 * 1_000\n\n/** Jobs one lookup reads. More than one can share a key; the newest plan wins. */\nexport const AI_PLAN_REUSE_CANDIDATES = 5\n\n/** A job whose plan might be reused, as the finder reports it. */\nexport interface AiJobPlanCandidate {\n jobId: string\n status: AiJobStatus\n plan: AiJobPlan\n}\n\n/**\n * The digest a plan is reused by: the whole request, never the answer.\n *\n * It hashes what the model was asked and what it was shown — the job's kind\n * and site, the user turn (which carries the trimmed brief and every scalar\n * input), the model that would answer, every rendered system block including\n * the site inventory, and the tool's schema. Two requests that hash the same\n * would have been sent the same bytes to the same model, so the second can\n * keep the first's answer.\n *\n * The version tag is the escape hatch: anything that changes what a key\n * MEANS, rather than what it covers, bumps it and strands every old key\n * harmlessly, since a key nothing matches simply asks the model.\n */\nexport function aiJobPlanKey(input: {\n job: Pick<AiJob, 'kind' | 'hostId'>\n prompt: string\n model: string\n system: readonly AiSystemBlock[]\n /** The plan tool the request offers (AGL-3433); the plain one when absent. */\n tool?: AiTool\n}): string {\n const digest = createHash('sha256')\n for (const part of [\n 'plan.v1',\n input.job.kind,\n input.job.hostId ?? '',\n input.model,\n input.prompt,\n ...input.system.map((block) => block.text),\n JSON.stringify(input.tool ?? AI_BUILD_PLAN_TOOL),\n ]) {\n // Length-prefixed, so two different splits of the same characters cannot\n // collide by running into one another.\n digest.update(`${part.length}:${part}\\u0000`)\n }\n return digest.digest('hex')\n}\n\n/** A job that could not have produced a reusable plan, whatever its key says. */\nconst UNREUSABLE: readonly AiJobStatus[] = ['canceled', 'failed']\n\nfunction planMillis(plan: AiJobPlan): number | null {\n const at = plan.proposedAt as unknown\n if (at instanceof Date) return at.getTime()\n if (at && typeof (at as { toMillis?: unknown }).toMillis === 'function') {\n return (at as { toMillis(): number }).toMillis()\n }\n return null\n}\n\n/**\n * The newest plan worth reusing out of what the finder returned: another\n * job's, still proposed or already confirmed, on a job that was neither\n * canceled nor failed, proposed inside the window.\n *\n * A canceled or failed job is excluded even though its plan may be perfectly\n * good: those are the jobs a member walked away from, and handing their plan\n * to the next one would make a rejected answer look like a fresh one.\n */\nexport function aiReusablePlan(\n candidates: readonly AiJobPlanCandidate[],\n context: { jobId: string; now: Date },\n): AiJobPlanCandidate | null {\n const floor = context.now.getTime() - AI_PLAN_REUSE_WINDOW_MS\n let best: AiJobPlanCandidate | null = null\n let bestAt = -1\n for (const candidate of candidates) {\n if (candidate.jobId === context.jobId) continue\n if (UNREUSABLE.includes(candidate.status)) continue\n if (candidate.plan.status !== 'proposed' && candidate.plan.status !== 'confirmed') continue\n const at = planMillis(candidate.plan)\n if (at === null || at < floor || at > context.now.getTime()) continue\n if (at > bestAt) {\n best = candidate\n bestAt = at\n }\n }\n return best\n}\n\nexport type AiJobPlanFinder = (\n orgId: string,\n key: string,\n firestore?: FirebaseFirestore.Firestore,\n) => Promise<AiJobPlanCandidate[]>\n\n/**\n * The finder the step uses in production: an equality on `plan.key` inside\n * the org's own jobs.\n *\n * One equality on one field, with no ordering beside it, so Firestore's\n * automatic single-field index answers it and no composite index is deployed.\n * The window and the statuses are applied in memory instead, which is what\n * keeps it that way.\n */\nexport const findAiJobsByPlanKey: AiJobPlanFinder = async (orgId, key, firestore) => {\n if (!firestore) return []\n const snapshot = await firestore\n .collection('orgs')\n .doc(orgId)\n .collection(AI_JOBS_COLLECTION)\n .where('plan.key', '==', key)\n .limit(AI_PLAN_REUSE_CANDIDATES)\n .get()\n return snapshot.docs.flatMap((doc) => {\n const data = doc.data() as { status?: AiJobStatus; plan?: AiJobPlan | null }\n return data.plan\n ? [{ jobId: doc.id, status: data.status ?? 'queued', plan: data.plan }]\n : []\n })\n}\n\n/** What a job may create on its site, read for the plan step; `null` for no site. */\nexport type AiPlanCapabilitiesReader = (input: {\n job: AiJob\n org: Partial<AglynOrgBilling> | null\n firestore: FirebaseFirestore.Firestore\n}) => Promise<AiPlanCapabilities | null>\n\n/** The reader the step uses in production: the draft bands' own arithmetic, for the job's site. */\nexport const readAiJobPlanCapabilities: AiPlanCapabilitiesReader = async ({ job, org, firestore }) =>\n job.hostId ? readAiPlanCapabilities(firestore, { hostId: job.hostId, org }) : null\n\nexport interface AiJobPlanStepDeps {\n /** The inventory reader; specs hand in a fake. */\n readInventory?: typeof readSiteInventory\n /** The reuse lookup; specs hand in a fake, and `null` turns reuse off. */\n findPlansByKey?: AiJobPlanFinder | null\n /** What the job may create on its site (AGL-3030); specs and the eval recorder hand in their own. */\n readCapabilities?: AiPlanCapabilitiesReader\n /** The kind's admission, asked of a plan before it is kept; the registry's otherwise. */\n admissionRefusal?: typeof aiJobAdmissionRefusal\n}\n\nexport function createAiJobPlanStep(deps: AiJobPlanStepDeps = {}): AiJobStepRunner {\n const readInventory = deps.readInventory ?? readSiteInventory\n const findPlansByKey =\n deps.findPlansByKey === undefined ? findAiJobsByPlanKey : deps.findPlansByKey\n const readCapabilities = deps.readCapabilities ?? readAiJobPlanCapabilities\n const admissionRefusal = deps.admissionRefusal ?? aiJobAdmissionRefusal\n return async ({ job, now, signal, firestore, modelFor, org: orgDocument }) => {\n const org = (orgDocument ?? null) as Partial<AglynOrgBilling> | null\n const [inventory, workspace] = await Promise.all([\n job.hostId ? readInventory(job.orgId, job.hostId, { firestore }) : Promise.resolve(null),\n readCapabilities({ job, org, firestore }),\n ])\n const scope = AI_JOB_PLAN_SCOPES[job.kind] ?? null\n const capabilities = aiSitePlanCapabilities(\n job,\n workspace ? aiPlanCapabilitiesForJob(workspace, scope) : null,\n )\n const freeTaste = capabilities?.freeTaste === true\n const site = job.kind === 'site'\n // The model switch's answer for this job (AGL-2942): the creator's pick\n // where the plan, the org restriction and the allotment allowlists allow\n // it, and Auto held to those same lists otherwise. Without a resolver the\n // doctrine asks the routing table itself. A Free site plans on its\n // provider's fast tier (AGL-3594), unless the creator picked a model.\n const model = aiSitePlanModel(job, modelFor?.('job.plan'), freeTaste)\n const route = AI_ROUTING_TABLE['job.plan']\n const prompt = aiJobPlanPrompt(job, capabilities, inventory)\n\n /**\n * The confirm door's answer for this plan, asked before the plan is kept\n * (AGL-3030): the kind's own admission, handed the plan as the resume\n * door hands it. A refusal fails the job with the door's sentence; a door\n * that could not answer keeps the plan, since the resume door asks again\n * before anything is built.\n */\n const refusalOnKeep = async (plan: AiJobPlan, outcome: AiJobStepOutcome): Promise<AiJobStepOutcome | null> => {\n let refusal: Awaited<ReturnType<typeof aiJobAdmissionRefusal>> = null\n try {\n refusal = await admissionRefusal(job.kind, {\n firestore,\n orgId: job.orgId,\n hostId: job.hostId ?? null,\n inputs: job.inputs ?? {},\n org,\n plan,\n uid: job.createdBy,\n })\n } catch (error) {\n console.error('ai plan admission failed', { orgId: job.orgId, jobId: job.$id, error })\n }\n return refusal ? { ...outcome, failure: refusal.error } : null\n }\n\n // Reuse before asking (AGL-2937). The key covers the whole request, so a\n // hit is a request that would have been sent the same bytes to the same\n // model; the window covers what the key cannot see. A reused plan spends\n // nothing, so the machine's spent-nothing branch releases the reservation\n // and meters no credit, and the job still stops for the member to confirm\n // — this job's plan is proposed to this member, whatever the last one did\n // with theirs.\n const resolved = model ?? aiModelForStep('job.plan')\n // A brief that asks for a video is offered the plan's list of players\n // (AGL-3433), and a job that may bind a dataset a record template on\n // each screen (AGL-3475).\n const tool = aiBuildPlanToolFor(job.brief, {\n records: Boolean(inventory?.datasets.length) || capabilities?.create.dataset.allowed === true,\n })\n const key = aiJobPlanKey({\n job,\n prompt,\n model: resolved,\n system: aiDoctrineSystemBlocks(inventory, { instructions: AI_JOB_PLAN_INSTRUCTIONS }),\n tool,\n })\n const reused = findPlansByKey\n ? aiReusablePlan(await findPlansByKey(job.orgId, key, firestore), {\n jobId: job.$id,\n now,\n })\n : null\n if (reused) {\n // The reused plan's draft ids name the other job's drafts; this job's are its own.\n const plan: AiJobPlan = aiPlanWithDraftIds(job.kind, {\n ...reused.plan,\n status: 'proposed',\n labels: aiPlanLabels(reused.plan, inventory),\n proposedAt: now as unknown as AiJobPlan['proposedAt'],\n confirmedAt: null,\n confirmedBy: null,\n key,\n reusedFrom: reused.jobId,\n })\n const unspent = aiUnspentOutcome(resolved)\n return (\n (await refusalOnKeep(plan, unspent)) ?? {\n ...unspent,\n plan,\n review: { reason: 'plan', message: AI_JOB_PLAN_REVIEW_COPY, findings: [] },\n }\n )\n }\n\n const result = await runValidatedGeneration('plan', {\n step: 'job.plan',\n ...(model ? { model } : {}),\n instructions: AI_JOB_PLAN_INSTRUCTIONS,\n inventory,\n messages: [{ role: 'user', content: prompt }],\n tool,\n // The routing ceiling, lowered on a tier too slow to look records up,\n // answer and ask again inside the least time the step registered; a\n // scaffold's outline is held to its own, far lower ceiling (AGL-3594).\n maxTokens: aiSitePlanMaxTokens(job, resolved, freeTaste),\n // A scaffold plans an outline without thinking (AGL-3594): the thinking\n // was nearly all of the 13,491 tokens a Free site's plan spent.\n ...(site ? { thinking: 'off' as const } : route.thinking ? { thinking: route.thinking } : {}),\n ...(route.effort ? { effort: route.effort } : {}),\n ...(signal ? { signal } : {}),\n capabilities,\n extend: planViolations(scope, job.brief, freeTaste),\n })\n const spent: AiJobStepOutcome = {\n outputs: [],\n usage: result.usage,\n estCostUsd: result.estCostUsd,\n model: result.model,\n stopReason: result.stopReason,\n ...(result.effort ? { effort: result.effort } : {}),\n }\n if (result.status === 'refused') return { ...spent, refused: true }\n if (result.status === 'needs_input') {\n // Refused by our own checks, after the one re-ask (AGL-3594): the member\n // reads a sentence and an action, staff the rules; the machine gives the\n // credits back (`uncredited`), and on the Free taste Try again says when\n // it cannot work. Whether the give-back was made is the job's to say\n // (`refundedCredits`), so this sentence does not promise it.\n const review = aiDoctrineReview(result)\n const retryRefusal = freeTaste\n ? await aiFreePlanRetryRefusal({ job, org, firestore, now, site })\n : null\n return {\n ...spent,\n uncredited: true,\n review: {\n ...review,\n message: aiPlanFailureCopy({\n kind: job.kind,\n codes: result.violations.map((violation) => violation.code),\n refunded: false,\n }),\n detail: review.message,\n ...(retryRefusal ? { retryRefusal } : {}),\n },\n }\n }\n // Each draft the plan decides is named as the plan is kept, before anything is built (AGL-3079).\n const plan: AiJobPlan = aiPlanWithDraftIds(job.kind, {\n ...result.value,\n status: 'proposed',\n labels: aiPlanLabels(result.value, inventory),\n // A Date the Admin SDK stores as a timestamp, like every instant the machine writes.\n proposedAt: now as unknown as AiJobPlan['proposedAt'],\n confirmedAt: null,\n confirmedBy: null,\n key,\n })\n return (\n (await refusalOnKeep(plan, spent)) ?? {\n ...spent,\n plan,\n review: { reason: 'plan', message: AI_JOB_PLAN_REVIEW_COPY, findings: [] },\n }\n )\n }\n}\n\n/**\n * A site job's capabilities on the Free taste (AGL-3594): its plan holds at\n * most the pages the member asked for within the Free band, which the Free\n * wall then holds it to, and it changes no theme — the site was born with\n * one (AGL-3497), and a palette pass is credits the two pages need. Every\n * other job's capabilities pass through.\n */\nexport function aiSitePlanCapabilities(\n job: Pick<AiJob, 'kind' | 'inputs'>,\n capabilities: AiPlanCapabilities | null,\n): AiPlanCapabilities | null {\n if (job.kind !== 'site' || !capabilities?.freeTaste) return capabilities\n const asked = Number((job.inputs ?? {})['pages'])\n const pages =\n Number.isInteger(asked) && asked >= AI_SITE_FREE_PAGES.min\n ? Math.min(asked, AI_SITE_FREE_PAGES.max)\n : AI_SITE_FREE_PAGES.max\n return {\n ...capabilities,\n freeSitePages: pages,\n create: {\n ...capabilities.create,\n 'theme-change': {\n allowed: false,\n left: null,\n reason: \"a Free workspace's site start keeps the theme the site was created with\",\n },\n },\n }\n}\n\n/**\n * The model a plan runs on (AGL-3594): the resolved one, except a Free site\n * plans on its provider's fast tier — an outline the plan rules hold and\n * re-ask, at a fifth of the balanced tier's output rate. A model the creator\n * picked by name stays theirs.\n */\nexport function aiSitePlanModel(\n job: Pick<AiJob, 'kind' | 'model'>,\n resolved: string | undefined,\n freeTaste: boolean,\n): string | undefined {\n if (job.kind !== 'site' || !freeTaste) return resolved\n const picked = typeof job.model === 'string' ? job.model.trim() : ''\n if (picked && picked !== 'auto') return resolved\n const entry = aiCatalogEntry(resolved ?? aiModelForStep('job.plan'))\n return (entry ? aiDefaultModelFor(entry.provider, 'fast') : undefined) ?? resolved\n}\n\n/** The plan's answer ceiling: the step's own, and a scaffold's outline ceiling under it (AGL-3594). */\nexport function aiSitePlanMaxTokens(\n job: Pick<AiJob, 'kind'>,\n model: string,\n freeTaste: boolean,\n): number {\n const ceiling = aiJobPlanMaxTokens(model)\n if (job.kind !== 'site') return ceiling\n return Math.min(ceiling, freeTaste ? AI_SITE_PLAN_MAX_TOKENS.free : AI_SITE_PLAN_MAX_TOKENS.paid)\n}\n\n/**\n * Why trying a Free plan again cannot work this month, or `null` (AGL-3594):\n * the owner's Free allowance left, read off the account month the meter\n * writes net of give-backs, against what a plan of this kind costs at its\n * worst. Read before this step is metered, and the machine gives a refused\n * plan's credits back, so the figure read is the figure left. A read that fails\n * leaves the button on: the reservation still refuses at the wall.\n */\nasync function aiFreePlanRetryRefusal(input: {\n job: AiJob\n org: Partial<AglynOrgBilling> | null\n firestore: FirebaseFirestore.Firestore\n now: Date\n site: boolean\n}): Promise<string | null> {\n const account = freeAssistAccount(input.org as AssistMeteredOrg | null)\n if (!account?.accountUid) return null\n try {\n // The meter's month key, `YYYY-MM` in UTC (`assistUsageMonth`).\n const month = input.now.toISOString().slice(0, 7)\n const snapshot = await freeAccountUsageRef(input.firestore, account.accountUid, month).get()\n const spent = assistCreditsFromUsd(\n assistSpendAfterReturnsUsd(snapshot.get('estCostUsd'), snapshot.get(ASSIST_RETURNED_USD_FIELD)),\n )\n return aiPlanRetryRefusal({\n creditsLeft: FREE_AI_TASTE_CREDITS_PER_MONTH - spent,\n planCredits: input.site ? AI_FREE_SITE_WORST_CASE_CREDITS.plan : AI_FREE_PAGE_WORST_CASE_CREDITS.plan,\n })\n } catch (error) {\n console.error('ai plan retry allowance read failed', { orgId: input.job.orgId, jobId: input.job.$id, error })\n return null\n }\n}\n\nexport const runAiJobPlanStep = createAiJobPlanStep()\n\n/**\n * Registers the plan step every planned kind runs first, with the least time\n * a plan needs; the plugin's console surface calls it.\n */\nexport function registerAiJobPlan(): void {\n registerAiJobPlanStep(runAiJobPlanStep, { minimumMs: AI_JOB_PLAN_STEP_MINIMUM_MS })\n}\n"],"names":["createHash","AI_BUILD_PLAN_TOOL","aiBuildPlanToolFor","isAiPlanNewRef","AI_TEMPLATE_SUBJECT_DEFINITIONS","aiTemplateShownTokens","AI_JOB_CREATE_KINDS","AI_JOB_CREATE_NOUNS","AI_PAGE_CREATE_KINDS","aiPagePlanShapeRefusal","aiPlanCapabilitiesForJob","aiPlanCapabilityLines","AI_FREE_SITE_WORST_CASE_CREDITS","AI_SITE_CREATE_KINDS","AI_SITE_FREE_PAGES","AI_SITE_PLAN_MAX_TOKENS","aiFreeSiteSectionsWithin","aiSitePlanShapeRefusal","aiPlanFailureCopy","aiPlanRetryRefusal","FREE_AI_TASTE_CREDITS_PER_MONTH","aiDoctrineSystemBlocks","runValidatedGeneration","AI_STEP_TIERS","aiCatalogEntry","aiDefaultModelFor","AI_FREE_PAGE_WORST_CASE_CREDITS","aiPlanEmbedBriefViolations","assistCreditsFromUsd","ASSIST_RETURNED_USD_FIELD","assistSpendAfterReturnsUsd","freeAccountUsageRef","freeAssistAccount","AI_ROUTING_TABLE","aiModelForStep","readSiteInventory","aiJobAdmissionRefusal","readAiPlanCapabilities","AI_JOB_BRIEF_MAX_CHARS","aiJobStepBudget","aiDoctrineReview","aiUnspentOutcome","aiPlanWithDraftIds","AI_JOBS_COLLECTION","registerAiJobPlanStep","AI_JOB_PLAN_INSTRUCTIONS","text","AI_JOB_PLAN_STEP_BUDGET","tier","maxTokens","AI_JOB_PLAN_STEP_MINIMUM_MS","minimumMs","aiJobPlanMaxTokens","model","AI_JOB_PLAN_REVIEW_COPY","aiJobPlanPrompt","job","capabilities","inventory","lines","kind","brief","slice","key","value","Object","entries","inputs","push","String","aiPlanTemplateTokenLines","aiPlanSiteLines","join","pages","screens","filter","screen","template","starter","find","replaceable","length","page","name","slug","some","trim","cap","freeSitePages","undefined","sections","layouts","subject","definition","AI_JOB_PLAN_SCOPES","noun","creates","shapeRefusal","site","fromEntries","map","planViolations","scope","freeTaste","plan","message","rule","code","aiPlanLabels","names","Map","rows","components","templates","forms","datasets","collections","row","set","id","refs","reuse","entry","create","duplicateOf","flatMap","layout","section","uses","labels","ref","get","AI_PLAN_REUSE_WINDOW_MS","AI_PLAN_REUSE_CANDIDATES","aiJobPlanKey","input","digest","part","hostId","prompt","system","block","JSON","stringify","tool","update","UNREUSABLE","planMillis","at","proposedAt","Date","getTime","toMillis","aiReusablePlan","candidates","context","floor","now","best","bestAt","candidate","jobId","includes","status","findAiJobsByPlanKey","orgId","firestore","snapshot","collection","doc","where","limit","docs","data","readAiJobPlanCapabilities","org","createAiJobPlanStep","deps","readInventory","findPlansByKey","readCapabilities","admissionRefusal","signal","modelFor","orgDocument","workspace","Promise","all","resolve","aiSitePlanCapabilities","aiSitePlanModel","route","refusalOnKeep","outcome","refusal","uid","createdBy","error","console","$id","failure","resolved","records","Boolean","dataset","allowed","instructions","reused","confirmedAt","confirmedBy","reusedFrom","unspent","review","reason","findings","result","step","messages","role","content","aiSitePlanMaxTokens","thinking","effort","extend","spent","outputs","usage","estCostUsd","stopReason","refused","retryRefusal","aiFreePlanRetryRefusal","uncredited","codes","violations","violation","refunded","detail","asked","Number","isInteger","min","Math","max","left","picked","provider","ceiling","free","paid","account","accountUid","month","toISOString","creditsLeft","planCredits","runAiJobPlanStep","registerAiJobPlan"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-plan-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 { createHash } from 'node:crypto'\nimport type { AglynOrgBilling } from '@aglyn/aglyn/foundation/definitions/org-billing.types'\nimport {\n AI_BUILD_PLAN_TOOL,\n aiBuildPlanToolFor,\n isAiPlanNewRef,\n type AiBuildPlan,\n} from '../model/ai-build-plan'\nimport type { AiJob, AiJobKind, AiJobPlan, AiJobStatus } from '../model/ai-jobs.types'\nimport {\n AI_TEMPLATE_SUBJECT_DEFINITIONS,\n aiTemplateShownTokens,\n type AiTemplateSubject,\n} from '../model/ai-template-subjects'\nimport { AI_JOB_CREATE_KINDS, AI_JOB_CREATE_NOUNS } from '../model/ai-job-creations'\nimport { AI_PAGE_CREATE_KINDS, aiPagePlanShapeRefusal } from '../model/ai-page-job'\nimport {\n AI_PLAN_NO_FORM_SENTENCE,\n aiPlanCanPlaceForm,\n aiPlanCapabilitiesForJob,\n aiPlanCapabilityLines,\n type AiPlanCapabilities,\n type AiPlanJobScope,\n} from '../model/ai-plan-capabilities'\nimport type { AiSiteInventory } from '../model/ai-site-inventory'\nimport {\n AI_FREE_SITE_WORST_CASE_CREDITS,\n AI_SITE_CREATE_KINDS,\n AI_SITE_FREE_PAGES,\n AI_SITE_PLAN_MAX_TOKENS,\n aiFreeSiteSectionsWithin,\n aiSitePlanShapeRefusal,\n} from '../model/ai-site-job'\nimport { aiPlanFailureCopy, aiPlanRetryRefusal } from '../model/ai-job-failure-copy'\nimport { FREE_AI_TASTE_CREDITS_PER_MONTH } from '../plan-entitlements'\nimport { aiDoctrineSystemBlocks, runValidatedGeneration } from '../runtime/ai-doctrine'\nimport { AI_STEP_TIERS, aiCatalogEntry, aiDefaultModelFor } from '../providers/catalog'\nimport {\n AI_FREE_PAGE_WORST_CASE_CREDITS,\n aiPlanEmbedBriefViolations,\n type AiDoctrineViolation,\n} from '../runtime/ai-doctrine-validators'\nimport { assistCreditsFromUsd } from '../usage/assist-credits'\nimport { ASSIST_RETURNED_USD_FIELD, assistSpendAfterReturnsUsd } from '../usage/assist-credit-returns'\nimport { freeAccountUsageRef, freeAssistAccount, type AssistMeteredOrg } from '../usage/assist-free-taste'\nimport { AI_ROUTING_TABLE, aiModelForStep } from '../providers/routing'\nimport type { AiTool } from '../providers/contract'\nimport type { AiSystemBlock } from '../runtime/ai-runtime'\nimport { readSiteInventory } from '../runtime/site-inventory'\nimport { aiJobAdmissionRefusal } from './ai-job-admission'\nimport { readAiPlanCapabilities } from './ai-job-drafts'\nimport {\n AI_JOB_BRIEF_MAX_CHARS,\n type AiJobStepOutcome,\n type AiJobStepRunner,\n} from './ai-job-text-step'\nimport { aiJobStepBudget } from './ai-job-budget'\nimport { aiDoctrineReview, aiUnspentOutcome } from './ai-job-generation'\nimport { aiPlanWithDraftIds } from './ai-job-draft-ids'\nimport { AI_JOBS_COLLECTION, registerAiJobPlanStep } from './ai-jobs'\n\n/**\n * The plan step (AGL-2935): the first step of every job that builds site\n * structure. Before a node is generated, the model answers with a typed plan\n * — what the site already has that the job reuses, what it creates and why,\n * and the screens it builds from them — held to the doctrine's plan rules\n * against the site's inventory and re-asked once when it breaks one.\n *\n * A plan that passes is kept on the job, and the job stops for review: the\n * member reads what it will build before a credit is spent building it, and\n * confirms through the resume door. A plan that still breaks a rule on its\n * re-ask stops the job the same way, with the rules named. Either way the\n * step writes nothing itself; the machine records the plan, the spend and\n * the stop, as it records every step.\n *\n * ── Told what it may create, refused what it cannot build (AGL-3030) ─────\n *\n * Before it asks, the step reads what the job may create on its site: the\n * workspace's plan, the site's counts against its limits, and — for a kind\n * that builds only some plans — what the job builds itself. The user turn\n * states it, the plan rules refuse a creation outside it with the one re-ask,\n * and where the workspace keeps no reusable components they accept the page\n * built inline. A plan the confirm door would still refuse — the kind's own\n * admission, handed the plan, as the resume door hands it — is refused HERE,\n * before a member is shown a Confirm: the job fails with the door's sentence,\n * its plan is not kept, and it holds nothing.\n */\n\n/** The plan step's own instructions, cached after the doctrine. */\nexport const AI_JOB_PLAN_INSTRUCTIONS: readonly AiSystemBlock[] = [\n {\n text:\n 'You plan what a website builder job will build, before anything is built. You are given the kind of job and the brief from the person who owns the site. ' +\n 'Answer with submit_build_plan: what the site inventory already has that the job reuses, what it must create and why nothing listed will do, and each page it builds (the screens list) with its layout, template, slug, search title, search description and sections from top to bottom. ' +\n 'Refer to inventory records by id and to what the plan creates as new:<name>. Plan only what the brief asks for: a component, layout, template, form or email job plans no pages unless the brief asks for them, and a page job plans exactly one page.',\n },\n]\n\n/**\n * The plan step's time (AGL-3026, AGL-3036): its two inventory-lookup rounds,\n * its answer and its re-ask at the routing table's ceiling for `job.plan`, on\n * the tier that step kind is served from, with the step's reads and writes,\n * at the rates `ai-job-budget.ts` assumes — fitted to what a beat can give a\n * step, so the served tier asks a little under the routing ceiling.\n */\nexport const AI_JOB_PLAN_STEP_BUDGET = aiJobStepBudget({\n tier: AI_STEP_TIERS['job.plan'],\n maxTokens: AI_ROUTING_TABLE['job.plan'].maxTokens,\n})\n\n/**\n * The least time a plan needs before it starts. Registered with the step, so\n * an inline door never starts a plan. A plan thinks before it answers and\n * routinely runs past an inline door's budget, and a provider call that\n * budget cuts off is still generated and billed upstream while the meter\n * records nothing. The beat starts a plan only with this much of its own\n * budget left, and a spec holds it inside that budget.\n */\nexport const AI_JOB_PLAN_STEP_MINIMUM_MS = AI_JOB_PLAN_STEP_BUDGET.minimumMs\n\n/**\n * The plan's answer ceiling on the model a job runs: the most whose worst\n * case fits the least time the step registered, and never more than the\n * routing table's. A model the catalog does not know is planned at the\n * slowest.\n */\nexport function aiJobPlanMaxTokens(model: string): number {\n return AI_JOB_PLAN_STEP_BUDGET.maxTokens(model)\n}\n\n/** What the member reads while a plan waits for them. */\nexport const AI_JOB_PLAN_REVIEW_COPY =\n 'The plan is ready. Review what the job will reuse and create, then confirm it to build.'\n\n/**\n * The job as the plan step's user turn: its kind, its brief, its scalar\n * inputs and, where the job read them, what it may create on its site\n * (AGL-3030). Per workspace and per site, which is why they ride here and\n * never in a system block the platform's cache entry is keyed on.\n */\nexport function aiJobPlanPrompt(\n job: Pick<AiJob, 'kind' | 'brief' | 'inputs'>,\n capabilities: AiPlanCapabilities | null = null,\n inventory: AiSiteInventory | null = null,\n): string {\n const lines = [`Job kind: ${job.kind}`, `Brief: ${job.brief.slice(0, AI_JOB_BRIEF_MAX_CHARS)}`]\n for (const [key, value] of Object.entries(job.inputs ?? {})) {\n if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {\n lines.push(`${key}: ${String(value)}`)\n }\n }\n if (capabilities) lines.push(...aiPlanCapabilityLines(capabilities))\n // A form is a saved form or none (AGL-3596): said where the site read has\n // none to place and the job may create none.\n if (capabilities && inventory && !aiPlanCanPlaceForm(inventory, capabilities)) {\n lines.push(AI_PLAN_NO_FORM_SENTENCE)\n }\n lines.push(...aiPlanTemplateTokenLines(job))\n lines.push(...aiPlanSiteLines(job, inventory, capabilities))\n return lines.join('\\n')\n}\n\n/**\n * What a SITE job's plan is told beside the inventory (AGL-3594): the pages\n * the site already has and that their addresses are taken, which one — the\n * untouched starter home — a planned home page at `/` replaces, the Free page\n * cap and its guidance, and that a plan is an outline.\n *\n * A site job only. A new site is born with a published home page at `/`\n * (AGL-3497), and a plan that did not know it could replace it planned its own\n * home there and was refused for the clash on every new site. Kept off a page\n * job's turn, whose length the Free page's wall is proven at.\n */\nexport function aiPlanSiteLines(\n job: Pick<AiJob, 'kind'>,\n inventory: AiSiteInventory | null,\n capabilities: AiPlanCapabilities | null,\n): string[] {\n if (job.kind !== 'site') return []\n const lines: string[] = []\n const pages = (inventory?.screens ?? []).filter((screen) => !screen.template)\n const starter = pages.find((screen) => screen.replaceable)\n if (pages.length) {\n lines.push('Pages the site already has; their addresses are taken unless a line says otherwise:')\n for (const page of pages) {\n lines.push(\n page.replaceable\n ? `- ${page.name} at ${page.slug}: the starter home page the site was created with. Plan this site's home page at / and it replaces this one.`\n : `- ${page.name} at ${page.slug}`,\n )\n }\n if (!starter && pages.some((page) => page.slug.trim() === '/' || page.slug.trim() === '')) {\n lines.push(\"The home page at / is the owner's own and stays: give every page you plan another address.\")\n }\n }\n const cap = capabilities?.freeSitePages\n if (cap !== undefined) {\n const sections = aiFreeSiteSectionsWithin(\n { layouts: (inventory?.layouts.length ?? 0) ? 0 : 1, pages: cap },\n FREE_AI_TASTE_CREDITS_PER_MONTH,\n )\n lines.push(\n `This is a Free workspace: plan at most ${cap} ${cap === 1 ? 'page' : 'pages'} — the home page at / and the one page the brief most needs, such as services, booking or contact — with at most ${sections} sections across them.${aiPlanCanPlaceForm(inventory, capabilities) ? ' Put the contact form on one of them.' : ''}`,\n )\n }\n lines.push(\n \"Keep the plan an outline: each page's title, address, a short search title and description, and its sections named in a few words. The build writes the copy.\",\n )\n return lines\n}\n\n/**\n * What a TEMPLATE job's plan is told its creation's `fields` are (AGL-3143\n * §11): the subject's binding tokens, listed so the planner promises only\n * what the page can fill and the build can be held to it\n * (`aiPlanTemplateTokenViolations`).\n *\n * ⛔ It rides the job's own turn and NOT the `fields` description in the plan\n * tool, which every kind's request caches. Written there it read better and\n * cost 40 tokens of the shared prefix — 2,980 to 3,020 — which is one credit\n * of the Free page's 300-credit wall, taking the room it keeps for a re-asked\n * section from 45 credits to 44 and breaking `ai-job-free-page.spec.ts`. A\n * page job must not pay for a sentence about templates.\n *\n * The list is the tokens a reader sees, never the whole catalog (AGL-3143\n * §16). Offered a slug and the publish timestamp, a live plan promised them,\n * and its build kept the promise the only way a slug can be kept: it printed\n * \"entry slug: {{entry.slug}}\" on every article.\n */\nexport function aiPlanTemplateTokenLines(job: Pick<AiJob, 'kind' | 'inputs'>): string[] {\n if (job.kind !== 'template') return []\n const subject = (job.inputs ?? {})['subject']\n const definition =\n typeof subject === 'string'\n ? AI_TEMPLATE_SUBJECT_DEFINITIONS[subject as AiTemplateSubject]\n : undefined\n if (!definition) return []\n return [\n `The template's fields are the binding tokens its page shows a reader, each one of: ${aiTemplateShownTokens(\n definition,\n ).join(', ')}. A link or picture token is shown by the link or picture holding it. Promise only what the page fills.`,\n ]\n}\n\n/** A kind that builds only some plans: the creations it makes, and the shapes it refuses. */\nexport interface AiJobPlanScope extends AiPlanJobScope {\n /** Why a job of the kind cannot build a plan of this shape; `null` when it can. Pure. */\n shapeRefusal: (plan: AiBuildPlan, options?: { freeTaste?: boolean }) => string | null\n}\n\n/**\n * The kinds whose confirm door builds only some plans (AGL-3030). A page job\n * builds one screen and a scaffold four to eight; each builds only the\n * creations its list names. A template, layout, component, form or email job\n * builds its own record and what that record places (AGL-3143 §15,\n * `AI_JOB_CREATE_KINDS`), and holds no plan to a shape.\n */\nexport const AI_JOB_PLAN_SCOPES: Readonly<Partial<Record<AiJobKind, AiJobPlanScope>>> = {\n page: { noun: 'a page job', creates: AI_PAGE_CREATE_KINDS, shapeRefusal: aiPagePlanShapeRefusal },\n site: { noun: 'a site scaffold', creates: AI_SITE_CREATE_KINDS, shapeRefusal: aiSitePlanShapeRefusal },\n ...Object.fromEntries(\n Object.entries(AI_JOB_CREATE_KINDS).map(([kind, creates]) => [\n kind,\n { noun: AI_JOB_CREATE_NOUNS[kind as AiJobKind] as string, creates, shapeRefusal: () => null },\n ]),\n ),\n}\n\n/**\n * What the plan step holds beyond the doctrine's plan rules, as the\n * violations the plan's one re-ask names: the players the plan lists against\n * the brief it answers (AGL-3433), which only this step reads beside the plan,\n * and a kind's shape refusal.\n */\nfunction planViolations(\n scope: AiJobPlanScope | null,\n brief: string,\n freeTaste = false,\n): (plan: AiBuildPlan) => AiDoctrineViolation[] {\n return (plan) => {\n const message = scope?.shapeRefusal(plan, { freeTaste }) ?? null\n return [\n ...aiPlanEmbedBriefViolations(plan, brief),\n ...(message ? [{ rule: null, code: 'plan-job-shape', message }] : []),\n ]\n }\n}\n\n/**\n * The inventory names of every id the plan references, so the proposal reads\n * \"the Services layout\" rather than an id — kept on the plan because the\n * inventory is not read again when the member opens it.\n */\nexport function aiPlanLabels(\n plan: AiBuildPlan,\n inventory: AiSiteInventory | null,\n): Record<string, string> {\n const names = new Map<string, string>()\n for (const rows of [\n inventory?.components,\n inventory?.layouts,\n inventory?.templates,\n inventory?.forms,\n inventory?.datasets,\n inventory?.collections,\n inventory?.screens,\n ]) {\n for (const row of rows ?? []) names.set(row.id, row.name)\n }\n const refs = [\n ...plan.reuse.map((entry) => entry.id),\n ...plan.create.map((entry) => entry.duplicateOf),\n ...plan.screens.flatMap((screen) => [\n screen.layout,\n screen.template,\n screen.duplicateOf,\n ...screen.sections.flatMap((section) => section.uses),\n ]),\n ]\n const labels: Record<string, string> = {}\n for (const ref of refs) {\n if (!ref || isAiPlanNewRef(ref)) continue\n const name = names.get(ref)\n if (name) labels[ref] = name\n }\n return labels\n}\n\n/* ------------------------------------------------------------------------ *\n * Reusing an identical brief's plan\n * ------------------------------------------------------------------------ */\n\n/**\n * How long a plan may be reused for (AGL-2937).\n *\n * The window is what makes reuse safe rather than merely cheap. The key\n * already covers everything the request said — the brief, the inputs, the\n * model and the prompt as rendered, site inventory included — so a plan is\n * reused only when nothing the model was shown has changed. What the key\n * cannot see is the world outside the request: a page published, a component\n * renamed after the inventory was read, a member who meant something\n * different the second time. Fifteen minutes is short enough that the answer\n * is still the one the site would give, and long enough to cover what reuse\n * is actually for — the same brief run twice in a sitting, a job resubmitted\n * after a refused reservation, two members starting the same work.\n */\nexport const AI_PLAN_REUSE_WINDOW_MS = 15 * 60 * 1_000\n\n/** Jobs one lookup reads. More than one can share a key; the newest plan wins. */\nexport const AI_PLAN_REUSE_CANDIDATES = 5\n\n/** A job whose plan might be reused, as the finder reports it. */\nexport interface AiJobPlanCandidate {\n jobId: string\n status: AiJobStatus\n plan: AiJobPlan\n}\n\n/**\n * The digest a plan is reused by: the whole request, never the answer.\n *\n * It hashes what the model was asked and what it was shown — the job's kind\n * and site, the user turn (which carries the trimmed brief and every scalar\n * input), the model that would answer, every rendered system block including\n * the site inventory, and the tool's schema. Two requests that hash the same\n * would have been sent the same bytes to the same model, so the second can\n * keep the first's answer.\n *\n * The version tag is the escape hatch: anything that changes what a key\n * MEANS, rather than what it covers, bumps it and strands every old key\n * harmlessly, since a key nothing matches simply asks the model.\n */\nexport function aiJobPlanKey(input: {\n job: Pick<AiJob, 'kind' | 'hostId'>\n prompt: string\n model: string\n system: readonly AiSystemBlock[]\n /** The plan tool the request offers (AGL-3433); the plain one when absent. */\n tool?: AiTool\n}): string {\n const digest = createHash('sha256')\n for (const part of [\n 'plan.v1',\n input.job.kind,\n input.job.hostId ?? '',\n input.model,\n input.prompt,\n ...input.system.map((block) => block.text),\n JSON.stringify(input.tool ?? AI_BUILD_PLAN_TOOL),\n ]) {\n // Length-prefixed, so two different splits of the same characters cannot\n // collide by running into one another.\n digest.update(`${part.length}:${part}\\u0000`)\n }\n return digest.digest('hex')\n}\n\n/** A job that could not have produced a reusable plan, whatever its key says. */\nconst UNREUSABLE: readonly AiJobStatus[] = ['canceled', 'failed']\n\nfunction planMillis(plan: AiJobPlan): number | null {\n const at = plan.proposedAt as unknown\n if (at instanceof Date) return at.getTime()\n if (at && typeof (at as { toMillis?: unknown }).toMillis === 'function') {\n return (at as { toMillis(): number }).toMillis()\n }\n return null\n}\n\n/**\n * The newest plan worth reusing out of what the finder returned: another\n * job's, still proposed or already confirmed, on a job that was neither\n * canceled nor failed, proposed inside the window.\n *\n * A canceled or failed job is excluded even though its plan may be perfectly\n * good: those are the jobs a member walked away from, and handing their plan\n * to the next one would make a rejected answer look like a fresh one.\n */\nexport function aiReusablePlan(\n candidates: readonly AiJobPlanCandidate[],\n context: { jobId: string; now: Date },\n): AiJobPlanCandidate | null {\n const floor = context.now.getTime() - AI_PLAN_REUSE_WINDOW_MS\n let best: AiJobPlanCandidate | null = null\n let bestAt = -1\n for (const candidate of candidates) {\n if (candidate.jobId === context.jobId) continue\n if (UNREUSABLE.includes(candidate.status)) continue\n if (candidate.plan.status !== 'proposed' && candidate.plan.status !== 'confirmed') continue\n const at = planMillis(candidate.plan)\n if (at === null || at < floor || at > context.now.getTime()) continue\n if (at > bestAt) {\n best = candidate\n bestAt = at\n }\n }\n return best\n}\n\nexport type AiJobPlanFinder = (\n orgId: string,\n key: string,\n firestore?: FirebaseFirestore.Firestore,\n) => Promise<AiJobPlanCandidate[]>\n\n/**\n * The finder the step uses in production: an equality on `plan.key` inside\n * the org's own jobs.\n *\n * One equality on one field, with no ordering beside it, so Firestore's\n * automatic single-field index answers it and no composite index is deployed.\n * The window and the statuses are applied in memory instead, which is what\n * keeps it that way.\n */\nexport const findAiJobsByPlanKey: AiJobPlanFinder = async (orgId, key, firestore) => {\n if (!firestore) return []\n const snapshot = await firestore\n .collection('orgs')\n .doc(orgId)\n .collection(AI_JOBS_COLLECTION)\n .where('plan.key', '==', key)\n .limit(AI_PLAN_REUSE_CANDIDATES)\n .get()\n return snapshot.docs.flatMap((doc) => {\n const data = doc.data() as { status?: AiJobStatus; plan?: AiJobPlan | null }\n return data.plan\n ? [{ jobId: doc.id, status: data.status ?? 'queued', plan: data.plan }]\n : []\n })\n}\n\n/** What a job may create on its site, read for the plan step; `null` for no site. */\nexport type AiPlanCapabilitiesReader = (input: {\n job: AiJob\n org: Partial<AglynOrgBilling> | null\n firestore: FirebaseFirestore.Firestore\n}) => Promise<AiPlanCapabilities | null>\n\n/** The reader the step uses in production: the draft bands' own arithmetic, for the job's site. */\nexport const readAiJobPlanCapabilities: AiPlanCapabilitiesReader = async ({ job, org, firestore }) =>\n job.hostId ? readAiPlanCapabilities(firestore, { hostId: job.hostId, org }) : null\n\nexport interface AiJobPlanStepDeps {\n /** The inventory reader; specs hand in a fake. */\n readInventory?: typeof readSiteInventory\n /** The reuse lookup; specs hand in a fake, and `null` turns reuse off. */\n findPlansByKey?: AiJobPlanFinder | null\n /** What the job may create on its site (AGL-3030); specs and the eval recorder hand in their own. */\n readCapabilities?: AiPlanCapabilitiesReader\n /** The kind's admission, asked of a plan before it is kept; the registry's otherwise. */\n admissionRefusal?: typeof aiJobAdmissionRefusal\n}\n\nexport function createAiJobPlanStep(deps: AiJobPlanStepDeps = {}): AiJobStepRunner {\n const readInventory = deps.readInventory ?? readSiteInventory\n const findPlansByKey =\n deps.findPlansByKey === undefined ? findAiJobsByPlanKey : deps.findPlansByKey\n const readCapabilities = deps.readCapabilities ?? readAiJobPlanCapabilities\n const admissionRefusal = deps.admissionRefusal ?? aiJobAdmissionRefusal\n return async ({ job, now, signal, firestore, modelFor, org: orgDocument }) => {\n const org = (orgDocument ?? null) as Partial<AglynOrgBilling> | null\n const [inventory, workspace] = await Promise.all([\n job.hostId ? readInventory(job.orgId, job.hostId, { firestore }) : Promise.resolve(null),\n readCapabilities({ job, org, firestore }),\n ])\n const scope = AI_JOB_PLAN_SCOPES[job.kind] ?? null\n const capabilities = aiSitePlanCapabilities(\n job,\n workspace ? aiPlanCapabilitiesForJob(workspace, scope) : null,\n )\n const freeTaste = capabilities?.freeTaste === true\n const site = job.kind === 'site'\n // The model switch's answer for this job (AGL-2942): the creator's pick\n // where the plan, the org restriction and the allotment allowlists allow\n // it, and Auto held to those same lists otherwise. Without a resolver the\n // doctrine asks the routing table itself. A Free site plans on its\n // provider's fast tier (AGL-3594), unless the creator picked a model.\n const model = aiSitePlanModel(job, modelFor?.('job.plan'), freeTaste)\n const route = AI_ROUTING_TABLE['job.plan']\n const prompt = aiJobPlanPrompt(job, capabilities, inventory)\n\n /**\n * The confirm door's answer for this plan, asked before the plan is kept\n * (AGL-3030): the kind's own admission, handed the plan as the resume\n * door hands it. A refusal fails the job with the door's sentence; a door\n * that could not answer keeps the plan, since the resume door asks again\n * before anything is built.\n */\n const refusalOnKeep = async (plan: AiJobPlan, outcome: AiJobStepOutcome): Promise<AiJobStepOutcome | null> => {\n let refusal: Awaited<ReturnType<typeof aiJobAdmissionRefusal>> = null\n try {\n refusal = await admissionRefusal(job.kind, {\n firestore,\n orgId: job.orgId,\n hostId: job.hostId ?? null,\n inputs: job.inputs ?? {},\n org,\n plan,\n uid: job.createdBy,\n })\n } catch (error) {\n console.error('ai plan admission failed', { orgId: job.orgId, jobId: job.$id, error })\n }\n return refusal ? { ...outcome, failure: refusal.error } : null\n }\n\n // Reuse before asking (AGL-2937). The key covers the whole request, so a\n // hit is a request that would have been sent the same bytes to the same\n // model; the window covers what the key cannot see. A reused plan spends\n // nothing, so the machine's spent-nothing branch releases the reservation\n // and meters no credit, and the job still stops for the member to confirm\n // — this job's plan is proposed to this member, whatever the last one did\n // with theirs.\n const resolved = model ?? aiModelForStep('job.plan')\n // A brief that asks for a video is offered the plan's list of players\n // (AGL-3433), and a job that may bind a dataset a record template on\n // each screen (AGL-3475).\n const tool = aiBuildPlanToolFor(job.brief, {\n records: Boolean(inventory?.datasets.length) || capabilities?.create.dataset.allowed === true,\n })\n const key = aiJobPlanKey({\n job,\n prompt,\n model: resolved,\n system: aiDoctrineSystemBlocks(inventory, { instructions: AI_JOB_PLAN_INSTRUCTIONS }),\n tool,\n })\n const reused = findPlansByKey\n ? aiReusablePlan(await findPlansByKey(job.orgId, key, firestore), {\n jobId: job.$id,\n now,\n })\n : null\n if (reused) {\n // The reused plan's draft ids name the other job's drafts; this job's are its own.\n const plan: AiJobPlan = aiPlanWithDraftIds(job.kind, {\n ...reused.plan,\n status: 'proposed',\n labels: aiPlanLabels(reused.plan, inventory),\n proposedAt: now as unknown as AiJobPlan['proposedAt'],\n confirmedAt: null,\n confirmedBy: null,\n key,\n reusedFrom: reused.jobId,\n })\n const unspent = aiUnspentOutcome(resolved)\n return (\n (await refusalOnKeep(plan, unspent)) ?? {\n ...unspent,\n plan,\n review: { reason: 'plan', message: AI_JOB_PLAN_REVIEW_COPY, findings: [] },\n }\n )\n }\n\n const result = await runValidatedGeneration('plan', {\n step: 'job.plan',\n ...(model ? { model } : {}),\n instructions: AI_JOB_PLAN_INSTRUCTIONS,\n inventory,\n messages: [{ role: 'user', content: prompt }],\n tool,\n // The routing ceiling, lowered on a tier too slow to look records up,\n // answer and ask again inside the least time the step registered; a\n // scaffold's outline is held to its own, far lower ceiling (AGL-3594).\n maxTokens: aiSitePlanMaxTokens(job, resolved, freeTaste),\n // A scaffold plans an outline without thinking (AGL-3594): the thinking\n // was nearly all of the 13,491 tokens a Free site's plan spent.\n ...(site ? { thinking: 'off' as const } : route.thinking ? { thinking: route.thinking } : {}),\n ...(route.effort ? { effort: route.effort } : {}),\n ...(signal ? { signal } : {}),\n capabilities,\n extend: planViolations(scope, job.brief, freeTaste),\n })\n const spent: AiJobStepOutcome = {\n outputs: [],\n usage: result.usage,\n estCostUsd: result.estCostUsd,\n model: result.model,\n stopReason: result.stopReason,\n ...(result.effort ? { effort: result.effort } : {}),\n }\n if (result.status === 'refused') return { ...spent, refused: true }\n if (result.status === 'needs_input') {\n // Refused by our own checks, after the one re-ask (AGL-3594): the member\n // reads a sentence and an action, staff the rules; the machine gives the\n // credits back (`uncredited`), and on the Free taste Try again says when\n // it cannot work. Whether the give-back was made is the job's to say\n // (`refundedCredits`), so this sentence does not promise it.\n const review = aiDoctrineReview(result)\n const retryRefusal = freeTaste\n ? await aiFreePlanRetryRefusal({ job, org, firestore, now, site })\n : null\n return {\n ...spent,\n uncredited: true,\n review: {\n ...review,\n message: aiPlanFailureCopy({\n kind: job.kind,\n codes: result.violations.map((violation) => violation.code),\n refunded: false,\n }),\n detail: review.message,\n ...(retryRefusal ? { retryRefusal } : {}),\n },\n }\n }\n // Each draft the plan decides is named as the plan is kept, before anything is built (AGL-3079).\n const plan: AiJobPlan = aiPlanWithDraftIds(job.kind, {\n ...result.value,\n status: 'proposed',\n labels: aiPlanLabels(result.value, inventory),\n // A Date the Admin SDK stores as a timestamp, like every instant the machine writes.\n proposedAt: now as unknown as AiJobPlan['proposedAt'],\n confirmedAt: null,\n confirmedBy: null,\n key,\n })\n return (\n (await refusalOnKeep(plan, spent)) ?? {\n ...spent,\n plan,\n review: { reason: 'plan', message: AI_JOB_PLAN_REVIEW_COPY, findings: [] },\n }\n )\n }\n}\n\n/**\n * A site job's capabilities on the Free taste (AGL-3594): its plan holds at\n * most the pages the member asked for within the Free band, which the Free\n * wall then holds it to, and it changes no theme — the site was born with\n * one (AGL-3497), and a palette pass is credits the two pages need. Every\n * other job's capabilities pass through.\n */\nexport function aiSitePlanCapabilities(\n job: Pick<AiJob, 'kind' | 'inputs'>,\n capabilities: AiPlanCapabilities | null,\n): AiPlanCapabilities | null {\n if (job.kind !== 'site' || !capabilities?.freeTaste) return capabilities\n const asked = Number((job.inputs ?? {})['pages'])\n const pages =\n Number.isInteger(asked) && asked >= AI_SITE_FREE_PAGES.min\n ? Math.min(asked, AI_SITE_FREE_PAGES.max)\n : AI_SITE_FREE_PAGES.max\n return {\n ...capabilities,\n freeSitePages: pages,\n create: {\n ...capabilities.create,\n 'theme-change': {\n allowed: false,\n left: null,\n reason: \"a Free workspace's site start keeps the theme the site was created with\",\n },\n },\n }\n}\n\n/**\n * The model a plan runs on (AGL-3594): the resolved one, except a Free site\n * plans on its provider's fast tier — an outline the plan rules hold and\n * re-ask, at a fifth of the balanced tier's output rate. A model the creator\n * picked by name stays theirs.\n */\nexport function aiSitePlanModel(\n job: Pick<AiJob, 'kind' | 'model'>,\n resolved: string | undefined,\n freeTaste: boolean,\n): string | undefined {\n if (job.kind !== 'site' || !freeTaste) return resolved\n const picked = typeof job.model === 'string' ? job.model.trim() : ''\n if (picked && picked !== 'auto') return resolved\n const entry = aiCatalogEntry(resolved ?? aiModelForStep('job.plan'))\n return (entry ? aiDefaultModelFor(entry.provider, 'fast') : undefined) ?? resolved\n}\n\n/** The plan's answer ceiling: the step's own, and a scaffold's outline ceiling under it (AGL-3594). */\nexport function aiSitePlanMaxTokens(\n job: Pick<AiJob, 'kind'>,\n model: string,\n freeTaste: boolean,\n): number {\n const ceiling = aiJobPlanMaxTokens(model)\n if (job.kind !== 'site') return ceiling\n return Math.min(ceiling, freeTaste ? AI_SITE_PLAN_MAX_TOKENS.free : AI_SITE_PLAN_MAX_TOKENS.paid)\n}\n\n/**\n * Why trying a Free plan again cannot work this month, or `null` (AGL-3594):\n * the owner's Free allowance left, read off the account month the meter\n * writes net of give-backs, against what a plan of this kind costs at its\n * worst. Read before this step is metered, and the machine gives a refused\n * plan's credits back, so the figure read is the figure left. A read that fails\n * leaves the button on: the reservation still refuses at the wall.\n */\nasync function aiFreePlanRetryRefusal(input: {\n job: AiJob\n org: Partial<AglynOrgBilling> | null\n firestore: FirebaseFirestore.Firestore\n now: Date\n site: boolean\n}): Promise<string | null> {\n const account = freeAssistAccount(input.org as AssistMeteredOrg | null)\n if (!account?.accountUid) return null\n try {\n // The meter's month key, `YYYY-MM` in UTC (`assistUsageMonth`).\n const month = input.now.toISOString().slice(0, 7)\n const snapshot = await freeAccountUsageRef(input.firestore, account.accountUid, month).get()\n const spent = assistCreditsFromUsd(\n assistSpendAfterReturnsUsd(snapshot.get('estCostUsd'), snapshot.get(ASSIST_RETURNED_USD_FIELD)),\n )\n return aiPlanRetryRefusal({\n creditsLeft: FREE_AI_TASTE_CREDITS_PER_MONTH - spent,\n planCredits: input.site ? AI_FREE_SITE_WORST_CASE_CREDITS.plan : AI_FREE_PAGE_WORST_CASE_CREDITS.plan,\n })\n } catch (error) {\n console.error('ai plan retry allowance read failed', { orgId: input.job.orgId, jobId: input.job.$id, error })\n return null\n }\n}\n\nexport const runAiJobPlanStep = createAiJobPlanStep()\n\n/**\n * Registers the plan step every planned kind runs first, with the least time\n * a plan needs; the plugin's console surface calls it.\n */\nexport function registerAiJobPlan(): void {\n registerAiJobPlanStep(runAiJobPlanStep, { minimumMs: AI_JOB_PLAN_STEP_MINIMUM_MS })\n}\n"],"names":["createHash","AI_BUILD_PLAN_TOOL","aiBuildPlanToolFor","isAiPlanNewRef","AI_TEMPLATE_SUBJECT_DEFINITIONS","aiTemplateShownTokens","AI_JOB_CREATE_KINDS","AI_JOB_CREATE_NOUNS","AI_PAGE_CREATE_KINDS","aiPagePlanShapeRefusal","AI_PLAN_NO_FORM_SENTENCE","aiPlanCanPlaceForm","aiPlanCapabilitiesForJob","aiPlanCapabilityLines","AI_FREE_SITE_WORST_CASE_CREDITS","AI_SITE_CREATE_KINDS","AI_SITE_FREE_PAGES","AI_SITE_PLAN_MAX_TOKENS","aiFreeSiteSectionsWithin","aiSitePlanShapeRefusal","aiPlanFailureCopy","aiPlanRetryRefusal","FREE_AI_TASTE_CREDITS_PER_MONTH","aiDoctrineSystemBlocks","runValidatedGeneration","AI_STEP_TIERS","aiCatalogEntry","aiDefaultModelFor","AI_FREE_PAGE_WORST_CASE_CREDITS","aiPlanEmbedBriefViolations","assistCreditsFromUsd","ASSIST_RETURNED_USD_FIELD","assistSpendAfterReturnsUsd","freeAccountUsageRef","freeAssistAccount","AI_ROUTING_TABLE","aiModelForStep","readSiteInventory","aiJobAdmissionRefusal","readAiPlanCapabilities","AI_JOB_BRIEF_MAX_CHARS","aiJobStepBudget","aiDoctrineReview","aiUnspentOutcome","aiPlanWithDraftIds","AI_JOBS_COLLECTION","registerAiJobPlanStep","AI_JOB_PLAN_INSTRUCTIONS","text","AI_JOB_PLAN_STEP_BUDGET","tier","maxTokens","AI_JOB_PLAN_STEP_MINIMUM_MS","minimumMs","aiJobPlanMaxTokens","model","AI_JOB_PLAN_REVIEW_COPY","aiJobPlanPrompt","job","capabilities","inventory","lines","kind","brief","slice","key","value","Object","entries","inputs","push","String","aiPlanTemplateTokenLines","aiPlanSiteLines","join","pages","screens","filter","screen","template","starter","find","replaceable","length","page","name","slug","some","trim","cap","freeSitePages","undefined","sections","layouts","subject","definition","AI_JOB_PLAN_SCOPES","noun","creates","shapeRefusal","site","fromEntries","map","planViolations","scope","freeTaste","plan","message","rule","code","aiPlanLabels","names","Map","rows","components","templates","forms","datasets","collections","row","set","id","refs","reuse","entry","create","duplicateOf","flatMap","layout","section","uses","labels","ref","get","AI_PLAN_REUSE_WINDOW_MS","AI_PLAN_REUSE_CANDIDATES","aiJobPlanKey","input","digest","part","hostId","prompt","system","block","JSON","stringify","tool","update","UNREUSABLE","planMillis","at","proposedAt","Date","getTime","toMillis","aiReusablePlan","candidates","context","floor","now","best","bestAt","candidate","jobId","includes","status","findAiJobsByPlanKey","orgId","firestore","snapshot","collection","doc","where","limit","docs","data","readAiJobPlanCapabilities","org","createAiJobPlanStep","deps","readInventory","findPlansByKey","readCapabilities","admissionRefusal","signal","modelFor","orgDocument","workspace","Promise","all","resolve","aiSitePlanCapabilities","aiSitePlanModel","route","refusalOnKeep","outcome","refusal","uid","createdBy","error","console","$id","failure","resolved","records","Boolean","dataset","allowed","instructions","reused","confirmedAt","confirmedBy","reusedFrom","unspent","review","reason","findings","result","step","messages","role","content","aiSitePlanMaxTokens","thinking","effort","extend","spent","outputs","usage","estCostUsd","stopReason","refused","retryRefusal","aiFreePlanRetryRefusal","uncredited","codes","violations","violation","refunded","detail","asked","Number","isInteger","min","Math","max","left","picked","provider","ceiling","free","paid","account","accountUid","month","toISOString","creditsLeft","planCredits","runAiJobPlanStep","registerAiJobPlan"],"mappings":"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"}
|