@aglyn/plugins-ai 1.0.0-beta.220 → 1.0.0-beta.222

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/package.json +17 -17
  2. package/src/lib/components/ai-job-plan.component.js +1 -4
  3. package/src/lib/components/ai-job-plan.component.js.map +1 -1
  4. package/src/lib/jobs/ai-job-crm-step.d.ts +44 -3
  5. package/src/lib/jobs/ai-job-crm-step.js +298 -8
  6. package/src/lib/jobs/ai-job-crm-step.js.map +1 -1
  7. package/src/lib/jobs/ai-job-page-sections.d.ts +33 -1
  8. package/src/lib/jobs/ai-job-page-sections.js +55 -1
  9. package/src/lib/jobs/ai-job-page-sections.js.map +1 -1
  10. package/src/lib/jobs/ai-job-page-step.js +20 -11
  11. package/src/lib/jobs/ai-job-page-step.js.map +1 -1
  12. package/src/lib/jobs/ai-job-plan-step.js +6 -2
  13. package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
  14. package/src/lib/jobs/ai-job-seo-step.js +52 -15
  15. package/src/lib/jobs/ai-job-seo-step.js.map +1 -1
  16. package/src/lib/jobs/ai-job-site-step.js +6 -0
  17. package/src/lib/jobs/ai-job-site-step.js.map +1 -1
  18. package/src/lib/jobs/ai-job-workflow-step.js +8 -7
  19. package/src/lib/jobs/ai-job-workflow-step.js.map +1 -1
  20. package/src/lib/jobs/ai-workflow-records.d.ts +2 -2
  21. package/src/lib/jobs/ai-workflow-records.js +2 -2
  22. package/src/lib/jobs/ai-workflow-records.js.map +1 -1
  23. package/src/lib/jobs/fixtures/ai-free-page-built-recording.js +1 -0
  24. package/src/lib/jobs/fixtures/ai-free-page-built-recording.js.map +1 -1
  25. package/src/lib/jobs/fixtures/ai-free-page-recording.js +2 -0
  26. package/src/lib/jobs/fixtures/ai-free-page-recording.js.map +1 -1
  27. package/src/lib/jobs/fixtures/ai-page-briefs.js +2 -0
  28. package/src/lib/jobs/fixtures/ai-page-briefs.js.map +1 -1
  29. package/src/lib/model/ai-automation-draft.d.ts +4 -4
  30. package/src/lib/model/ai-automation-draft.js +15 -10
  31. package/src/lib/model/ai-automation-draft.js.map +1 -1
  32. package/src/lib/model/ai-automation-format.d.ts +139 -0
  33. package/src/lib/model/ai-automation-format.js +42 -0
  34. package/src/lib/model/ai-automation-format.js.map +1 -0
  35. package/src/lib/model/ai-automation-outline.d.ts +4 -4
  36. package/src/lib/model/ai-automation-outline.js +10 -8
  37. package/src/lib/model/ai-automation-outline.js.map +1 -1
  38. package/src/lib/model/ai-build-plan.d.ts +39 -2
  39. package/src/lib/model/ai-build-plan.js +80 -3
  40. package/src/lib/model/ai-build-plan.js.map +1 -1
  41. package/src/lib/model/ai-site-inventory.d.ts +5 -0
  42. package/src/lib/model/ai-site-inventory.js.map +1 -1
  43. package/src/lib/model/ai-workflow-job.js +2 -2
  44. package/src/lib/model/ai-workflow-job.js.map +1 -1
  45. package/src/lib/providers/catalog.d.ts +23 -0
  46. package/src/lib/providers/catalog.js +29 -1
  47. package/src/lib/providers/catalog.js.map +1 -1
  48. package/src/lib/runtime/ai-doctrine-validators.d.ts +8 -0
  49. package/src/lib/runtime/ai-doctrine-validators.js +34 -3
  50. package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
  51. package/src/lib/runtime/ai-doctrine.js +1 -1
  52. package/src/lib/runtime/ai-doctrine.js.map +1 -1
  53. package/src/lib/runtime/ai-eval.d.ts +2 -2
  54. package/src/lib/runtime/ai-eval.js +3 -3
  55. package/src/lib/runtime/ai-eval.js.map +1 -1
  56. package/src/lib/runtime/ai-palette.generated.js +8 -1
  57. package/src/lib/runtime/ai-palette.generated.js.map +1 -1
  58. package/src/lib/runtime/seo-audit.d.ts +5 -2
  59. package/src/lib/runtime/seo-audit.js +16 -7
  60. package/src/lib/runtime/seo-audit.js.map +1 -1
  61. package/src/lib/runtime/site-inventory.js +10 -3
  62. package/src/lib/runtime/site-inventory.js.map +1 -1
  63. package/src/lib/testing/stand-in-dataset-index.js +31 -5
  64. package/src/lib/testing/stand-in-dataset-index.js.map +1 -1
  65. package/src/lib/tools/ai-seo-tool.d.ts +5 -1
  66. package/src/lib/tools/ai-seo-tool.js +6 -2
  67. package/src/lib/tools/ai-seo-tool.js.map +1 -1
  68. package/src/lib/tools/ai-workflow-tool.d.ts +15 -4
  69. package/src/lib/tools/ai-workflow-tool.js +70 -12
  70. package/src/lib/tools/ai-workflow-tool.js.map +1 -1
  71. package/src/lib/usage-axes.js +14 -1
  72. package/src/lib/usage-axes.js.map +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-page-sections.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 { REUSABLE_INSTANCE_COMPONENT_ID } from '@aglyn/aglyn/app-utils/reusable-component-keys'\nimport { CANVAS_ROOT_ELEMENT_ID } from '@aglyn/aglyn/foundation/constants/canvas'\nimport type { NodesMap } from '@aglyn/aglyn/types/nodes'\nimport type { AiBuildPlanScreen, AiBuildPlanSection } from '../model/ai-build-plan'\nimport { aiPageTypeDefinition, parseAiPageJobInputs } from '../model/ai-page-job'\nimport type { AiJob, AiJobPlan } from '../model/ai-jobs.types'\nimport { aiHomeScreenIds, type AiSiteInventory } from '../model/ai-site-inventory'\nimport type { AiTool } from '../providers/contract'\nimport {\n AI_REASK_OFFENDING_MAX_CHARS,\n aiAnswerTree,\n aiNodeTreeContextFromInventory,\n type AiGenerationCheck,\n} from '../runtime/ai-doctrine'\nimport {\n detectCutLines,\n detectDisagreeingNodes,\n isAiLinkElement,\n validateAiDoctrineTree,\n walkTree,\n type AiDoctrineNode,\n type AiDoctrineTreeContext,\n type AiDoctrineViolation,\n} from '../runtime/ai-doctrine-validators'\nimport { AI_INSTANCE_REF_PROP, validateAiNodeTree } from '../runtime/ai-node-tree'\nimport { aiPageLinkTarget, aiPageScrollInteraction } from '../runtime/ai-page-links'\nimport type { AiLoadEstimate } from '../runtime/ai-palette'\nimport { AI_PALETTE_CATALOG } from '../runtime/ai-palette.generated'\nimport { AI_REPEAT_KEY, expandAiRepeatedItems } from '../runtime/ai-repeated-items'\nimport type { AiSystemBlock } from '../runtime/ai-runtime'\nimport { aiJobBriefLine, aiPlanReferenceLines } from './ai-job-generation'\nimport { aiPlanItemCountViolations } from './ai-job-plan-conformance'\n\n/**\n * A page built one section at a time (AGL-2907): what one section pass asks\n * for, how its answer is held to the building rules as part of the page, and\n * how it joins the page.\n *\n * Pure: no Firestore and no provider, so the page step, its specs and the\n * evals read one definition of a section.\n *\n * ── Held as part of the page ─────────────────────────────────────────────\n *\n * A section is checked twice over, by validators the doctrine already has:\n * the palette validator on the section as the model wrote it, then the\n * doctrine's whole page check on the page built so far with the section\n * added. So the page's one main, its one h1 and its heading outline, repeats\n * across sections, and its weight against the page budget are held on every\n * pass, not only on the last. The violations name the section's own nodes,\n * by the ids the model wrote, so a re-ask quotes this section and nothing\n * else — the page is never sent back.\n *\n * ── A repeated item written once (AGL-3053) ──────────────────────────────\n *\n * Where the workspace keeps no reusable components, an answer writes a\n * repeated item once and lists its copies' values, and the check draws the\n * copies before either validator reads the section (`ai-repeated-items.ts`).\n * Both then read the section as the page stores it, so the page's rules hold\n * on every copy exactly as on an item written out in full, and a finding on a\n * copy names the node the model wrote. Where the workspace keeps components\n * the same shape is written around an instance (AGL-3024), so a section of\n * many cards costs its structure once on every plan; an item that places no\n * instance is still refused there.\n */\n\n/** The section root id a pass writes, from the job and the plan section's index. */\nexport function aiPageSectionNodeId(jobId: string, index: number): string {\n return `ai-${jobId.slice(0, 12)}-section-${index + 1}`\n}\n\n/** The strict tool a section arrives through. The node map rides as JSON text, as a page's does. */\nexport const AI_PAGE_SECTION_TOOL: AiTool = {\n name: 'submit_section',\n description: 'Submit one section of the page as one flat node map.',\n strict: true,\n inputSchema: {\n type: 'object',\n additionalProperties: false,\n required: ['tree'],\n properties: {\n tree: {\n type: 'string',\n description:\n 'The section as JSON: {\"rootId\": \"<id>\", \"nodes\": {\"<id>\": {\"componentId\": \"...\", \"props\": {...}, \"sx\": {...}, \"nodes\": [\"<child id>\", ...]}}}. The root is the document wrapper (componentId \"div\") holding one Section.',\n },\n },\n },\n}\n\n/** The page step's own instructions, cached after the doctrine, with the screen palette after them. */\nexport const AI_JOB_PAGE_INSTRUCTIONS: readonly AiSystemBlock[] = [\n {\n text: [\n 'You build a web page one section at a time, in the order a confirmed plan gives. Each answer is ONE section: call submit_section with a flat node map whose root is the document wrapper (componentId \"div\") holding exactly one Section (section).',\n 'The first section is the top of the page and holds the page’s one h1, its title. Every later section opens with an h2, and headings below it step down one level at a time.',\n 'Place what the section’s plan line names. A component the site has is placed as an instance, never drawn again: componentId \"reusableInstance\", props {\"refId\": \"<component id>\", \"propValues\": {…}} filling the props the inventory lists. A form the site has is a Form (form) whose formId is that form’s id. Another page of the site is linked by its page id, with a Page Link (muiScreenLink) or a Button (muiButton) whose screenId is that id.',\n 'Where the request says the site keeps no saved forms or reusable components, draw them in the section instead: a repeated item is written once, and a form is a Form (form) with no formId, a formName saying what it collects and a submitLabel, holding one Form Field (formField) for each answer with its fieldName, label and fieldType.',\n 'Lay the section out with a Container, a Stack or a Grid (\"container\": true, \"spacing\": 3) of Grid items sized like \"xs:12 md:4\", and size nothing with a fixed width. Colors come from the theme’s palette tokens such as primary.main, spacing from the spacing scale, and type from the text variants.',\n 'The page renders inside the site’s layout, so a section is never a header, navigation or footer.',\n 'A picture is an Image (image) with alt text that says what it should show and no src, for the site owner to fill from the media library.',\n 'Write plain, specific copy in the site’s voice, with no filler. Where the brief leaves out a fact, such as a price, a phone number or an address, write the gap in square brackets instead of inventing it.',\n ].join('\\n'),\n },\n { text: AI_PALETTE_CATALOG.screen, cacheBreakpoint: true },\n]\n\n/** A plan reference as the request names it: the id with the name it was planned under. */\nfunction referenceOf(plan: AiJobPlan, ref: string): string {\n const label = plan.labels?.[ref]\n return label ? `${ref} (\"${label}\")` : ref\n}\n\nexport interface AiPageSectionPromptInput {\n job: Pick<AiJob, 'brief' | 'inputs'>\n plan: AiJobPlan\n screen: AiBuildPlanScreen\n index: number\n /** The most elements the section may carry at this pass's answer ceiling. */\n maxElements: number\n /**\n * Whether the workspace keeps reusable components and saved forms\n * (AGL-3030); `false` asks for the section built inline. Absent is `true`.\n */\n reusableComponents?: boolean\n}\n\n/** What a section request says where the workspace keeps no reusable components or saved forms. */\nexport const AI_PAGE_SECTION_INLINE_LINE =\n 'This site keeps no saved forms or reusable components: write a repeated item once, and draw a form as a Form holding its Form Fields.'\n\n/** How a repeated item is written once, in the words a request and a re-ask share. */\nconst AI_PAGE_SECTION_REPEAT_SHAPE = `put {{1}}, {{2}}… where its copies differ, and give its outermost node \"${AI_REPEAT_KEY}\", one list of values a copy, in that order, as [[\"Title 1\", \"Text 1\"], [\"Title 2\", \"Text 2\"]]`\n\n/**\n * How a repeated item is written once (AGL-3053): the item's subtree once,\n * with placeholders where its copies differ and their values listed, which\n * the section check draws into its copies (`ai-repeated-items.ts`). It rides\n * the request of a section whose plan line shows items on a workspace that\n * keeps no reusable components, and no other: a section with nothing to repeat\n * pays nothing for it, and the cached prefix every workspace shares says only\n * that a repeated item is written once.\n */\nexport const AI_PAGE_SECTION_REPEAT_LINE = `Write a repeated item once: ${AI_PAGE_SECTION_REPEAT_SHAPE}.`\n\n/**\n * How a workspace that keeps reusable components writes a repeated item\n * (AGL-3143 §10): the shape a Free section writes, around an instance of a\n * component the section already places — the instance ONCE, `{{n}}` through\n * the values it fills, and one list of values a copy — rather than the item's\n * subtree drawn once per copy, or one instance node written per copy.\n *\n * Six cards written as six instance nodes spend their structure six times;\n * written once they spend it once, which is the difference between a section\n * that fits its ceiling and one that is cut off. A section stays ONE section\n * either way: splitting it would give the page a second h2 for the same idea,\n * which is not what the member asked for and not what its outline should say.\n *\n * ⛔ The repeated item is the GRID ITEM, not the instance. Measured live on\n * 2026-09-22: told only to \"place ONE instance … and give its outermost node\n * repeat\", the model made the instance itself the repeated node and hung it\n * straight off the Grid container. The section fitted — the ceiling failure\n * was gone — and the page was then refused by rule 12, which requires every\n * child of a Grid container to be a sized Grid item (AGL-3055). The line has\n * to name the wrapper, because \"outermost node\" reads as the instance when\n * the instance is all the request mentions.\n *\n * It rides the request of a section whose plan line shows items AND names a\n * record to place, on the same terms the repeat line rides a Free one: a\n * section with nothing to repeat, or nothing to repeat it FROM, pays nothing\n * for it.\n *\n * ## Why a paid section could not be built without it (AGL-3024)\n *\n * The Free path is reconciled at both ends — its plan is held to the sections\n * its wall pays for (AGL-3070) and its request is shown the repeat shape\n * (AGL-3053). The paid path was reconciled at neither: `AI_BUILD_PLAN_LIMITS`\n * lets a plan promise up to `items: 500` in one section, the pass that builds\n * it fits `aiJobPageSectionMaxElements` — fifteen at the balanced tier — and\n * the request said only how many elements to keep under, never how to get\n * there. A six-card grid measured live on 2026-09-21 was drawn inline, ran\n * past its ceiling, and was cut off; the re-ask then said to place an instance\n * \"of a component the site has\" without naming one, so it was cut off too, and\n * `AI_GENERATION_MAX_ATTEMPTS` ended the job with no page. A four-card grid on\n * the same site had fitted, which is why this went unseen. Naming the shape\n * was not enough on its own: `ai-repeated-items.ts` refused `repeat` outright\n * on such a workspace, so the only shape the request could ask for was the\n * expensive one.\n *\n * ⛔ This does NOT make the plan and the pass agree about what fits — a plan\n * may still promise more items than any ceiling can carry, and this line only\n * gives the pass its cheapest way to try. That bound belongs on the plan step\n * and is deliberately not taken here.\n */\nexport const AI_PAGE_SECTION_INSTANCE_LINE = `Write a repeated item once: put ONE instance of the component this section places inside ONE Grid item sized like \"xs:12 sm:6 md:4\", and repeat THAT GRID ITEM rather than the instance — every child of a Grid container is itself a Grid item. Put {{1}}, {{2}}… where the copies differ, and give the Grid item \"${AI_REPEAT_KEY}\", one list of values a copy, in that order, as [[\"Title 1\", \"Text 1\"], [\"Title 2\", \"Text 2\"]].`\n\n/**\n * What a pass whose answer ran past its ceiling is told makes a section\n * smaller (AGL-3042): fewer elements, down to the budget the request gave,\n * shorter copy, and a repeated item drawn no more than once — placed as an\n * instance where the workspace keeps reusable components, and written once\n * where it keeps none (AGL-3053), with the shape spelled out, since a section\n * whose plan line showed no items was never told it. It rides the re-ask,\n * never the first request, so a pass that fits sends no byte more.\n */\nexport function aiPageSectionSmaller(input: Pick<AiPageSectionPromptInput, 'maxElements' | 'reusableComponents'>): string {\n return input.reusableComponents === false\n ? `Make it smaller: use fewer elements, at most ${input.maxElements}; write shorter copy; and write a repeated item once instead of drawing it again: ${AI_PAGE_SECTION_REPEAT_SHAPE}.`\n : `Make it smaller: use fewer elements, at most ${input.maxElements}; write shorter copy; and write a repeated item once instead of drawing it again: put ONE instance of a component the site has inside ONE Grid item sized like \"xs:12 sm:6 md:4\", and repeat that Grid item — ${AI_PAGE_SECTION_REPEAT_SHAPE}.`\n}\n\n/** One pass's user turn: the page, the brief, the plan, this section and what is built above it. */\nexport function aiPageSectionPrompt(input: AiPageSectionPromptInput): string {\n const { job, plan, screen, index } = input\n const inputs = parseAiPageJobInputs(job.inputs)\n const type = typeof inputs === 'string' ? null : aiPageTypeDefinition(inputs.pageType)\n const section = screen.sections[index] as AiBuildPlanSection\n const places = section.uses.length\n ? ` It places ${section.uses.map((ref) => referenceOf(plan, ref)).join(', ')}.`\n : ''\n const items = section.items ? ` It shows ${section.items} items.` : ''\n const built = screen.sections.slice(0, index).map((entry, position) => `${position + 1}. ${entry.name}`)\n return [\n `Page: \"${screen.title}\" at ${screen.slug}`,\n ...(type ? [`Page type: ${type.purpose}`] : []),\n aiJobBriefLine(job),\n ...aiPlanReferenceLines(plan),\n `Build section ${index + 1} of ${screen.sections.length}: \"${section.name}\".${places}${items}`,\n built.length\n ? `Already built, above it: ${built.join('; ')}. The page’s h1 is in section 1.`\n : 'Nothing is built yet: this section holds the page’s h1.',\n ...(input.reusableComponents === false\n ? [AI_PAGE_SECTION_INLINE_LINE, ...(section.items ? [AI_PAGE_SECTION_REPEAT_LINE] : [])]\n : section.items && section.uses.length\n ? [AI_PAGE_SECTION_INSTANCE_LINE]\n : []),\n `Keep this section to at most ${input.maxElements} elements.`,\n ].join('\\n')\n}\n\n/** A validated section: its nodes keyed as the page stores them, its root under the page root. */\nexport interface AiPageSection {\n rootId: string\n nodes: NodesMap\n /** The page's weight with the section added, as the doctrine measured it. */\n load: AiLoadEstimate | null\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/** A page with nothing on it yet: the document wrapper. */\nexport function aiEmptyPage(): NodesMap {\n return {\n [CANVAS_ROOT_ELEMENT_ID]: {\n $id: CANVAS_ROOT_ELEMENT_ID,\n componentId: 'div',\n parentId: null,\n nodes: [],\n },\n } as unknown as NodesMap\n}\n\n/**\n * The page with a section in its plan position: after every section with a\n * lower index and before every one with a higher, the page's other children\n * — whatever a member placed — kept where they are. A section already on the\n * page is replaced, never doubled.\n */\nexport function aiPageWithSection(\n page: NodesMap,\n section: AiPageSection,\n sectionIds: readonly string[],\n): NodesMap {\n const nodes: Record<string, Record<string, unknown>> = {}\n const previous = (page as unknown as Record<string, Record<string, unknown>>)[section.rootId]\n const replaced = new Set<string>()\n if (previous) {\n for (const { id } of walkTree({\n rootId: section.rootId,\n nodes: page as unknown as Record<string, AiDoctrineNode>,\n })) {\n replaced.add(id)\n }\n }\n for (const [id, node] of Object.entries(page)) {\n if (!replaced.has(id)) nodes[id] = { ...(node as unknown as Record<string, unknown>) }\n }\n const root = nodes[CANVAS_ROOT_ELEMENT_ID] ?? (aiEmptyPage()[CANVAS_ROOT_ELEMENT_ID] as unknown as Record<string, unknown>)\n const children = (Array.isArray(root['nodes']) ? (root['nodes'] as string[]) : []).filter(\n (id) => id !== section.rootId,\n )\n const order = new Map(sectionIds.map((id, position) => [id, position]))\n const at = order.get(section.rootId) ?? sectionIds.length\n const before = children.findIndex((id) => (order.get(id) ?? -1) > at)\n children.splice(before === -1 ? children.length : before, 0, section.rootId)\n nodes[CANVAS_ROOT_ELEMENT_ID] = { ...root, nodes: children }\n for (const [id, node] of Object.entries(section.nodes)) {\n nodes[id] = { ...(node as unknown as Record<string, unknown>) }\n }\n return nodes as unknown as NodesMap\n}\n\n/**\n * What a tree generated against this inventory may reference, the brand it is\n * held to, where the workspace keeps no reusable components or saved forms\n * (AGL-3030) that the page is built inline, the sections its plan names,\n * which a link may take a visitor to (AGL-3097), and the players its plan\n * lists for this page, the only ones it may embed (AGL-3433).\n */\nexport function aiPageCheckContext(\n inventory: AiSiteInventory | null,\n options: {\n reusableComponents?: boolean\n sections?: readonly string[]\n embeds?: AiDoctrineTreeContext['plannedEmbeds']\n } = {},\n): AiDoctrineTreeContext {\n return {\n brand: inventory?.theme ? { colors: inventory.theme.colors, fonts: inventory.theme.fonts } : null,\n ...aiNodeTreeContextFromInventory(inventory),\n homeScreenIds: aiHomeScreenIds(inventory),\n ...(options.reusableComponents === false ? { reusableComponents: false } : {}),\n ...(options.sections?.length ? { pageSections: options.sections } : {}),\n ...(options.embeds?.length ? { plannedEmbeds: options.embeds } : {}),\n }\n}\n\nexport interface AiPageSectionCheckInput {\n /** The page as stored so far; an empty page on the first pass. */\n page: NodesMap\n /** Every section root id of the plan, in order. */\n sectionIds: readonly string[]\n index: number\n context: AiDoctrineTreeContext\n /**\n * The plan line this pass answers for: what it places by inventory id, and\n * how many items it promised, which the answer is held to (AGL-3024).\n */\n section: Pick<AiBuildPlanSection, 'name' | 'uses' | 'items'>\n inventory: AiSiteInventory | null\n}\n\n/** The parts of an answer the violations name, as the model wrote them, capped as the doctrine caps a re-ask. */\nfunction offendingOf(raw: unknown, violations: readonly AiDoctrineViolation[]): { offending?: Record<string, unknown> } {\n const rawNodes = isRecord(raw) && isRecord(raw['nodes']) ? raw['nodes'] : {}\n const offending: Record<string, unknown> = {}\n let used = 2\n for (const id of new Set(violations.flatMap((violation) => violation.nodeIds ?? []))) {\n if (rawNodes[id] === undefined) continue\n const size = JSON.stringify(rawNodes[id]).length + id.length + 4\n if (used + size > AI_REASK_OFFENDING_MAX_CHARS) break\n offending[id] = rawNodes[id]\n used += size\n }\n return Object.keys(offending).length ? { offending } : {}\n}\n\n/**\n * The check a section's answer is held to: a repeated item written once drawn\n * into its copies (AGL-3053), then the palette validator on the section, with\n * any line it cut at its ceiling refused (AGL-3076), the doctrine's page check\n * on the page with the section added, and the plan line — every component it\n * names placed as an instance, every form bound by its id (rule 7), and as\n * many items as it promised (AGL-3024). Violations\n * name the section's own nodes by the ids the MODEL wrote, and a copy's nodes\n * by the item's; one that names only nodes an earlier pass stored belongs to\n * the page rather than to this answer, and is left to the last pass (AGL-3078).\n */\nexport function aiPageSectionCheck(input: AiPageSectionCheckInput): AiGenerationCheck<AiPageSection> {\n const sectionId = input.sectionIds[input.index]\n const components = new Set((input.inventory?.components ?? []).map((row) => row.id))\n const forms = new Set((input.inventory?.forms ?? []).map((row) => row.id))\n return (answer) => {\n const raw = aiAnswerTree(answer)\n // Before anything is drawn or dropped: a repeated item written and never\n // placed is content the section loses silently, and a child named and\n // never written ends the pass with nothing to answer (AGL-3143).\n const disagreeing = detectDisagreeingNodes(raw)\n if (disagreeing.length) {\n return { value: null, violations: disagreeing, ...offendingOf(raw, disagreeing) }\n }\n const drawn = expandAiRepeatedItems(raw, { inline: input.context.reusableComponents === false, noun: 'section' })\n if (drawn.ok === false) {\n return { value: null, violations: drawn.violations, ...offendingOf(raw, drawn.violations) }\n }\n // Every id below is the drawn tree's; a copy's leads back to the node the model wrote.\n const written = (id: string): string => drawn.sourceIds[id] ?? id\n const validated = validateAiNodeTree(drawn.tree, 'screen', input.context)\n if (validated.ok === false) {\n return {\n value: null,\n violations: [\n {\n rule: null,\n code: `tree-${validated.code}`,\n message: 'The answer could not be used as a section.',\n detail: validated.error,\n },\n ],\n }\n }\n const wrapper = validated.nodes[validated.rootId] as unknown as AiDoctrineNode | undefined\n const top = (wrapper?.nodes ?? []).filter((id) => validated.nodes[id])\n const sectionNode = top.length === 1 ? (validated.nodes[top[0]] as unknown as AiDoctrineNode) : null\n if (!sectionNode || sectionNode.componentId !== 'section') {\n return {\n value: null,\n violations: [\n {\n rule: null,\n code: 'section-shape',\n message: 'Answer with exactly one Section element inside the document wrapper.',\n },\n ],\n }\n }\n\n // The section's root takes its plan id, so a pass run again finds it, and\n // hangs under the page root; every other minted id stays as minted.\n const minted = top[0]\n const modelIds: Record<string, string> = Object.fromEntries(\n Object.entries(validated.sourceIds).map(([id, source]) => [id, written(source)]),\n )\n modelIds[sectionId] = written(validated.sourceIds[minted] ?? minted)\n const sectionNodes: Record<string, Record<string, unknown>> = {}\n for (const { id, node } of walkTree({\n rootId: minted,\n nodes: validated.nodes as unknown as Record<string, AiDoctrineNode>,\n })) {\n const stored = { ...(node as unknown as Record<string, unknown>) }\n if (id === minted) {\n stored['$id'] = sectionId\n stored['parentId'] = CANVAS_ROOT_ELEMENT_ID\n } else if (stored['parentId'] === minted) {\n stored['parentId'] = sectionId\n }\n sectionNodes[id === minted ? sectionId : id] = stored\n }\n const drawnNodes = isRecord(drawn.tree) && isRecord(drawn.tree['nodes']) ? drawn.tree['nodes'] : {}\n // What the model wrote of one of the section's nodes, by the id the page stores it under.\n const drawnIdOf = (stored: string): string =>\n validated.sourceIds[stored === sectionId ? minted : stored] ?? stored\n // A link that names a section of this page carries the platform's Scroll to\n // element interaction to that section's root, whose id is minted from the\n // plan before the section is built (AGL-3097).\n const sections = input.context.pageSections ?? []\n for (const [id, node] of Object.entries(sectionNodes)) {\n const props = isRecord(node['props']) ? node['props'] : {}\n if (!isAiLinkElement(node['componentId']) || props['screenId'] || props['href']) continue\n const target = aiPageLinkTarget(drawnNodes[drawnIdOf(id)], sections)\n if (target?.kind === 'section') {\n node['interactions'] = [aiPageScrollInteraction(input.sectionIds[target.section], sections[target.section])]\n }\n }\n const section: AiPageSection = { rootId: sectionId, nodes: sectionNodes as unknown as NodesMap, load: null }\n\n const page = aiPageWithSection(input.page, section, input.sectionIds)\n const own = new Set(Object.keys(sectionNodes))\n // The page check reads the section as the page stores it, where the palette\n // validator has already dropped what it could not read, such as a\n // `container` written as text or a link's `scrollTo`; what the model wrote\n // of each of the section's nodes is read from the section as it was drawn\n // (AGL-3078). A link the page already carries to one of its section roots\n // goes there, whichever pass wrote it (AGL-3097).\n const report = validateAiDoctrineTree({ rootId: CANVAS_ROOT_ELEMENT_ID, nodes: page }, 'page', {\n ...input.context,\n writtenNode: (id) => (own.has(id) ? drawnNodes[drawnIdOf(id)] : undefined),\n scrollTargetIds: input.sectionIds,\n })\n // The page check mints its ids afresh: its map leads back to the page's\n // ids, and the section's own lead back to the model's.\n const pageIds = report.tree?.sourceIds ?? {}\n const toModel = (id: string): string | null => {\n const stored = pageIds[id] ?? id\n return own.has(stored) ? (modelIds[stored] ?? stored) : null\n }\n // The page check reads the section as it is stored, where a line the\n // palette validator cut is already cut, so the cut is read from the\n // section's own validation, first (AGL-3076). Copies of one item name one\n // node the model wrote, once.\n //\n // A PASS ANSWERS ONLY FOR THE SECTION IT WROTE (AGL-3078). The page check\n // reads the whole page, so it also names nodes an earlier pass stored; the\n // model is not shown those and cannot mend them, so a finding that names\n // none of this section's nodes is not held against this answer. Dropping\n // it neither hides nor forgives it: the last pass runs the same check on\n // the finished page, where every node is the page's and the review names\n // each one and outlines it. Kept, such a finding would re-ask the model\n // for a node it never saw, refuse the same answer twice, and stop the page\n // with findings whose ids all filtered away — no node ids, and so no\n // outline either.\n const violations: AiDoctrineViolation[] = [\n ...detectCutLines(validated.repairs, drawnNodes, written),\n ...report.violations.flatMap((violation) => {\n if (!violation.nodeIds?.length) return [violation]\n const named = [...new Set(violation.nodeIds.map(toModel).filter((id): id is string => id !== null))]\n return named.length ? [{ ...violation, nodeIds: named }] : []\n }),\n ]\n\n const placed = new Set<string>()\n const bound = new Set<string>()\n for (const node of Object.values(sectionNodes)) {\n const props = isRecord(node['props']) ? node['props'] : {}\n if (node['componentId'] === REUSABLE_INSTANCE_COMPONENT_ID && typeof props[AI_INSTANCE_REF_PROP] === 'string') {\n placed.add(props[AI_INSTANCE_REF_PROP] as string)\n }\n if (node['componentId'] === 'form' && typeof props['formId'] === 'string') bound.add(props['formId'])\n }\n const missing = input.section.uses.filter(\n (ref) => (components.has(ref) && !placed.has(ref)) || (forms.has(ref) && !bound.has(ref)),\n )\n if (missing.length) {\n violations.push({\n rule: 7,\n code: 'plan-reuse-not-placed',\n message: `The confirmed plan places ${missing.join(', ')} in this section, and the section does not. Place each one by its id.`,\n })\n }\n // The count the plan promised, against the section as the page stores it\n // (AGL-3024): the prompt states it, and until now nothing read it back.\n violations.push(\n ...aiPlanItemCountViolations(input.section, {\n rootId: sectionId,\n nodes: sectionNodes as unknown as Record<string, AiDoctrineNode>,\n }),\n )\n\n return {\n value: { ...section, load: report.load },\n violations,\n ...offendingOf(raw, violations),\n }\n }\n}\n"],"names":["REUSABLE_INSTANCE_COMPONENT_ID","CANVAS_ROOT_ELEMENT_ID","aiPageTypeDefinition","parseAiPageJobInputs","aiHomeScreenIds","AI_REASK_OFFENDING_MAX_CHARS","aiAnswerTree","aiNodeTreeContextFromInventory","detectCutLines","detectDisagreeingNodes","isAiLinkElement","validateAiDoctrineTree","walkTree","AI_INSTANCE_REF_PROP","validateAiNodeTree","aiPageLinkTarget","aiPageScrollInteraction","AI_PALETTE_CATALOG","AI_REPEAT_KEY","expandAiRepeatedItems","aiJobBriefLine","aiPlanReferenceLines","aiPlanItemCountViolations","aiPageSectionNodeId","jobId","index","slice","AI_PAGE_SECTION_TOOL","name","description","strict","inputSchema","type","additionalProperties","required","properties","tree","AI_JOB_PAGE_INSTRUCTIONS","text","join","screen","cacheBreakpoint","referenceOf","plan","ref","label","labels","AI_PAGE_SECTION_INLINE_LINE","AI_PAGE_SECTION_REPEAT_SHAPE","AI_PAGE_SECTION_REPEAT_LINE","AI_PAGE_SECTION_INSTANCE_LINE","aiPageSectionSmaller","input","reusableComponents","maxElements","aiPageSectionPrompt","job","inputs","pageType","section","sections","places","uses","length","map","items","built","entry","position","title","slug","purpose","isRecord","value","Array","isArray","aiEmptyPage","$id","componentId","parentId","nodes","aiPageWithSection","page","sectionIds","order","previous","rootId","replaced","Set","id","add","node","Object","entries","has","root","children","filter","Map","at","get","before","findIndex","splice","aiPageCheckContext","inventory","options","brand","theme","colors","fonts","homeScreenIds","pageSections","embeds","plannedEmbeds","offendingOf","raw","violations","rawNodes","offending","used","flatMap","violation","nodeIds","undefined","size","JSON","stringify","keys","aiPageSectionCheck","sectionId","components","row","forms","answer","validated","report","disagreeing","drawn","inline","context","noun","ok","written","sourceIds","rule","code","message","detail","error","wrapper","top","sectionNode","minted","modelIds","fromEntries","source","sectionNodes","stored","drawnNodes","drawnIdOf","props","target","kind","load","own","writtenNode","scrollTargetIds","pageIds","toModel","repairs","named","placed","bound","values","missing","push"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-page-sections.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 { REUSABLE_INSTANCE_COMPONENT_ID } from '@aglyn/aglyn/app-utils/reusable-component-keys'\nimport { CANVAS_ROOT_ELEMENT_ID } from '@aglyn/aglyn/foundation/constants/canvas'\nimport type { NodesMap } from '@aglyn/aglyn/types/nodes'\nimport {\n aiPlanCreateFor,\n type AiBuildPlanRecord,\n type AiBuildPlanScreen,\n type AiBuildPlanSection,\n} from '../model/ai-build-plan'\nimport { aiPageTypeDefinition, parseAiPageJobInputs } from '../model/ai-page-job'\nimport type { AiJob, AiJobPlan } from '../model/ai-jobs.types'\nimport { aiHomeScreenIds, type AiSiteInventory } from '../model/ai-site-inventory'\nimport type { AiTool } from '../providers/contract'\nimport {\n AI_REASK_OFFENDING_MAX_CHARS,\n aiAnswerTree,\n aiNodeTreeContextFromInventory,\n type AiGenerationCheck,\n} from '../runtime/ai-doctrine'\nimport {\n detectCutLines,\n detectDisagreeingNodes,\n isAiLinkElement,\n validateAiDoctrineTree,\n walkTree,\n type AiDoctrineNode,\n type AiDoctrineTreeContext,\n type AiDoctrineViolation,\n} from '../runtime/ai-doctrine-validators'\nimport { AI_INSTANCE_REF_PROP, validateAiNodeTree } from '../runtime/ai-node-tree'\nimport { aiPageLinkTarget, aiPageScrollInteraction } from '../runtime/ai-page-links'\nimport type { AiLoadEstimate } from '../runtime/ai-palette'\nimport { AI_PALETTE_CATALOG } from '../runtime/ai-palette.generated'\nimport { AI_REPEAT_KEY, expandAiRepeatedItems } from '../runtime/ai-repeated-items'\nimport type { AiSystemBlock } from '../runtime/ai-runtime'\nimport { aiJobBriefLine, aiPlanReferenceLines } from './ai-job-generation'\nimport { aiPlanItemCountViolations } from './ai-job-plan-conformance'\n\n/**\n * A page built one section at a time (AGL-2907): what one section pass asks\n * for, how its answer is held to the building rules as part of the page, and\n * how it joins the page.\n *\n * Pure: no Firestore and no provider, so the page step, its specs and the\n * evals read one definition of a section.\n *\n * ── Held as part of the page ─────────────────────────────────────────────\n *\n * A section is checked twice over, by validators the doctrine already has:\n * the palette validator on the section as the model wrote it, then the\n * doctrine's whole page check on the page built so far with the section\n * added. So the page's one main, its one h1 and its heading outline, repeats\n * across sections, and its weight against the page budget are held on every\n * pass, not only on the last. The violations name the section's own nodes,\n * by the ids the model wrote, so a re-ask quotes this section and nothing\n * else — the page is never sent back.\n *\n * ── A repeated item written once (AGL-3053) ──────────────────────────────\n *\n * Where the workspace keeps no reusable components, an answer writes a\n * repeated item once and lists its copies' values, and the check draws the\n * copies before either validator reads the section (`ai-repeated-items.ts`).\n * Both then read the section as the page stores it, so the page's rules hold\n * on every copy exactly as on an item written out in full, and a finding on a\n * copy names the node the model wrote. Where the workspace keeps components\n * the same shape is written around an instance (AGL-3024), so a section of\n * many cards costs its structure once on every plan; an item that places no\n * instance is still refused there.\n */\n\n/** The section root id a pass writes, from the job and the plan section's index. */\nexport function aiPageSectionNodeId(jobId: string, index: number): string {\n return `ai-${jobId.slice(0, 12)}-section-${index + 1}`\n}\n\n/** The strict tool a section arrives through. The node map rides as JSON text, as a page's does. */\nexport const AI_PAGE_SECTION_TOOL: AiTool = {\n name: 'submit_section',\n description: 'Submit one section of the page as one flat node map.',\n strict: true,\n inputSchema: {\n type: 'object',\n additionalProperties: false,\n required: ['tree'],\n properties: {\n tree: {\n type: 'string',\n description:\n 'The section as JSON: {\"rootId\": \"<id>\", \"nodes\": {\"<id>\": {\"componentId\": \"...\", \"props\": {...}, \"sx\": {...}, \"nodes\": [\"<child id>\", ...]}}}. The root is the document wrapper (componentId \"div\") holding one Section.',\n },\n },\n },\n}\n\n/** The page step's own instructions, cached after the doctrine, with the screen palette after them. */\nexport const AI_JOB_PAGE_INSTRUCTIONS: readonly AiSystemBlock[] = [\n {\n text: [\n 'You build a web page one section at a time, in the order a confirmed plan gives. Each answer is ONE section: call submit_section with a flat node map whose root is the document wrapper (componentId \"div\") holding exactly one Section (section).',\n 'The first section is the top of the page and holds the page’s one h1, its title. Every later section opens with an h2, and headings below it step down one level at a time.',\n 'Place what the section’s plan line names. A component the site has is placed as an instance, never drawn again: componentId \"reusableInstance\", props {\"refId\": \"<component id>\", \"propValues\": {…}} filling the props the inventory lists. A form the site has is a Form (form) whose formId is that form’s id. Another page of the site is linked by its page id, with a Page Link (muiScreenLink) or a Button (muiButton) whose screenId is that id.',\n 'Where the request says the site keeps no saved forms or reusable components, draw them in the section instead: a repeated item is written once, and a form is a Form (form) with no formId, a formName saying what it collects and a submitLabel, holding one Form Field (formField) for each answer with its fieldName, label and fieldType.',\n 'Lay the section out with a Container, a Stack or a Grid (\"container\": true, \"spacing\": 3) of Grid items sized like \"xs:12 md:4\", and size nothing with a fixed width. Colors come from the theme’s palette tokens such as primary.main, spacing from the spacing scale, and type from the text variants.',\n 'The page renders inside the site’s layout, so a section is never a header, navigation or footer.',\n 'A picture is an Image (image) with alt text that says what it should show and no src, for the site owner to fill from the media library.',\n 'Write plain, specific copy in the site’s voice, with no filler. Where the brief leaves out a fact, such as a price, a phone number or an address, write the gap in square brackets instead of inventing it.',\n ].join('\\n'),\n },\n { text: AI_PALETTE_CATALOG.screen, cacheBreakpoint: true },\n]\n\n/** A plan reference as the request names it: the id with the name it was planned under. */\nfunction referenceOf(plan: AiJobPlan, ref: string): string {\n const label = plan.labels?.[ref]\n return label ? `${ref} (\"${label}\")` : ref\n}\n\n/**\n * A record template as a page pass reads it (AGL-3475): the dataset by name,\n * its base, and the fields its copy binds, as `{{item.<id>}}` with the name a\n * member gave each. A dataset the plan creates has no ids yet, so its tokens\n * are the field names it was planned with.\n */\nexport interface AiPageRecordTemplate {\n /** The dataset: an inventory id, or the plan's `new:<name>`. */\n dataset: string\n name: string\n base: string\n fields: Array<{ id: string; name: string }>\n}\n\n/** The record template a plan screen is, read against the site's datasets; null for a page that is one page. */\nexport function aiPageRecordTemplate(\n record: AiBuildPlanRecord | null | undefined,\n inventory: AiSiteInventory | null,\n plan: AiJobPlan,\n): AiPageRecordTemplate | null {\n if (!record) return null\n const row = inventory?.datasets.find((entry) => entry.id === record.dataset)\n if (row) {\n return {\n dataset: record.dataset,\n name: row.name,\n base: record.base,\n fields: row.fields.map((name, index) => ({ id: row.fieldIds?.[index] || name, name })),\n }\n }\n const created = aiPlanCreateFor(plan, record.dataset)\n return {\n dataset: record.dataset,\n name: created?.name ?? plan.labels?.[record.dataset] ?? record.dataset,\n base: record.base,\n fields: (created?.fields ?? []).map((name) => ({ id: name, name })),\n }\n}\n\n/** Every token a record template's page may bind: each field, and `{{item.url}}`. */\nexport function aiPageRecordTokens(template: AiPageRecordTemplate | null): string[] {\n if (!template) return []\n return [...template.fields.map((field) => `{{item.${field.id}}}`), '{{item.url}}']\n}\n\n/** What a section request says on a record template's page. */\nexport function aiPageRecordLine(template: AiPageRecordTemplate): string {\n const fields = template.fields.length\n ? ` Its fields: ${template.fields\n .map((field) => (field.name === field.id ? `{{item.${field.id}}}` : `{{item.${field.id}}} (${field.name})`))\n .join(', ')}.`\n : ''\n return `This page is the record template of the dataset \"${template.name}\": it is served once per record, at /${template.base}/<record address>. Write what differs from one record to the next as {{item.<field>}}, never one record's copy, and write out what every record page shares.${fields}`\n}\n\n/**\n * What a record template's draft tells the member to do next. The binding is\n * a route, so the job never writes it (rule 13): the member saves it where\n * every record template is set up.\n */\nexport function aiPageRecordNote(template: AiPageRecordTemplate): string {\n return `This page is drafted as the record template of \"${template.name}\". To serve a page per record at /${template.base}/…, open Page Properties → Record pages and save its dataset, address and fields.`\n}\n\nexport interface AiPageSectionPromptInput {\n job: Pick<AiJob, 'brief' | 'inputs'>\n plan: AiJobPlan\n screen: AiBuildPlanScreen\n index: number\n /** The most elements the section may carry at this pass's answer ceiling. */\n maxElements: number\n /**\n * Whether the workspace keeps reusable components and saved forms\n * (AGL-3030); `false` asks for the section built inline. Absent is `true`.\n */\n reusableComponents?: boolean\n /** The dataset this page is the record template of; absent for a page that is one page. */\n record?: AiPageRecordTemplate | null\n}\n\n/** What a section request says where the workspace keeps no reusable components or saved forms. */\nexport const AI_PAGE_SECTION_INLINE_LINE =\n 'This site keeps no saved forms or reusable components: write a repeated item once, and draw a form as a Form holding its Form Fields.'\n\n/** How a repeated item is written once, in the words a request and a re-ask share. */\nconst AI_PAGE_SECTION_REPEAT_SHAPE = `put {{1}}, {{2}}… where its copies differ, and give its outermost node \"${AI_REPEAT_KEY}\", one list of values a copy, in that order, as [[\"Title 1\", \"Text 1\"], [\"Title 2\", \"Text 2\"]]`\n\n/**\n * How a repeated item is written once (AGL-3053): the item's subtree once,\n * with placeholders where its copies differ and their values listed, which\n * the section check draws into its copies (`ai-repeated-items.ts`). It rides\n * the request of a section whose plan line shows items on a workspace that\n * keeps no reusable components, and no other: a section with nothing to repeat\n * pays nothing for it, and the cached prefix every workspace shares says only\n * that a repeated item is written once.\n */\nexport const AI_PAGE_SECTION_REPEAT_LINE = `Write a repeated item once: ${AI_PAGE_SECTION_REPEAT_SHAPE}.`\n\n/**\n * How a workspace that keeps reusable components writes a repeated item\n * (AGL-3143 §10): the shape a Free section writes, around an instance of a\n * component the section already places — the instance ONCE, `{{n}}` through\n * the values it fills, and one list of values a copy — rather than the item's\n * subtree drawn once per copy, or one instance node written per copy.\n *\n * Six cards written as six instance nodes spend their structure six times;\n * written once they spend it once, which is the difference between a section\n * that fits its ceiling and one that is cut off. A section stays ONE section\n * either way: splitting it would give the page a second h2 for the same idea,\n * which is not what the member asked for and not what its outline should say.\n *\n * ⛔ The repeated item is the GRID ITEM, not the instance. Measured live on\n * 2026-09-22: told only to \"place ONE instance … and give its outermost node\n * repeat\", the model made the instance itself the repeated node and hung it\n * straight off the Grid container. The section fitted — the ceiling failure\n * was gone — and the page was then refused by rule 12, which requires every\n * child of a Grid container to be a sized Grid item (AGL-3055). The line has\n * to name the wrapper, because \"outermost node\" reads as the instance when\n * the instance is all the request mentions.\n *\n * It rides the request of a section whose plan line shows items AND names a\n * record to place, on the same terms the repeat line rides a Free one: a\n * section with nothing to repeat, or nothing to repeat it FROM, pays nothing\n * for it.\n *\n * ## Why a paid section could not be built without it (AGL-3024)\n *\n * The Free path is reconciled at both ends — its plan is held to the sections\n * its wall pays for (AGL-3070) and its request is shown the repeat shape\n * (AGL-3053). The paid path was reconciled at neither: `AI_BUILD_PLAN_LIMITS`\n * lets a plan promise up to `items: 500` in one section, the pass that builds\n * it fits `aiJobPageSectionMaxElements` — fifteen at the balanced tier — and\n * the request said only how many elements to keep under, never how to get\n * there. A six-card grid measured live on 2026-09-21 was drawn inline, ran\n * past its ceiling, and was cut off; the re-ask then said to place an instance\n * \"of a component the site has\" without naming one, so it was cut off too, and\n * `AI_GENERATION_MAX_ATTEMPTS` ended the job with no page. A four-card grid on\n * the same site had fitted, which is why this went unseen. Naming the shape\n * was not enough on its own: `ai-repeated-items.ts` refused `repeat` outright\n * on such a workspace, so the only shape the request could ask for was the\n * expensive one.\n *\n * ⛔ This does NOT make the plan and the pass agree about what fits — a plan\n * may still promise more items than any ceiling can carry, and this line only\n * gives the pass its cheapest way to try. That bound belongs on the plan step\n * and is deliberately not taken here.\n */\nexport const AI_PAGE_SECTION_INSTANCE_LINE = `Write a repeated item once: put ONE instance of the component this section places inside ONE Grid item sized like \"xs:12 sm:6 md:4\", and repeat THAT GRID ITEM rather than the instance — every child of a Grid container is itself a Grid item. Put {{1}}, {{2}}… where the copies differ, and give the Grid item \"${AI_REPEAT_KEY}\", one list of values a copy, in that order, as [[\"Title 1\", \"Text 1\"], [\"Title 2\", \"Text 2\"]].`\n\n/**\n * What a pass whose answer ran past its ceiling is told makes a section\n * smaller (AGL-3042): fewer elements, down to the budget the request gave,\n * shorter copy, and a repeated item drawn no more than once — placed as an\n * instance where the workspace keeps reusable components, and written once\n * where it keeps none (AGL-3053), with the shape spelled out, since a section\n * whose plan line showed no items was never told it. It rides the re-ask,\n * never the first request, so a pass that fits sends no byte more.\n */\nexport function aiPageSectionSmaller(input: Pick<AiPageSectionPromptInput, 'maxElements' | 'reusableComponents'>): string {\n return input.reusableComponents === false\n ? `Make it smaller: use fewer elements, at most ${input.maxElements}; write shorter copy; and write a repeated item once instead of drawing it again: ${AI_PAGE_SECTION_REPEAT_SHAPE}.`\n : `Make it smaller: use fewer elements, at most ${input.maxElements}; write shorter copy; and write a repeated item once instead of drawing it again: put ONE instance of a component the site has inside ONE Grid item sized like \"xs:12 sm:6 md:4\", and repeat that Grid item — ${AI_PAGE_SECTION_REPEAT_SHAPE}.`\n}\n\n/** One pass's user turn: the page, the brief, the plan, this section and what is built above it. */\nexport function aiPageSectionPrompt(input: AiPageSectionPromptInput): string {\n const { job, plan, screen, index } = input\n const inputs = parseAiPageJobInputs(job.inputs)\n const type = typeof inputs === 'string' ? null : aiPageTypeDefinition(inputs.pageType)\n const section = screen.sections[index] as AiBuildPlanSection\n const places = section.uses.length\n ? ` It places ${section.uses.map((ref) => referenceOf(plan, ref)).join(', ')}.`\n : ''\n const items = section.items ? ` It shows ${section.items} items.` : ''\n const built = screen.sections.slice(0, index).map((entry, position) => `${position + 1}. ${entry.name}`)\n return [\n `Page: \"${screen.title}\" at ${screen.slug}`,\n ...(type ? [`Page type: ${type.purpose}`] : []),\n ...(input.record ? [aiPageRecordLine(input.record)] : []),\n aiJobBriefLine(job),\n ...aiPlanReferenceLines(plan),\n `Build section ${index + 1} of ${screen.sections.length}: \"${section.name}\".${places}${items}`,\n built.length\n ? `Already built, above it: ${built.join('; ')}. The page’s h1 is in section 1.`\n : 'Nothing is built yet: this section holds the page’s h1.',\n ...(input.reusableComponents === false\n ? [AI_PAGE_SECTION_INLINE_LINE, ...(section.items ? [AI_PAGE_SECTION_REPEAT_LINE] : [])]\n : section.items && section.uses.length\n ? [AI_PAGE_SECTION_INSTANCE_LINE]\n : []),\n `Keep this section to at most ${input.maxElements} elements.`,\n ].join('\\n')\n}\n\n/** A validated section: its nodes keyed as the page stores them, its root under the page root. */\nexport interface AiPageSection {\n rootId: string\n nodes: NodesMap\n /** The page's weight with the section added, as the doctrine measured it. */\n load: AiLoadEstimate | null\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/** A page with nothing on it yet: the document wrapper. */\nexport function aiEmptyPage(): NodesMap {\n return {\n [CANVAS_ROOT_ELEMENT_ID]: {\n $id: CANVAS_ROOT_ELEMENT_ID,\n componentId: 'div',\n parentId: null,\n nodes: [],\n },\n } as unknown as NodesMap\n}\n\n/**\n * The page with a section in its plan position: after every section with a\n * lower index and before every one with a higher, the page's other children\n * — whatever a member placed — kept where they are. A section already on the\n * page is replaced, never doubled.\n */\nexport function aiPageWithSection(\n page: NodesMap,\n section: AiPageSection,\n sectionIds: readonly string[],\n): NodesMap {\n const nodes: Record<string, Record<string, unknown>> = {}\n const previous = (page as unknown as Record<string, Record<string, unknown>>)[section.rootId]\n const replaced = new Set<string>()\n if (previous) {\n for (const { id } of walkTree({\n rootId: section.rootId,\n nodes: page as unknown as Record<string, AiDoctrineNode>,\n })) {\n replaced.add(id)\n }\n }\n for (const [id, node] of Object.entries(page)) {\n if (!replaced.has(id)) nodes[id] = { ...(node as unknown as Record<string, unknown>) }\n }\n const root = nodes[CANVAS_ROOT_ELEMENT_ID] ?? (aiEmptyPage()[CANVAS_ROOT_ELEMENT_ID] as unknown as Record<string, unknown>)\n const children = (Array.isArray(root['nodes']) ? (root['nodes'] as string[]) : []).filter(\n (id) => id !== section.rootId,\n )\n const order = new Map(sectionIds.map((id, position) => [id, position]))\n const at = order.get(section.rootId) ?? sectionIds.length\n const before = children.findIndex((id) => (order.get(id) ?? -1) > at)\n children.splice(before === -1 ? children.length : before, 0, section.rootId)\n nodes[CANVAS_ROOT_ELEMENT_ID] = { ...root, nodes: children }\n for (const [id, node] of Object.entries(section.nodes)) {\n nodes[id] = { ...(node as unknown as Record<string, unknown>) }\n }\n return nodes as unknown as NodesMap\n}\n\n/**\n * What a tree generated against this inventory may reference, the brand it is\n * held to, where the workspace keeps no reusable components or saved forms\n * (AGL-3030) that the page is built inline, the sections its plan names,\n * which a link may take a visitor to (AGL-3097), and the players its plan\n * lists for this page, the only ones it may embed (AGL-3433).\n */\nexport function aiPageCheckContext(\n inventory: AiSiteInventory | null,\n options: {\n reusableComponents?: boolean\n sections?: readonly string[]\n embeds?: AiDoctrineTreeContext['plannedEmbeds']\n /** A record template's tokens, which its links and images may name (AGL-3475). */\n recordTokens?: readonly string[]\n } = {},\n): AiDoctrineTreeContext {\n return {\n brand: inventory?.theme ? { colors: inventory.theme.colors, fonts: inventory.theme.fonts } : null,\n ...aiNodeTreeContextFromInventory(inventory),\n homeScreenIds: aiHomeScreenIds(inventory),\n ...(options.reusableComponents === false ? { reusableComponents: false } : {}),\n ...(options.sections?.length ? { pageSections: options.sections } : {}),\n ...(options.embeds?.length ? { plannedEmbeds: options.embeds } : {}),\n ...(options.recordTokens?.length ? { bindingTokens: options.recordTokens } : {}),\n }\n}\n\nexport interface AiPageSectionCheckInput {\n /** The page as stored so far; an empty page on the first pass. */\n page: NodesMap\n /** Every section root id of the plan, in order. */\n sectionIds: readonly string[]\n index: number\n context: AiDoctrineTreeContext\n /**\n * The plan line this pass answers for: what it places by inventory id, and\n * how many items it promised, which the answer is held to (AGL-3024).\n */\n section: Pick<AiBuildPlanSection, 'name' | 'uses' | 'items'>\n inventory: AiSiteInventory | null\n}\n\n/** The parts of an answer the violations name, as the model wrote them, capped as the doctrine caps a re-ask. */\nfunction offendingOf(raw: unknown, violations: readonly AiDoctrineViolation[]): { offending?: Record<string, unknown> } {\n const rawNodes = isRecord(raw) && isRecord(raw['nodes']) ? raw['nodes'] : {}\n const offending: Record<string, unknown> = {}\n let used = 2\n for (const id of new Set(violations.flatMap((violation) => violation.nodeIds ?? []))) {\n if (rawNodes[id] === undefined) continue\n const size = JSON.stringify(rawNodes[id]).length + id.length + 4\n if (used + size > AI_REASK_OFFENDING_MAX_CHARS) break\n offending[id] = rawNodes[id]\n used += size\n }\n return Object.keys(offending).length ? { offending } : {}\n}\n\n/**\n * The check a section's answer is held to: a repeated item written once drawn\n * into its copies (AGL-3053), then the palette validator on the section, with\n * any line it cut at its ceiling refused (AGL-3076), the doctrine's page check\n * on the page with the section added, and the plan line — every component it\n * names placed as an instance, every form bound by its id (rule 7), and as\n * many items as it promised (AGL-3024). Violations\n * name the section's own nodes by the ids the MODEL wrote, and a copy's nodes\n * by the item's; one that names only nodes an earlier pass stored belongs to\n * the page rather than to this answer, and is left to the last pass (AGL-3078).\n */\nexport function aiPageSectionCheck(input: AiPageSectionCheckInput): AiGenerationCheck<AiPageSection> {\n const sectionId = input.sectionIds[input.index]\n const components = new Set((input.inventory?.components ?? []).map((row) => row.id))\n const forms = new Set((input.inventory?.forms ?? []).map((row) => row.id))\n return (answer) => {\n const raw = aiAnswerTree(answer)\n // Before anything is drawn or dropped: a repeated item written and never\n // placed is content the section loses silently, and a child named and\n // never written ends the pass with nothing to answer (AGL-3143).\n const disagreeing = detectDisagreeingNodes(raw)\n if (disagreeing.length) {\n return { value: null, violations: disagreeing, ...offendingOf(raw, disagreeing) }\n }\n const drawn = expandAiRepeatedItems(raw, { inline: input.context.reusableComponents === false, noun: 'section' })\n if (drawn.ok === false) {\n return { value: null, violations: drawn.violations, ...offendingOf(raw, drawn.violations) }\n }\n // Every id below is the drawn tree's; a copy's leads back to the node the model wrote.\n const written = (id: string): string => drawn.sourceIds[id] ?? id\n const validated = validateAiNodeTree(drawn.tree, 'screen', input.context)\n if (validated.ok === false) {\n return {\n value: null,\n violations: [\n {\n rule: null,\n code: `tree-${validated.code}`,\n message: 'The answer could not be used as a section.',\n detail: validated.error,\n },\n ],\n }\n }\n const wrapper = validated.nodes[validated.rootId] as unknown as AiDoctrineNode | undefined\n const top = (wrapper?.nodes ?? []).filter((id) => validated.nodes[id])\n const sectionNode = top.length === 1 ? (validated.nodes[top[0]] as unknown as AiDoctrineNode) : null\n if (!sectionNode || sectionNode.componentId !== 'section') {\n return {\n value: null,\n violations: [\n {\n rule: null,\n code: 'section-shape',\n message: 'Answer with exactly one Section element inside the document wrapper.',\n },\n ],\n }\n }\n\n // The section's root takes its plan id, so a pass run again finds it, and\n // hangs under the page root; every other minted id stays as minted.\n const minted = top[0]\n const modelIds: Record<string, string> = Object.fromEntries(\n Object.entries(validated.sourceIds).map(([id, source]) => [id, written(source)]),\n )\n modelIds[sectionId] = written(validated.sourceIds[minted] ?? minted)\n const sectionNodes: Record<string, Record<string, unknown>> = {}\n for (const { id, node } of walkTree({\n rootId: minted,\n nodes: validated.nodes as unknown as Record<string, AiDoctrineNode>,\n })) {\n const stored = { ...(node as unknown as Record<string, unknown>) }\n if (id === minted) {\n stored['$id'] = sectionId\n stored['parentId'] = CANVAS_ROOT_ELEMENT_ID\n } else if (stored['parentId'] === minted) {\n stored['parentId'] = sectionId\n }\n sectionNodes[id === minted ? sectionId : id] = stored\n }\n const drawnNodes = isRecord(drawn.tree) && isRecord(drawn.tree['nodes']) ? drawn.tree['nodes'] : {}\n // What the model wrote of one of the section's nodes, by the id the page stores it under.\n const drawnIdOf = (stored: string): string =>\n validated.sourceIds[stored === sectionId ? minted : stored] ?? stored\n // A link that names a section of this page carries the platform's Scroll to\n // element interaction to that section's root, whose id is minted from the\n // plan before the section is built (AGL-3097).\n const sections = input.context.pageSections ?? []\n for (const [id, node] of Object.entries(sectionNodes)) {\n const props = isRecord(node['props']) ? node['props'] : {}\n if (!isAiLinkElement(node['componentId']) || props['screenId'] || props['href']) continue\n const target = aiPageLinkTarget(drawnNodes[drawnIdOf(id)], sections)\n if (target?.kind === 'section') {\n node['interactions'] = [aiPageScrollInteraction(input.sectionIds[target.section], sections[target.section])]\n }\n }\n const section: AiPageSection = { rootId: sectionId, nodes: sectionNodes as unknown as NodesMap, load: null }\n\n const page = aiPageWithSection(input.page, section, input.sectionIds)\n const own = new Set(Object.keys(sectionNodes))\n // The page check reads the section as the page stores it, where the palette\n // validator has already dropped what it could not read, such as a\n // `container` written as text or a link's `scrollTo`; what the model wrote\n // of each of the section's nodes is read from the section as it was drawn\n // (AGL-3078). A link the page already carries to one of its section roots\n // goes there, whichever pass wrote it (AGL-3097).\n const report = validateAiDoctrineTree({ rootId: CANVAS_ROOT_ELEMENT_ID, nodes: page }, 'page', {\n ...input.context,\n writtenNode: (id) => (own.has(id) ? drawnNodes[drawnIdOf(id)] : undefined),\n scrollTargetIds: input.sectionIds,\n })\n // The page check mints its ids afresh: its map leads back to the page's\n // ids, and the section's own lead back to the model's.\n const pageIds = report.tree?.sourceIds ?? {}\n const toModel = (id: string): string | null => {\n const stored = pageIds[id] ?? id\n return own.has(stored) ? (modelIds[stored] ?? stored) : null\n }\n // The page check reads the section as it is stored, where a line the\n // palette validator cut is already cut, so the cut is read from the\n // section's own validation, first (AGL-3076). Copies of one item name one\n // node the model wrote, once.\n //\n // A PASS ANSWERS ONLY FOR THE SECTION IT WROTE (AGL-3078). The page check\n // reads the whole page, so it also names nodes an earlier pass stored; the\n // model is not shown those and cannot mend them, so a finding that names\n // none of this section's nodes is not held against this answer. Dropping\n // it neither hides nor forgives it: the last pass runs the same check on\n // the finished page, where every node is the page's and the review names\n // each one and outlines it. Kept, such a finding would re-ask the model\n // for a node it never saw, refuse the same answer twice, and stop the page\n // with findings whose ids all filtered away — no node ids, and so no\n // outline either.\n const violations: AiDoctrineViolation[] = [\n ...detectCutLines(validated.repairs, drawnNodes, written),\n ...report.violations.flatMap((violation) => {\n if (!violation.nodeIds?.length) return [violation]\n const named = [...new Set(violation.nodeIds.map(toModel).filter((id): id is string => id !== null))]\n return named.length ? [{ ...violation, nodeIds: named }] : []\n }),\n ]\n\n const placed = new Set<string>()\n const bound = new Set<string>()\n for (const node of Object.values(sectionNodes)) {\n const props = isRecord(node['props']) ? node['props'] : {}\n if (node['componentId'] === REUSABLE_INSTANCE_COMPONENT_ID && typeof props[AI_INSTANCE_REF_PROP] === 'string') {\n placed.add(props[AI_INSTANCE_REF_PROP] as string)\n }\n if (node['componentId'] === 'form' && typeof props['formId'] === 'string') bound.add(props['formId'])\n }\n const missing = input.section.uses.filter(\n (ref) => (components.has(ref) && !placed.has(ref)) || (forms.has(ref) && !bound.has(ref)),\n )\n if (missing.length) {\n violations.push({\n rule: 7,\n code: 'plan-reuse-not-placed',\n message: `The confirmed plan places ${missing.join(', ')} in this section, and the section does not. Place each one by its id.`,\n })\n }\n // The count the plan promised, against the section as the page stores it\n // (AGL-3024): the prompt states it, and until now nothing read it back.\n violations.push(\n ...aiPlanItemCountViolations(input.section, {\n rootId: sectionId,\n nodes: sectionNodes as unknown as Record<string, AiDoctrineNode>,\n }),\n )\n\n return {\n value: { ...section, load: report.load },\n violations,\n ...offendingOf(raw, violations),\n }\n }\n}\n"],"names":["REUSABLE_INSTANCE_COMPONENT_ID","CANVAS_ROOT_ELEMENT_ID","aiPlanCreateFor","aiPageTypeDefinition","parseAiPageJobInputs","aiHomeScreenIds","AI_REASK_OFFENDING_MAX_CHARS","aiAnswerTree","aiNodeTreeContextFromInventory","detectCutLines","detectDisagreeingNodes","isAiLinkElement","validateAiDoctrineTree","walkTree","AI_INSTANCE_REF_PROP","validateAiNodeTree","aiPageLinkTarget","aiPageScrollInteraction","AI_PALETTE_CATALOG","AI_REPEAT_KEY","expandAiRepeatedItems","aiJobBriefLine","aiPlanReferenceLines","aiPlanItemCountViolations","aiPageSectionNodeId","jobId","index","slice","AI_PAGE_SECTION_TOOL","name","description","strict","inputSchema","type","additionalProperties","required","properties","tree","AI_JOB_PAGE_INSTRUCTIONS","text","join","screen","cacheBreakpoint","referenceOf","plan","ref","label","labels","aiPageRecordTemplate","record","inventory","created","row","datasets","find","entry","id","dataset","base","fields","map","fieldIds","aiPageRecordTokens","template","field","aiPageRecordLine","length","aiPageRecordNote","AI_PAGE_SECTION_INLINE_LINE","AI_PAGE_SECTION_REPEAT_SHAPE","AI_PAGE_SECTION_REPEAT_LINE","AI_PAGE_SECTION_INSTANCE_LINE","aiPageSectionSmaller","input","reusableComponents","maxElements","aiPageSectionPrompt","job","inputs","pageType","section","sections","places","uses","items","built","position","title","slug","purpose","isRecord","value","Array","isArray","aiEmptyPage","$id","componentId","parentId","nodes","aiPageWithSection","page","sectionIds","order","previous","rootId","replaced","Set","add","node","Object","entries","has","root","children","filter","Map","at","get","before","findIndex","splice","aiPageCheckContext","options","brand","theme","colors","fonts","homeScreenIds","pageSections","embeds","plannedEmbeds","recordTokens","bindingTokens","offendingOf","raw","violations","rawNodes","offending","used","flatMap","violation","nodeIds","undefined","size","JSON","stringify","keys","aiPageSectionCheck","sectionId","components","forms","answer","validated","report","disagreeing","drawn","inline","context","noun","ok","written","sourceIds","rule","code","message","detail","error","wrapper","top","sectionNode","minted","modelIds","fromEntries","source","sectionNodes","stored","drawnNodes","drawnIdOf","props","target","kind","load","own","writtenNode","scrollTargetIds","pageIds","toModel","repairs","named","placed","bound","values","missing","push"],"mappings":"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"}
@@ -34,7 +34,7 @@ import { aiJobDraftId } from "./ai-job-draft-ids.js";
34
34
  import { aiDraftAdmissionRefusal, aiDraftAllowanceRefusal, aiSiteSubdomain, readAiDraft, readAiDraftNodes, readAiPlanCapabilities, updateAiDraftNodes, writeAiDraft, writeAiDraftScreenSeo } from "./ai-job-drafts.js";
35
35
  import { aiBracketedFactsNote, aiConfirmedPlan, aiDoctrineReview, aiGenerationSpent, aiLimitReview, aiModelNodeIds, aiUnspentOutcome } from "./ai-job-generation.js";
36
36
  import { AI_JOB_PAGE_SECTION_MAX_TOKENS, AI_JOB_PAGE_SECTION_TOKENS, AI_JOB_PAGE_STEP_BUDGET, AI_JOB_PAGE_STEP_MINIMUM_MS, aiJobPageSectionMaxTokens } from "./ai-job-page-budget.js";
37
- import { AI_JOB_PAGE_INSTRUCTIONS, AI_PAGE_SECTION_TOOL, aiEmptyPage, aiPageCheckContext, aiPageSectionCheck, aiPageSectionNodeId, aiPageSectionPrompt, aiPageSectionSmaller, aiPageWithSection } from "./ai-job-page-sections.js";
37
+ import { AI_JOB_PAGE_INSTRUCTIONS, AI_PAGE_SECTION_TOOL, aiEmptyPage, aiPageCheckContext, aiPageRecordNote, aiPageRecordTemplate, aiPageRecordTokens, aiPageSectionCheck, aiPageSectionNodeId, aiPageSectionPrompt, aiPageSectionSmaller, aiPageWithSection } from "./ai-job-page-sections.js";
38
38
  import { AI_PLAN_ITEMS_MIN, aiPlanCopiedPageViolations } from "./ai-job-plan-conformance.js";
39
39
  import { aiCreationUnit, aiRunJobUnit, aiSiteBuiltRefs, aiSitePendingUnits, aiSiteUnitJob } from "./ai-job-site-step.js";
40
40
  import { aiJobStepRunMinimumMs, aiJobStepRunnerFor, registerAiJobStep, registerAiJobStepPasses } from "./ai-jobs.js";
@@ -366,6 +366,10 @@ export function createAiJobPageStep(deps = {}) {
366
366
  if (written == null ? void 0 : written.deleted) return _extends({}, aiUnspentOutcome(model), {
367
367
  failure: AI_JOB_PAGE_DELETED_COPY
368
368
  });
369
+ // A record template's copy binds its dataset's fields, and its draft says
370
+ // where the member saves the binding (AGL-3475).
371
+ const record = aiPageRecordTemplate(screen.record, inventory, plan);
372
+ const recordNote = record ? aiPageRecordNote(record) : null;
369
373
  // The plan starts from a copy of a screen the site has (rule 15): the copy
370
374
  // is the draft — unrouted, as every copy is — and nothing is generated.
371
375
  // Only a page is a page's start: a collection entry template's copy stays
@@ -424,7 +428,7 @@ export function createAiJobPageStep(deps = {}) {
424
428
  versionId: copy.versionId,
425
429
  name: copy.name,
426
430
  hostSubdomain
427
- })
431
+ }, null, recordNote)
428
432
  ]
