@aglyn/plugins-ai 1.0.0-beta.291 → 1.0.0-beta.293

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 (149) hide show
  1. package/package.json +17 -17
  2. package/src/lib/components/ai-site-import-job.component.js +5 -4
  3. package/src/lib/components/ai-site-import-job.component.js.map +1 -1
  4. package/src/lib/components/ai-site-import-start.component.js +3 -3
  5. package/src/lib/components/ai-site-import-start.component.js.map +1 -1
  6. package/src/lib/components/ai-site-start-card.component.d.ts +1 -1
  7. package/src/lib/components/ai-site-start-card.component.js +129 -29
  8. package/src/lib/components/ai-site-start-card.component.js.map +1 -1
  9. package/src/lib/components/ai-site-start-steps.component.d.ts +15 -3
  10. package/src/lib/components/ai-site-start-steps.component.js +42 -25
  11. package/src/lib/components/ai-site-start-steps.component.js.map +1 -1
  12. package/src/lib/components/ai-site-style-previews.d.ts +17 -0
  13. package/src/lib/components/ai-site-style-previews.js +26 -95
  14. package/src/lib/components/ai-site-style-previews.js.map +1 -1
  15. package/src/lib/declarations.server.js +22 -0
  16. package/src/lib/declarations.server.js.map +1 -1
  17. package/src/lib/jobs/ai-job-import-step.d.ts +21 -2
  18. package/src/lib/jobs/ai-job-import-step.js +179 -49
  19. package/src/lib/jobs/ai-job-import-step.js.map +1 -1
  20. package/src/lib/jobs/ai-job-layout-language.d.ts +27 -3
  21. package/src/lib/jobs/ai-job-layout-language.js +156 -113
  22. package/src/lib/jobs/ai-job-layout-language.js.map +1 -1
  23. package/src/lib/jobs/ai-job-layout-step.js +56 -17
  24. package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
  25. package/src/lib/jobs/ai-job-page-language.d.ts +25 -15
  26. package/src/lib/jobs/ai-job-page-language.js +120 -66
  27. package/src/lib/jobs/ai-job-page-language.js.map +1 -1
  28. package/src/lib/jobs/ai-job-page-step.js +27 -7
  29. package/src/lib/jobs/ai-job-page-step.js.map +1 -1
  30. package/src/lib/jobs/ai-job-site-datasets.d.ts +12 -4
  31. package/src/lib/jobs/ai-job-site-datasets.js +36 -16
  32. package/src/lib/jobs/ai-job-site-datasets.js.map +1 -1
  33. package/src/lib/jobs/ai-jobs.js +6 -2
  34. package/src/lib/jobs/ai-jobs.js.map +1 -1
  35. package/src/lib/jobs/ai-layout-stock-photos.d.ts +50 -1
  36. package/src/lib/jobs/ai-layout-stock-photos.js +586 -35
  37. package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
  38. package/src/lib/jobs/ai-site-import-compile.d.ts +147 -0
  39. package/src/lib/jobs/ai-site-import-compile.js +1143 -0
  40. package/src/lib/jobs/ai-site-import-compile.js.map +1 -0
  41. package/src/lib/jobs/ai-site-import-document.d.ts +9 -36
  42. package/src/lib/jobs/ai-site-import-document.js +23 -489
  43. package/src/lib/jobs/ai-site-import-document.js.map +1 -1
  44. package/src/lib/jobs/ai-site-import-look.d.ts +54 -7
  45. package/src/lib/jobs/ai-site-import-look.js +163 -52
  46. package/src/lib/jobs/ai-site-import-look.js.map +1 -1
  47. package/src/lib/jobs/ai-site-import-publish.d.ts +7 -1
  48. package/src/lib/jobs/ai-site-import-publish.js +19 -5
  49. package/src/lib/jobs/ai-site-import-publish.js.map +1 -1
  50. package/src/lib/jobs/ai-site-late-photos.js +4 -2
  51. package/src/lib/jobs/ai-site-late-photos.js.map +1 -1
  52. package/src/lib/jobs/ai-site-publish.d.ts +10 -1
  53. package/src/lib/jobs/ai-site-publish.js +8 -3
  54. package/src/lib/jobs/ai-site-publish.js.map +1 -1
  55. package/src/lib/jobs/fixtures/ai-stock-photo-agl4092-1029-recordings.d.ts +23 -0
  56. package/src/lib/jobs/fixtures/ai-stock-photo-agl4092-1029-recordings.js +677 -0
  57. package/src/lib/jobs/fixtures/ai-stock-photo-agl4092-1029-recordings.js.map +1 -0
  58. package/src/lib/layout-language/ai-layout-compiler.d.ts +26 -0
  59. package/src/lib/layout-language/ai-layout-compiler.js +677 -33
  60. package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
  61. package/src/lib/layout-language/ai-layout-frame-hollows.js +5 -1
  62. package/src/lib/layout-language/ai-layout-frame-hollows.js.map +1 -1
  63. package/src/lib/layout-language/ai-layout-frame-source.d.ts +196 -0
  64. package/src/lib/layout-language/ai-layout-frame-source.js +726 -0
  65. package/src/lib/layout-language/ai-layout-frame-source.js.map +1 -0
  66. package/src/lib/layout-language/ai-layout-frame.d.ts +22 -0
  67. package/src/lib/layout-language/ai-layout-frame.js +121 -29
  68. package/src/lib/layout-language/ai-layout-frame.js.map +1 -1
  69. package/src/lib/layout-language/ai-layout-language.d.ts +103 -37
  70. package/src/lib/layout-language/ai-layout-language.js +85 -120
  71. package/src/lib/layout-language/ai-layout-language.js.map +1 -1
  72. package/src/lib/model/ai-credit-estimate.d.ts +11 -7
  73. package/src/lib/model/ai-credit-estimate.js +11 -7
  74. package/src/lib/model/ai-credit-estimate.js.map +1 -1
  75. package/src/lib/model/ai-site-import.d.ts +307 -13
  76. package/src/lib/model/ai-site-import.js +227 -19
  77. package/src/lib/model/ai-site-import.js.map +1 -1
  78. package/src/lib/model/ai-site-look.d.ts +65 -1
  79. package/src/lib/model/ai-site-look.js +141 -23
  80. package/src/lib/model/ai-site-look.js.map +1 -1
  81. package/src/lib/model/ai-site-photos.d.ts +2 -0
  82. package/src/lib/model/ai-site-photos.js.map +1 -1
  83. package/src/lib/model/ai-site-start-steps.d.ts +4 -3
  84. package/src/lib/model/ai-site-start-steps.js +11 -3
  85. package/src/lib/model/ai-site-start-steps.js.map +1 -1
  86. package/src/lib/model/ai-site-start.d.ts +23 -2
  87. package/src/lib/model/ai-site-start.js +25 -3
  88. package/src/lib/model/ai-site-start.js.map +1 -1
  89. package/src/lib/providers/routing.d.ts +11 -0
  90. package/src/lib/providers/routing.js +23 -0
  91. package/src/lib/providers/routing.js.map +1 -1
  92. package/src/lib/runtime/ai-doctrine-validators.d.ts +4 -2
  93. package/src/lib/runtime/ai-doctrine-validators.js +7 -2
  94. package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
  95. package/src/lib/runtime/ai-icon-library.d.ts +4 -0
  96. package/src/lib/runtime/ai-icon-library.js +4 -0
  97. package/src/lib/runtime/ai-icon-library.js.map +1 -1
  98. package/src/lib/runtime/ai-node-tree.js +5 -1
  99. package/src/lib/runtime/ai-node-tree.js.map +1 -1
  100. package/src/lib/runtime/ai-palette.d.ts +2 -0
  101. package/src/lib/runtime/ai-palette.generated.js +19 -3
  102. package/src/lib/runtime/ai-palette.generated.js.map +1 -1
  103. package/src/lib/runtime/ai-palette.js +2 -1
  104. package/src/lib/runtime/ai-palette.js.map +1 -1
  105. package/src/lib/site-import/fixtures/site-layout-recordings.d.ts +50 -0
  106. package/src/lib/site-import/fixtures/site-layout-recordings.js +86 -0
  107. package/src/lib/site-import/fixtures/site-layout-recordings.js.map +1 -0
  108. package/src/lib/site-import/site-import-crawl.d.ts +18 -1
  109. package/src/lib/site-import/site-import-crawl.js +70 -10
  110. package/src/lib/site-import/site-import-crawl.js.map +1 -1
  111. package/src/lib/site-import/site-import-css.d.ts +110 -0
  112. package/src/lib/site-import/site-import-css.js +826 -0
  113. package/src/lib/site-import/site-import-css.js.map +1 -0
  114. package/src/lib/site-import/site-import-html.d.ts +116 -1
  115. package/src/lib/site-import/site-import-html.js +538 -46
  116. package/src/lib/site-import/site-import-html.js.map +1 -1
  117. package/src/lib/site-import/site-import-layout.d.ts +79 -0
  118. package/src/lib/site-import/site-import-layout.js +1608 -0
  119. package/src/lib/site-import/site-import-layout.js.map +1 -0
  120. package/src/lib/site-import/site-import-nav.d.ts +40 -0
  121. package/src/lib/site-import/site-import-nav.js +437 -0
  122. package/src/lib/site-import/site-import-nav.js.map +1 -0
  123. package/src/lib/site-import/site-import-page.d.ts +8 -1
  124. package/src/lib/site-import/site-import-page.js +24 -2
  125. package/src/lib/site-import/site-import-page.js.map +1 -1
  126. package/src/lib/assets/site-styles/beauty.webp +0 -0
  127. package/src/lib/assets/site-styles/blog.webp +0 -0
  128. package/src/lib/assets/site-styles/business.webp +0 -0
  129. package/src/lib/assets/site-styles/coming-soon.webp +0 -0
  130. package/src/lib/assets/site-styles/education.webp +0 -0
  131. package/src/lib/assets/site-styles/events.webp +0 -0
  132. package/src/lib/assets/site-styles/fitness.webp +0 -0
  133. package/src/lib/assets/site-styles/landing.webp +0 -0
  134. package/src/lib/assets/site-styles/music.webp +0 -0
  135. package/src/lib/assets/site-styles/nonprofit.webp +0 -0
  136. package/src/lib/assets/site-styles/personal.webp +0 -0
  137. package/src/lib/assets/site-styles/photography.webp +0 -0
  138. package/src/lib/assets/site-styles/portfolio.webp +0 -0
  139. package/src/lib/assets/site-styles/professional.webp +0 -0
  140. package/src/lib/assets/site-styles/realestate.webp +0 -0
  141. package/src/lib/assets/site-styles/restaurant.webp +0 -0
  142. package/src/lib/assets/site-styles/store.webp +0 -0
  143. package/src/lib/assets/site-styles/studio.webp +0 -0
  144. package/src/lib/assets/site-styles/trades.webp +0 -0
  145. package/src/lib/assets/site-styles/wellness.webp +0 -0
  146. package/src/lib/assets/site-styles/yoga.webp +0 -0
  147. package/src/lib/site-import/site-import-review.d.ts +0 -40
  148. package/src/lib/site-import/site-import-review.js +0 -149
  149. package/src/lib/site-import/site-import-review.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-layout-step.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { throwIfAiJobCanceled } from './ai-job-cancel'\nimport { aiSiteStyleTokens } from '../model/ai-site-kinds'\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 type { AiJob, AiJobOutput, AiJobPlan, AiJobReview } from '../model/ai-jobs.types'\nimport { aiHomeScreenIds, type AiSiteInventory } from '../model/ai-site-inventory'\nimport { AI_STEP_TIERS } from '../providers/catalog'\nimport { AI_ROUTING_TABLE, aiModelForStep } from '../providers/routing'\nimport {\n aiDoctrineTreeTool,\n runValidatedGeneration,\n type AiValidatedTree,\n} from '../runtime/ai-doctrine'\nimport {\n walkTree,\n type AiDoctrineNode,\n type AiDoctrineTree,\n type AiDoctrineViolation,\n} from '../runtime/ai-doctrine-validators'\nimport type { AiLoadEstimate } from '../runtime/ai-palette'\nimport type { AiSystemBlock } from '../runtime/ai-runtime'\nimport { readSiteInventory } from '../runtime/site-inventory'\nimport { registerAiJobAdmission, type AiJobAdmission } from './ai-job-admission'\nimport {\n aiBuildingPlanCreations,\n aiJobPlanCreationsRefusal,\n aiPlanCreationsRunMinimumMs,\n} from './ai-job-plan-creations'\nimport { aiJobDraftId, aiOriginJobId } from './ai-job-draft-ids'\nimport {\n aiDraftAdmissionRefusal,\n aiDraftAllowanceRefusal,\n aiSiteSubdomain,\n readAiDraft,\n readAiDraftNodes,\n writeAiDraft,\n type AiDraftRecord,\n} from './ai-job-drafts'\nimport {\n aiBracketedFactsNote,\n aiCopySourceLines,\n aiConfirmedPlan,\n aiDoctrineReview,\n aiGenerationSpent,\n aiJobBriefLine,\n aiLimitReview,\n aiModelNodeIds,\n aiPlacedComponentIds,\n aiPlanCreation,\n aiPlanReferenceLines,\n aiPlanReuseViolations,\n aiUnspentOutcome,\n} from './ai-job-generation'\nimport { aiPlanRegionViolations, aiPlannedLayoutRegions } from './ai-job-plan-conformance'\nimport { aiLayoutSitePages, aiLayoutSitePagesLines, aiLayoutWithSitePages } from './ai-job-layout-site-pages'\nimport { aiLayoutInventedContactViolations, aiLayoutWithSiteName } from './ai-layout-site-facts'\nimport { aiLayoutWithFullHeight } from './ai-layout-full-height'\nimport type { AiJobStepRunner } from './ai-job-text-step'\nimport { AI_LAYOUT_FRAME_TOOL } from '../layout-language/ai-layout-language'\nimport {\n AI_JOB_LAYOUT_LANGUAGE_BUDGET,\n AI_LAYOUT_FRAME_KIND,\n aiJobLayoutLanguageInstructions,\n aiLayoutFrameCheck,\n aiLayoutFramePrompt,\n aiLayoutFrameTargets,\n aiLayoutHomeId,\n aiLayoutNavPages,\n aiLayoutSiteBrandOf,\n aiLayoutSiteFactsText,\n aiLayoutSiteName,\n type AiLayoutFrameBuilt,\n} from './ai-job-layout-language'\nimport { AI_LAYOUT_LANGUAGE_THINKING, aiJobUsesLayoutLanguage, aiLayoutDesignOf } from './ai-job-page-language'\nimport { aiJobStepBudget } from './ai-job-budget'\nimport { registerAiJobStep } from './ai-jobs'\nimport { aiStoreFrameLinksOf } from '../model/ai-site-store-pages'\n\n/**\n * The layout step (AGL-2909): the generation step of a `layout` job, run once\n * a member confirmed the job's plan.\n *\n * It builds ONE shared layout — a header with the site's navigation, the\n * Layout Slot each page renders in, a footer — through\n * `runValidatedGeneration('layout', …)`, so every building rule holds on the\n * tree before it is kept, and adds three checks of its own through `extend`: a\n * component the confirmed plan reuses is placed (rule 7), every region the\n * plan gives the layout is built (AGL-3024), and a navigation or menu\n * component the site already has is placed rather than drawn again (rule 1).\n *\n * The layout lands as a new draft (`ai-job-drafts.ts`): no screen uses it and\n * no layout nests it, so nothing on the live site changes until a member\n * assigns it. A plan that starts from a copy of a layout the site has gets\n * that copy, through the platform's duplicate module, and nothing generated —\n * so the copy is read back and held to the plan's regions the same way\n * (`aiCopiedLayoutReview`), because a branch that generates nothing is the one\n * branch where a promise can go unmet with no model to answer for it. A\n * source that already lacks a region the plan gives the layout is not copied\n * at all: the layout is built from it (AGL-3143 §14).\n *\n * A generation step runs on the job beat, never at an inline door: it\n * registers the least time its lookup rounds, its answer and its re-ask need\n * (AGL-3035), runs without extended thinking — the plan was reasoned out and\n * confirmed before it — and asks for no more of a tighter ceiling than the\n * doctrine's default for a layout than fits that time on the model it runs.\n */\n\n/** The longest answer a layout may run to; the tree is held to the layout budget either way. */\nexport const AI_JOB_LAYOUT_MAX_TOKENS = AI_ROUTING_TABLE['job.layout'].maxTokens\n\n/**\n * The layout step's time (AGL-3035, AGL-3036): its two inventory-lookup\n * rounds, its answer and its re-ask at the routing table's ceiling on the tier\n * `job.layout` is served from, fitted to what a beat can give a step.\n */\nexport const AI_JOB_LAYOUT_STEP_BUDGET = aiJobStepBudget({\n tier: AI_STEP_TIERS['job.layout'],\n maxTokens: AI_JOB_LAYOUT_MAX_TOKENS,\n})\n\n/** The least time one layout step needs before it starts. */\nexport const AI_JOB_LAYOUT_STEP_MINIMUM_MS = AI_JOB_LAYOUT_STEP_BUDGET.minimumMs\n\n/** A layout's name when the plan names none. */\nexport const AI_JOB_LAYOUT_DEFAULT_NAME = 'Site layout'\n\n/** The layout step's own instructions, cached after the doctrine. */\nexport const AI_JOB_LAYOUT_INSTRUCTIONS: readonly AiSystemBlock[] = [\n {\n text: [\n 'You build one shared layout for a website: the frame every page of the site renders inside. Answer with submit_layout: the whole layout as one flat node map.',\n 'A layout is a header, one Layout Slot and a footer, top to bottom. The header is an App Bar (muiAppBar) holding a Toolbar Content (muiToolbar) with the site’s name as text and its navigation. The footer is a Section (section) whose element is footer.',\n 'Navigation links the site’s own pages by id: a Page Link (muiScreenLink) whose screenId is a page id from the site inventory, in the order the brief and the confirmed plan give, seven at most in the header. Never invent a page id, and never link a page by its address.',\n 'When the site inventory lists a component whose name says it is navigation, a menu or a mega menu, place it in the header as an instance instead of building the navigation again. Place every component the confirmed plan reuses.',\n 'The Layout Slot (layoutSlot) is where each page renders and is the page’s main landmark: leave its component unset or main, and put nothing inside it. A layout carries no h1, because every page brings its own.',\n ].join('\\n'),\n },\n]\n\n/**\n * The layout job as the generation's user turn: the name, the brief, the\n * confirmed plan, and the layout a short copy would have started from\n * (`aiCopySourceLines`, AGL-3143 §14).\n */\nexport function aiJobLayoutPrompt(\n job: Pick<AiJob, 'brief'> & Partial<Pick<AiJob, 'inputs'>>,\n plan: AiJobPlan | null,\n name: string,\n source: readonly string[] = [],\n): string {\n return [\n `Layout name: ${name}`,\n aiJobBriefLine(job),\n ...aiPlanReferenceLines(plan),\n // A scaffold's layout is built before its pages, whose navigation the platform writes (AGL-3596).\n ...aiLayoutSitePagesLines(aiLayoutSitePages(job.inputs)),\n ...source,\n ].join('\\n')\n}\n\n/** A component name that says it is the site's navigation; a footer's links are not. */\nconst NAVIGATION_NAME = /\\b(?:nav|navigation|navbar|menu|mega ?menu)\\b/i\nconst FOOTER_NAME = /\\bfooter\\b/i\n\n/** A node that links somewhere: a Screen Link, or a Button pointed at a screen or an address. */\nfunction isLink(node: AiDoctrineNode): boolean {\n if (node.componentId === 'muiScreenLink') return true\n return (\n node.componentId === 'muiButton' &&\n (typeof node.props?.['screenId'] === 'string' || typeof node.props?.['href'] === 'string')\n )\n}\n\n/**\n * The layout door's checks on a tree the doctrine admitted: every component\n * the confirmed plan reuses is placed, every region the confirmed plan gives\n * the layout is built (AGL-3024), and navigation the site already keeps as a\n * component is placed rather than drawn again in the header.\n */\nexport function aiLayoutReuseCheck(\n inventory: AiSiteInventory | null,\n plan: AiJobPlan | null,\n): (tree: AiValidatedTree) => AiDoctrineViolation[] {\n const components = inventory?.components ?? []\n const navigation = components.filter(\n (component) => NAVIGATION_NAME.test(component.name) && !FOOTER_NAME.test(component.name),\n )\n return (tree) => {\n const placed = aiPlacedComponentIds(tree)\n const violations: AiDoctrineViolation[] = [\n ...aiPlanReuseViolations(inventory, plan, placed, 'layout'),\n ...aiPlanRegionViolations(plan, {\n rootId: tree.rootId,\n nodes: tree.nodes as unknown as Record<string, AiDoctrineNode>,\n }),\n ]\n if (navigation.length && !navigation.some((component) => placed.has(component.id))) {\n const visits = walkTree({\n rootId: tree.rootId,\n nodes: tree.nodes as unknown as Record<string, AiDoctrineNode>,\n })\n const slot = visits.findIndex(({ node }) => node.componentId === 'layoutSlot')\n const header = slot === -1 ? visits : visits.slice(0, slot)\n const links = header.filter(({ node }) => isLink(node)).map(({ id }) => id)\n if (links.length >= 2) {\n violations.push({\n rule: 1,\n code: 'navigation-rebuilt',\n message: `The site already has the \"${navigation[0].name}\" component. Place it in the header instead of building the navigation again.`,\n nodeIds: aiModelNodeIds(links, tree.sourceIds),\n })\n }\n }\n return violations\n }\n}\n\n/**\n * Every check the layout door adds to the doctrine's: the reuse and region\n * checks above, and no contact detail the brief does not give (AGL-3596).\n */\nexport function aiLayoutChecks(\n inventory: AiSiteInventory | null,\n plan: AiJobPlan | null,\n brief: string,\n): (tree: AiValidatedTree) => AiDoctrineViolation[] {\n const reuse = aiLayoutReuseCheck(inventory, plan)\n return (tree) => [\n ...reuse(tree),\n ...aiLayoutInventedContactViolations(\n { rootId: tree.rootId, nodes: tree.nodes as unknown as Record<string, AiDoctrineNode> },\n brief,\n ).map((violation) => ({ ...violation, nodeIds: aiModelNodeIds(violation.nodeIds ?? [], tree.sourceIds) })),\n ]\n}\n\n/**\n * The regions the confirmed plan gives the layout, against the layout a COPY\n * actually produced (AGL-3024); `null` when the copy keeps the plan, or when\n * the plan promised no region to keep.\n *\n * The duplicate branch generates nothing, so it is the one place a plan's\n * promise can go unmet without a model ever answering for it. A copy that\n * cannot be read — deleted between the write and this read, or stored under a\n * root this check does not know — settles nothing, and a promise this step\n * cannot settle is reviewed by a person rather than reported as kept.\n */\nexport async function aiCopiedLayoutReview(\n firestore: FirebaseFirestore.Firestore,\n input: { hostId: string; id: string; name: string; plan: AiJobPlan | null },\n): Promise<AiJobReview | null> {\n const planned = aiPlannedLayoutRegions(input.plan)\n if (!planned.regions.length && !planned.unreadable.length) return null\n const stored = await readAiDraftNodes(firestore, {\n kind: 'layout',\n hostId: input.hostId,\n id: input.id,\n })\n const nodes = (stored?.nodes ?? {}) as unknown as Record<string, AiDoctrineNode>\n if (!nodes[CANVAS_ROOT_ELEMENT_ID]) {\n return {\n reason: 'doctrine',\n message: `\"${input.name}\" was copied from the layout the plan names, and this job could not read the copy back to check it has the regions the plan gives it. Open the layout and check it before you put a page in it.`,\n findings: [],\n }\n }\n const violations = aiPlanRegionViolations(input.plan, { rootId: CANVAS_ROOT_ELEMENT_ID, nodes })\n if (!violations.length) return null\n return aiDoctrineReview({\n message: `\"${input.name}\" is a copy of the layout the plan names, and a copy is all it is: ${violations[0].message}`,\n violations,\n })\n}\n\n/**\n * A layout job is admitted for a site of the job's own org with a shared\n * layout to spare, and confirmed only for a plan whose other creations it\n * builds (AGL-3143 §15).\n */\nexport const aiLayoutJobAdmission: AiJobAdmission = (context) =>\n aiDraftAdmissionRefusal(context.firestore, {\n orgId: context.orgId,\n hostId: context.hostId,\n kind: 'layout',\n org: context.org,\n ownCheck: (hostId) => aiJobPlanCreationsRefusal('layout', context, hostId),\n })\n\nexport interface AiJobLayoutStepDeps {\n /** The inventory reader; specs hand in a fake. */\n readInventory?: typeof readSiteInventory\n /** The platform's duplicate module, for a plan that starts from a copy. */\n duplicate?: typeof duplicateResource\n}\n\nexport function createAiJobLayoutStep(deps: AiJobLayoutStepDeps = {}): AiJobStepRunner {\n const readInventory = deps.readInventory ?? readSiteInventory\n const duplicate = deps.duplicate ?? duplicateResource\n return async ({ job, now, signal, firestore, modelFor }) => {\n const hostId = job.hostId\n if (!hostId) throw new Error('a layout job names no site, and its admission refuses one')\n const output = (draft: AiDraftRecord, load?: AiLoadEstimate | null, note?: string | null): AiJobOutput => ({\n resource: 'layout',\n id: draft.id,\n versionId: draft.versionId,\n hostId,\n hostSubdomain: draft.hostSubdomain,\n label: draft.name,\n ...(load ? { load } : {}),\n ...(note ? { note } : {}),\n })\n // The switch's answer for this job, else the routing table's (AGL-2942).\n const model = modelFor?.('job.layout') ?? aiModelForStep('job.layout')\n\n // A run cut off after its draft was written reports that draft, found by the id the job recorded.\n const draftId = aiJobDraftId(job, 'layout')\n const written = await readAiDraft(firestore, { kind: 'layout', hostId, id: draftId })\n if (written) return aiUnspentOutcome(model, { outputs: [output(written)] })\n\n const [inventory, orgSnapshot] = await Promise.all([\n readInventory(job.orgId, hostId, { firestore }),\n firestore.collection('orgs').doc(job.orgId).get(),\n ])\n const org = (orgSnapshot.data() ?? null) as Partial<AglynOrgBilling> | null\n const plan = aiConfirmedPlan(job)\n const creation = aiPlanCreation(plan, 'layout')\n const name = creation?.name || AI_JOB_LAYOUT_DEFAULT_NAME\n\n // The plan starts from a copy of a layout the site has (rule 15): the copy\n // is the draft, and nothing is generated. A source gone since the plan\n // was made is built from the brief instead. A source that cannot keep the\n // plan's regions is built FROM instead (AGL-3143 §14), as the page step\n // builds from a short source: copying it could only end in the review\n // below, and Try again would copy the same thing. Only a source read and\n // found short diverts; one this step cannot read still copies, and the\n // review after the copy answers for it.\n const source = creation?.duplicateOf\n ? inventory.layouts.find((layout) => layout.id === creation.duplicateOf)\n : undefined\n const sourceReview = source\n ? await aiCopiedLayoutReview(firestore, { hostId, id: source.id, plan, name: source.name })\n : null\n const sourceShort = Boolean(sourceReview?.findings.length)\n const sourceTree =\n source && sourceShort ? await readAiDraftNodes(firestore, { kind: 'layout', hostId, id: source.id }) : null\n const sourceLines =\n source && sourceShort ? aiCopySourceLines('layout', source.name, sourceTree?.nodes ?? null) : []\n if (creation?.duplicateOf && source && !sourceShort) {\n const copy = await duplicate('layout', {\n orgId: job.orgId,\n hostId,\n sourceId: creation.duplicateOf,\n name,\n uid: job.createdBy,\n org: org as Record<string, unknown> | null,\n // The same claim the page step makes (AGL-3024): a copy core mints is\n // written under core's own id, never `draftId`, so a step that runs\n // again — a pass resumed, a run cut off after the copy — would mint a\n // second. The job's recorded id makes core replay the one it made.\n attemptKey: draftId,\n })\n if (copy.ok) {\n // A copy is not a build (AGL-3024). This branch generates nothing, so\n // whatever the plan promised BEYOND the source is not in the draft\n // unless the source already had it: a plan reading \"from a copy of the\n // base layout, adding a sidebar\" gets the base layout, the sidebar\n // nowhere in it, and until now reported Done. The copy's own regions\n // answer the plan's, and a copy that cannot be read answers nothing —\n // which is a stop for a person, never a Done.\n const review = await aiCopiedLayoutReview(firestore, { hostId, id: copy.id, plan, name })\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 allowance = await aiDraftAllowanceRefusal(firestore, { kind: 'layout', hostId, org })\n if (allowance) return aiUnspentOutcome(model, { review: aiLimitReview(allowance) })\n\n if (aiJobUsesLayoutLanguage(job)) {\n // ── The header and footer designed in the layout language (AGL-3660) ──\n const siteName = aiLayoutSiteName(job)\n const pages = aiLayoutNavPages(job, inventory)\n const targets = aiLayoutFrameTargets(job, inventory)\n const result = await runValidatedGeneration<AiLayoutFrameBuilt>(AI_LAYOUT_FRAME_KIND, {\n step: 'job.layout',\n model,\n instructions: aiJobLayoutLanguageInstructions(job),\n inventory,\n messages: [{ role: 'user', content: aiLayoutFramePrompt({ job, siteName, pages, targets }) }],\n tool: AI_LAYOUT_FRAME_TOOL,\n maxTokens: AI_JOB_LAYOUT_LANGUAGE_BUDGET.maxTokens(model),\n ...AI_LAYOUT_LANGUAGE_THINKING,\n check: aiLayoutFrameCheck({\n ...aiSiteStyleTokens(job.inputs),\n siteName,\n homeId: aiLayoutHomeId(pages, inventory),\n pages,\n targets,\n // A site brought in may state what its own header and footer state (AGL-4084).\n extend: aiLayoutChecks(inventory, plan, [job.brief, aiLayoutSiteFactsText(job.inputs)].filter(Boolean).join('\\n')),\n design: aiLayoutDesignOf(job, '/'),\n brand: aiLayoutSiteBrandOf(job.inputs),\n storeLinks: aiStoreFrameLinksOf(job.inputs),\n // The frame's own words in the site's language (AGL-3760).\n ...(job.plan?.language ? { language: job.plan.language } : {}),\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 // Never written once the job is canceled (AGL-3616).\n throwIfAiJobCanceled(signal)\n const draft = await writeAiDraft(firestore, {\n kind: 'layout',\n hostId,\n id: draftId,\n uid: job.createdBy,\n org,\n name,\n nodes: result.value.nodes,\n aiJobId: aiOriginJobId(job),\n now,\n })\n if (draft.ok === false) {\n if (draft.status === 404) throw new Error(`site ${hostId} vanished while its layout was generated`)\n return { ...spent, review: aiLimitReview(draft.error) }\n }\n const note = aiBracketedFactsNote({\n tree: { rootId: result.value.rootId, nodes: result.value.nodes as unknown as AiDoctrineTree['nodes'] },\n inventory,\n })\n return { ...spent, outputs: [output(draft, result.value.load, note)] }\n }\n\n // The pages a site scaffold builds after this layout (AGL-3596): their\n // ids are the plan's, so the header links them by id, written by the\n // platform before the doctrine checks the tree.\n const sitePages = aiLayoutSitePages(job.inputs)\n const result = await runValidatedGeneration('layout', {\n step: 'job.layout',\n model,\n instructions: AI_JOB_LAYOUT_INSTRUCTIONS,\n inventory,\n messages: [{ role: 'user', content: aiJobLayoutPrompt(job, plan, name, sourceLines) }],\n tool: aiDoctrineTreeTool('layout'),\n maxTokens: AI_JOB_LAYOUT_STEP_BUDGET.maxTokens(model),\n ...(AI_ROUTING_TABLE['job.layout'].thinking ? { thinking: AI_ROUTING_TABLE['job.layout'].thinking } : {}),\n ...(AI_ROUTING_TABLE['job.layout'].effort ? { effort: AI_ROUTING_TABLE['job.layout'].effort } : {}),\n extend: aiLayoutChecks(inventory, plan, job.brief),\n // The site's name is written as the token that reads it, and the\n // layout fills the window so a short page keeps its footer at the\n // bottom (AGL-3596).\n complete: (tree: unknown) =>\n aiLayoutWithFullHeight(\n aiLayoutWithSiteName(\n sitePages.length ? aiLayoutWithSitePages(tree, sitePages, { homeScreenIds: aiHomeScreenIds(inventory) }) : tree,\n typeof job.inputs?.['businessName'] === 'string' ? job.inputs['businessName'] : null,\n ),\n ),\n ...(sitePages.length\n ? { context: { screenIds: [...inventory.screens.map((screen) => screen.id), ...sitePages.map((page) => page.id)] } }\n : {}),\n ...(signal ? { signal } : {}),\n })\n const spent = aiGenerationSpent(result)\n if (result.status === 'refused') return { ...spent, refused: true }\n if (result.status === 'needs_input') return { ...spent, review: aiDoctrineReview(result) }\n\n // Never written once the job is canceled (AGL-3616).\n throwIfAiJobCanceled(signal)\n const draft = await writeAiDraft(firestore, {\n kind: 'layout',\n hostId,\n id: draftId,\n uid: job.createdBy,\n org,\n name,\n nodes: result.value.nodes,\n aiJobId: aiOriginJobId(job),\n now,\n })\n if (draft.ok === false) {\n if (draft.status === 404) throw new Error(`site ${hostId} vanished while its layout was generated`)\n return { ...spent, review: aiLimitReview(draft.error) }\n }\n // The facts the brief did not give, which the member fills before publishing (AGL-3056).\n const note = aiBracketedFactsNote({\n tree: { rootId: result.value.rootId, nodes: result.value.nodes as unknown as AiDoctrineTree['nodes'] },\n inventory,\n })\n return { ...spent, outputs: [output(draft, result.value.load, note)] }\n }\n}\n\n/** The layout step, building the components and forms its plan creates before the layout (AGL-3143 §15). */\nexport const runAiJobLayoutStep = aiBuildingPlanCreations('layout', 'job.layout', createAiJobLayoutStep())\n\n/** Registers the layout step and the check a layout job passes before it is created or resumed. */\nexport function registerAiLayoutJob(): void {\n registerAiJobStep('layout', runAiJobLayoutStep, {\n minimumMs: AI_JOB_LAYOUT_STEP_MINIMUM_MS,\n minimumMsFor: aiPlanCreationsRunMinimumMs('layout'),\n })\n registerAiJobAdmission('layout', aiLayoutJobAdmission)\n}\n"],"names":["throwIfAiJobCanceled","aiSiteStyleTokens","CANVAS_ROOT_ELEMENT_ID","duplicateResource","aiHomeScreenIds","AI_STEP_TIERS","AI_ROUTING_TABLE","aiModelForStep","aiDoctrineTreeTool","runValidatedGeneration","walkTree","readSiteInventory","registerAiJobAdmission","aiBuildingPlanCreations","aiJobPlanCreationsRefusal","aiPlanCreationsRunMinimumMs","aiJobDraftId","aiOriginJobId","aiDraftAdmissionRefusal","aiDraftAllowanceRefusal","aiSiteSubdomain","readAiDraft","readAiDraftNodes","writeAiDraft","aiBracketedFactsNote","aiCopySourceLines","aiConfirmedPlan","aiDoctrineReview","aiGenerationSpent","aiJobBriefLine","aiLimitReview","aiModelNodeIds","aiPlacedComponentIds","aiPlanCreation","aiPlanReferenceLines","aiPlanReuseViolations","aiUnspentOutcome","aiPlanRegionViolations","aiPlannedLayoutRegions","aiLayoutSitePages","aiLayoutSitePagesLines","aiLayoutWithSitePages","aiLayoutInventedContactViolations","aiLayoutWithSiteName","aiLayoutWithFullHeight","AI_LAYOUT_FRAME_TOOL","AI_JOB_LAYOUT_LANGUAGE_BUDGET","AI_LAYOUT_FRAME_KIND","aiJobLayoutLanguageInstructions","aiLayoutFrameCheck","aiLayoutFramePrompt","aiLayoutFrameTargets","aiLayoutHomeId","aiLayoutNavPages","aiLayoutSiteBrandOf","aiLayoutSiteFactsText","aiLayoutSiteName","AI_LAYOUT_LANGUAGE_THINKING","aiJobUsesLayoutLanguage","aiLayoutDesignOf","aiJobStepBudget","registerAiJobStep","aiStoreFrameLinksOf","AI_JOB_LAYOUT_MAX_TOKENS","maxTokens","AI_JOB_LAYOUT_STEP_BUDGET","tier","AI_JOB_LAYOUT_STEP_MINIMUM_MS","minimumMs","AI_JOB_LAYOUT_DEFAULT_NAME","AI_JOB_LAYOUT_INSTRUCTIONS","text","join","aiJobLayoutPrompt","job","plan","name","source","inputs","NAVIGATION_NAME","FOOTER_NAME","isLink","node","componentId","props","aiLayoutReuseCheck","inventory","components","navigation","filter","component","test","tree","placed","violations","rootId","nodes","length","some","has","id","visits","slot","findIndex","header","slice","links","map","push","rule","code","message","nodeIds","sourceIds","aiLayoutChecks","brief","reuse","violation","aiCopiedLayoutReview","firestore","input","planned","regions","unreadable","stored","kind","hostId","reason","findings","aiLayoutJobAdmission","context","orgId","org","ownCheck","createAiJobLayoutStep","deps","readInventory","duplicate","now","signal","modelFor","orgSnapshot","Error","output","draft","load","note","resource","versionId","hostSubdomain","label","model","draftId","written","outputs","Promise","all","collection","doc","get","data","creation","duplicateOf","layouts","find","layout","undefined","sourceReview","sourceShort","Boolean","sourceTree","sourceLines","copy","sourceId","uid","createdBy","attemptKey","ok","review","status","error","allowance","siteName","pages","targets","result","step","instructions","messages","role","content","tool","check","homeId","extend","design","brand","storeLinks","language","spent","refused","value","aiJobId","sitePages","thinking","effort","complete","homeScreenIds","screenIds","screens","screen","page","runAiJobLayoutStep","registerAiLayoutJob","minimumMsFor"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,oBAAoB,QAAQ,qBAAiB;AACtD,SAASC,iBAAiB,QAAQ,4BAAwB;AAC1D,SAASC,sBAAsB,QAAQ,2CAA0C;AAEjF,SAASC,iBAAiB,QAAQ,qDAAoD;AAEtF,SAASC,eAAe,QAA8B,gCAA4B;AAClF,SAASC,aAAa,QAAQ,0BAAsB;AACpD,SAASC,gBAAgB,EAAEC,cAAc,QAAQ,0BAAsB;AACvE,SACEC,kBAAkB,EAClBC,sBAAsB,QAEjB,4BAAwB;AAC/B,SACEC,QAAQ,QAIH,uCAAmC;AAG1C,SAASC,iBAAiB,QAAQ,+BAA2B;AAC7D,SAASC,sBAAsB,QAA6B,wBAAoB;AAChF,SACEC,uBAAuB,EACvBC,yBAAyB,EACzBC,2BAA2B,QACtB,6BAAyB;AAChC,SAASC,YAAY,EAAEC,aAAa,QAAQ,wBAAoB;AAChE,SACEC,uBAAuB,EACvBC,uBAAuB,EACvBC,eAAe,EACfC,WAAW,EACXC,gBAAgB,EAChBC,YAAY,QAEP,qBAAiB;AACxB,SACEC,oBAAoB,EACpBC,iBAAiB,EACjBC,eAAe,EACfC,gBAAgB,EAChBC,iBAAiB,EACjBC,cAAc,EACdC,aAAa,EACbC,cAAc,EACdC,oBAAoB,EACpBC,cAAc,EACdC,oBAAoB,EACpBC,qBAAqB,EACrBC,gBAAgB,QACX,yBAAqB;AAC5B,SAASC,sBAAsB,EAAEC,sBAAsB,QAAQ,+BAA2B;AAC1F,SAASC,iBAAiB,EAAEC,sBAAsB,EAAEC,qBAAqB,QAAQ,gCAA4B;AAC7G,SAASC,iCAAiC,EAAEC,oBAAoB,QAAQ,4BAAwB;AAChG,SAASC,sBAAsB,QAAQ,6BAAyB;AAEhE,SAASC,oBAAoB,QAAQ,2CAAuC;AAC5E,SACEC,6BAA6B,EAC7BC,oBAAoB,EACpBC,+BAA+B,EAC/BC,kBAAkB,EAClBC,mBAAmB,EACnBC,oBAAoB,EACpBC,cAAc,EACdC,gBAAgB,EAChBC,mBAAmB,EACnBC,qBAAqB,EACrBC,gBAAgB,QAEX,8BAA0B;AACjC,SAASC,2BAA2B,EAAEC,uBAAuB,EAAEC,gBAAgB,QAAQ,4BAAwB;AAC/G,SAASC,eAAe,QAAQ,qBAAiB;AACjD,SAASC,iBAAiB,QAAQ,eAAW;AAC7C,SAASC,mBAAmB,QAAQ,kCAA8B;AAElE;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2BC,GAED,8FAA8F,GAC9F,OAAO,MAAMC,2BAA2BzD,gBAAgB,CAAC,aAAa,CAAC0D,SAAS,CAAA;AAEhF;;;;CAIC,GACD,OAAO,MAAMC,4BAA4BL,gBAAgB;IACvDM,MAAM7D,aAAa,CAAC,aAAa;IACjC2D,WAAWD;AACb,GAAE;AAEF,2DAA2D,GAC3D,OAAO,MAAMI,gCAAgCF,0BAA0BG,SAAS,CAAA;AAEhF,8CAA8C,GAC9C,OAAO,MAAMC,6BAA6B,cAAa;AAEvD,mEAAmE,GACnE,OAAO,MAAMC,6BAAuD;IAClE;QACEC,MAAM;YACJ;YACA;YACA;YACA;YACA;SACD,CAACC,IAAI,CAAC;IACT;CACD,CAAA;AAED;;;;CAIC,GACD,OAAO,SAASC,kBACdC,GAA0D,EAC1DC,IAAsB,EACtBC,IAAY,EACZC,SAA4B,EAAE;IAE9B,OAAO;QACL,CAAC,aAAa,EAAED,MAAM;QACtB/C,eAAe6C;WACZxC,qBAAqByC;QACxB,kGAAkG;WAC/FnC,uBAAuBD,kBAAkBmC,IAAII,MAAM;WACnDD;KACJ,CAACL,IAAI,CAAC;AACT;AAEA,sFAAsF,GACtF,MAAMO,kBAAkB;AACxB,MAAMC,cAAc;AAEpB,+FAA+F,GAC/F,SAASC,OAAOC,IAAoB;QAIxBA,aAAgDA;IAH1D,IAAIA,KAAKC,WAAW,KAAK,iBAAiB,OAAO;IACjD,OACED,KAAKC,WAAW,KAAK,eACpB,CAAA,SAAOD,cAAAA,KAAKE,KAAK,qBAAVF,WAAY,CAAC,WAAW,MAAK,YAAY,SAAOA,eAAAA,KAAKE,KAAK,qBAAVF,YAAY,CAAC,OAAO,MAAK,QAAO;AAE5F;AAEA;;;;;CAKC,GACD,OAAO,SAASG,mBACdC,SAAiC,EACjCX,IAAsB;;IAEtB,MAAMY,qBAAaD,6BAAAA,UAAWC,UAAU,mBAAI,EAAE;IAC9C,MAAMC,aAAaD,WAAWE,MAAM,CAClC,CAACC,YAAcX,gBAAgBY,IAAI,CAACD,UAAUd,IAAI,KAAK,CAACI,YAAYW,IAAI,CAACD,UAAUd,IAAI;IAEzF,OAAO,CAACgB;QACN,MAAMC,SAAS7D,qBAAqB4D;QACpC,MAAME,aAAoC;eACrC3D,sBAAsBmD,WAAWX,MAAMkB,QAAQ;eAC/CxD,uBAAuBsC,MAAM;gBAC9BoB,QAAQH,KAAKG,MAAM;gBACnBC,OAAOJ,KAAKI,KAAK;YACnB;SACD;QACD,IAAIR,WAAWS,MAAM,IAAI,CAACT,WAAWU,IAAI,CAAC,CAACR,YAAcG,OAAOM,GAAG,CAACT,UAAUU,EAAE,IAAI;YAClF,MAAMC,SAAS3F,SAAS;gBACtBqF,QAAQH,KAAKG,MAAM;gBACnBC,OAAOJ,KAAKI,KAAK;YACnB;YACA,MAAMM,OAAOD,OAAOE,SAAS,CAAC,CAAC,EAAErB,IAAI,EAAE,GAAKA,KAAKC,WAAW,KAAK;YACjE,MAAMqB,SAASF,SAAS,CAAC,IAAID,SAASA,OAAOI,KAAK,CAAC,GAAGH;YACtD,MAAMI,QAAQF,OAAOf,MAAM,CAAC,CAAC,EAAEP,IAAI,EAAE,GAAKD,OAAOC,OAAOyB,GAAG,CAAC,CAAC,EAAEP,EAAE,EAAE,GAAKA;YACxE,IAAIM,MAAMT,MAAM,IAAI,GAAG;gBACrBH,WAAWc,IAAI,CAAC;oBACdC,MAAM;oBACNC,MAAM;oBACNC,SAAS,CAAC,0BAA0B,EAAEvB,UAAU,CAAC,EAAE,CAACZ,IAAI,CAAC,6EAA6E,CAAC;oBACvIoC,SAASjF,eAAe2E,OAAOd,KAAKqB,SAAS;gBAC/C;YACF;QACF;QACA,OAAOnB;IACT;AACF;AAEA;;;CAGC,GACD,OAAO,SAASoB,eACd5B,SAAiC,EACjCX,IAAsB,EACtBwC,KAAa;IAEb,MAAMC,QAAQ/B,mBAAmBC,WAAWX;IAC5C,OAAO,CAACiB,OAAS;eACZwB,MAAMxB;eACNlD,kCACD;gBAAEqD,QAAQH,KAAKG,MAAM;gBAAEC,OAAOJ,KAAKI,KAAK;YAA8C,GACtFmB,OACAR,GAAG,CAAC,CAACU;oBAAuDA;uBAAxC,aAAKA;oBAAWL,SAASjF,gBAAesF,qBAAAA,UAAUL,OAAO,YAAjBK,qBAAqB,EAAE,EAAEzB,KAAKqB,SAAS;;;SACtG;AACH;AAEA;;;;;;;;;;CAUC,GACD,OAAO,eAAeK,qBACpBC,SAAsC,EACtCC,KAA2E;;IAE3E,MAAMC,UAAUnF,uBAAuBkF,MAAM7C,IAAI;IACjD,IAAI,CAAC8C,QAAQC,OAAO,CAACzB,MAAM,IAAI,CAACwB,QAAQE,UAAU,CAAC1B,MAAM,EAAE,OAAO;IAClE,MAAM2B,SAAS,MAAMtG,iBAAiBiG,WAAW;QAC/CM,MAAM;QACNC,QAAQN,MAAMM,MAAM;QACpB1B,IAAIoB,MAAMpB,EAAE;IACd;IACA,MAAMJ,gBAAS4B,0BAAAA,OAAQ5B,KAAK,mBAAI,CAAC;IACjC,IAAI,CAACA,KAAK,CAAC9F,uBAAuB,EAAE;QAClC,OAAO;YACL6H,QAAQ;YACRhB,SAAS,CAAC,CAAC,EAAES,MAAM5C,IAAI,CAAC,+LAA+L,CAAC;YACxNoD,UAAU,EAAE;QACd;IACF;IACA,MAAMlC,aAAazD,uBAAuBmF,MAAM7C,IAAI,EAAE;QAAEoB,QAAQ7F;QAAwB8F;IAAM;IAC9F,IAAI,CAACF,WAAWG,MAAM,EAAE,OAAO;IAC/B,OAAOtE,iBAAiB;QACtBoF,SAAS,CAAC,CAAC,EAAES,MAAM5C,IAAI,CAAC,mEAAmE,EAAEkB,UAAU,CAAC,EAAE,CAACiB,OAAO,EAAE;QACpHjB;IACF;AACF;AAEA;;;;CAIC,GACD,OAAO,MAAMmC,uBAAuC,CAACC,UACnDhH,wBAAwBgH,QAAQX,SAAS,EAAE;QACzCY,OAAOD,QAAQC,KAAK;QACpBL,QAAQI,QAAQJ,MAAM;QACtBD,MAAM;QACNO,KAAKF,QAAQE,GAAG;QAChBC,UAAU,CAACP,SAAWhH,0BAA0B,UAAUoH,SAASJ;IACrE,GAAE;AASJ,OAAO,SAASQ,sBAAsBC,OAA4B,CAAC,CAAC;QAC5CA,qBACJA;IADlB,MAAMC,iBAAgBD,sBAAAA,KAAKC,aAAa,YAAlBD,sBAAsB5H;IAC5C,MAAM8H,aAAYF,kBAAAA,KAAKE,SAAS,YAAdF,kBAAkBpI;IACpC,OAAO,OAAO,EAAEuE,GAAG,EAAEgE,GAAG,EAAEC,MAAM,EAAEpB,SAAS,EAAEqB,QAAQ,EAAE;kBAyBxCC;QAxBb,MAAMf,SAASpD,IAAIoD,MAAM;QACzB,IAAI,CAACA,QAAQ,MAAM,IAAIgB,MAAM;QAC7B,MAAMC,SAAS,CAACC,OAAsBC,MAA8BC,OAAuC;gBACzGC,UAAU;gBACV/C,IAAI4C,MAAM5C,EAAE;gBACZgD,WAAWJ,MAAMI,SAAS;gBAC1BtB;gBACAuB,eAAeL,MAAMK,aAAa;gBAClCC,OAAON,MAAMpE,IAAI;eACbqE,OAAO;gBAAEA;YAAK,IAAI,CAAC,GACnBC,OAAO;gBAAEA;YAAK,IAAI,CAAC;QAEzB,yEAAyE;QACzE,MAAMK,gBAAQX,4BAAAA,SAAW,gCAAiBrI,eAAe;QAEzD,kGAAkG;QAClG,MAAMiJ,UAAUxI,aAAa0D,KAAK;QAClC,MAAM+E,UAAU,MAAMpI,YAAYkG,WAAW;YAAEM,MAAM;YAAUC;YAAQ1B,IAAIoD;QAAQ;QACnF,IAAIC,SAAS,OAAOrH,iBAAiBmH,OAAO;YAAEG,SAAS;gBAACX,OAAOU;aAAS;QAAC;QAEzE,MAAM,CAACnE,WAAWuD,YAAY,GAAG,MAAMc,QAAQC,GAAG,CAAC;YACjDpB,cAAc9D,IAAIyD,KAAK,EAAEL,QAAQ;gBAAEP;YAAU;YAC7CA,UAAUsC,UAAU,CAAC,QAAQC,GAAG,CAACpF,IAAIyD,KAAK,EAAE4B,GAAG;SAChD;QACD,MAAM3B,OAAOS,oBAAAA,YAAYmB,IAAI,cAAhBnB,oBAAsB;QACnC,MAAMlE,OAAOjD,gBAAgBgD;QAC7B,MAAMuF,WAAWhI,eAAe0C,MAAM;QACtC,MAAMC,OAAOqF,CAAAA,4BAAAA,SAAUrF,IAAI,KAAIP;QAE/B,2EAA2E;QAC3E,uEAAuE;QACvE,0EAA0E;QAC1E,wEAAwE;QACxE,sEAAsE;QACtE,yEAAyE;QACzE,uEAAuE;QACvE,wCAAwC;QACxC,MAAMQ,SAASoF,CAAAA,4BAAAA,SAAUC,WAAW,IAChC5E,UAAU6E,OAAO,CAACC,IAAI,CAAC,CAACC,SAAWA,OAAOjE,EAAE,KAAK6D,SAASC,WAAW,IACrEI;QACJ,MAAMC,eAAe1F,SACjB,MAAMyC,qBAAqBC,WAAW;YAAEO;YAAQ1B,IAAIvB,OAAOuB,EAAE;YAAEzB;YAAMC,MAAMC,OAAOD,IAAI;QAAC,KACvF;QACJ,MAAM4F,cAAcC,QAAQF,gCAAAA,aAAcvC,QAAQ,CAAC/B,MAAM;QACzD,MAAMyE,aACJ7F,UAAU2F,cAAc,MAAMlJ,iBAAiBiG,WAAW;YAAEM,MAAM;YAAUC;YAAQ1B,IAAIvB,OAAOuB,EAAE;QAAC,KAAK;QACzG,MAAMuE,cACJ9F,UAAU2F,cAAc/I,kBAAkB,UAAUoD,OAAOD,IAAI,WAAE8F,8BAAAA,WAAY1E,KAAK,oBAAI,QAAQ,EAAE;QAClG,IAAIiE,CAAAA,4BAAAA,SAAUC,WAAW,KAAIrF,UAAU,CAAC2F,aAAa;YACnD,MAAMI,OAAO,MAAMnC,UAAU,UAAU;gBACrCN,OAAOzD,IAAIyD,KAAK;gBAChBL;gBACA+C,UAAUZ,SAASC,WAAW;gBAC9BtF;gBACAkG,KAAKpG,IAAIqG,SAAS;gBAClB3C,KAAKA;gBACL,sEAAsE;gBACtE,oEAAoE;gBACpE,sEAAsE;gBACtE,mEAAmE;gBACnE4C,YAAYxB;YACd;YACA,IAAIoB,KAAKK,EAAE,EAAE;gBACX,sEAAsE;gBACtE,mEAAmE;gBACnE,uEAAuE;gBACvE,mEAAmE;gBACnE,qEAAqE;gBACrE,sEAAsE;gBACtE,8CAA8C;gBAC9C,MAAMC,SAAS,MAAM5D,qBAAqBC,WAAW;oBAAEO;oBAAQ1B,IAAIwE,KAAKxE,EAAE;oBAAEzB;oBAAMC;gBAAK;gBACvF,IAAIsG,QAAQ,OAAO9I,iBAAiBmH,OAAO;oBAAE2B;gBAAO;gBACpD,MAAM7B,gBAAgB,MAAMjI,gBAAgBmG,WAAWO;gBACvD,OAAO1F,iBAAiBmH,OAAO;oBAC7BG,SAAS;wBAACX,OAAO;4BAAE3C,IAAIwE,KAAKxE,EAAE;4BAAEgD,WAAWwB,KAAKxB,SAAS;4BAAExE,MAAMgG,KAAKhG,IAAI;4BAAEyE;wBAAc;qBAAG;gBAC/F;YACF;YACA,IAAIuB,KAAKK,EAAE,KAAK,SAASL,KAAKO,MAAM,KAAK,KAAK;gBAC5C,OAAO/I,iBAAiBmH,OAAO;oBAAE2B,QAAQpJ,cAAc8I,KAAKQ,KAAK;gBAAE;YACrE;QACF;QAEA,MAAMC,YAAY,MAAMlK,wBAAwBoG,WAAW;YAAEM,MAAM;YAAUC;YAAQM;QAAI;QACzF,IAAIiD,WAAW,OAAOjJ,iBAAiBmH,OAAO;YAAE2B,QAAQpJ,cAAcuJ;QAAW;QAEjF,IAAI3H,wBAAwBgB,MAAM;gBA0BxBA;YAzBR,yEAAyE;YACzE,MAAM4G,WAAW9H,iBAAiBkB;YAClC,MAAM6G,QAAQlI,iBAAiBqB,KAAKY;YACpC,MAAMkG,UAAUrI,qBAAqBuB,KAAKY;YAC1C,MAAMmG,SAAS,MAAMhL,uBAA2CsC,sBAAsB;gBACpF2I,MAAM;gBACNnC;gBACAoC,cAAc3I,gCAAgC0B;gBAC9CY;gBACAsG,UAAU;oBAAC;wBAAEC,MAAM;wBAAQC,SAAS5I,oBAAoB;4BAAEwB;4BAAK4G;4BAAUC;4BAAOC;wBAAQ;oBAAG;iBAAE;gBAC7FO,MAAMlJ;gBACNmB,WAAWlB,8BAA8BkB,SAAS,CAACuF;eAChD9F;gBACHuI,OAAO/I,mBAAmB,aACrBhD,kBAAkByE,IAAII,MAAM;oBAC/BwG;oBACAW,QAAQ7I,eAAemI,OAAOjG;oBAC9BiG;oBACAC;oBACA,+EAA+E;oBAC/EU,QAAQhF,eAAe5B,WAAWX,MAAM;wBAACD,IAAIyC,KAAK;wBAAE5D,sBAAsBmB,IAAII,MAAM;qBAAE,CAACW,MAAM,CAACgF,SAASjG,IAAI,CAAC;oBAC5G2H,QAAQxI,iBAAiBe,KAAK;oBAC9B0H,OAAO9I,oBAAoBoB,IAAII,MAAM;oBACrCuH,YAAYvI,oBAAoBY,IAAII,MAAM;mBAEtCJ,EAAAA,YAAAA,IAAIC,IAAI,qBAARD,UAAU4H,QAAQ,IAAG;oBAAEA,UAAU5H,IAAIC,IAAI,CAAC2H,QAAQ;gBAAC,IAAI,CAAC;eAE1D3D,SAAS;gBAAEA;YAAO,IAAI,CAAC;YAE7B,MAAM4D,QAAQ3K,kBAAkB6J;YAChC,IAAIA,OAAON,MAAM,KAAK,WAAW,OAAO,aAAKoB;gBAAOC,SAAS;;YAC7D,IAAIf,OAAON,MAAM,KAAK,eAAe,OAAO,aAAKoB;gBAAOrB,QAAQvJ,iBAAiB8J;;YACjF,qDAAqD;YACrDzL,qBAAqB2I;YACrB,MAAMK,QAAQ,MAAMzH,aAAagG,WAAW;gBAC1CM,MAAM;gBACNC;gBACA1B,IAAIoD;gBACJsB,KAAKpG,IAAIqG,SAAS;gBAClB3C;gBACAxD;gBACAoB,OAAOyF,OAAOgB,KAAK,CAACzG,KAAK;gBACzB0G,SAASzL,cAAcyD;gBACvBgE;YACF;YACA,IAAIM,MAAMiC,EAAE,KAAK,OAAO;gBACtB,IAAIjC,MAAMmC,MAAM,KAAK,KAAK,MAAM,IAAIrC,MAAM,CAAC,KAAK,EAAEhB,OAAO,wCAAwC,CAAC;gBAClG,OAAO,aAAKyE;oBAAOrB,QAAQpJ,cAAckH,MAAMoC,KAAK;;YACtD;YACA,MAAMlC,OAAO1H,qBAAqB;gBAChCoE,MAAM;oBAAEG,QAAQ0F,OAAOgB,KAAK,CAAC1G,MAAM;oBAAEC,OAAOyF,OAAOgB,KAAK,CAACzG,KAAK;gBAAuC;gBACrGV;YACF;YACA,OAAO,aAAKiH;gBAAO7C,SAAS;oBAACX,OAAOC,OAAOyC,OAAOgB,KAAK,CAACxD,IAAI,EAAEC;iBAAM;;QACtE;QAEA,uEAAuE;QACvE,qEAAqE;QACrE,gDAAgD;QAChD,MAAMyD,YAAYpK,kBAAkBmC,IAAII,MAAM;QAC9C,MAAM2G,SAAS,MAAMhL,uBAAuB,UAAU;YACpDiL,MAAM;YACNnC;YACAoC,cAAcrH;YACdgB;YACAsG,UAAU;gBAAC;oBAAEC,MAAM;oBAAQC,SAASrH,kBAAkBC,KAAKC,MAAMC,MAAM+F;gBAAa;aAAE;YACtFoB,MAAMvL,mBAAmB;YACzBwD,WAAWC,0BAA0BD,SAAS,CAACuF;WAC3CjJ,gBAAgB,CAAC,aAAa,CAACsM,QAAQ,GAAG;YAAEA,UAAUtM,gBAAgB,CAAC,aAAa,CAACsM,QAAQ;QAAC,IAAI,CAAC,GACnGtM,gBAAgB,CAAC,aAAa,CAACuM,MAAM,GAAG;YAAEA,QAAQvM,gBAAgB,CAAC,aAAa,CAACuM,MAAM;QAAC,IAAI,CAAC;YACjGX,QAAQhF,eAAe5B,WAAWX,MAAMD,IAAIyC,KAAK;YACjD,iEAAiE;YACjE,kEAAkE;YAClE,qBAAqB;YACrB2F,UAAU,CAAClH;oBAIElB;uBAHX9B,uBACED,qBACEgK,UAAU1G,MAAM,GAAGxD,sBAAsBmD,MAAM+G,WAAW;oBAAEI,eAAe3M,gBAAgBkF;gBAAW,KAAKM,MAC3G,SAAOlB,cAAAA,IAAII,MAAM,qBAAVJ,WAAY,CAAC,eAAe,MAAK,WAAWA,IAAII,MAAM,CAAC,eAAe,GAAG;;WAGlF6H,UAAU1G,MAAM,GAChB;YAAEiC,SAAS;gBAAE8E,WAAW;uBAAI1H,UAAU2H,OAAO,CAACtG,GAAG,CAAC,CAACuG,SAAWA,OAAO9G,EAAE;uBAAMuG,UAAUhG,GAAG,CAAC,CAACwG,OAASA,KAAK/G,EAAE;iBAAE;YAAC;QAAE,IACjH,CAAC,GACDuC,SAAS;YAAEA;QAAO,IAAI,CAAC;QAE7B,MAAM4D,QAAQ3K,kBAAkB6J;QAChC,IAAIA,OAAON,MAAM,KAAK,WAAW,OAAO,aAAKoB;YAAOC,SAAS;;QAC7D,IAAIf,OAAON,MAAM,KAAK,eAAe,OAAO,aAAKoB;YAAOrB,QAAQvJ,iBAAiB8J;;QAEjF,qDAAqD;QACrDzL,qBAAqB2I;QACrB,MAAMK,QAAQ,MAAMzH,aAAagG,WAAW;YAC1CM,MAAM;YACNC;YACA1B,IAAIoD;YACJsB,KAAKpG,IAAIqG,SAAS;YAClB3C;YACAxD;YACAoB,OAAOyF,OAAOgB,KAAK,CAACzG,KAAK;YACzB0G,SAASzL,cAAcyD;YACvBgE;QACF;QACA,IAAIM,MAAMiC,EAAE,KAAK,OAAO;YACtB,IAAIjC,MAAMmC,MAAM,KAAK,KAAK,MAAM,IAAIrC,MAAM,CAAC,KAAK,EAAEhB,OAAO,wCAAwC,CAAC;YAClG,OAAO,aAAKyE;gBAAOrB,QAAQpJ,cAAckH,MAAMoC,KAAK;;QACtD;QACA,yFAAyF;QACzF,MAAMlC,OAAO1H,qBAAqB;YAChCoE,MAAM;gBAAEG,QAAQ0F,OAAOgB,KAAK,CAAC1G,MAAM;gBAAEC,OAAOyF,OAAOgB,KAAK,CAACzG,KAAK;YAAuC;YACrGV;QACF;QACA,OAAO,aAAKiH;YAAO7C,SAAS;gBAACX,OAAOC,OAAOyC,OAAOgB,KAAK,CAACxD,IAAI,EAAEC;aAAM;;IACtE;AACF;AAEA,0GAA0G,GAC1G,OAAO,MAAMkE,qBAAqBvM,wBAAwB,UAAU,cAAcyH,yBAAwB;AAE1G,iGAAiG,GACjG,OAAO,SAAS+E;IACdxJ,kBAAkB,UAAUuJ,oBAAoB;QAC9ChJ,WAAWD;QACXmJ,cAAcvM,4BAA4B;IAC5C;IACAH,uBAAuB,UAAUqH;AACnC"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-layout-step.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { throwIfAiJobCanceled } from './ai-job-cancel'\nimport { aiSiteStyleTokens } from '../model/ai-site-kinds'\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 type { AiJob, AiJobOutput, AiJobPlan, AiJobReview } from '../model/ai-jobs.types'\nimport { aiHomeScreenIds, type AiSiteInventory } from '../model/ai-site-inventory'\nimport { AI_STEP_TIERS } from '../providers/catalog'\nimport { AI_ROUTING_TABLE, aiModelForStep } from '../providers/routing'\nimport {\n aiDoctrineTreeTool,\n runValidatedGeneration,\n type AiValidatedTree,\n} from '../runtime/ai-doctrine'\nimport {\n walkTree,\n type AiDoctrineNode,\n type AiDoctrineTree,\n type AiDoctrineViolation,\n} from '../runtime/ai-doctrine-validators'\nimport type { AiLoadEstimate } from '../runtime/ai-palette'\nimport type { AiSystemBlock } from '../runtime/ai-runtime'\nimport { readSiteInventory } from '../runtime/site-inventory'\nimport { registerAiJobAdmission, type AiJobAdmission } from './ai-job-admission'\nimport {\n aiBuildingPlanCreations,\n aiJobPlanCreationsRefusal,\n aiPlanCreationsRunMinimumMs,\n} from './ai-job-plan-creations'\nimport { aiJobDraftId, aiOriginJobId } from './ai-job-draft-ids'\nimport {\n aiDraftAdmissionRefusal,\n aiDraftAllowanceRefusal,\n aiSiteSubdomain,\n readAiDraft,\n readAiDraftNodes,\n writeAiDraft,\n type AiDraftRecord,\n} from './ai-job-drafts'\nimport {\n aiBracketedFactsNote,\n aiCopySourceLines,\n aiConfirmedPlan,\n aiDoctrineReview,\n aiGenerationSpent,\n aiJobBriefLine,\n aiLimitReview,\n aiModelNodeIds,\n aiPlacedComponentIds,\n aiPlanCreation,\n aiPlanReferenceLines,\n aiPlanReuseViolations,\n aiUnspentOutcome,\n} from './ai-job-generation'\nimport { aiPlanRegionViolations, aiPlannedLayoutRegions } from './ai-job-plan-conformance'\nimport { aiLayoutSitePages, aiLayoutSitePagesLines, aiLayoutWithSitePages } from './ai-job-layout-site-pages'\nimport { aiLayoutInventedContactViolations, aiLayoutWithSiteName } from './ai-layout-site-facts'\nimport { aiLayoutWithFullHeight } from './ai-layout-full-height'\nimport type { AiJobStepRunner } from './ai-job-text-step'\nimport { AI_LAYOUT_FRAME_TOOL } from '../layout-language/ai-layout-language'\nimport {\n AI_JOB_LAYOUT_LANGUAGE_BUDGET,\n AI_LAYOUT_FRAME_KIND,\n aiJobLayoutLanguageInstructions,\n aiLayoutFrameCheck,\n aiLayoutFramePrompt,\n aiLayoutFrameTargets,\n aiLayoutHomeId,\n aiLayoutNavPages,\n aiLayoutSiteBrandOf,\n aiLayoutSiteFactsText,\n aiLayoutSiteFrameOf,\n aiLayoutSiteName,\n aiLayoutSourceFrameBuild,\n type AiLayoutFrameBuilt,\n type AiLayoutFrameInput,\n} from './ai-job-layout-language'\nimport { AI_LAYOUT_LANGUAGE_THINKING, aiJobUsesLayoutLanguage, aiLayoutDesignOf } from './ai-job-page-language'\nimport { aiJobStepBudget } from './ai-job-budget'\nimport { registerAiJobStep } from './ai-jobs'\nimport { aiStoreFrameLinksOf } from '../model/ai-site-store-pages'\n\n/**\n * The layout step (AGL-2909): the generation step of a `layout` job, run once\n * a member confirmed the job's plan.\n *\n * It builds ONE shared layout — a header with the site's navigation, the\n * Layout Slot each page renders in, a footer — through\n * `runValidatedGeneration('layout', …)`, so every building rule holds on the\n * tree before it is kept, and adds three checks of its own through `extend`: a\n * component the confirmed plan reuses is placed (rule 7), every region the\n * plan gives the layout is built (AGL-3024), and a navigation or menu\n * component the site already has is placed rather than drawn again (rule 1).\n *\n * The layout lands as a new draft (`ai-job-drafts.ts`): no screen uses it and\n * no layout nests it, so nothing on the live site changes until a member\n * assigns it. A plan that starts from a copy of a layout the site has gets\n * that copy, through the platform's duplicate module, and nothing generated —\n * so the copy is read back and held to the plan's regions the same way\n * (`aiCopiedLayoutReview`), because a branch that generates nothing is the one\n * branch where a promise can go unmet with no model to answer for it. A\n * source that already lacks a region the plan gives the layout is not copied\n * at all: the layout is built from it (AGL-3143 §14).\n *\n * A generation step runs on the job beat, never at an inline door: it\n * registers the least time its lookup rounds, its answer and its re-ask need\n * (AGL-3035), runs without extended thinking — the plan was reasoned out and\n * confirmed before it — and asks for no more of a tighter ceiling than the\n * doctrine's default for a layout than fits that time on the model it runs.\n */\n\n/** The longest answer a layout may run to; the tree is held to the layout budget either way. */\nexport const AI_JOB_LAYOUT_MAX_TOKENS = AI_ROUTING_TABLE['job.layout'].maxTokens\n\n/**\n * The layout step's time (AGL-3035, AGL-3036): its two inventory-lookup\n * rounds, its answer and its re-ask at the routing table's ceiling on the tier\n * `job.layout` is served from, fitted to what a beat can give a step.\n */\nexport const AI_JOB_LAYOUT_STEP_BUDGET = aiJobStepBudget({\n tier: AI_STEP_TIERS['job.layout'],\n maxTokens: AI_JOB_LAYOUT_MAX_TOKENS,\n})\n\n/** The least time one layout step needs before it starts. */\nexport const AI_JOB_LAYOUT_STEP_MINIMUM_MS = AI_JOB_LAYOUT_STEP_BUDGET.minimumMs\n\n/** A layout's name when the plan names none. */\nexport const AI_JOB_LAYOUT_DEFAULT_NAME = 'Site layout'\n\n/** The layout step's own instructions, cached after the doctrine. */\nexport const AI_JOB_LAYOUT_INSTRUCTIONS: readonly AiSystemBlock[] = [\n {\n text: [\n 'You build one shared layout for a website: the frame every page of the site renders inside. Answer with submit_layout: the whole layout as one flat node map.',\n 'A layout is a header, one Layout Slot and a footer, top to bottom. The header is an App Bar (muiAppBar) holding a Toolbar Content (muiToolbar) with the site’s name as text and its navigation. The footer is a Section (section) whose element is footer.',\n 'Navigation links the site’s own pages by id: a Page Link (muiScreenLink) whose screenId is a page id from the site inventory, in the order the brief and the confirmed plan give, seven at most in the header. Never invent a page id, and never link a page by its address.',\n 'When the site inventory lists a component whose name says it is navigation, a menu or a mega menu, place it in the header as an instance instead of building the navigation again. Place every component the confirmed plan reuses.',\n 'The Layout Slot (layoutSlot) is where each page renders and is the page’s main landmark: leave its component unset or main, and put nothing inside it. A layout carries no h1, because every page brings its own.',\n ].join('\\n'),\n },\n]\n\n/**\n * The layout job as the generation's user turn: the name, the brief, the\n * confirmed plan, and the layout a short copy would have started from\n * (`aiCopySourceLines`, AGL-3143 §14).\n */\nexport function aiJobLayoutPrompt(\n job: Pick<AiJob, 'brief'> & Partial<Pick<AiJob, 'inputs'>>,\n plan: AiJobPlan | null,\n name: string,\n source: readonly string[] = [],\n): string {\n return [\n `Layout name: ${name}`,\n aiJobBriefLine(job),\n ...aiPlanReferenceLines(plan),\n // A scaffold's layout is built before its pages, whose navigation the platform writes (AGL-3596).\n ...aiLayoutSitePagesLines(aiLayoutSitePages(job.inputs)),\n ...source,\n ].join('\\n')\n}\n\n/** A component name that says it is the site's navigation; a footer's links are not. */\nconst NAVIGATION_NAME = /\\b(?:nav|navigation|navbar|menu|mega ?menu)\\b/i\nconst FOOTER_NAME = /\\bfooter\\b/i\n\n/** A node that links somewhere: a Screen Link, or a Button pointed at a screen or an address. */\nfunction isLink(node: AiDoctrineNode): boolean {\n if (node.componentId === 'muiScreenLink') return true\n return (\n node.componentId === 'muiButton' &&\n (typeof node.props?.['screenId'] === 'string' || typeof node.props?.['href'] === 'string')\n )\n}\n\n/**\n * The layout door's checks on a tree the doctrine admitted: every component\n * the confirmed plan reuses is placed, every region the confirmed plan gives\n * the layout is built (AGL-3024), and navigation the site already keeps as a\n * component is placed rather than drawn again in the header.\n */\nexport function aiLayoutReuseCheck(\n inventory: AiSiteInventory | null,\n plan: AiJobPlan | null,\n): (tree: AiValidatedTree) => AiDoctrineViolation[] {\n const components = inventory?.components ?? []\n const navigation = components.filter(\n (component) => NAVIGATION_NAME.test(component.name) && !FOOTER_NAME.test(component.name),\n )\n return (tree) => {\n const placed = aiPlacedComponentIds(tree)\n const violations: AiDoctrineViolation[] = [\n ...aiPlanReuseViolations(inventory, plan, placed, 'layout'),\n ...aiPlanRegionViolations(plan, {\n rootId: tree.rootId,\n nodes: tree.nodes as unknown as Record<string, AiDoctrineNode>,\n }),\n ]\n if (navigation.length && !navigation.some((component) => placed.has(component.id))) {\n const visits = walkTree({\n rootId: tree.rootId,\n nodes: tree.nodes as unknown as Record<string, AiDoctrineNode>,\n })\n const slot = visits.findIndex(({ node }) => node.componentId === 'layoutSlot')\n const header = slot === -1 ? visits : visits.slice(0, slot)\n const links = header.filter(({ node }) => isLink(node)).map(({ id }) => id)\n if (links.length >= 2) {\n violations.push({\n rule: 1,\n code: 'navigation-rebuilt',\n message: `The site already has the \"${navigation[0].name}\" component. Place it in the header instead of building the navigation again.`,\n nodeIds: aiModelNodeIds(links, tree.sourceIds),\n })\n }\n }\n return violations\n }\n}\n\n/**\n * Every check the layout door adds to the doctrine's: the reuse and region\n * checks above, and no contact detail the brief does not give (AGL-3596).\n */\nexport function aiLayoutChecks(\n inventory: AiSiteInventory | null,\n plan: AiJobPlan | null,\n brief: string,\n): (tree: AiValidatedTree) => AiDoctrineViolation[] {\n const reuse = aiLayoutReuseCheck(inventory, plan)\n return (tree) => [\n ...reuse(tree),\n ...aiLayoutInventedContactViolations(\n { rootId: tree.rootId, nodes: tree.nodes as unknown as Record<string, AiDoctrineNode> },\n brief,\n ).map((violation) => ({ ...violation, nodeIds: aiModelNodeIds(violation.nodeIds ?? [], tree.sourceIds) })),\n ]\n}\n\n/**\n * The regions the confirmed plan gives the layout, against the layout a COPY\n * actually produced (AGL-3024); `null` when the copy keeps the plan, or when\n * the plan promised no region to keep.\n *\n * The duplicate branch generates nothing, so it is the one place a plan's\n * promise can go unmet without a model ever answering for it. A copy that\n * cannot be read — deleted between the write and this read, or stored under a\n * root this check does not know — settles nothing, and a promise this step\n * cannot settle is reviewed by a person rather than reported as kept.\n */\nexport async function aiCopiedLayoutReview(\n firestore: FirebaseFirestore.Firestore,\n input: { hostId: string; id: string; name: string; plan: AiJobPlan | null },\n): Promise<AiJobReview | null> {\n const planned = aiPlannedLayoutRegions(input.plan)\n if (!planned.regions.length && !planned.unreadable.length) return null\n const stored = await readAiDraftNodes(firestore, {\n kind: 'layout',\n hostId: input.hostId,\n id: input.id,\n })\n const nodes = (stored?.nodes ?? {}) as unknown as Record<string, AiDoctrineNode>\n if (!nodes[CANVAS_ROOT_ELEMENT_ID]) {\n return {\n reason: 'doctrine',\n message: `\"${input.name}\" was copied from the layout the plan names, and this job could not read the copy back to check it has the regions the plan gives it. Open the layout and check it before you put a page in it.`,\n findings: [],\n }\n }\n const violations = aiPlanRegionViolations(input.plan, { rootId: CANVAS_ROOT_ELEMENT_ID, nodes })\n if (!violations.length) return null\n return aiDoctrineReview({\n message: `\"${input.name}\" is a copy of the layout the plan names, and a copy is all it is: ${violations[0].message}`,\n violations,\n })\n}\n\n/**\n * A layout job is admitted for a site of the job's own org with a shared\n * layout to spare, and confirmed only for a plan whose other creations it\n * builds (AGL-3143 §15).\n */\nexport const aiLayoutJobAdmission: AiJobAdmission = (context) =>\n aiDraftAdmissionRefusal(context.firestore, {\n orgId: context.orgId,\n hostId: context.hostId,\n kind: 'layout',\n org: context.org,\n ownCheck: (hostId) => aiJobPlanCreationsRefusal('layout', context, hostId),\n })\n\nexport interface AiJobLayoutStepDeps {\n /** The inventory reader; specs hand in a fake. */\n readInventory?: typeof readSiteInventory\n /** The platform's duplicate module, for a plan that starts from a copy. */\n duplicate?: typeof duplicateResource\n}\n\nexport function createAiJobLayoutStep(deps: AiJobLayoutStepDeps = {}): AiJobStepRunner {\n const readInventory = deps.readInventory ?? readSiteInventory\n const duplicate = deps.duplicate ?? duplicateResource\n return async ({ job, now, signal, firestore, modelFor }) => {\n const hostId = job.hostId\n if (!hostId) throw new Error('a layout job names no site, and its admission refuses one')\n const output = (draft: AiDraftRecord, load?: AiLoadEstimate | null, note?: string | null): AiJobOutput => ({\n resource: 'layout',\n id: draft.id,\n versionId: draft.versionId,\n hostId,\n hostSubdomain: draft.hostSubdomain,\n label: draft.name,\n ...(load ? { load } : {}),\n ...(note ? { note } : {}),\n })\n // The switch's answer for this job, else the routing table's (AGL-2942).\n const model = modelFor?.('job.layout') ?? aiModelForStep('job.layout')\n\n // A run cut off after its draft was written reports that draft, found by the id the job recorded.\n const draftId = aiJobDraftId(job, 'layout')\n const written = await readAiDraft(firestore, { kind: 'layout', hostId, id: draftId })\n if (written) return aiUnspentOutcome(model, { outputs: [output(written)] })\n\n const [inventory, orgSnapshot] = await Promise.all([\n readInventory(job.orgId, hostId, { firestore }),\n firestore.collection('orgs').doc(job.orgId).get(),\n ])\n const org = (orgSnapshot.data() ?? null) as Partial<AglynOrgBilling> | null\n const plan = aiConfirmedPlan(job)\n const creation = aiPlanCreation(plan, 'layout')\n const name = creation?.name || AI_JOB_LAYOUT_DEFAULT_NAME\n\n // The plan starts from a copy of a layout the site has (rule 15): the copy\n // is the draft, and nothing is generated. A source gone since the plan\n // was made is built from the brief instead. A source that cannot keep the\n // plan's regions is built FROM instead (AGL-3143 §14), as the page step\n // builds from a short source: copying it could only end in the review\n // below, and Try again would copy the same thing. Only a source read and\n // found short diverts; one this step cannot read still copies, and the\n // review after the copy answers for it.\n const source = creation?.duplicateOf\n ? inventory.layouts.find((layout) => layout.id === creation.duplicateOf)\n : undefined\n const sourceReview = source\n ? await aiCopiedLayoutReview(firestore, { hostId, id: source.id, plan, name: source.name })\n : null\n const sourceShort = Boolean(sourceReview?.findings.length)\n const sourceTree =\n source && sourceShort ? await readAiDraftNodes(firestore, { kind: 'layout', hostId, id: source.id }) : null\n const sourceLines =\n source && sourceShort ? aiCopySourceLines('layout', source.name, sourceTree?.nodes ?? null) : []\n if (creation?.duplicateOf && source && !sourceShort) {\n const copy = await duplicate('layout', {\n orgId: job.orgId,\n hostId,\n sourceId: creation.duplicateOf,\n name,\n uid: job.createdBy,\n org: org as Record<string, unknown> | null,\n // The same claim the page step makes (AGL-3024): a copy core mints is\n // written under core's own id, never `draftId`, so a step that runs\n // again — a pass resumed, a run cut off after the copy — would mint a\n // second. The job's recorded id makes core replay the one it made.\n attemptKey: draftId,\n })\n if (copy.ok) {\n // A copy is not a build (AGL-3024). This branch generates nothing, so\n // whatever the plan promised BEYOND the source is not in the draft\n // unless the source already had it: a plan reading \"from a copy of the\n // base layout, adding a sidebar\" gets the base layout, the sidebar\n // nowhere in it, and until now reported Done. The copy's own regions\n // answer the plan's, and a copy that cannot be read answers nothing —\n // which is a stop for a person, never a Done.\n const review = await aiCopiedLayoutReview(firestore, { hostId, id: copy.id, plan, name })\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 allowance = await aiDraftAllowanceRefusal(firestore, { kind: 'layout', hostId, org })\n if (allowance) return aiUnspentOutcome(model, { review: aiLimitReview(allowance) })\n\n if (aiJobUsesLayoutLanguage(job)) {\n // ── The header and footer designed in the layout language (AGL-3660) ──\n const siteName = aiLayoutSiteName(job)\n const pages = aiLayoutNavPages(job, inventory)\n const targets = aiLayoutFrameTargets(job, inventory)\n const frameInput: AiLayoutFrameInput = {\n ...aiSiteStyleTokens(job.inputs),\n siteName,\n homeId: aiLayoutHomeId(pages, inventory),\n pages,\n targets,\n // A site brought in may state what its own header and footer state (AGL-4084).\n extend: aiLayoutChecks(inventory, plan, [job.brief, aiLayoutSiteFactsText(job.inputs)].filter(Boolean).join('\\n')),\n design: aiLayoutDesignOf(job, '/'),\n brand: aiLayoutSiteBrandOf(job.inputs),\n storeLinks: aiStoreFrameLinksOf(job.inputs),\n // The frame's own words in the site's language (AGL-3760).\n ...(job.plan?.language ? { language: job.plan.language } : {}),\n }\n // A site brought in whose menu the reader found (AGL-4108): its own\n // header and footer, drawn by code, and no model asked. One that does\n // not pass the layout's checks is designed by the model instead.\n const source = aiLayoutSiteFrameOf(job.inputs)\n if (source) {\n const drawn = aiLayoutSourceFrameBuild({ ...frameInput, source })\n if (drawn.value) {\n throwIfAiJobCanceled(signal)\n const draft = await writeAiDraft(firestore, {\n kind: 'layout',\n hostId,\n id: draftId,\n uid: job.createdBy,\n org,\n name,\n nodes: drawn.value.nodes,\n aiJobId: aiOriginJobId(job),\n now,\n })\n if (draft.ok === false) {\n if (draft.status === 404) throw new Error(`site ${hostId} vanished while its layout was built`)\n return aiUnspentOutcome(model, { review: aiLimitReview(draft.error) })\n }\n return aiUnspentOutcome(model, { outputs: [output(draft, drawn.value.load)] })\n }\n console.error('ai layout: a source frame did not pass its checks; designing it instead', {\n jobId: job.$id,\n violations: drawn.violations.map((violation) => violation.code),\n })\n }\n const result = await runValidatedGeneration<AiLayoutFrameBuilt>(AI_LAYOUT_FRAME_KIND, {\n step: 'job.layout',\n model,\n instructions: aiJobLayoutLanguageInstructions(job),\n inventory,\n messages: [{ role: 'user', content: aiLayoutFramePrompt({ job, siteName, pages, targets }) }],\n tool: AI_LAYOUT_FRAME_TOOL,\n maxTokens: AI_JOB_LAYOUT_LANGUAGE_BUDGET.maxTokens(model),\n ...AI_LAYOUT_LANGUAGE_THINKING,\n check: aiLayoutFrameCheck(frameInput),\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 // Never written once the job is canceled (AGL-3616).\n throwIfAiJobCanceled(signal)\n const draft = await writeAiDraft(firestore, {\n kind: 'layout',\n hostId,\n id: draftId,\n uid: job.createdBy,\n org,\n name,\n nodes: result.value.nodes,\n aiJobId: aiOriginJobId(job),\n now,\n })\n if (draft.ok === false) {\n if (draft.status === 404) throw new Error(`site ${hostId} vanished while its layout was generated`)\n return { ...spent, review: aiLimitReview(draft.error) }\n }\n const note = aiBracketedFactsNote({\n tree: { rootId: result.value.rootId, nodes: result.value.nodes as unknown as AiDoctrineTree['nodes'] },\n inventory,\n })\n return { ...spent, outputs: [output(draft, result.value.load, note)] }\n }\n\n // The pages a site scaffold builds after this layout (AGL-3596): their\n // ids are the plan's, so the header links them by id, written by the\n // platform before the doctrine checks the tree.\n const sitePages = aiLayoutSitePages(job.inputs)\n const result = await runValidatedGeneration('layout', {\n step: 'job.layout',\n model,\n instructions: AI_JOB_LAYOUT_INSTRUCTIONS,\n inventory,\n messages: [{ role: 'user', content: aiJobLayoutPrompt(job, plan, name, sourceLines) }],\n tool: aiDoctrineTreeTool('layout'),\n maxTokens: AI_JOB_LAYOUT_STEP_BUDGET.maxTokens(model),\n ...(AI_ROUTING_TABLE['job.layout'].thinking ? { thinking: AI_ROUTING_TABLE['job.layout'].thinking } : {}),\n ...(AI_ROUTING_TABLE['job.layout'].effort ? { effort: AI_ROUTING_TABLE['job.layout'].effort } : {}),\n extend: aiLayoutChecks(inventory, plan, job.brief),\n // The site's name is written as the token that reads it, and the\n // layout fills the window so a short page keeps its footer at the\n // bottom (AGL-3596).\n complete: (tree: unknown) =>\n aiLayoutWithFullHeight(\n aiLayoutWithSiteName(\n sitePages.length ? aiLayoutWithSitePages(tree, sitePages, { homeScreenIds: aiHomeScreenIds(inventory) }) : tree,\n typeof job.inputs?.['businessName'] === 'string' ? job.inputs['businessName'] : null,\n ),\n ),\n ...(sitePages.length\n ? { context: { screenIds: [...inventory.screens.map((screen) => screen.id), ...sitePages.map((page) => page.id)] } }\n : {}),\n ...(signal ? { signal } : {}),\n })\n const spent = aiGenerationSpent(result)\n if (result.status === 'refused') return { ...spent, refused: true }\n if (result.status === 'needs_input') return { ...spent, review: aiDoctrineReview(result) }\n\n // Never written once the job is canceled (AGL-3616).\n throwIfAiJobCanceled(signal)\n const draft = await writeAiDraft(firestore, {\n kind: 'layout',\n hostId,\n id: draftId,\n uid: job.createdBy,\n org,\n name,\n nodes: result.value.nodes,\n aiJobId: aiOriginJobId(job),\n now,\n })\n if (draft.ok === false) {\n if (draft.status === 404) throw new Error(`site ${hostId} vanished while its layout was generated`)\n return { ...spent, review: aiLimitReview(draft.error) }\n }\n // The facts the brief did not give, which the member fills before publishing (AGL-3056).\n const note = aiBracketedFactsNote({\n tree: { rootId: result.value.rootId, nodes: result.value.nodes as unknown as AiDoctrineTree['nodes'] },\n inventory,\n })\n return { ...spent, outputs: [output(draft, result.value.load, note)] }\n }\n}\n\n/** The layout step, building the components and forms its plan creates before the layout (AGL-3143 §15). */\nexport const runAiJobLayoutStep = aiBuildingPlanCreations('layout', 'job.layout', createAiJobLayoutStep())\n\n/** Registers the layout step and the check a layout job passes before it is created or resumed. */\nexport function registerAiLayoutJob(): void {\n registerAiJobStep('layout', runAiJobLayoutStep, {\n minimumMs: AI_JOB_LAYOUT_STEP_MINIMUM_MS,\n minimumMsFor: aiPlanCreationsRunMinimumMs('layout'),\n })\n registerAiJobAdmission('layout', aiLayoutJobAdmission)\n}\n"],"names":["throwIfAiJobCanceled","aiSiteStyleTokens","CANVAS_ROOT_ELEMENT_ID","duplicateResource","aiHomeScreenIds","AI_STEP_TIERS","AI_ROUTING_TABLE","aiModelForStep","aiDoctrineTreeTool","runValidatedGeneration","walkTree","readSiteInventory","registerAiJobAdmission","aiBuildingPlanCreations","aiJobPlanCreationsRefusal","aiPlanCreationsRunMinimumMs","aiJobDraftId","aiOriginJobId","aiDraftAdmissionRefusal","aiDraftAllowanceRefusal","aiSiteSubdomain","readAiDraft","readAiDraftNodes","writeAiDraft","aiBracketedFactsNote","aiCopySourceLines","aiConfirmedPlan","aiDoctrineReview","aiGenerationSpent","aiJobBriefLine","aiLimitReview","aiModelNodeIds","aiPlacedComponentIds","aiPlanCreation","aiPlanReferenceLines","aiPlanReuseViolations","aiUnspentOutcome","aiPlanRegionViolations","aiPlannedLayoutRegions","aiLayoutSitePages","aiLayoutSitePagesLines","aiLayoutWithSitePages","aiLayoutInventedContactViolations","aiLayoutWithSiteName","aiLayoutWithFullHeight","AI_LAYOUT_FRAME_TOOL","AI_JOB_LAYOUT_LANGUAGE_BUDGET","AI_LAYOUT_FRAME_KIND","aiJobLayoutLanguageInstructions","aiLayoutFrameCheck","aiLayoutFramePrompt","aiLayoutFrameTargets","aiLayoutHomeId","aiLayoutNavPages","aiLayoutSiteBrandOf","aiLayoutSiteFactsText","aiLayoutSiteFrameOf","aiLayoutSiteName","aiLayoutSourceFrameBuild","AI_LAYOUT_LANGUAGE_THINKING","aiJobUsesLayoutLanguage","aiLayoutDesignOf","aiJobStepBudget","registerAiJobStep","aiStoreFrameLinksOf","AI_JOB_LAYOUT_MAX_TOKENS","maxTokens","AI_JOB_LAYOUT_STEP_BUDGET","tier","AI_JOB_LAYOUT_STEP_MINIMUM_MS","minimumMs","AI_JOB_LAYOUT_DEFAULT_NAME","AI_JOB_LAYOUT_INSTRUCTIONS","text","join","aiJobLayoutPrompt","job","plan","name","source","inputs","NAVIGATION_NAME","FOOTER_NAME","isLink","node","componentId","props","aiLayoutReuseCheck","inventory","components","navigation","filter","component","test","tree","placed","violations","rootId","nodes","length","some","has","id","visits","slot","findIndex","header","slice","links","map","push","rule","code","message","nodeIds","sourceIds","aiLayoutChecks","brief","reuse","violation","aiCopiedLayoutReview","firestore","input","planned","regions","unreadable","stored","kind","hostId","reason","findings","aiLayoutJobAdmission","context","orgId","org","ownCheck","createAiJobLayoutStep","deps","readInventory","duplicate","now","signal","modelFor","orgSnapshot","Error","output","draft","load","note","resource","versionId","hostSubdomain","label","model","draftId","written","outputs","Promise","all","collection","doc","get","data","creation","duplicateOf","layouts","find","layout","undefined","sourceReview","sourceShort","Boolean","sourceTree","sourceLines","copy","sourceId","uid","createdBy","attemptKey","ok","review","status","error","allowance","siteName","pages","targets","frameInput","homeId","extend","design","brand","storeLinks","language","drawn","value","aiJobId","console","jobId","$id","result","step","instructions","messages","role","content","tool","check","spent","refused","sitePages","thinking","effort","complete","homeScreenIds","screenIds","screens","screen","page","runAiJobLayoutStep","registerAiLayoutJob","minimumMsFor"],"mappings":"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"}
@@ -375,30 +375,40 @@ export interface AiLayoutPageRunInput {
375
375
  */
376
376
  export declare function aiRunLayoutPage(input: AiLayoutPageRunInput): Promise<AiValidatedGeneration<AiLayoutPageBuilt>>;
377
377
  /**
378
- * What an arranged page's answer is told (AGL-4084), cached: the page's words
379
- * are the source's and fixed, and the answer only lays them out. The tool has
380
- * no field for a word of copy, so a rewording has nowhere to go.
378
+ * What a main page's design answer is told (AGL-4107), cached: the page's
379
+ * words and structure are the source's and fixed, read by code, and the
380
+ * answer only chooses how its sections are drawn. The tool has no field for
381
+ * a word of copy, a line or a block, so nothing it says can reword or move
382
+ * one.
381
383
  */
382
- export declare const AI_LAYOUT_ARRANGE_TEXT: string;
383
- /** The arranged page's cached instructions, and the rule that fenced text is data. */
384
- export declare const AI_JOB_PAGE_ARRANGE_INSTRUCTIONS: readonly AiSystemBlock[];
385
- /** An arranged page's user turn: the page, the kind of site it is, and its numbered lines, fenced. */
386
- export declare function aiLayoutArrangePrompt(input: {
384
+ export declare const AI_LAYOUT_DESIGN_TEXT: string;
385
+ /** The design answer's cached instructions, and the rule that fenced text is data. */
386
+ export declare const AI_JOB_PAGE_DESIGN_INSTRUCTIONS: readonly AiSystemBlock[];
387
+ /** A design answer's user turn: the page, the kind of site it is, and its structure, the sections' names fenced. */
388
+ export declare function aiLayoutDesignPrompt(input: {
387
389
  job: Pick<AiJob, 'inputs' | '$id'>;
388
390
  screen: Pick<AiBuildPlanScreen, 'title' | 'slug' | 'sections'>;
389
- doc: Pick<AiSiteImportDocument, 'blocks' | 'title' | 'url'>;
391
+ doc: Pick<AiSiteImportDocument, 'blocks' | 'title' | 'url' | 'layout'>;
390
392
  }): string;
391
393
  /**
392
- * The check a page brought in is held to (AGL-4084): the arrangement read
393
- * (or none, for a page copied as it is), the source's lines substituted
394
- * into it, the page stored and checked whole as a page of the owner's own
395
- * words. Nothing in an answer can change a word.
394
+ * The check a page brought in is held to (AGL-4084, AGL-4107): its layout
395
+ * compiled by the site compiler with every word as written — with the design
396
+ * answer where one is given — stored, and checked whole as a page of its
397
+ * owner's own words. An answer whose design cannot be drawn is set aside and
398
+ * the page is drawn as its source lays it out, so no answer is asked again
399
+ * for what code decides. Nothing in an answer can change a word.
396
400
  */
397
401
  export declare function aiImportedPageCheck(input: Pick<AiLayoutPageCheckInput, 'screen' | 'sectionIds' | 'targets' | 'context' | 'motion' | 'design'> & {
398
402
  doc: AiSiteImportDocument;
403
+ language?: string;
399
404
  }): (answer: Record<string, unknown> | null) => {
400
405
  value: AiLayoutPageBuilt | null;
401
406
  violations: AiDoctrineViolation[];
402
407
  };
403
- /** The most an arrangement answer runs to: a few hundred tokens of line numbers, inside a page's ceiling. */
404
- export declare const AI_LAYOUT_ARRANGE_KIND = "layout-page";
408
+ /** The design answer's kind of generation, which shares the page's ceilings. */
409
+ export declare const AI_LAYOUT_DESIGN_KIND = "layout-page";
410
+ /**
411
+ * The most a design answer runs to (AGL-4107): a few tokens a section and a
412
+ * group, with room to think, inside a page's ceiling on the balanced tier.
413
+ */
414
+ export declare const AI_LAYOUT_DESIGN_MAX_TOKENS = 3000;
@@ -21,7 +21,7 @@ import { aiHomeScreenIds } from "../model/ai-site-inventory.js";
21
21
  import { AI_LAYOUT_POST_ADDRESS_TOKENS, aiCompileLayoutPage } from "../layout-language/ai-layout-compiler.js";
22
22
  import { aiSiteMotionStyle, aiWithSiteMotion } from "../layout-language/ai-layout-motion.js";
23
23
  import { aiLayoutListingAt, aiLayoutListingsOf, aiLayoutRecordHref } from "../layout-language/ai-layout-listings.js";
24
- import { AI_LAYOUT_ARRANGE_TOOL, AI_LAYOUT_LANGUAGE_TEXT, AI_LAYOUT_PAGE_TOOL, aiReadLayoutArrangement, aiReadLayoutPage } from "../layout-language/ai-layout-language.js";
24
+ import { AI_LAYOUT_DESIGN_TOOL, AI_LAYOUT_LANGUAGE_TEXT, AI_LAYOUT_PAGE_TOOL, aiReadLayoutDesign, aiReadLayoutPage } from "../layout-language/ai-layout-language.js";
25
25
  import { aiLayoutCopyCheck, aiLayoutWithoutSections } from "../layout-language/ai-layout-gaps.js";
26
26
  import { aiLayoutPromisedCopyViolations, aiLayoutPromisedSections } from "../layout-language/ai-layout-promised-copy.js";
27
27
  import { aiLayoutStoredTree } from "../layout-language/ai-layout-store.js";
@@ -38,7 +38,8 @@ import { aiJobBriefLine, aiPlanReferenceLines } from "./ai-job-generation.js";
38
38
  import { aiLayoutIsHomeSlug, aiLayoutSiteAliases, aiLayoutSitePages } from "./ai-job-layout-site-pages.js";
39
39
  import { aiLayoutInventedContactViolations } from "./ai-layout-site-facts.js";
40
40
  import { aiSiteContactOf } from "../model/ai-site-details.js";
41
- import { AI_SITE_IMPORT_ARRANGE_MAX_CHARS, aiCompileImportedDocument, aiSiteImportArrangeText, aiSiteImportDocLines, aiSiteImportDocumentOf } from "./ai-site-import-document.js";
41
+ import { aiSiteImportDocumentOf } from "./ai-site-import-document.js";
42
+ import { AI_SITE_IMPORT_DESIGN_MAX_CHARS, aiCompileImportedPage, aiSiteImportDesignText, aiSiteImportSectionNames } from "./ai-site-import-compile.js";
42
43
  /**
43
44
  * A page built in the compact layout language (AGL-3660): the page step's
44
45
  * pass for a site's page units — a guided start's and a build's — in place of
@@ -861,68 +862,89 @@ export const AI_JOB_PAGE_LANGUAGE_BUDGET = aiJobStepBudget({
861
862
  return _extends({}, second, spentTogether(first, second));
862
863
  }
863
864
  /*------------------------------------------
864
- * A page brought in: copied, or arranged (AGL-4084)
865
+ * A page brought in: built by the site compiler (AGL-4084, AGL-4107)
865
866
  *-----------------------------------------*/ /**
866
- * What an arranged page's answer is told (AGL-4084), cached: the page's words
867
- * are the source's and fixed, and the answer only lays them out. The tool has
868
- * no field for a word of copy, so a rewording has nowhere to go.
869
- */ export const AI_LAYOUT_ARRANGE_TEXT = [
870
- "You arrange a page brought in from its owner's own website. Arrange; keep every line as written; add nothing; reword nothing. Its words are fixed: every line of the page is shown numbered, and the platform places each line you name exactly as its source wrote it, in the site's theme. You never write a word of copy.",
867
+ * What a main page's design answer is told (AGL-4107), cached: the page's
868
+ * words and structure are the source's and fixed, read by code, and the
869
+ * answer only chooses how its sections are drawn. The tool has no field for
870
+ * a word of copy, a line or a block, so nothing it says can reword or move
871
+ * one.
872
+ */ export const AI_LAYOUT_DESIGN_TEXT = [
873
+ "You design how a page brought in from its owner's own website is drawn. Its words, its sections and what each section shows are fixed: the platform reads them from the source and places every line exactly as written, in the site's theme. You choose only the design; you never write a word.",
871
874
  '',
872
- 'Answer with submit_arrangement: one entry in sections for each section of the page, in order. A section has:',
873
- '- band: plain (the page background), soft (a light band) or dark. Vary the bands down the page so neighboring sections read apart, and keep dark to one or two sections a page.',
874
- '- cols: [] for one column, or relative column widths for a row, such as [1, 1] or [7, 5]; at most 4.',
875
- '- align: start or center.',
876
- '- blocks: top to bottom. A block has kind, col (-1 to span the section, or the column of the row it sits in, 0 first), from (the numbers of the lines it shows, in order), style (primary, secondary or quiet for a button, none otherwise) and items.',
877
- '',
878
- 'Block kinds: heading, eyebrow, lede, text and note show their lines one under another; list shows each of its lines as an item; cards, steps, stats and faq show items, each item\'s from its title line first and then its text lines (from is then []); button is a line that is a link; image is a line that is a picture.',
879
- "Every line goes in exactly one block of its own section. Name no line twice and no line of another section. A line you leave out is still shown, at the end of its section, so leave none out on purpose. The page's first section opens with its heading line."
875
+ 'Answer with submit_design: one entry in sections for each section listed, in order. A section has:',
876
+ '- hero: for the first section, where it opens the page with a picture: split or bleed (the words beside the picture), cover or poster (the words over the picture, dimmed), panel (the words on a card over the picture), column or editorial (the words above a wide picture). Choose the one closest to how the source places its picture unless another reads better for this site; none keeps the source\'s own arrangement. Every other section: none.',
877
+ "- band: source keeps the section's own background. Only a section with no band of its own may take plain, soft or dark, so neighboring sections read apart; every other section: source.",
878
+ '- align: source, start or center.',
879
+ "- groups: one entry for each of the section's groups, in the order they are numbered. kind: source, or cards, steps, stats or list where the group plainly is one (numbered items are steps, a figure over its label is stats). style: cards (the theme's cards), ruled (open columns under a thin rule), icons (open columns led by their icons, only where each item has one), pictures (each item's picture over its words, open) or articles (the same, in cards), pictures and articles only where each item has a picture.",
880
+ 'Draw the page at least as well as its source: keep its boxes where it boxes its items, its icons where it draws them and its pictures where it shows them.'
880
881
  ].join('\n');
881
- /** The arranged page's cached instructions, and the rule that fenced text is data. */ export const AI_JOB_PAGE_ARRANGE_INSTRUCTIONS = [
882
+ /** The design answer's cached instructions, and the rule that fenced text is data. */ export const AI_JOB_PAGE_DESIGN_INSTRUCTIONS = [
882
883
  {
883
- text: AI_LAYOUT_ARRANGE_TEXT,
884
+ text: AI_LAYOUT_DESIGN_TEXT,
884
885
  cacheBreakpoint: true
885
886
  },
886
887
  {
887
888
  text: UNTRUSTED_TEXT_RULE
888
889
  }
889
890
  ];
890
- /** An arranged page's user turn: the page, the kind of site it is, and its numbered lines, fenced. */ export function aiLayoutArrangePrompt(input) {
891
- const { sections } = aiSiteImportDocLines(input.doc.blocks, input.doc.title);
891
+ /** A design answer's user turn: the page, the kind of site it is, and its structure, the sections' names fenced. */ export function aiLayoutDesignPrompt(input) {
892
+ const count = aiSiteImportSectionNames(input.doc).length;
892
893
  return [
893
894
  `Page: "${input.screen.title}" at ${input.screen.slug}`,
894
895
  ...aiSiteKindDesignLines(input.job.inputs),
895
- `Its ${sections.length === 1 ? 'one section' : `${sections.length} sections`}, each with its numbered lines:`,
896
- fenceUntrustedText(`The owner's page ${input.doc.url}`, aiSiteImportArrangeText(input.doc), {
897
- maxChars: AI_SITE_IMPORT_ARRANGE_MAX_CHARS
896
+ `Its ${count === 1 ? 'one section' : `${count} sections`}, each with what it shows:`,
897
+ fenceUntrustedText(`The structure of the owner's page ${input.doc.url}`, aiSiteImportDesignText(input.doc), {
898
+ maxChars: AI_SITE_IMPORT_DESIGN_MAX_CHARS
898
899
  }),
899
900
  `Design seed: ${input.job.$id.slice(-6)}.`
900
901
  ].join('\n');
901
902
  }
903
+ /** The site a page brought in is drawn for: its design, else the page's own, so its hero has a home to open (AGL-4107). */ function importedDesign(design, screen) {
904
+ return design != null ? design : {
905
+ kind: 'business',
906
+ seed: 0,
907
+ home: aiLayoutIsHomeSlug(screen.slug)
908
+ };
909
+ }
902
910
  /**
903
- * The check a page brought in is held to (AGL-4084): the arrangement read
904
- * (or none, for a page copied as it is), the source's lines substituted
905
- * into it, the page stored and checked whole as a page of the owner's own
906
- * words. Nothing in an answer can change a word.
911
+ * The check a page brought in is held to (AGL-4084, AGL-4107): its layout
912
+ * compiled by the site compiler with every word as written — with the design
913
+ * answer where one is given — stored, and checked whole as a page of its
914
+ * owner's own words. An answer whose design cannot be drawn is set aside and
915
+ * the page is drawn as its source lays it out, so no answer is asked again
916
+ * for what code decides. Nothing in an answer can change a word.
907
917
  */ export function aiImportedPageCheck(input) {
908
918
  const context = _extends({}, aiLayoutListingContext(input.context, input.targets), {
909
919
  ownWords: true
910
920
  });
911
- return (answer)=>{
921
+ const plan = {
922
+ title: input.screen.title,
923
+ sections: input.screen.sections.map((section)=>({
924
+ name: section.name,
925
+ uses: [],
926
+ items: 0
927
+ }))
928
+ };
929
+ const build = (design)=>{
912
930
  var _ref, _input_motion;
913
- const arrangement = answer && input.doc.build === 'arranged' ? aiReadLayoutArrangement(answer, input.sectionIds.length) : null;
914
- const compiled = aiCompileImportedDocument({
931
+ const compiled = aiCompileImportedPage(_extends({
915
932
  doc: input.doc,
916
933
  sectionIds: input.sectionIds,
917
- pages: input.targets.pages,
918
- arrangement
919
- });
934
+ plan,
935
+ targets: input.targets,
936
+ design: importedDesign(input.design, input.screen),
937
+ answer: design
938
+ }, input.language ? {
939
+ language: input.language
940
+ } : {}));
920
941
  const stored = aiLayoutStoredTree(compiled.tree, 'screen', context, input.sectionIds);
921
942
  if (stored.ok === false) {
922
943
  console.error('ai imported page: a compiled page did not store', {
923
944
  error: stored.error
924
945
  });
925
946
  return {
947
+ compiled,
926
948
  value: null,
927
949
  violations: [
928
950
  {
@@ -942,10 +964,12 @@ export const AI_JOB_PAGE_LANGUAGE_BUDGET = aiJobStepBudget({
942
964
  repeatsCompiled: true
943
965
  }));
944
966
  if (report.violations.length) return {
967
+ compiled,
945
968
  value: null,
946
969
  violations: report.violations
947
970
  };
948
971
  return {
972
+ compiled,
949
973
  value: {
950
974
  nodes: aiWithSiteMotion(stored.nodes, (_ref = (_input_motion = input.motion) != null ? _input_motion : input.design) != null ? _ref : {
951
975
  seed: 0
@@ -953,15 +977,39 @@ export const AI_JOB_PAGE_LANGUAGE_BUDGET = aiJobStepBudget({
953
977
  sectionIds: input.sectionIds
954
978
  }),
955
979
  load: report.load,
956
- settled: [],
980
+ settled: compiled.settled,
957
981
  dropped: [],
958
- items: input.sectionIds.map(()=>0)
982
+ items: compiled.sectionRoots.map(()=>0)
959
983
  },
960
984
  violations: []
961
985
  };
962
986
  };
987
+ return (answer)=>{
988
+ var _ref;
989
+ var _designed_violations_;
990
+ const design = answer ? aiReadLayoutDesign(answer, input.sectionIds.length) : null;
991
+ const designed = build(design);
992
+ if (designed.value || !design) return {
993
+ value: designed.value,
994
+ violations: designed.violations
995
+ };
996
+ // The answer's design could not be drawn: the page as its source lays it out.
997
+ const plain = build(null);
998
+ if (plain.value) plain.value.settled.push({
999
+ at: 'design',
1000
+ what: `the design answer set aside: ${(_ref = (_designed_violations_ = designed.violations[0]) == null ? void 0 : _designed_violations_.code) != null ? _ref : 'not drawn'}`
1001
+ });
1002
+ return {
1003
+ value: plain.value,
1004
+ violations: plain.violations
1005
+ };
1006
+ };
963
1007
  }
964
- /** The most an arrangement answer runs to: a few hundred tokens of line numbers, inside a page's ceiling. */ export const AI_LAYOUT_ARRANGE_KIND = AI_LAYOUT_PAGE_KIND;
1008
+ /** The design answer's kind of generation, which shares the page's ceilings. */ export const AI_LAYOUT_DESIGN_KIND = AI_LAYOUT_PAGE_KIND;
1009
+ /**
1010
+ * The most a design answer runs to (AGL-4107): a few tokens a section and a
1011
+ * group, with room to think, inside a page's ceiling on the balanced tier.
1012
+ */ export const AI_LAYOUT_DESIGN_MAX_TOKENS = 3000;
965
1013
  const NO_USAGE = {
966
1014
  inputTokens: 0,
967
1015
  outputTokens: 0,
@@ -969,62 +1017,68 @@ const NO_USAGE = {
969
1017
  cacheWriteTokens: 0
970
1018
  };
971
1019
  /**
972
- * A page brought in (AGL-4084). Copied as it is: compiled by code, no model
973
- * at all, nothing spent. Arranged: one answer on the page step's tier that
974
- * lays the numbered lines out, every word still the source's.
1020
+ * A page brought in (AGL-4084, AGL-4107). Its layout is compiled first with
1021
+ * no model: a page of a collection is that page, nothing spent; a page that
1022
+ * cannot be built so is reviewed before anything is spent on it. A main page
1023
+ * then asks one design answer on the page step's balanced tier and is drawn
1024
+ * with it, every word still the source's.
975
1025
  */ async function aiRunImportedPage(input, doc) {
976
1026
  var _input_observe;
977
- const check = aiImportedPageCheck({
1027
+ const check = aiImportedPageCheck(_extends({
978
1028
  screen: input.screen,
979
1029
  sectionIds: input.sectionIds,
980
1030
  targets: input.targets,
981
1031
  context: input.context,
982
1032
  design: aiLayoutDesignOf(input.job, input.screen.slug),
983
- motion: aiLayoutMotionSiteOf(input.job, input.screen.slug),
1033
+ motion: aiLayoutMotionSiteOf(input.job, input.screen.slug)
1034
+ }, input.plan.language ? {
1035
+ language: input.plan.language
1036
+ } : {}, {
984
1037
  doc
985
- });
986
- if (doc.build === 'copied') {
987
- const spend = {
988
- attempts: 0,
989
- usage: NO_USAGE,
990
- estCostUsd: 0,
991
- model: input.model,
992
- stopReason: null,
993
- effort: null
994
- };
995
- const result = check(null);
996
- return result.value ? _extends({}, spend, {
997
- status: 'ok',
998
- value: result.value
999
- }) : _extends({}, spend, {
1038
+ }));
1039
+ const spend = {
1040
+ attempts: 0,
1041
+ usage: NO_USAGE,
1042
+ estCostUsd: 0,
1043
+ model: input.model,
1044
+ stopReason: null,
1045
+ effort: null
1046
+ };
1047
+ const plain = check(null);
1048
+ if (!plain.value) {
1049
+ return _extends({}, spend, {
1000
1050
  status: 'needs_input',
1001
- violations: result.violations,
1002
- message: aiDoctrineNeedsInputMessage(result.violations),
1051
+ violations: plain.violations,
1052
+ message: aiDoctrineNeedsInputMessage(plain.violations),
1003
1053
  answer: null
1004
1054
  });
1005
1055
  }
1056
+ if (doc.build === 'copied') return _extends({}, spend, {
1057
+ status: 'ok',
1058
+ value: plain.value
1059
+ });
1006
1060
  const observe = (_input_observe = input.observe) != null ? _input_observe : (inner)=>inner;
1007
- return runValidatedGeneration(AI_LAYOUT_ARRANGE_KIND, _extends({
1061
+ return runValidatedGeneration(AI_LAYOUT_DESIGN_KIND, _extends({
1008
1062
  step: 'job.page',
1009
1063
  model: input.model,
1010
- instructions: AI_JOB_PAGE_ARRANGE_INSTRUCTIONS,
1011
- // The lines are the whole question: no lookup of the site is offered.
1064
+ instructions: AI_JOB_PAGE_DESIGN_INSTRUCTIONS,
1065
+ // The structure is the whole question: no lookup of the site is offered.
1012
1066
  inventory: null,
1013
1067
  messages: [
1014
1068
  {
1015
1069
  role: 'user',
1016
- content: aiLayoutArrangePrompt({
1070
+ content: aiLayoutDesignPrompt({
1017
1071
  job: input.job,
1018
1072
  screen: input.screen,
1019
1073
  doc
1020
1074
  })
1021
1075
  }
1022
1076
  ],
1023
- tool: AI_LAYOUT_ARRANGE_TOOL,
1024
- maxTokens: AI_JOB_PAGE_LANGUAGE_BUDGET.maxTokens(input.model),
1077
+ tool: AI_LAYOUT_DESIGN_TOOL,
1078
+ maxTokens: Math.min(AI_LAYOUT_DESIGN_MAX_TOKENS, AI_JOB_PAGE_LANGUAGE_BUDGET.maxTokens(input.model)),
1025
1079
  cutOff: {
1026
- noun: 'page',
1027
- smaller: 'Use fewer blocks: give each block more of its lines.'
1080
+ noun: 'design',
1081
+ smaller: 'Answer with the choices alone.'
1028
1082
  }
1029
1083
  }, AI_LAYOUT_LANGUAGE_THINKING, {
1030
1084
  check: observe((answer)=>check(answer))