@aglyn/plugins-ai 1.0.0-beta.233 → 1.0.0-beta.235

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 (199) hide show
  1. package/package.json +17 -17
  2. package/src/lib/activity/ai-activity-actions.d.ts +25 -0
  3. package/src/lib/activity/ai-activity-actions.js +85 -4
  4. package/src/lib/activity/ai-activity-actions.js.map +1 -1
  5. package/src/lib/components/ai-brief-dialog.component.js +46 -2
  6. package/src/lib/components/ai-brief-dialog.component.js.map +1 -1
  7. package/src/lib/components/ai-campaign-create.component.js +1 -1
  8. package/src/lib/components/ai-campaign-create.component.js.map +1 -1
  9. package/src/lib/components/ai-credits-card.component.js +3 -3
  10. package/src/lib/components/ai-credits-card.component.js.map +1 -1
  11. package/src/lib/components/ai-crm-email-draft.component.js +1 -1
  12. package/src/lib/components/ai-crm-email-draft.component.js.map +1 -1
  13. package/src/lib/components/ai-crm-import-mapping.component.js +1 -1
  14. package/src/lib/components/ai-crm-import-mapping.component.js.map +1 -1
  15. package/src/lib/components/ai-crm-record-card.component.js +1 -1
  16. package/src/lib/components/ai-crm-record-card.component.js.map +1 -1
  17. package/src/lib/components/ai-describe-automation.component.js +4 -2
  18. package/src/lib/components/ai-describe-automation.component.js.map +1 -1
  19. package/src/lib/components/ai-describe-org-automation.component.js +1 -1
  20. package/src/lib/components/ai-describe-org-automation.component.js.map +1 -1
  21. package/src/lib/components/ai-experiment-cards.component.js +2 -2
  22. package/src/lib/components/ai-experiment-cards.component.js.map +1 -1
  23. package/src/lib/components/ai-explain-automation.component.js +1 -1
  24. package/src/lib/components/ai-explain-automation.component.js.map +1 -1
  25. package/src/lib/components/ai-insight-card.component.js +3 -1
  26. package/src/lib/components/ai-insight-card.component.js.map +1 -1
  27. package/src/lib/components/ai-job-links.d.ts +6 -0
  28. package/src/lib/components/ai-job-links.js +9 -0
  29. package/src/lib/components/ai-job-links.js.map +1 -1
  30. package/src/lib/components/ai-jobs-list-page.component.d.ts +6 -1
  31. package/src/lib/components/ai-jobs-list-page.component.js +15 -2
  32. package/src/lib/components/ai-jobs-list-page.component.js.map +1 -1
  33. package/src/lib/components/ai-logic.component.js +5 -3
  34. package/src/lib/components/ai-logic.component.js.map +1 -1
  35. package/src/lib/components/ai-media-create.component.js +88 -16
  36. package/src/lib/components/ai-media-create.component.js.map +1 -1
  37. package/src/lib/components/ai-model-selector.component.js +8 -0
  38. package/src/lib/components/ai-model-selector.component.js.map +1 -1
  39. package/src/lib/components/ai-overlay-cards.component.js +2 -2
  40. package/src/lib/components/ai-overlay-cards.component.js.map +1 -1
  41. package/src/lib/components/ai-product-copy-card.component.js +2 -1
  42. package/src/lib/components/ai-product-copy-card.component.js.map +1 -1
  43. package/src/lib/components/ai-products-hub-card.component.js +2 -1
  44. package/src/lib/components/ai-products-hub-card.component.js.map +1 -1
  45. package/src/lib/components/ai-revise-automation.component.js +1 -1
  46. package/src/lib/components/ai-revise-automation.component.js.map +1 -1
  47. package/src/lib/components/ai-seo-fields-card.component.js +7 -3
  48. package/src/lib/components/ai-seo-fields-card.component.js.map +1 -1
  49. package/src/lib/components/ai-site-batch-card.component.js +7 -4
  50. package/src/lib/components/ai-site-batch-card.component.js.map +1 -1
  51. package/src/lib/components/ai-site-build-page.component.js +48 -6
  52. package/src/lib/components/ai-site-build-page.component.js.map +1 -1
  53. package/src/lib/components/ai-site-seo-start-card.component.js +7 -4
  54. package/src/lib/components/ai-site-seo-start-card.component.js.map +1 -1
  55. package/src/lib/components/ai-site-start-card.component.js +152 -36
  56. package/src/lib/components/ai-site-start-card.component.js.map +1 -1
  57. package/src/lib/components/ai-theme-proposal-card.component.js +1 -0
  58. package/src/lib/components/ai-theme-proposal-card.component.js.map +1 -1
  59. package/src/lib/components/ai-upsell-dialog.component.js +17 -3
  60. package/src/lib/components/ai-upsell-dialog.component.js.map +1 -1
  61. package/src/lib/components/ai-usage-strip.component.js +13 -2
  62. package/src/lib/components/ai-usage-strip.component.js.map +1 -1
  63. package/src/lib/components/assist-build-card.component.js +20 -3
  64. package/src/lib/components/assist-build-card.component.js.map +1 -1
  65. package/src/lib/components/assist-jobs-drawer.component.js +7 -1
  66. package/src/lib/components/assist-jobs-drawer.component.js.map +1 -1
  67. package/src/lib/components/billing-ai-allotments.component.js +2 -2
  68. package/src/lib/components/billing-ai-allotments.component.js.map +1 -1
  69. package/src/lib/components/billing-ai-top-users.component.js +1 -0
  70. package/src/lib/components/billing-ai-top-users.component.js.map +1 -1
  71. package/src/lib/components/billing-assist-overage-card.component.js +2 -2
  72. package/src/lib/components/billing-assist-overage-card.component.js.map +1 -1
  73. package/src/lib/components/host-ai-allotments.component.js +2 -2
  74. package/src/lib/components/host-ai-allotments.component.js.map +1 -1
  75. package/src/lib/components/member-ai-allotment-card.component.js +2 -2
  76. package/src/lib/components/member-ai-allotment-card.component.js.map +1 -1
  77. package/src/lib/components/staff-org-ai-card.component.js +1 -0
  78. package/src/lib/components/staff-org-ai-card.component.js.map +1 -1
  79. package/src/lib/components/staff-org-ai-conversations.component.d.ts +9 -3
  80. package/src/lib/components/staff-org-ai-conversations.component.js +174 -83
  81. package/src/lib/components/staff-org-ai-conversations.component.js.map +1 -1
  82. package/src/lib/components/staff-user-ai-usage-card.component.js +2 -1
  83. package/src/lib/components/staff-user-ai-usage-card.component.js.map +1 -1
  84. package/src/lib/jobs/ai-job-admission.d.ts +2 -1
  85. package/src/lib/jobs/ai-job-admission.js.map +1 -1
  86. package/src/lib/jobs/ai-job-email-step.d.ts +39 -0
  87. package/src/lib/jobs/ai-job-email-step.js +116 -5
  88. package/src/lib/jobs/ai-job-email-step.js.map +1 -1
  89. package/src/lib/jobs/ai-job-layout-language.d.ts +3 -0
  90. package/src/lib/jobs/ai-job-layout-language.js +6 -1
  91. package/src/lib/jobs/ai-job-layout-language.js.map +1 -1
  92. package/src/lib/jobs/ai-job-layout-site-pages.d.ts +14 -0
  93. package/src/lib/jobs/ai-job-layout-site-pages.js +43 -11
  94. package/src/lib/jobs/ai-job-layout-site-pages.js.map +1 -1
  95. package/src/lib/jobs/ai-job-layout-step.js +3 -2
  96. package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
  97. package/src/lib/jobs/ai-job-page-language.d.ts +26 -1
  98. package/src/lib/jobs/ai-job-page-language.js +75 -13
  99. package/src/lib/jobs/ai-job-page-language.js.map +1 -1
  100. package/src/lib/jobs/ai-job-page-step.d.ts +3 -0
  101. package/src/lib/jobs/ai-job-page-step.js +32 -7
  102. package/src/lib/jobs/ai-job-page-step.js.map +1 -1
  103. package/src/lib/jobs/ai-job-plan-step.d.ts +34 -0
  104. package/src/lib/jobs/ai-job-plan-step.js +106 -14
  105. package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
  106. package/src/lib/jobs/ai-job-site-content.d.ts +81 -13
  107. package/src/lib/jobs/ai-job-site-content.js +210 -43
  108. package/src/lib/jobs/ai-job-site-content.js.map +1 -1
  109. package/src/lib/jobs/ai-job-site-step.d.ts +19 -0
  110. package/src/lib/jobs/ai-job-site-step.js +152 -51
  111. package/src/lib/jobs/ai-job-site-step.js.map +1 -1
  112. package/src/lib/jobs/ai-jobs.d.ts +8 -4
  113. package/src/lib/jobs/ai-jobs.js +19 -2
  114. package/src/lib/jobs/ai-jobs.js.map +1 -1
  115. package/src/lib/jobs/ai-layout-stock-photos.d.ts +98 -0
  116. package/src/lib/jobs/ai-layout-stock-photos.js +248 -0
  117. package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -0
  118. package/src/lib/jobs/ai-site-publish.d.ts +11 -0
  119. package/src/lib/jobs/ai-site-publish.js +31 -1
  120. package/src/lib/jobs/ai-site-publish.js.map +1 -1
  121. package/src/lib/jobs/fixtures/ai-juniper-portfolio-page-recording.d.ts +151 -0
  122. package/src/lib/jobs/fixtures/ai-juniper-portfolio-page-recording.js +183 -0
  123. package/src/lib/jobs/fixtures/ai-juniper-portfolio-page-recording.js.map +1 -0
  124. package/src/lib/jobs/fixtures/ai-slow-roads-site-plan-recording.d.ts +27 -0
  125. package/src/lib/jobs/fixtures/ai-slow-roads-site-plan-recording.js +192 -0
  126. package/src/lib/jobs/fixtures/ai-slow-roads-site-plan-recording.js.map +1 -0
  127. package/src/lib/jobs/fixtures/ai-yoga-site-plan-recording.d.ts +27 -0
  128. package/src/lib/jobs/fixtures/ai-yoga-site-plan-recording.js +222 -0
  129. package/src/lib/jobs/fixtures/ai-yoga-site-plan-recording.js.map +1 -0
  130. package/src/lib/layout-language/ai-layout-compiler.d.ts +37 -0
  131. package/src/lib/layout-language/ai-layout-compiler.js +1003 -19
  132. package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
  133. package/src/lib/layout-language/ai-layout-design.d.ts +114 -0
  134. package/src/lib/layout-language/ai-layout-design.js +209 -0
  135. package/src/lib/layout-language/ai-layout-design.js.map +1 -0
  136. package/src/lib/layout-language/ai-layout-frame.d.ts +12 -0
  137. package/src/lib/layout-language/ai-layout-frame.js +33 -8
  138. package/src/lib/layout-language/ai-layout-frame.js.map +1 -1
  139. package/src/lib/layout-language/ai-layout-gaps.js +18 -2
  140. package/src/lib/layout-language/ai-layout-gaps.js.map +1 -1
  141. package/src/lib/layout-language/ai-layout-language.d.ts +2 -0
  142. package/src/lib/layout-language/ai-layout-language.js +15 -1
  143. package/src/lib/layout-language/ai-layout-language.js.map +1 -1
  144. package/src/lib/layout-language/ai-layout-links.d.ts +36 -0
  145. package/src/lib/layout-language/ai-layout-links.js +26 -2
  146. package/src/lib/layout-language/ai-layout-links.js.map +1 -1
  147. package/src/lib/layout-language/ai-layout-listings.d.ts +125 -0
  148. package/src/lib/layout-language/ai-layout-listings.js +187 -0
  149. package/src/lib/layout-language/ai-layout-listings.js.map +1 -0
  150. package/src/lib/layout-language/ai-layout-pictures.d.ts +11 -5
  151. package/src/lib/layout-language/ai-layout-pictures.js +80 -17
  152. package/src/lib/layout-language/ai-layout-pictures.js.map +1 -1
  153. package/src/lib/model/ai-job-notice.d.ts +9 -1
  154. package/src/lib/model/ai-job-notice.js +23 -0
  155. package/src/lib/model/ai-job-notice.js.map +1 -1
  156. package/src/lib/model/ai-plan-capabilities.d.ts +7 -0
  157. package/src/lib/model/ai-plan-capabilities.js.map +1 -1
  158. package/src/lib/model/ai-site-build-progress.d.ts +12 -2
  159. package/src/lib/model/ai-site-build-progress.js +104 -15
  160. package/src/lib/model/ai-site-build-progress.js.map +1 -1
  161. package/src/lib/model/ai-site-job.d.ts +236 -24
  162. package/src/lib/model/ai-site-job.js +269 -22
  163. package/src/lib/model/ai-site-job.js.map +1 -1
  164. package/src/lib/model/ai-site-kinds.d.ts +30 -8
  165. package/src/lib/model/ai-site-kinds.js +735 -116
  166. package/src/lib/model/ai-site-kinds.js.map +1 -1
  167. package/src/lib/model/ai-site-look.d.ts +21 -0
  168. package/src/lib/model/ai-site-look.js +63 -14
  169. package/src/lib/model/ai-site-look.js.map +1 -1
  170. package/src/lib/model/ai-site-start-seo.d.ts +30 -0
  171. package/src/lib/model/ai-site-start-seo.js +132 -11
  172. package/src/lib/model/ai-site-start-seo.js.map +1 -1
  173. package/src/lib/model/ai-site-start.d.ts +2 -2
  174. package/src/lib/model/ai-site-start.js +3 -3
  175. package/src/lib/model/ai-site-start.js.map +1 -1
  176. package/src/lib/plugin.js +15 -3
  177. package/src/lib/plugin.js.map +1 -1
  178. package/src/lib/runtime/ai-doctrine-validators.d.ts +32 -1
  179. package/src/lib/runtime/ai-doctrine-validators.js +227 -4
  180. package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
  181. package/src/lib/runtime/ai-doctrine.js +5 -3
  182. package/src/lib/runtime/ai-doctrine.js.map +1 -1
  183. package/src/lib/runtime/ai-palette.generated.d.ts +2 -0
  184. package/src/lib/runtime/ai-palette.generated.js +31 -5
  185. package/src/lib/runtime/ai-palette.generated.js.map +1 -1
  186. package/src/lib/server/ai-admin-conversations.d.ts +9 -0
  187. package/src/lib/server/ai-admin-conversations.js +151 -9
  188. package/src/lib/server/ai-admin-conversations.js.map +1 -1
  189. package/src/lib/server/ai-jobs-resume.js.map +1 -1
  190. package/src/lib/server/ai-jobs-route.js +15 -3
  191. package/src/lib/server/ai-jobs-route.js.map +1 -1
  192. package/src/lib/server/assist-edit.js +5 -2
  193. package/src/lib/server/assist-edit.js.map +1 -1
  194. package/src/lib/usage/free-ai-credits-left.d.ts +54 -0
  195. package/src/lib/usage/free-ai-credits-left.js +62 -0
  196. package/src/lib/usage/free-ai-credits-left.js.map +1 -0
  197. package/src/lib/usage/staff-org-ai-conversations.d.ts +23 -0
  198. package/src/lib/usage/staff-org-ai-conversations.js +45 -0
  199. package/src/lib/usage/staff-org-ai-conversations.js.map +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-site-build-progress.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 { aiJobConfirmingOwnPlan, aiJobKindNoun } from './ai-job-activity'\nimport { aiJobRefundCopy } from './ai-job-failure-copy'\nimport { aiBuildItemRows, aiBuildOutcomeLine, aiSitePartialCopy } from './ai-build-progress'\nimport type { AiJobSummary } from './ai-jobs.types'\n\n/**\n * What the \"Building your site\" page shows of a site job (AGL-3594): one row\n * a stage — planning the pages, then each page — with its state, the state\n * the whole job is in, and the job's one price.\n *\n * Read off the job summary every surface already follows: the plan step's\n * status, the plan's pages, and the pages the build has reported as outputs.\n * A scaffold builds its units in plan order and reports each page when it is\n * written, so the pages written so far are the first pages of the plan.\n *\n * Pure: no React, no request. The page and its spec read the same rows.\n */\n\nexport type AiSiteBuildRowState = 'done' | 'active' | 'waiting' | 'failed' | 'skipped'\n\nexport interface AiSiteBuildRow {\n id: string\n label: string\n state: AiSiteBuildRowState\n /** What the person should know about this row (AGL-3616): a build item's failure, note or refund. */\n detail?: string | null\n /** What the row is doing while it is the active one, in a sentence. */\n hint?: string | null\n /** A page's planned sections, in order, so the person sees what is being written. */\n sections?: string[]\n /** The credits this row's work used, once it has finished; absent while it runs. */\n credits?: number\n /** When this row's work started, for the elapsed time beside the active row. */\n startedAt?: string | null\n}\n\n/** Where the whole job stands, as the page's header and actions read it. */\nexport type AiSiteBuildPhase = 'working' | 'done' | 'stopped' | 'failed' | 'canceled'\n\nexport function aiSiteBuildPhase(\n job: Pick<AiJobSummary, 'status'> & Partial<Pick<AiJobSummary, 'review' | 'autoConfirm' | 'updatedAt'>>,\n): AiSiteBuildPhase {\n // A guided start's plan parked for the instant its confirmation takes is\n // the build going on (AGL-3596), never a stop.\n if (aiJobConfirmingOwnPlan(job)) return 'working'\n switch (job.status) {\n case 'done':\n return 'done'\n case 'failed':\n return 'failed'\n case 'canceled':\n return 'canceled'\n case 'needs_review':\n case 'needs_input':\n return 'stopped'\n default:\n return 'working'\n }\n}\n\n/** The creations a scaffold builds before its pages, in its build order, and what each row says. */\nconst CREATION_ROWS: ReadonlyArray<{ kind: string; resource: string; label: (name: string) => string }> = [\n { kind: 'theme-change', resource: 'theme', label: () => AI_SITE_LOOK_ROW_LABEL },\n { kind: 'layout', resource: 'layout', label: (name) => `Building the header and footer: ${name}` },\n { kind: 'form', resource: 'form', label: (name) => `Building the form: ${name}` },\n]\n\n/**\n * One row a stage: planning, then each creation the plan builds before its\n * pages — the colors, the layout, the form — then each page (AGL-3596). A\n * scaffold builds them in that order and reports each as an output when it\n * is written, so the first row without its output is where the build is, and\n * where a failure stopped it: a layout that broke is the layout's row, never\n * the first page's.\n */\nexport function aiSiteBuildRows(\n job: Pick<AiJobSummary, 'status' | 'steps' | 'plan' | 'outputs' | 'review'> &\n Partial<Pick<AiJobSummary, 'items' | 'kind' | 'siteInputs' | 'sitePublish'>>,\n): AiSiteBuildRow[] {\n const phase = aiSiteBuildPhase(job)\n // A build is read off its item ledger (AGL-3616): one row an item, each with\n // its own state and what became of its credits.\n if (job.items?.length) {\n const site = job.kind !== 'build'\n const pages = (job.items ?? []).filter((row) => row.op === 'page')\n // The plan keeps the credits it used once the items take over the page.\n const planStep = job.steps.find((step) => step.name === 'plan')\n return [\n {\n id: 'plan',\n label: site ? 'Planning your pages' : 'Planning what to build',\n state: 'done',\n ...(planStep ? { credits: Math.max(0, Math.floor(planStep.creditsSpent ?? 0)) } : {}),\n },\n ...aiBuildItemRows(job).map((row, index) => {\n const ledger = (job.items ?? [])[index]\n // A site keeps the scaffold's own words for each stage (AGL-3596).\n const label =\n !site || !ledger\n ? row.label\n : ledger.op === 'theme'\n ? AI_SITE_LOOK_ROW_LABEL\n : ledger.op === 'layout'\n ? `Building the header and footer: ${ledger.label}`\n : ledger.op === 'form'\n ? `Building the form: ${ledger.label}`\n : ledger.op === 'page'\n ? `Writing page ${pages.indexOf(ledger) + 1} of ${pages.length}: ${ledger.label}`\n : ledger.op === 'email'\n ? 'Writing your welcome email'\n : row.label\n const screen = ledger?.op === 'page' ? aiPlanScreenFor(job.plan, ledger.label, pages.indexOf(ledger)) : null\n const finished = row.state === 'done' || row.state === 'failed'\n const net = ledger ? Math.max(0, Math.floor((ledger.creditsSpent ?? 0) - (ledger.creditsRefunded ?? 0))) : 0\n return {\n id: row.slot,\n label,\n state: row.state,\n // A part that failed while the rest is still being built says the\n // build goes on (AGL-3596): read alone, it looked like the site had.\n detail:\n row.state === 'failed' && phase === 'working'\n ? [row.detail, site ? 'The rest of your site keeps building.' : 'The rest keeps building.'].filter(Boolean).join(' ')\n : row.detail,\n ...(row.state === 'active'\n ? {\n startedAt: row.startedAt ?? null,\n hint:\n ledger?.op === 'page'\n ? AI_SITE_PAGE_HINT\n : site && ledger?.op === 'theme'\n ? AI_SITE_LOOK_HINT\n : AI_SITE_ITEM_HINT,\n }\n : {}),\n ...(screen?.sections.length ? { sections: screen.sections.map((section) => section.name) } : {}),\n ...(finished && ledger ? { credits: net } : {}),\n }\n }),\n ...aiSitePublishRow(job, phase),\n ]\n }\n const planStep = job.steps.find((step) => step.name === 'plan')\n const planDone = planStep?.status === 'done' || Boolean(job.plan)\n const stopped = phase === 'failed' || phase === 'stopped'\n const planState: AiSiteBuildRowState = planDone ? 'done' : stopped ? 'failed' : phase === 'working' ? 'active' : 'waiting'\n const rows: AiSiteBuildRow[] = [\n {\n id: 'plan',\n label: 'Planning your pages',\n state: planState,\n startedAt: planStep?.startedAt ?? null,\n hint: planState === 'active' ? AI_SITE_PLAN_HINT : null,\n ...(planDone && planStep ? { credits: Math.max(0, Math.floor(planStep.creditsSpent ?? 0)) } : {}),\n },\n ]\n // Before there is a plan, the stages a guided start always goes through\n // are named from its answers, so the page shows the whole way from the\n // start rather than one row.\n if (!job.plan && job.kind !== 'build') {\n const pagesAsked = typeof job.siteInputs?.['pages'] === 'number' ? Math.round(job.siteInputs['pages'] as number) : 0\n if (job.siteInputs) {\n // The look is designed first, before the header and footer (AGL-3660).\n rows.push({ id: 'look', label: AI_SITE_LOOK_ROW_LABEL, state: 'waiting' })\n rows.push({ id: 'layout', label: 'Building the header and footer', state: 'waiting' })\n // A guided start that says where its contact form's submissions go\n // plans that form, built after the layout and before the pages (AGL-3596).\n const submissions = job.siteInputs['submissions']\n if (submissions === 'inbox' || submissions === 'lead') {\n rows.push({ id: 'form', label: 'Building your contact form', state: 'waiting' })\n }\n rows.push({\n id: 'pages',\n label: pagesAsked > 1 ? `Writing your ${pagesAsked} pages` : 'Writing your pages',\n state: 'waiting',\n })\n rows.push(...aiSitePublishRow(job, phase))\n }\n return rows\n }\n const built = new Map<string, number>()\n for (const output of job.outputs) built.set(output.resource, (built.get(output.resource) ?? 0) + 1)\n const take = (resource: string): boolean => {\n const left = built.get(resource) ?? 0\n if (left <= 0) return false\n built.set(resource, left - 1)\n return true\n }\n const stages: Array<{ id: string; label: string; resource: string; sections?: string[] }> = []\n for (const row of CREATION_ROWS) {\n const creation = job.plan?.create.find((entry) => entry.kind === row.kind)\n // A job read without its item ledger is one from before the look had a\n // unit of its own (AGL-3660): its look is a stage only where it built one.\n if (creation || (row.resource === 'theme' && job.outputs.some((output) => output.resource === 'theme'))) {\n stages.push({ id: row.resource, label: row.label(creation?.name ?? ''), resource: row.resource })\n }\n }\n const pages = job.plan?.screens ?? []\n pages.forEach((page, index) => {\n stages.push({\n id: `page-${index}`,\n label: `Writing page ${index + 1} of ${pages.length}: ${page.title}`,\n resource: 'screen',\n sections: page.sections.map((section) => section.name),\n })\n })\n let reached = false\n for (const stage of stages) {\n const done = phase === 'done' || take(stage.resource)\n const current = !done && !reached && planDone\n if (current) reached = true\n rows.push({\n id: stage.id,\n label: stage.label,\n state: done ? 'done' : current && stopped ? 'failed' : current && phase === 'working' ? 'active' : 'waiting',\n ...(stage.sections?.length ? { sections: stage.sections } : {}),\n })\n }\n rows.push(...aiSitePublishRow(job, phase))\n return rows\n}\n\n/** The row the site's look is designed on (AGL-3660). */\nexport const AI_SITE_LOOK_ROW_LABEL = 'Designing your look'\n\n/** What the look's row says while it runs. */\nexport const AI_SITE_LOOK_HINT =\n 'Choosing a base theme, colors, fonts and the style of your buttons, cards and forms for your kind of site. This takes a few seconds.'\n\n/** What the planning row says while it runs. */\nexport const AI_SITE_PLAN_HINT =\n 'Reading your answers and choosing your pages, what each one says, and the forms and layout they need. This usually takes under a minute.'\n\n/** What the page being written says while it is the active row (AGL-3596). */\nexport const AI_SITE_PAGE_HINT =\n 'Writing this page’s sections from your plan. It is done once every section is in.'\n\n/** What any other part being built says while it is the active row. */\nexport const AI_SITE_ITEM_HINT = 'Building this now. The next step starts when it is done.'\n\n/** The plan's page an item builds: by its title, else by its place among the pages. */\nfunction aiPlanScreenFor(\n plan: AiJobSummary['plan'],\n title: string,\n index: number,\n): NonNullable<AiJobSummary['plan']>['screens'][number] | null {\n const screens = plan?.screens ?? []\n return screens.find((screen) => screen.title === title) ?? (index >= 0 ? (screens[index] ?? null) : null)\n}\n\n/**\n * A guided start ends by putting its pages live (AGL-3596): the last row, so\n * the person sees that step coming and when it is done. Only a site job\n * publishes on its own; a build publishes when asked, and says so in its plan.\n */\nfunction aiSitePublishRow(\n job: Pick<AiJobSummary, 'status'> & Partial<Pick<AiJobSummary, 'kind' | 'sitePublish'>>,\n phase: AiSiteBuildPhase,\n): AiSiteBuildRow[] {\n if (job.kind !== 'site') return []\n const published = (job.sitePublish?.published.length ?? 0) > 0\n return [\n {\n id: 'publish',\n label: 'Publishing your site',\n // A job that stopped or failed publishes nothing, so the row is passed over.\n state: phase === 'done' ? (published ? 'done' : 'skipped') : phase === 'working' ? 'waiting' : 'skipped',\n },\n ]\n}\n\n/**\n * How far the job is, from 0 to 1, by its rows: a finished row counts whole\n * and the active one half. `null` before there is more than one row to count.\n */\nexport function aiSiteBuildFraction(rows: readonly AiSiteBuildRow[]): number | null {\n if (rows.length < 2) return null\n const counted = rows.reduce(\n (sum, row) => sum + (row.state === 'active' ? 0.5 : row.state === 'waiting' ? 0 : 1),\n 0,\n )\n return Math.min(1, counted / rows.length)\n}\n\n/**\n * The job's one price (AGL-3594), never a plan's on its own: what it has used\n * so far while it works, what it used once done, and, on a failure we gave\n * back, that nothing was charged.\n */\nexport function aiSiteBuildCreditsLine(\n job: Pick<AiJobSummary, 'status' | 'creditsSpent' | 'refundedCredits'> & Partial<Pick<AiJobSummary, 'kind'>>,\n): string | null {\n const spent = Math.max(0, job.creditsSpent ?? 0)\n const refunded = Math.max(0, job.refundedCredits ?? 0)\n const net = Math.max(0, spent - refunded)\n const phase = aiSiteBuildPhase(job)\n // What the job made, by its kind; a job that names none is a site's.\n const subject = !job.kind || job.kind === 'site' ? 'This site' : aiJobKindNoun(job.kind) === 'AI job' ? 'This AI job' : `This ${aiJobKindNoun(job.kind)}`\n // A failure on our side says so in the words every surface uses (AGL-3596),\n // from the job's recorded give-back.\n const refund = phase === 'working' || phase === 'done' ? null : aiJobRefundCopy(job)\n if (refund && refunded > 0) return refund\n if (phase === 'done') return `${subject} used ${net} credits.`\n if (phase === 'working') return `Credits used so far: ${net}`\n if (refund) return `${subject} used ${net} credits. ${refund}`\n return net > 0 ? `${subject} used ${net} credits.` : null\n}\n\n/** The heading and the sentence under it, for one job's page in its current phase. */\nexport interface AiJobPageCopy {\n heading: string\n lede: string\n}\n\n/**\n * What one job's page says at the top (AGL-3594, AGL-3596). A site job reads\n * as \"Building your site\"; any other kind names what it makes — \"Building\n * your page\" — or, for a kind with no noun, the job itself.\n *\n * A guided site start publishes its pages when it finishes, and says so\n * (`sitePublish`): with a page live the heading is \"Your site is live\". A\n * done site job without that record — an older job, or one that was not a\n * guided start — built drafts, and says that instead.\n */\nexport function aiJobPageCopy(\n job: Pick<AiJobSummary, 'kind' | 'status' | 'review' | 'error'> & Partial<Pick<AiJobSummary, 'sitePublish' | 'items'>>,\n brand: string,\n): AiJobPageCopy {\n const phase = aiSiteBuildPhase(job)\n // A build is what the person asked for, item by item (AGL-3616).\n if (job.kind === 'build') return aiBuildPageCopy(job, brand)\n const noun = job.kind === 'site' ? 'site' : aiJobKindNoun(job.kind)\n const generic = noun === 'AI job'\n // `products` is the one plural noun.\n const isAre = noun.endsWith('s') ? 'are' : 'is'\n const why = job.review?.message ?? job.error ?? null\n if (phase === 'working') {\n return {\n heading: generic ? 'Your AI job is running' : `Building your ${noun}`,\n lede:\n job.kind === 'site'\n ? `${brand} AI is planning your pages and writing each one. You can leave this page; it keeps going.`\n : `${brand} AI is working on it. You can leave this page; it keeps going.`,\n }\n }\n if (phase === 'done') {\n const published = job.sitePublish?.published.length ?? 0\n // Part of the site was built (AGL-3616): what was, what was not, and its credits.\n const partial = job.kind === 'site' ? aiSitePartialCopy({ items: job.items, status: job.status }) : null\n if (job.kind === 'site' && published > 0) {\n return {\n heading: 'Your site is live',\n lede: [\n partial,\n job.sitePublish?.drafts.length\n ? 'Your pages are published, except the ones listed below, which stayed drafts.'\n : 'Your pages are published, and anyone can visit your site now.',\n ]\n .filter(Boolean)\n .join(' '),\n }\n }\n if (partial) return { heading: 'Your site is ready', lede: `${partial} Your new pages are drafts. Publish them when you’re happy.` }\n if (job.kind === 'site' && job.sitePublish) {\n return {\n heading: 'Your site is ready',\n lede: 'None of your new pages could be published, so they are drafts. The reasons are below.',\n }\n }\n return {\n heading: generic ? 'Your AI job finished' : `Your ${noun} ${isAre} ready`,\n lede:\n job.kind === 'site'\n ? 'Your new pages are drafts. Publish them when you’re happy.'\n : 'Everything it built is an unpublished draft until you publish it.',\n }\n }\n if (phase === 'canceled') {\n return { heading: generic ? 'Your AI job was canceled' : `Your ${noun} was not built`, lede: 'The job was canceled.' }\n }\n return {\n heading: generic ? 'Your AI job stopped' : `Your ${noun} ${noun.endsWith('s') ? 'were' : 'was'} not built`,\n lede: why ?? (generic ? 'Something went wrong running this job.' : `Something went wrong building your ${noun}.`),\n }\n}\n\n/** A build's page heading and sentence (AGL-3616). */\nfunction aiBuildPageCopy(\n job: Pick<AiJobSummary, 'status' | 'review' | 'error'> & Partial<Pick<AiJobSummary, 'items' | 'sitePublish'>>,\n brand: string,\n): AiJobPageCopy {\n const phase = aiSiteBuildPhase(job)\n const outcome = aiBuildOutcomeLine({ items: job.items ?? [], status: job.status })\n if (phase === 'working') {\n return {\n heading: 'Building what you asked for',\n lede: `${brand} AI is building each part in turn. You can leave this page; it keeps going.`,\n }\n }\n if (phase === 'stopped') {\n return {\n heading: job.review?.reason === 'plan' ? 'Your plan is ready' : 'Your build needs you',\n lede: job.review?.message ?? job.error ?? 'It waits for your decision.',\n }\n }\n if (phase === 'done') {\n const failed = (job.items ?? []).some((row) => row.status === 'failed' || row.status === 'skipped')\n const live = job.sitePublish?.published.length ?? 0\n return {\n heading: failed ? 'Most of it is ready' : 'Everything you asked for is ready',\n lede: [\n outcome,\n live ? 'Your new pages are published.' : 'Everything it built is an unpublished draft until you publish it.',\n failed ? 'Try again builds only what failed.' : '',\n ]\n .filter(Boolean)\n .join(' '),\n }\n }\n if (phase === 'canceled') return { heading: 'Your build was canceled', lede: 'The job was canceled.' }\n return {\n heading: 'Nothing could be built',\n lede: [job.error ?? 'Something went wrong building what you asked for.', outcome].filter(Boolean).join(' '),\n }\n}\n"],"names":["aiJobConfirmingOwnPlan","aiJobKindNoun","aiJobRefundCopy","aiBuildItemRows","aiBuildOutcomeLine","aiSitePartialCopy","aiSiteBuildPhase","job","status","CREATION_ROWS","kind","resource","label","AI_SITE_LOOK_ROW_LABEL","name","aiSiteBuildRows","planStep","built","phase","items","length","site","pages","filter","row","op","steps","find","step","id","state","credits","Math","max","floor","creditsSpent","map","index","ledger","indexOf","screen","aiPlanScreenFor","plan","finished","net","creditsRefunded","slot","detail","Boolean","join","startedAt","hint","AI_SITE_PAGE_HINT","AI_SITE_LOOK_HINT","AI_SITE_ITEM_HINT","sections","section","aiSitePublishRow","planDone","stopped","planState","rows","AI_SITE_PLAN_HINT","pagesAsked","siteInputs","round","push","submissions","Map","output","outputs","set","get","take","left","stages","creation","create","entry","some","screens","forEach","page","title","reached","stage","done","current","published","sitePublish","aiSiteBuildFraction","counted","reduce","sum","min","aiSiteBuildCreditsLine","spent","refunded","refundedCredits","subject","refund","aiJobPageCopy","brand","aiBuildPageCopy","noun","generic","isAre","endsWith","why","review","message","error","heading","lede","partial","drafts","outcome","reason","failed","live"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-site-build-progress.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 { aiJobConfirmingOwnPlan, aiJobKindNoun } from './ai-job-activity'\nimport { aiJobRefundCopy } from './ai-job-failure-copy'\nimport { aiBuildItemRows, aiBuildOpNoun, aiBuildOutcomeLine, aiSitePartialCopy } from './ai-build-progress'\nimport { AI_JOB_PAUSED_NEXT_COPY, aiJobPausedTitle } from './ai-job-notice'\nimport type { AiJobSummary } from './ai-jobs.types'\n\n/**\n * What the \"Building your site\" page shows of a site job (AGL-3594): one row\n * a stage — planning the pages, then each page — with its state, the state\n * the whole job is in, and the job's one price.\n *\n * Read off the job summary every surface already follows: the plan step's\n * status, the plan's pages, and the pages the build has reported as outputs.\n * A scaffold builds its units in plan order and reports each page when it is\n * written, so the pages written so far are the first pages of the plan.\n *\n * Pure: no React, no request. The page and its spec read the same rows.\n */\n\n/**\n * `paused` (AGL-3660): the row a job the meter paused stopped at — not\n * running, not failed, and carried on from by Resume.\n */\nexport type AiSiteBuildRowState = 'done' | 'active' | 'waiting' | 'failed' | 'skipped' | 'paused'\n\nexport interface AiSiteBuildRow {\n id: string\n label: string\n state: AiSiteBuildRowState\n /** What the person should know about this row (AGL-3616): a build item's failure, note or refund. */\n detail?: string | null\n /** What the row is doing while it is the active one, in a sentence. */\n hint?: string | null\n /** A page's planned sections, in order, so the person sees what is being written. */\n sections?: string[]\n /** The credits this row's work used, once it has finished; absent while it runs. */\n credits?: number\n /** When this row's work started, for the elapsed time beside the active row. */\n startedAt?: string | null\n}\n\n/** Where the whole job stands, as the page's header and actions read it. */\nexport type AiSiteBuildPhase = 'working' | 'done' | 'stopped' | 'failed' | 'canceled'\n\nexport function aiSiteBuildPhase(\n job: Pick<AiJobSummary, 'status'> & Partial<Pick<AiJobSummary, 'review' | 'autoConfirm' | 'updatedAt'>>,\n): AiSiteBuildPhase {\n // A guided start's plan parked for the instant its confirmation takes is\n // the build going on (AGL-3596), never a stop.\n if (aiJobConfirmingOwnPlan(job)) return 'working'\n switch (job.status) {\n case 'done':\n return 'done'\n case 'failed':\n return 'failed'\n case 'canceled':\n return 'canceled'\n case 'needs_review':\n case 'needs_input':\n return 'stopped'\n default:\n return 'working'\n }\n}\n\n/** The creations a scaffold builds before its pages, in its build order, and what each row says. */\nconst CREATION_ROWS: ReadonlyArray<{ kind: string; resource: string; label: (name: string) => string }> = [\n { kind: 'theme-change', resource: 'theme', label: () => AI_SITE_LOOK_ROW_LABEL },\n { kind: 'layout', resource: 'layout', label: (name) => `Building the header and footer: ${name}` },\n { kind: 'form', resource: 'form', label: (name) => `Building the form: ${name}` },\n]\n\n/**\n * One row a stage: planning, then each creation the plan builds before its\n * pages — the colors, the layout, the form — then each page (AGL-3596). A\n * scaffold builds them in that order and reports each as an output when it\n * is written, so the first row without its output is where the build is, and\n * where a failure stopped it: a layout that broke is the layout's row, never\n * the first page's.\n */\nexport function aiSiteBuildRows(\n job: Pick<AiJobSummary, 'status' | 'steps' | 'plan' | 'outputs' | 'review'> &\n Partial<Pick<AiJobSummary, 'items' | 'kind' | 'siteInputs' | 'sitePublish'>>,\n): AiSiteBuildRow[] {\n const rows = aiSiteBuildRowsOf(job)\n return job.status === 'needs_input' ? aiPausedRows(rows) : rows\n}\n\n/**\n * A job the meter paused (AGL-3660) shows no spinner: the row it stopped at —\n * one still marked running, else the first not yet built — reads `paused`,\n * with no running clock and no \"building now\" hint, and every row after it\n * keeps waiting.\n */\nfunction aiPausedRows(rows: AiSiteBuildRow[]): AiSiteBuildRow[] {\n const at = rows.findIndex((row) => row.state === 'active' || row.state === 'paused')\n const index = at >= 0 ? at : rows.findIndex((row) => row.state === 'waiting')\n return rows.map((row, position) => {\n if (row.state === 'active' && position !== index) return { ...row, state: 'waiting', hint: null, startedAt: null }\n if (position !== index) return row\n return { ...row, state: 'paused', hint: null, startedAt: null }\n })\n}\n\nfunction aiSiteBuildRowsOf(\n job: Pick<AiJobSummary, 'status' | 'steps' | 'plan' | 'outputs' | 'review'> &\n Partial<Pick<AiJobSummary, 'items' | 'kind' | 'siteInputs' | 'sitePublish'>>,\n): AiSiteBuildRow[] {\n const phase = aiSiteBuildPhase(job)\n // A build is read off its item ledger (AGL-3616): one row an item, each with\n // its own state and what became of its credits.\n if (job.items?.length) {\n const site = job.kind !== 'build'\n const pages = (job.items ?? []).filter((row) => row.op === 'page')\n // The plan keeps the credits it used once the items take over the page.\n const planStep = job.steps.find((step) => step.name === 'plan')\n return [\n {\n id: 'plan',\n label: site ? 'Planning your pages' : 'Planning what to build',\n state: 'done',\n ...(planStep ? { credits: Math.max(0, Math.floor(planStep.creditsSpent ?? 0)) } : {}),\n },\n ...aiBuildItemRows(job).map((row, index) => {\n const ledger = (job.items ?? [])[index]\n // A site keeps the scaffold's own words for each stage (AGL-3596).\n const label =\n !site || !ledger\n ? row.label\n : ledger.op === 'theme'\n ? AI_SITE_LOOK_ROW_LABEL\n : ledger.op === 'layout'\n ? `Building the header and footer: ${ledger.label}`\n : ledger.op === 'form'\n ? `Building the form: ${ledger.label}`\n : ledger.op === 'page'\n ? `Writing page ${pages.indexOf(ledger) + 1} of ${pages.length}: ${ledger.label}`\n : ledger.op === 'email'\n ? 'Writing your welcome email'\n : row.label\n const screen = ledger?.op === 'page' ? aiPlanScreenFor(job.plan, ledger.label, pages.indexOf(ledger)) : null\n const finished = row.state === 'done' || row.state === 'failed'\n const net = ledger ? Math.max(0, Math.floor((ledger.creditsSpent ?? 0) - (ledger.creditsRefunded ?? 0))) : 0\n return {\n id: row.slot,\n label,\n state: row.state,\n // A part that failed while the rest is still being built says the\n // build goes on (AGL-3596): read alone, it looked like the site had.\n detail:\n row.state === 'failed' && phase === 'working'\n ? [row.detail, site ? 'The rest of your site keeps building.' : 'The rest keeps building.'].filter(Boolean).join(' ')\n : row.detail,\n ...(row.state === 'active'\n ? {\n startedAt: row.startedAt ?? null,\n hint:\n ledger?.op === 'page'\n ? AI_SITE_PAGE_HINT\n : site && ledger?.op === 'theme'\n ? AI_SITE_LOOK_HINT\n : AI_SITE_ITEM_HINT,\n }\n : {}),\n ...(screen?.sections.length ? { sections: screen.sections.map((section) => section.name) } : {}),\n ...(finished && ledger ? { credits: net } : {}),\n }\n }),\n ...aiSitePublishRow(job, phase),\n ]\n }\n const planStep = job.steps.find((step) => step.name === 'plan')\n const planDone = planStep?.status === 'done' || Boolean(job.plan)\n const stopped = phase === 'failed' || phase === 'stopped'\n const paused = job.status === 'needs_input'\n const planState: AiSiteBuildRowState = planDone\n ? 'done'\n : paused\n ? 'paused'\n : stopped\n ? 'failed'\n : phase === 'working'\n ? 'active'\n : 'waiting'\n const rows: AiSiteBuildRow[] = [\n {\n id: 'plan',\n label: 'Planning your pages',\n state: planState,\n startedAt: planStep?.startedAt ?? null,\n hint: planState === 'active' ? AI_SITE_PLAN_HINT : null,\n ...(planDone && planStep ? { credits: Math.max(0, Math.floor(planStep.creditsSpent ?? 0)) } : {}),\n },\n ]\n // Before there is a plan, the stages a guided start always goes through\n // are named from its answers, so the page shows the whole way from the\n // start rather than one row.\n if (!job.plan && job.kind !== 'build') {\n const pagesAsked = typeof job.siteInputs?.['pages'] === 'number' ? Math.round(job.siteInputs['pages'] as number) : 0\n if (job.siteInputs) {\n // The look is designed first, before the header and footer (AGL-3660).\n rows.push({ id: 'look', label: AI_SITE_LOOK_ROW_LABEL, state: 'waiting' })\n rows.push({ id: 'layout', label: 'Building the header and footer', state: 'waiting' })\n // A guided start that says where its contact form's submissions go\n // plans that form, built after the layout and before the pages (AGL-3596).\n const submissions = job.siteInputs['submissions']\n if (submissions === 'inbox' || submissions === 'lead') {\n rows.push({ id: 'form', label: 'Building your contact form', state: 'waiting' })\n }\n rows.push({\n id: 'pages',\n label: pagesAsked > 1 ? `Writing your ${pagesAsked} pages` : 'Writing your pages',\n state: 'waiting',\n })\n rows.push(...aiSitePublishRow(job, phase))\n }\n return rows\n }\n const built = new Map<string, number>()\n for (const output of job.outputs) built.set(output.resource, (built.get(output.resource) ?? 0) + 1)\n const take = (resource: string): boolean => {\n const left = built.get(resource) ?? 0\n if (left <= 0) return false\n built.set(resource, left - 1)\n return true\n }\n const stages: Array<{ id: string; label: string; resource: string; sections?: string[]; hint?: string }> = []\n // A scaffold designs its look first on every start (AGL-3660), and writes\n // its item ledger only with that first pass's outcome — so while the look\n // is being designed, the job is read here, without a ledger, and the look\n // is the stage it is on: a moving site job that has built nothing yet. A\n // job read without its ledger that has built something and no look is one\n // from before the look had a unit of its own, and has the stage only where\n // it built one.\n const moving = (phase === 'working' || paused) && job.outputs.length === 0\n for (const row of CREATION_ROWS) {\n const creation = job.plan?.create.find((entry) => entry.kind === row.kind)\n const look = row.resource === 'theme'\n if (\n creation ||\n (look && job.kind !== 'build' && moving) ||\n (look && job.outputs.some((output) => output.resource === 'theme'))\n ) {\n stages.push({\n id: look ? 'look' : row.resource,\n label: row.label(creation?.name ?? ''),\n resource: row.resource,\n ...(look ? { hint: AI_SITE_LOOK_HINT } : {}),\n })\n }\n }\n const pages = job.plan?.screens ?? []\n pages.forEach((page, index) => {\n stages.push({\n id: `page-${index}`,\n label: `Writing page ${index + 1} of ${pages.length}: ${page.title}`,\n resource: 'screen',\n sections: page.sections.map((section) => section.name),\n })\n })\n // The build step's start: when the first stage after the plan began.\n const buildStartedAt = job.steps.find((step) => step.name !== 'plan' && step.startedAt)?.startedAt ?? null\n let reached = false\n for (const stage of stages) {\n const done = phase === 'done' || take(stage.resource)\n const current = !done && !reached && planDone\n if (current) reached = true\n const state: AiSiteBuildRowState = done\n ? 'done'\n : current && paused\n ? 'paused'\n : current && stopped\n ? 'failed'\n : current && phase === 'working'\n ? 'active'\n : 'waiting'\n rows.push({\n id: stage.id,\n label: stage.label,\n state,\n ...(state === 'active' && stage.hint ? { hint: stage.hint, startedAt: buildStartedAt } : {}),\n ...(stage.sections?.length ? { sections: stage.sections } : {}),\n })\n }\n rows.push(...aiSitePublishRow(job, phase))\n return rows\n}\n\n/** The row the site's look is designed on (AGL-3660). */\nexport const AI_SITE_LOOK_ROW_LABEL = 'Designing your look'\n\n/** What the look's row says while it runs. */\nexport const AI_SITE_LOOK_HINT =\n 'Choosing a base theme, colors, fonts and the style of your buttons, cards and forms for your kind of site. This takes a few seconds.'\n\n/** What the planning row says while it runs. */\nexport const AI_SITE_PLAN_HINT =\n 'Reading your answers and choosing your pages, what each one says, and the forms and layout they need. This usually takes under a minute.'\n\n/** What the page being written says while it is the active row (AGL-3596). */\nexport const AI_SITE_PAGE_HINT =\n 'Writing this page’s sections from your plan. It is done once every section is in.'\n\n/** What any other part being built says while it is the active row. */\nexport const AI_SITE_ITEM_HINT = 'Building this now. The next step starts when it is done.'\n\n/** The plan's page an item builds: by its title, else by its place among the pages. */\nfunction aiPlanScreenFor(\n plan: AiJobSummary['plan'],\n title: string,\n index: number,\n): NonNullable<AiJobSummary['plan']>['screens'][number] | null {\n const screens = plan?.screens ?? []\n return screens.find((screen) => screen.title === title) ?? (index >= 0 ? (screens[index] ?? null) : null)\n}\n\n/**\n * A guided start ends by putting its pages live (AGL-3596): the last row, so\n * the person sees that step coming and when it is done. Only a site job\n * publishes on its own; a build publishes when asked, and says so in its plan.\n */\nfunction aiSitePublishRow(\n job: Pick<AiJobSummary, 'status'> & Partial<Pick<AiJobSummary, 'kind' | 'sitePublish'>>,\n phase: AiSiteBuildPhase,\n): AiSiteBuildRow[] {\n if (job.kind !== 'site') return []\n const published = (job.sitePublish?.published.length ?? 0) > 0\n return [\n {\n id: 'publish',\n label: 'Publishing your site',\n // A job that stopped or failed publishes nothing, so the row is passed over.\n state: phase === 'done' ? (published ? 'done' : 'skipped') : phase === 'working' ? 'waiting' : 'skipped',\n },\n ]\n}\n\n/**\n * How far the job is, from 0 to 1, by its rows: a finished row counts whole\n * and the active one half. `null` before there is more than one row to count.\n */\nexport function aiSiteBuildFraction(rows: readonly AiSiteBuildRow[]): number | null {\n if (rows.length < 2) return null\n const counted = rows.reduce(\n (sum, row) => sum + (row.state === 'active' ? 0.5 : row.state === 'waiting' || row.state === 'paused' ? 0 : 1),\n 0,\n )\n return Math.min(1, counted / rows.length)\n}\n\n/**\n * The job's one price (AGL-3594), never a plan's on its own: what it has used\n * so far while it works, what it used once done, and, on a failure we gave\n * back, that nothing was charged.\n */\nexport function aiSiteBuildCreditsLine(\n job: Pick<AiJobSummary, 'status' | 'creditsSpent' | 'refundedCredits'> & Partial<Pick<AiJobSummary, 'kind'>>,\n): string | null {\n const spent = Math.max(0, job.creditsSpent ?? 0)\n const refunded = Math.max(0, job.refundedCredits ?? 0)\n const net = Math.max(0, spent - refunded)\n const phase = aiSiteBuildPhase(job)\n // What the job made, by its kind; a job that names none is a site's.\n const subject = !job.kind || job.kind === 'site' ? 'This site' : aiJobKindNoun(job.kind) === 'AI job' ? 'This AI job' : `This ${aiJobKindNoun(job.kind)}`\n // A failure on our side says so in the words every surface uses (AGL-3596),\n // from the job's recorded give-back.\n const refund = phase === 'working' || phase === 'done' ? null : aiJobRefundCopy(job)\n if (refund && refunded > 0) return refund\n if (phase === 'done') return `${subject} used ${net} credits.`\n if (phase === 'working') return `Credits used so far: ${net}`\n if (refund) return `${subject} used ${net} credits. ${refund}`\n return net > 0 ? `${subject} used ${net} credits.` : null\n}\n\n/** The heading and the sentence under it, for one job's page in its current phase. */\nexport interface AiJobPageCopy {\n heading: string\n lede: string\n}\n\n/**\n * What one job's page says at the top (AGL-3594, AGL-3596). A site job reads\n * as \"Building your site\"; any other kind names what it makes — \"Building\n * your page\" — or, for a kind with no noun, the job itself.\n *\n * A guided site start publishes its pages when it finishes, and says so\n * (`sitePublish`): with a page live the heading is \"Your site is live\". A\n * done site job without that record — an older job, or one that was not a\n * guided start — built drafts, and says that instead.\n */\nexport function aiJobPageCopy(\n job: Pick<AiJobSummary, 'kind' | 'status' | 'review' | 'error'> &\n Partial<Pick<AiJobSummary, 'sitePublish' | 'items' | 'outputs'>>,\n brand: string,\n): AiJobPageCopy {\n const phase = aiSiteBuildPhase(job)\n // Paused by the meter (AGL-3660): why, what is already built, and that it\n // carries on — never \"was not built\", which read as a dead end.\n if (job.status === 'needs_input') {\n return {\n heading: aiJobPausedTitle(job.kind),\n lede: [job.error, aiJobBuiltSoFar(job), AI_JOB_PAUSED_NEXT_COPY].filter(Boolean).join(' '),\n }\n }\n // A build is what the person asked for, item by item (AGL-3616).\n if (job.kind === 'build') return aiBuildPageCopy(job, brand)\n const noun = job.kind === 'site' ? 'site' : aiJobKindNoun(job.kind)\n const generic = noun === 'AI job'\n // `products` is the one plural noun.\n const isAre = noun.endsWith('s') ? 'are' : 'is'\n const why = job.review?.message ?? job.error ?? null\n if (phase === 'working') {\n return {\n heading: generic ? 'Your AI job is running' : `Building your ${noun}`,\n lede:\n job.kind === 'site'\n ? `${brand} AI is planning your pages and writing each one. You can leave this page; it keeps going.`\n : `${brand} AI is working on it. You can leave this page; it keeps going.`,\n }\n }\n if (phase === 'done') {\n const published = job.sitePublish?.published.length ?? 0\n // Part of the site was built (AGL-3616): what was, what was not, and its credits.\n const partial = job.kind === 'site' ? aiSitePartialCopy({ items: job.items, status: job.status }) : null\n if (job.kind === 'site' && published > 0) {\n return {\n heading: 'Your site is live',\n lede: [\n partial,\n job.sitePublish?.drafts.length\n ? 'Your pages are published, except the ones listed below, which stayed drafts.'\n : 'Your pages are published, and anyone can visit your site now.',\n ]\n .filter(Boolean)\n .join(' '),\n }\n }\n if (partial) return { heading: 'Your site is ready', lede: `${partial} Your new pages are drafts. Publish them when you’re happy.` }\n if (job.kind === 'site' && job.sitePublish) {\n return {\n heading: 'Your site is ready',\n lede: 'None of your new pages could be published, so they are drafts. The reasons are below.',\n }\n }\n return {\n heading: generic ? 'Your AI job finished' : `Your ${noun} ${isAre} ready`,\n lede:\n job.kind === 'site'\n ? 'Your new pages are drafts. Publish them when you’re happy.'\n : 'Everything it built is an unpublished draft until you publish it.',\n }\n }\n if (phase === 'canceled') {\n return { heading: generic ? 'Your AI job was canceled' : `Your ${noun} was not built`, lede: 'The job was canceled.' }\n }\n return {\n heading: generic ? 'Your AI job stopped' : `Your ${noun} ${noun.endsWith('s') ? 'were' : 'was'} not built`,\n lede: why ?? (generic ? 'Something went wrong running this job.' : `Something went wrong building your ${noun}.`),\n }\n}\n\nconst joinWords = (names: string[]): string =>\n names.length <= 1 ? (names[0] ?? '') : `${names.slice(0, -1).join(', ')} and ${names[names.length - 1]}`\n\n/**\n * What a job has already built, in a sentence (AGL-3660): its look, the header\n * and footer, its form, its pages — from the item ledger where it has one,\n * else from what it has reported as outputs. `null` when it has built nothing.\n */\nexport function aiJobBuiltSoFar(\n job: Pick<AiJobSummary, 'kind'> & Partial<Pick<AiJobSummary, 'items' | 'outputs'>>,\n): string | null {\n const built = (job.items ?? []).filter((row) => row.status === 'succeeded' || row.status === 'degraded')\n const names: string[] = []\n if (job.items?.length) {\n for (const row of built) {\n if (row.op === 'theme') names.push('your look')\n else if (row.op === 'layout') names.push('the header and footer')\n else if (row.op === 'form') names.push(`the form “${row.label}”`)\n else if (row.op === 'page') names.push(`the page “${row.label}”`)\n else if (row.op === 'email') names.push('your welcome email')\n else names.push(`${aiBuildOpNoun(row.op).toLowerCase()} “${row.label}”`)\n }\n } else {\n const outputs = job.outputs ?? []\n const count = (resource: string) => outputs.filter((output) => output.resource === resource).length\n if (count('theme')) names.push('your look')\n if (count('layout')) names.push('the header and footer')\n if (count('form')) names.push(count('form') === 1 ? 'the form' : `${count('form')} forms`)\n const pages = count('screen')\n if (pages) names.push(pages === 1 ? '1 page' : `${pages} pages`)\n }\n return names.length ? `Built so far: ${joinWords(names)}.` : null\n}\n\n/** A build's page heading and sentence (AGL-3616). */\nfunction aiBuildPageCopy(\n job: Pick<AiJobSummary, 'status' | 'review' | 'error'> & Partial<Pick<AiJobSummary, 'items' | 'sitePublish'>>,\n brand: string,\n): AiJobPageCopy {\n const phase = aiSiteBuildPhase(job)\n const outcome = aiBuildOutcomeLine({ items: job.items ?? [], status: job.status })\n if (phase === 'working') {\n return {\n heading: 'Building what you asked for',\n lede: `${brand} AI is building each part in turn. You can leave this page; it keeps going.`,\n }\n }\n if (phase === 'stopped') {\n return {\n heading: job.review?.reason === 'plan' ? 'Your plan is ready' : 'Your build needs you',\n lede: job.review?.message ?? job.error ?? 'It waits for your decision.',\n }\n }\n if (phase === 'done') {\n const failed = (job.items ?? []).some((row) => row.status === 'failed' || row.status === 'skipped')\n const live = job.sitePublish?.published.length ?? 0\n return {\n heading: failed ? 'Most of it is ready' : 'Everything you asked for is ready',\n lede: [\n outcome,\n live ? 'Your new pages are published.' : 'Everything it built is an unpublished draft until you publish it.',\n failed ? 'Try again builds only what failed.' : '',\n ]\n .filter(Boolean)\n .join(' '),\n }\n }\n if (phase === 'canceled') return { heading: 'Your build was canceled', lede: 'The job was canceled.' }\n return {\n heading: 'Nothing could be built',\n lede: [job.error ?? 'Something went wrong building what you asked for.', outcome].filter(Boolean).join(' '),\n }\n}\n"],"names":["aiJobConfirmingOwnPlan","aiJobKindNoun","aiJobRefundCopy","aiBuildItemRows","aiBuildOpNoun","aiBuildOutcomeLine","aiSitePartialCopy","AI_JOB_PAUSED_NEXT_COPY","aiJobPausedTitle","aiSiteBuildPhase","job","status","CREATION_ROWS","kind","resource","label","AI_SITE_LOOK_ROW_LABEL","name","aiSiteBuildRows","rows","aiSiteBuildRowsOf","aiPausedRows","at","findIndex","row","state","index","map","position","hint","startedAt","planStep","built","phase","items","length","site","pages","filter","op","steps","find","step","id","credits","Math","max","floor","creditsSpent","ledger","indexOf","screen","aiPlanScreenFor","plan","finished","net","creditsRefunded","slot","detail","Boolean","join","AI_SITE_PAGE_HINT","AI_SITE_LOOK_HINT","AI_SITE_ITEM_HINT","sections","section","aiSitePublishRow","planDone","stopped","paused","planState","AI_SITE_PLAN_HINT","pagesAsked","siteInputs","round","push","submissions","Map","output","outputs","set","get","take","left","stages","moving","creation","create","entry","look","some","screens","forEach","page","title","buildStartedAt","reached","stage","done","current","published","sitePublish","aiSiteBuildFraction","counted","reduce","sum","min","aiSiteBuildCreditsLine","spent","refunded","refundedCredits","subject","refund","aiJobPageCopy","brand","heading","lede","error","aiJobBuiltSoFar","aiBuildPageCopy","noun","generic","isAre","endsWith","why","review","message","partial","drafts","joinWords","names","slice","toLowerCase","count","outcome","reason","failed","live"],"mappings":"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"}
@@ -49,7 +49,7 @@ export declare const AI_SITE_PAGES: {
49
49
  * and the one page the brief most needs. A product decision, not a figure the
50
50
  * wall derives — the wall's proof (`jobs/ai-job-free-site.spec.ts`) shows a
51
51
  * plan and a build of this many pages fit the Free taste with room for one
52
- * retried section.
52
+ * retried page.
53
53
  */
54
54
  export declare const AI_SITE_FREE_PAGES: {
55
55
  readonly min: 1;
@@ -86,50 +86,262 @@ export interface AiFreeSiteWorstCase {
86
86
  plan: number;
87
87
  /** The site's look (AGL-3660): its one answer at its ceiling, on the fast tier. */
88
88
  look: number;
89
- /** The one layout a Free plan includes, built first on a site with none. */
89
+ /**
90
+ * The layout a site with none has built first (AGL-3660): its header and
91
+ * footer as ONE answer in the layout language, writing its cached prefix,
92
+ * at the most a live frame answer wrote, and never under what the live
93
+ * guided start's layout step metered.
94
+ */
90
95
  layout: number;
91
- /** A page's first section pass, which writes the cached prefix. */
92
- firstSection: number;
93
- /** Each later section pass. */
94
- laterSection: number;
96
+ /** The saved form a guided start makes (AGL-3596), at what the live guided start's form step metered. */
97
+ form: number;
98
+ /**
99
+ * A language page's answer before its sections (AGL-3660): its cached
100
+ * prefix written, its turn, and the whole room its ceiling keeps for
101
+ * thinking. A page is ONE answer, not a pass a section.
102
+ */
103
+ page: number;
104
+ /** Each planned section's words in that answer, at the most a section is written in. */
105
+ section: number;
106
+ /**
107
+ * Room for one page asked again: the answer once more, reading its cached
108
+ * prefix, with its thinking room and a full page of sections.
109
+ */
110
+ retry: number;
95
111
  /** A page's listing. */
96
112
  listing: number;
97
113
  }
98
114
  /**
99
- * The Free site start's wall at its worst (AGL-3594): the site plan as
100
- * `jobs/ai-job-free-site.spec.ts` derives it from the plan request as it
101
- * stands, on the model a Free site plans with, every answer at its ceiling;
102
- * the build's exchanges are the Free page's (`AI_FREE_PAGE_WORST_CASE_CREDITS`),
103
- * which the same spec holds these to. The spec fails when a figure it derives
115
+ * The Free site start's wall at its worst (AGL-3594, re-derived for the
116
+ * layout language in AGL-3660): the plan and the look as
117
+ * `jobs/ai-job-free-site.spec.ts` derives them from their requests at their
118
+ * ceilings; the layout, form and pages from the language requests as they
119
+ * stand, priced as each field says. The spec fails when a figure it derives
104
120
  * moves and this does not.
105
121
  */
106
122
  export declare const AI_FREE_SITE_WORST_CASE_CREDITS: Readonly<AiFreeSiteWorstCase>;
107
123
  /**
108
- * The most sections a Free site's plan fits in the Free taste at its worst,
109
- * across all its pages: the plan, the look, the layouts it builds first, each page's
110
- * listing and first pass, and ROOM FOR ONE RETRIED SECTION, then as many later
111
- * passes as the rest pays for; none when that is already past the wall. Every
112
- * page's first pass is counted as a cache write, which errs dear.
124
+ * The most sections a Free site plans across its pages (AGL-3660), whatever
125
+ * the wall leaves: a full home of five and a page of two or three. A product
126
+ * figure, so the spend a site EXPECTS stays near what it measured; the wall
127
+ * proves it fits.
113
128
  */
114
- export declare function aiFreeSiteSectionsWithin(creations: {
129
+ export declare const AI_FREE_SITE_MAX_SECTIONS = 8;
130
+ /** What a Free site start builds before its sections: the layouts it builds first, its forms and its pages. */
131
+ export interface AiFreeSiteCreations {
115
132
  layouts: number;
116
133
  pages: number;
117
- }, taste: number, credits?: Readonly<AiFreeSiteWorstCase>): number;
118
- /** Sections a Free site's page is assumed to hold before its plan names them. */
119
- export declare const AI_FREE_SITE_NOMINAL_SECTIONS = 3;
134
+ /** The saved forms it makes; a guided start makes one where it places a form. */
135
+ forms: number;
136
+ }
137
+ /** A Free site start's credits at its worst for this many sections, its room for one retried page included. */
138
+ export declare function aiFreeSiteWorstCaseCredits(creations: AiFreeSiteCreations, sections: number, credits?: Readonly<AiFreeSiteWorstCase>): number;
139
+ /**
140
+ * The most sections a Free site's plan fits in the Free taste at its worst,
141
+ * across all its pages (AGL-3660): the plan, the look, the layout and form it
142
+ * builds first, each page's answer and listing, ROOM FOR ONE RETRIED PAGE,
143
+ * and as many sections as the rest pays for — at most
144
+ * `AI_FREE_SITE_MAX_SECTIONS`, and at most what the pages hold. None when the
145
+ * rest is already past the wall.
146
+ */
147
+ export declare function aiFreeSiteSectionsWithin(creations: AiFreeSiteCreations, taste: number, credits?: Readonly<AiFreeSiteWorstCase>): number;
120
148
  /**
121
- * About what a Free site start of this many pages costs at its worst, in
122
- * credits, for the dialog that asks for one: the site plan, its look, each page's
123
- * listing and its sections at the nominal count, from the figures the wall is
124
- * proven with rather than the nominal credits a paid estimate counts.
149
+ * What a Free site start of this many pages can cost at its worst, in
150
+ * credits (AGL-3660), for the dialog that asks for one and the door that
151
+ * admits it: `aiFreeSiteWorstCaseCredits` for an empty site — the plan, its
152
+ * look, the layout and form a guided start builds, each page's answer and
153
+ * listing, the sections its plan is held to (`aiFreeSiteSectionsWithin` at
154
+ * the month's whole taste, which is what the plan step allows) and the room
155
+ * for one retried page. The pre-start check asks for all of it, so a Free
156
+ * start is never let begin a job what is left cannot finish: a figure that
157
+ * left out any of those items let one pause between its form and its pages.
125
158
  */
126
159
  export declare function aiFreeSiteCreditEstimate(pages: number, credits?: Readonly<AiFreeSiteWorstCase>): number;
160
+ /**
161
+ * What a Free workspace has left of its Free AI credits this month: the less
162
+ * of its own band's balance and its owner's, since the owner's allowance is
163
+ * shared by every Free workspace they hold (AGL-2925) and the reservation
164
+ * refuses at whichever wall is reached first. `resetsOn` is the UTC day the
165
+ * month — and with it both balances — rolls over, `YYYY-MM-DD`.
166
+ */
167
+ export interface AiFreeCreditsLeft {
168
+ left: number;
169
+ total: number;
170
+ resetsOn: string;
171
+ }
172
+ /** The first UTC day of the month after `now`, `YYYY-MM-DD`: when the Free credits come back. */
173
+ export declare function aiFreeCreditsResetOn(now: Date): string;
174
+ /** A reset day as a person reads it: "November 1". */
175
+ export declare function aiFreeCreditsResetLabel(resetsOn: string): string;
176
+ /**
177
+ * The credits a Free site start needs and does not have (AGL-3660), or `null`
178
+ * when what is left covers it — or when nothing is known about what is left,
179
+ * since the reservation still refuses at the wall. The figure is the one the
180
+ * dialog quotes (`aiFreeSiteCreditEstimate`), so the number a person is shown
181
+ * is the number they are held to.
182
+ */
183
+ export declare function aiFreeSiteShortfall(credits: Pick<AiFreeCreditsLeft, 'left'> | null | undefined, pages: number): {
184
+ needed: number;
185
+ left: number;
186
+ } | null;
187
+ /** Why a Free site start is refused before it spends: what is left, when it comes back, and the way on. */
188
+ export declare function aiFreeSiteShortfallText(shortfall: {
189
+ needed: number;
190
+ left: number;
191
+ }, resetsOn: string): string;
127
192
  /**
128
193
  * The most sections one scaffolded page may hold. The plan model allows more
129
194
  * for a page job, which builds one page; a scaffold builds eight, and each
130
195
  * section is a pass of its own.
131
196
  */
132
197
  export declare const AI_SITE_MAX_SECTIONS = 8;
198
+ /**
199
+ * The fewest sections a site start's home page is planned with (AGL-3660).
200
+ * A new site is a full website (Zach, 2026-10-04): its home reads as one — a
201
+ * hero, then bands such as the offer, why us, social proof, and a closing
202
+ * call to action. A plan is an outline and nothing asked for more than a
203
+ * ceiling, so a live Free yoga start (2026-10-07) planned its home with two
204
+ * sections. The plan step names this count in the plan's turn and re-asks a
205
+ * first answer whose home is under it (`aiSiteThinHomeViolations`).
206
+ */
207
+ export declare const AI_SITE_HOME_MIN_SECTIONS = 5;
208
+ /**
209
+ * The blog a paid guided start writes its first posts into (AGL-3676), and
210
+ * the addresses it may answer at, in order of preference: the first one no
211
+ * planned page takes.
212
+ */
213
+ export declare const AI_SITE_BLOG_NAME = "Blog";
214
+ export declare const AI_SITE_BLOG_SLUGS: readonly ["blog", "posts", "journal", "articles", "writing", "stories"];
215
+ /** The address the blog answers at, beside these planned pages: the first of `AI_SITE_BLOG_SLUGS` none of them takes. */
216
+ export declare function aiSiteBlogSlug(screens: ReadonlyArray<{
217
+ slug: string;
218
+ }>): string;
219
+ /** The header's link to the blog (AGL-3660): a path, not a page, since the blog is the posts collection. */
220
+ export declare const AI_SITE_BLOG_NAV_ID = "aiSiteBlog";
221
+ /** The nav entry a site whose start writes posts links its blog by. */
222
+ export declare function aiSiteBlogNavPage(screens: ReadonlyArray<{
223
+ slug: string;
224
+ }>): {
225
+ id: string;
226
+ label: string;
227
+ slug: string;
228
+ href: string;
229
+ };
230
+ /**
231
+ * Whether a planned page stands in for the blog a start writes (AGL-3660):
232
+ * its address is one of the blog's, or its name says it lists posts. The
233
+ * home page never does.
234
+ */
235
+ export declare function aiSiteIsBlogStandIn(screen: {
236
+ slug: string;
237
+ title: string;
238
+ }): boolean;
239
+ /**
240
+ * A start that writes posts, with its plan's stand-in pages taken out
241
+ * (AGL-3676): the plan step asks once for a plan without one and keeps a
242
+ * second answer that still has one, so the scaffold merges it into the blog
243
+ * instead — the page is not built, its links go to the blog, and the blog
244
+ * takes the address it left free. The kept screens hold their plan index,
245
+ * which a page unit's slot is named by.
246
+ */
247
+ export declare function aiSitePlanWithoutBlogStandIns<T extends {
248
+ slug: string;
249
+ title: string;
250
+ }>(screens: readonly T[]): {
251
+ screens: T[];
252
+ standIns: T[];
253
+ };
254
+ /** The code a planned page that stands in for the written blog is re-asked under. */
255
+ export declare const AI_SITE_BLOG_PAGE_CODE = "plan-blog-page-duplicate";
256
+ /**
257
+ * A site plan's pages that stand in for the blog its start writes (AGL-3660):
258
+ * the live Slow Roads start (2026-10-08) planned an "Articles" page of
259
+ * featured cards beside the posts it wrote at /blog, and the header linked
260
+ * Articles and never the blog. A page whose address is one of the blog's, or
261
+ * whose name says it lists posts, duplicates it; the home page never does.
262
+ */
263
+ export declare function aiSiteBlogStandInViolations(plan: Pick<AiBuildPlan, 'screens'>): Array<{
264
+ rule: null;
265
+ code: string;
266
+ message: string;
267
+ paths: string[];
268
+ }>;
269
+ /** The code a site plan's work section planned without its pieces is re-asked under. */
270
+ export declare const AI_SITE_EMPTY_GALLERY_CODE = "plan-empty-gallery";
271
+ /** The pieces a section that shows the work is planned with (AGL-3660). */
272
+ export declare const AI_SITE_GALLERY_MIN_ITEMS = 3;
273
+ export declare const AI_SITE_GALLERY_MAX_ITEMS = 6;
274
+ /** Whether a planned section's name says it shows the work. */
275
+ export declare function aiSiteSectionShowsWork(name: string): boolean;
276
+ /** The sentence a site plan's turn states about a section that shows the work. */
277
+ export declare const AI_SITE_GALLERY_SENTENCE = "A section that shows the work \u2014 a gallery, portfolio, collection or selected works \u2014 counts its pieces in its items: 3 to 6.";
278
+ /**
279
+ * A site plan's sections that show the work with fewer than three pieces
280
+ * (AGL-3660). The live Juniper Clay start (2026-10-08) planned its Portfolio
281
+ * page's "Works Gallery" with no items, and the page's works lost their place
282
+ * to an inquiry form: a page about the work showed none.
283
+ */
284
+ export declare function aiSiteEmptyGalleryViolations(plan: Pick<AiBuildPlan, 'screens'>): Array<{
285
+ rule: null;
286
+ code: string;
287
+ message: string;
288
+ paths: string[];
289
+ }>;
290
+ /** The violation a site plan whose home page is under its fewest sections is re-asked under. */
291
+ export declare const AI_SITE_THIN_HOME_CODE = "plan-thin-home";
292
+ /** What a site plan's home is composed of, in the words the plan's turn and its re-ask both use. */
293
+ export declare const AI_SITE_HOME_BANDS = "a hero first, then bands such as services or the offer, about or why us, and testimonials or other social proof, and a closing call to action or contact band last";
294
+ /**
295
+ * The fewest sections this site start's home is held to (AGL-3660): five, or
296
+ * 0 — no minimum — where the Free wall cannot pay for a full home beside the
297
+ * other pages. `across` is the sections the Free taste fits across `pages`
298
+ * pages, each other page keeping at least one; `null` is a paid start, which
299
+ * no wall shares out.
300
+ *
301
+ * ⛔ NEVER a lowered minimum. A Free guided start creates its site empty and
302
+ * plans its layout, and the wall then fits 4 sections across two pages. Told
303
+ * "a home of at least 3" beside that, the live yoga start of 2026-10-08
304
+ * planned 4 + 3 and then 4 + 2, was refused for the wall twice, and stopped:
305
+ * a home asked for in bands the wall cannot pay for is a start that fails.
306
+ */
307
+ export declare function aiSiteHomeMinSections(input: {
308
+ pages: number;
309
+ across: number | null;
310
+ }): number;
311
+ /**
312
+ * What a site start's plan is told to BUILD with (AGL-3660): the sections are
313
+ * a budget to use, never a ceiling to stay under. Told only "at most 8", the
314
+ * live prod start of 2026-10-08 planned a home of two sections, and Zach: "that
315
+ * didn't mean do as little as possible." On the Free taste the sentence shares
316
+ * the wall's sections out — a home of five or six, the other page two or
317
+ * three, about seven or eight in all; a paid start's home is asked to be rich.
318
+ * `min` is the home's fewest (`aiSiteHomeMinSections`), 0 where the plan
319
+ * plans no home or the wall cannot pay for a full one.
320
+ */
321
+ export declare function aiSiteFullPlanSentence(input: {
322
+ pages: number;
323
+ across: number | null;
324
+ min: number;
325
+ }): string;
326
+ /** Whether a planned page is the site's home: the one at `/`. */
327
+ export declare function aiSitePlanIsHome(screen: {
328
+ slug: string;
329
+ }): boolean;
330
+ /**
331
+ * A site plan's home page under its fewest sections (AGL-3660), as the plan
332
+ * step's re-ask names it. A plan with no page at `/` — the owner's own home
333
+ * stays — has no home to hold. `across` is the Free wall's section count, so
334
+ * the re-ask says where the sections come from rather than tripping the wall.
335
+ */
336
+ export declare function aiSiteThinHomeViolations(plan: Pick<AiBuildPlan, 'screens'>, options: {
337
+ min: number;
338
+ across: number | null;
339
+ }): Array<{
340
+ rule: null;
341
+ code: string;
342
+ message: string;
343
+ paths: string[];
344
+ }>;
133
345
  /**
134
346
  * Sections a page is assumed to hold before a plan names them, for the
135
347
  * estimate a member reads when they ask for a scaffold. A declared