429
433
  });
430
434
  }
@@ -456,7 +460,8 @@ export function createAiJobPageStep(deps = {}) {
456
460
  const context = aiPageCheckContext(inventory, {
457
461
  reusableComponents,
458
462
  sections,
459
- embeds
463
+ embeds,
464
+ recordTokens: aiPageRecordTokens(record)
460
465
  });
461
466
  // ── The last pass: the whole page, its listing, and the draft reported ──
462
467
  if (index === -1 && written) {
@@ -469,13 +474,16 @@ export function createAiJobPageStep(deps = {}) {
469
474
  scrollTargetIds: sectionIds
470
475
  }));
471
476
  // The facts the brief did not give, on the page or in the defaults its components show (AGL-3056).
472
- const note = aiBracketedFactsNote({
473
- tree: {
474
- rootId: CANVAS_ROOT_ELEMENT_ID,
475
- nodes: page
476
- },
477
- inventory
478
- });
477
+ const note = [
478
+ aiBracketedFactsNote({
479
+ tree: {
480
+ rootId: CANVAS_ROOT_ELEMENT_ID,
481
+ nodes: page
482
+ },
483
+ inventory
484
+ }),
485
+ recordNote
486
+ ].filter(Boolean).join(' ') || null;
479
487
  // The page's own draft, reported once however many times the last pass
480
488
  // runs (AGL-3143): a refused pass reports it and a later one that
481
489
  // passes does not report it twice.
@@ -607,7 +615,8 @@ export function createAiJobPageStep(deps = {}) {
607
615
  screen,
608
616
  index,
609
617
  maxElements,
610
- reusableComponents
618
+ reusableComponents,
619
+ record
611
620
  })
612
621
  }
613
622
  ],
@@ -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 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\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 })],\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, { reusableComponents, sections, embeds })\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 = aiBracketedFactsNote({\n tree: { rootId: CANVAS_ROOT_ELEMENT_ID, nodes: page as unknown as AiDoctrineTree['nodes'] },\n inventory,\n })\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 }),\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","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","sourceReview","duplicateOf","sourceShort","row","template","copy","sourceId","uid","createdBy","attemptKey","ok","review","page","findIndex","reusableComponents","embeds","report","scrollTargetIds","tree","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 { 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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"}
@@ -396,8 +396,12 @@ export function createAiJobPlanStep(deps = {}) {
396
396
  // — this job's plan is proposed to this member, whatever the last one did
397
397
  // with theirs.
398
398
  const resolved = model != null ? model : aiModelForStep('job.plan');
399
- // A brief that asks for a video is offered the plan's list of players (AGL-3433).
400
- const tool = aiBuildPlanToolFor(job.brief);
399
+ // A brief that asks for a video is offered the plan's list of players
400
+ // (AGL-3433), and a job that may bind a dataset a record template on
401
+ // each screen (AGL-3475).
402
+ const tool = aiBuildPlanToolFor(job.brief, {
403
+ records: Boolean(inventory == null ? void 0 : inventory.datasets.length) || (capabilities == null ? void 0 : capabilities.create.dataset.allowed) === true
404
+ });
401
405
  const key = aiJobPlanKey({
402
406
  job,
403
407
  prompt,