@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/jobs/ai-site-publish.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 { isFirstPublishedRoute } from '@aglyn/aglyn/app-utils/analytics-events'\nimport { createResourceUid } from '@aglyn/aglyn/app-utils/create-resource-uid'\nimport { hostPublicOrigin } from '@aglyn/aglyn/app-utils/host-naming'\nimport {\n blockingRouteOwner,\n normalizeScreenSlug,\n SCREEN_ROOT_PATH,\n screenRoutePathToUrl,\n} from '@aglyn/aglyn/app-utils/screen-route'\nimport { decodeStoredNodes, encodeStoredNodes } from '@aglyn/aglyn/app-utils/stored-nodes'\nimport { dropPluginSiteCache } from '@aglyn/aglyn/plugin-manager/plugin-site-cache'\nimport type { NodesMap } from '@aglyn/aglyn/types/nodes'\nimport { sendGa4SitePublished } from '@aglyn/tenant-data-admin/server/ga4-measurement-protocol'\nimport { FieldValue, type Firestore } from 'firebase-admin/firestore'\nimport type { AiJob, AiJobOutput, AiJobSitePublish } from '../model/ai-jobs.types'\n\n/**\n * A guided site start publishes what it built (AGL-3596).\n *\n * A person who asked for \"a website\" in the guided start and watched it build\n * was then handed drafts: a site whose address still answered with the\n * starter, or with nothing. A `site` job started from the guided start (its\n * plan confirmed on the person's behalf, `inputs.autoConfirm`) therefore ends\n * by putting every page it built on the site, through the same writes a\n * person's Publish makes:\n *\n * - each page's address in the host's routing map, `publishedAt` on the page,\n * in ONE batch with the publish outbox entry that drops the live site's\n * cached pages (AGL-2575), the drop itself attempted at once;\n * - the home page takes `/` from the starter home page the site was created\n * with (AGL-3408), and an untouched starter goes to the trash rather than\n * staying beside it as a second \"Home\";\n * - the navigation entries the pages proposed are added to the header of\n * the layout they render inside, as a new live version of that layout;\n * - `site_published` once, server-side, as the scheduled publish sends it.\n *\n * A page that cannot be published — deleted meanwhile, no address, an address\n * another page holds — stays a draft and is reported with the plain sentence\n * for why. It never fails the job: the site is still built, and the person\n * fixes that one page in Pages. Every other job kind keeps producing drafts.\n *\n * The allowance is respected by construction: a draft is counted against the\n * plan's pages when it is written (`aiDraftBandRefusal`), so publishing\n * drafts adds none, and retiring the starter frees the one it held.\n */\n\nexport interface AiSitePublishDeps {\n sendSitePublished?: typeof sendGa4SitePublished\n dropCache?: typeof dropPluginSiteCache\n}\n\n/**\n * Whether a build puts its pages live (AGL-3616): only where its request\n * asked to (`publish`, set by Assist's proposal) AND the member ticked the\n * plan card's box when confirming (`publishConfirmed`, set by the resume\n * door). Every other build leaves drafts.\n */\nexport function aiJobPublishesBuild(job: Pick<AiJob, 'kind' | 'inputs'>): boolean {\n return job.kind === 'build' && job.inputs?.['publish'] === true && job.inputs?.['publishConfirmed'] === true\n}\n\n/** Whether a finished job of this shape publishes what it built. */\nexport function aiJobPublishesSite(job: Pick<AiJob, 'kind' | 'inputs'>): boolean {\n return job.kind === 'site' && job.inputs?.['autoConfirm'] === true\n}\n\n/**\n * Where a site answers: its custom domain, else its subdomain under the\n * deployment's tenant apex — never a hard-coded platform host, so a\n * self-hosted deployment links to its own sites.\n */\nexport function aiSiteLiveUrl(host: { cname?: unknown; subdomain?: unknown }): string | null {\n const text = (value: unknown) => (typeof value === 'string' ? value.trim() : '')\n const origin = hostPublicOrigin({ cname: text(host.cname) || null, subdomain: text(host.subdomain) || null })\n return origin ? `${origin}/` : null\n}\n\n/** The sentences a page that stayed a draft is reported with. */\nexport const AI_SITE_PUBLISH_DELETED = 'It was deleted before it could be published.'\nexport const AI_SITE_PUBLISH_NO_ADDRESS = 'It has no address yet. Give it one in Pages and publish it.'\nexport const aiSitePublishTakenCopy = (path: string): string =>\n `Another page is already published at ${screenRoutePathToUrl(path)}. Give this one another address in Pages and publish it.`\nexport const AI_SITE_PUBLISH_FAILED = 'It could not be published just now. Publish it from Pages.'\n\n/** A navigation entry a page proposed, as the page step reports it. */\ninterface NavEntry {\n screenId: string\n label: string\n}\n\nfunction navEntriesOf(outputs: readonly AiJobOutput[]): NavEntry[] {\n const entries: NavEntry[] = []\n for (const output of outputs) {\n if (output.resource !== 'screen') continue\n const navigation = output.proposal?.['navigation'] as { label?: unknown } | undefined\n const label = typeof navigation?.label === 'string' ? navigation.label.trim() : ''\n if (label && !entries.some((entry) => entry.screenId === output.id)) {\n entries.push({ screenId: output.id, label })\n }\n }\n return entries\n}\n\ntype MutableNode = {\n $id: string\n componentId?: string\n parentId?: string\n props?: Record<string, unknown>\n nodes?: string[]\n [key: string]: unknown\n}\n\n/**\n * The layout's nodes with its links to the retired starter home pointed at\n * the new home, and a link for each proposed entry the header does not\n * already carry, placed in the header's toolbar after its existing links.\n * `null` when nothing changes, or when the layout has no toolbar to hold an\n * entry and no link to repoint.\n */\nexport function aiLayoutWithNavigation(\n nodes: NodesMap,\n input: { entries: readonly NavEntry[]; retiredHomeId?: string | null; homeId?: string | null },\n): NodesMap | null {\n const map: Record<string, MutableNode> = {}\n for (const [id, node] of Object.entries(nodes as unknown as Record<string, MutableNode>)) {\n map[id] = { ...node, ...(node.props ? { props: { ...node.props } } : {}), ...(node.nodes ? { nodes: [...node.nodes] } : {}) }\n }\n let changed = false\n const links = Object.values(map).filter((node) => node.componentId === 'muiScreenLink')\n if (input.retiredHomeId && input.homeId) {\n for (const link of links) {\n if (link.props?.['screenId'] === input.retiredHomeId) {\n link.props = { ...link.props, screenId: input.homeId }\n changed = true\n }\n }\n }\n const linked = new Set(links.map((link) => link.props?.['screenId']).filter(Boolean))\n const toolbar = Object.values(map).find((node) => node.componentId === 'muiToolbar')\n const missing = input.entries.filter((entry) => !linked.has(entry.screenId))\n if (toolbar && missing.length) {\n const children = toolbar.nodes ?? []\n // After the last link already in the toolbar (the brand), before the\n // search, the mode switch and the call to action.\n const lastLink = children.reduce(\n (at, id, index) => (map[id]?.componentId === 'muiScreenLink' ? index : at),\n -1,\n )\n const added = missing.map((entry) => {\n const id = `ai_nav_${createResourceUid()}`\n map[id] = {\n $id: id,\n componentId: 'muiScreenLink',\n pluginId: 'mui',\n parentId: toolbar.$id,\n props: { children: entry.label, screenId: entry.screenId, renderAs: 'link', color: 'inherit' },\n sx: { textDecoration: 'none', whiteSpace: 'nowrap' },\n nodes: [],\n }\n return id\n })\n toolbar.nodes = [...children.slice(0, lastLink + 1), ...added, ...children.slice(lastLink + 1)]\n changed = true\n }\n return changed ? (map as unknown as NodesMap) : null\n}\n\n/**\n * Publishes the pages a guided site start built. Never throws for a page; a\n * Firestore failure of the publish write itself reports every page as a\n * draft, for the same reason, and is logged.\n */\nexport async function aiPublishGuidedSite(\n firestore: Firestore,\n input: { job: Pick<AiJob, '$id' | 'orgId' | 'hostId'>; outputs: readonly AiJobOutput[]; now: Date },\n deps: AiSitePublishDeps = {},\n): Promise<AiJobSitePublish> {\n const sendSitePublished = deps.sendSitePublished ?? sendGa4SitePublished\n const dropCache = deps.dropCache ?? dropPluginSiteCache\n const { job, now } = input\n const hostId = job.hostId ?? ''\n const pages = input.outputs.filter(\n (output, index, all) =>\n output.resource === 'screen' &&\n (output.hostId ?? hostId) === hostId &&\n all.findIndex((other) => other.resource === 'screen' && other.id === output.id) === index,\n )\n const hostRef = firestore.collection('hosts').doc(hostId)\n const host = await hostRef.get()\n const result: AiJobSitePublish = {\n liveUrl: aiSiteLiveUrl({ cname: host.get('cname'), subdomain: host.get('subdomain') }),\n published: [],\n drafts: [],\n }\n if (!host.exists || !pages.length) return result\n\n const before = { ...((host.get('screens') ?? {}) as Record<string, string>) }\n const placeholder = (host.get('defaultHomeScreenId') as string | undefined) || null\n const routing = { ...before }\n const entries: Record<string, string> = {}\n const accepted: Array<{ id: string; label: string; path: string; slug: string }> = []\n for (const page of pages) {\n const snapshot = await hostRef.collection('screens').doc(page.id).get()\n const label = String(snapshot.get('displayName') ?? page.label ?? '') || page.label\n if (!snapshot.exists || snapshot.get('deletedAt') != null) {\n result.drafts.push({ id: page.id, label, reason: AI_SITE_PUBLISH_DELETED })\n continue\n }\n const slug = normalizeScreenSlug(String(snapshot.get('slug') ?? ''))\n if (!slug) {\n result.drafts.push({ id: page.id, label, reason: AI_SITE_PUBLISH_NO_ADDRESS })\n continue\n }\n const owner = blockingRouteOwner(routing, slug, placeholder)\n if (owner && owner !== page.id) {\n result.drafts.push({ id: page.id, label, reason: aiSitePublishTakenCopy(slug) })\n continue\n }\n routing[page.id] = slug\n entries[page.id] = slug\n accepted.push({ id: page.id, label, path: slug, slug })\n }\n if (!accepted.length) return result\n\n // The first real home page replaces the placeholder in the same write.\n const home = accepted.find((page) => page.path === SCREEN_ROOT_PATH) ?? null\n const releases =\n home && placeholder && placeholder !== home.id && before[placeholder] === SCREEN_ROOT_PATH\n ? placeholder\n : null\n // An untouched starter home (still its one first version) is retired to the\n // trash; one its owner edited stays in Pages as their draft.\n const retire = releases\n ? (await hostRef.collection('screens').doc(releases).collection('versions').limit(2).get()).size <= 1\n : false\n\n // The header of the layout the pages render inside learns their entries.\n const layoutWrite = await aiNavigationLayoutWrite(firestore, {\n hostId,\n pageIds: accepted.map((page) => page.id),\n entries: navEntriesOf(pages).filter((entry) => entries[entry.screenId]),\n retiredHomeId: releases,\n homeId: home?.id ?? null,\n jobId: job.$id,\n now,\n }).catch((error: unknown) => {\n console.error('ai site publish: navigation not applied', { hostId, jobId: job.$id, error })\n return null\n })\n\n const paths = [\n ...new Set(\n [\n ...Object.values(entries),\n ...(releases ? [SCREEN_ROOT_PATH] : []),\n ].map((path) => screenRoutePathToUrl(path)),\n ),\n ]\n const batch = firestore.batch()\n const hostUpdates: Record<string, unknown> = { updatedAt: now }\n for (const [id, path] of Object.entries(entries)) hostUpdates[`screens.${id}`] = path\n if (releases) {\n hostUpdates[`screens.${releases}`] = FieldValue.delete()\n hostUpdates['defaultHomeScreenId'] = FieldValue.delete()\n batch.set(\n hostRef.collection('screens').doc(releases),\n { publishedAt: FieldValue.delete(), ...(retire ? { deletedAt: now } : {}) },\n { merge: true },\n )\n } else if (placeholder && entries[placeholder]) {\n hostUpdates['defaultHomeScreenId'] = FieldValue.delete()\n }\n batch.update(hostRef, hostUpdates)\n for (const page of accepted) {\n batch.set(hostRef.collection('screens').doc(page.id), { slug: page.slug, publishedAt: now }, { merge: true })\n }\n if (layoutWrite) {\n batch.create(layoutWrite.versionRef, layoutWrite.version)\n batch.set(layoutWrite.layoutRef, { versionId: layoutWrite.versionRef.id, updatedAt: now }, { merge: true })\n }\n // The announce, written down in the publish's own batch (AGL-2575): a\n // layout change reaches every page, so it asks for the whole site.\n const outboxRef = firestore.collection('publishOutbox').doc(createResourceUid())\n batch.set(outboxRef, {\n hostId,\n paths: paths.length ? paths : [SCREEN_ROOT_PATH],\n createdAt: FieldValue.serverTimestamp(),\n attempts: 0,\n ...(layoutWrite ? { entireHost: true } : {}),\n })\n try {\n await batch.commit()\n } catch (error) {\n console.error('ai site publish failed', { hostId, jobId: job.$id, error })\n for (const page of accepted) result.drafts.push({ id: page.id, label: page.label, reason: AI_SITE_PUBLISH_FAILED })\n return result\n }\n result.published = accepted.map(({ id, label, path }) => ({ id, label, path: screenRoutePathToUrl(path) }))\n\n // The live site's cached pages go now; the outbox entry stays for the\n // drain unless the drop certainly happened.\n const dropped = await dropCache({\n hostIds: [hostId],\n reason: 'guided AI site start published',\n ...(layoutWrite ? {} : { paths: { [hostId]: paths } }),\n })\n if (dropped.complete) await outboxRef.delete().catch(() => undefined)\n // A site that came alive with nobody's browser on the publish (AGL-1589).\n await sendSitePublished({ hostId, firstPublish: isFirstPublishedRoute(before, placeholder) })\n return result\n}\n\ninterface LayoutWrite {\n layoutRef: FirebaseFirestore.DocumentReference\n versionRef: FirebaseFirestore.DocumentReference\n version: Record<string, unknown>\n}\n\n/**\n * The new live version of the layout the published pages render inside, with\n * their navigation entries and the home link moved off a retired starter;\n * `null` when there is nothing to change or no one layout to change.\n */\nasync function aiNavigationLayoutWrite(\n firestore: Firestore,\n input: {\n hostId: string\n pageIds: readonly string[]\n entries: readonly NavEntry[]\n retiredHomeId: string | null\n homeId: string | null\n jobId: string\n now: Date\n },\n): Promise<LayoutWrite | null> {\n if (!input.entries.length && !input.retiredHomeId) return null\n const hostRef = firestore.collection('hosts').doc(input.hostId)\n const layoutIds = new Set<string>()\n for (const id of input.pageIds) {\n const screen = await hostRef.collection('screens').doc(id).get()\n const versionId = screen.get('versionId')\n const version =\n typeof versionId === 'string' && versionId\n ? await hostRef.collection('screens').doc(id).collection('versions').doc(versionId).get()\n : null\n const layoutId = version?.get('layoutId') ?? screen.get('layoutId')\n if (typeof layoutId === 'string' && layoutId) layoutIds.add(layoutId)\n }\n if (layoutIds.size !== 1) return null\n const [layoutId] = [...layoutIds]\n const layoutRef = hostRef.collection('layouts').doc(layoutId)\n const layout = await layoutRef.get()\n const currentVersionId = layout.get('versionId')\n if (!layout.exists || layout.get('deletedAt') != null || typeof currentVersionId !== 'string') return null\n const current = await layoutRef.collection('versions').doc(currentVersionId).get()\n const nodes = current.exists ? decodeStoredNodes<NodesMap>(current.get('nodes')) : null\n if (!nodes) return null\n const next = aiLayoutWithNavigation(nodes, input)\n const packed = next ? encodeStoredNodes(next) : null\n if (!next || !packed) return null\n const versionRef = layoutRef.collection('versions').doc(createResourceUid())\n return {\n layoutRef,\n versionRef,\n version: {\n layoutId,\n hostId: input.hostId,\n displayName: 'Navigation for the new pages',\n nodes: Buffer.from(packed),\n createdAt: input.now,\n updatedAt: input.now,\n aiJobId: input.jobId,\n },\n }\n}\n"],"names":["isFirstPublishedRoute","createResourceUid","hostPublicOrigin","blockingRouteOwner","normalizeScreenSlug","SCREEN_ROOT_PATH","screenRoutePathToUrl","decodeStoredNodes","encodeStoredNodes","dropPluginSiteCache","sendGa4SitePublished","FieldValue","aiJobPublishesBuild","job","kind","inputs","aiJobPublishesSite","aiSiteLiveUrl","host","text","value","trim","origin","cname","subdomain","AI_SITE_PUBLISH_DELETED","AI_SITE_PUBLISH_NO_ADDRESS","aiSitePublishTakenCopy","path","AI_SITE_PUBLISH_FAILED","navEntriesOf","outputs","entries","output","resource","navigation","proposal","label","some","entry","screenId","id","push","aiLayoutWithNavigation","nodes","input","map","node","Object","props","changed","links","values","filter","componentId","retiredHomeId","homeId","link","linked","Set","Boolean","toolbar","find","missing","has","length","children","lastLink","reduce","at","index","added","$id","pluginId","parentId","renderAs","color","sx","textDecoration","whiteSpace","slice","aiPublishGuidedSite","firestore","deps","accepted","sendSitePublished","dropCache","now","hostId","pages","all","findIndex","other","hostRef","collection","doc","get","result","liveUrl","published","drafts","exists","before","placeholder","routing","page","snapshot","String","reason","slug","owner","home","releases","retire","limit","size","layoutWrite","aiNavigationLayoutWrite","pageIds","jobId","catch","error","console","paths","batch","hostUpdates","updatedAt","delete","set","publishedAt","deletedAt","merge","update","create","versionRef","version","layoutRef","versionId","outboxRef","createdAt","serverTimestamp","attempts","entireHost","commit","dropped","hostIds","complete","undefined","firstPublish","layoutIds","screen","layoutId","add","layout","currentVersionId","current","next","packed","displayName","Buffer","from","aiJobId"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-site-publish.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 { isFirstPublishedRoute } from '@aglyn/aglyn/app-utils/analytics-events'\nimport { createResourceUid } from '@aglyn/aglyn/app-utils/create-resource-uid'\nimport { hostPublicOrigin } from '@aglyn/aglyn/app-utils/host-naming'\nimport {\n blockingRouteOwner,\n normalizeScreenSlug,\n SCREEN_ROOT_PATH,\n screenRoutePathToUrl,\n} from '@aglyn/aglyn/app-utils/screen-route'\nimport { decodeStoredNodes, encodeStoredNodes } from '@aglyn/aglyn/app-utils/stored-nodes'\nimport { dropPluginSiteCache } from '@aglyn/aglyn/plugin-manager/plugin-site-cache'\nimport type { NodesMap } from '@aglyn/aglyn/types/nodes'\nimport { sendGa4SitePublished } from '@aglyn/tenant-data-admin/server/ga4-measurement-protocol'\nimport { FieldValue, type Firestore } from 'firebase-admin/firestore'\nimport type { AiJob, AiJobOutput, AiJobSitePublish } from '../model/ai-jobs.types'\nimport { AI_SITE_BLOG_SLUGS } from '../model/ai-site-job'\n\n/**\n * A guided site start publishes what it built (AGL-3596).\n *\n * A person who asked for \"a website\" in the guided start and watched it build\n * was then handed drafts: a site whose address still answered with the\n * starter, or with nothing. A `site` job started from the guided start (its\n * plan confirmed on the person's behalf, `inputs.autoConfirm`) therefore ends\n * by putting every page it built on the site, through the same writes a\n * person's Publish makes:\n *\n * - each page's address in the host's routing map, `publishedAt` on the page,\n * in ONE batch with the publish outbox entry that drops the live site's\n * cached pages (AGL-2575), the drop itself attempted at once;\n * - the home page takes `/` from the starter home page the site was created\n * with (AGL-3408), and an untouched starter goes to the trash rather than\n * staying beside it as a second \"Home\";\n * - the navigation entries the pages proposed are added to the header of\n * the layout they render inside, as a new live version of that layout;\n * - `site_published` once, server-side, as the scheduled publish sends it.\n *\n * A page that cannot be published — deleted meanwhile, no address, an address\n * another page holds — stays a draft and is reported with the plain sentence\n * for why. It never fails the job: the site is still built, and the person\n * fixes that one page in Pages. Every other job kind keeps producing drafts.\n *\n * The allowance is respected by construction: a draft is counted against the\n * plan's pages when it is written (`aiDraftBandRefusal`), so publishing\n * drafts adds none, and retiring the starter frees the one it held.\n */\n\nexport interface AiSitePublishDeps {\n sendSitePublished?: typeof sendGa4SitePublished\n dropCache?: typeof dropPluginSiteCache\n}\n\n/**\n * Whether a build puts its pages live (AGL-3616): only where its request\n * asked to (`publish`, set by Assist's proposal) AND the member ticked the\n * plan card's box when confirming (`publishConfirmed`, set by the resume\n * door). Every other build leaves drafts.\n */\nexport function aiJobPublishesBuild(job: Pick<AiJob, 'kind' | 'inputs'>): boolean {\n return job.kind === 'build' && job.inputs?.['publish'] === true && job.inputs?.['publishConfirmed'] === true\n}\n\n/** Whether a finished job of this shape publishes what it built. */\nexport function aiJobPublishesSite(job: Pick<AiJob, 'kind' | 'inputs'>): boolean {\n return job.kind === 'site' && job.inputs?.['autoConfirm'] === true\n}\n\n/**\n * Where a site answers: its custom domain, else its subdomain under the\n * deployment's tenant apex — never a hard-coded platform host, so a\n * self-hosted deployment links to its own sites.\n */\nexport function aiSiteLiveUrl(host: { cname?: unknown; subdomain?: unknown }): string | null {\n const text = (value: unknown) => (typeof value === 'string' ? value.trim() : '')\n const origin = hostPublicOrigin({ cname: text(host.cname) || null, subdomain: text(host.subdomain) || null })\n return origin ? `${origin}/` : null\n}\n\n/** The sentences a page that stayed a draft is reported with. */\nexport const AI_SITE_PUBLISH_DELETED = 'It was deleted before it could be published.'\nexport const AI_SITE_PUBLISH_NO_ADDRESS = 'It has no address yet. Give it one in Pages and publish it.'\nexport const aiSitePublishTakenCopy = (path: string): string =>\n `Another page is already published at ${screenRoutePathToUrl(path)}. Give this one another address in Pages and publish it.`\nexport const AI_SITE_PUBLISH_FAILED = 'It could not be published just now. Publish it from Pages.'\n\n/** A navigation entry a page proposed, as the page step reports it. */\ninterface NavEntry {\n screenId: string\n label: string\n}\n\nfunction navEntriesOf(outputs: readonly AiJobOutput[]): NavEntry[] {\n const entries: NavEntry[] = []\n for (const output of outputs) {\n if (output.resource !== 'screen') continue\n const navigation = output.proposal?.['navigation'] as { label?: unknown } | undefined\n const label = typeof navigation?.label === 'string' ? navigation.label.trim() : ''\n if (label && !entries.some((entry) => entry.screenId === output.id)) {\n entries.push({ screenId: output.id, label })\n }\n }\n return entries\n}\n\ntype MutableNode = {\n $id: string\n componentId?: string\n parentId?: string\n props?: Record<string, unknown>\n nodes?: string[]\n [key: string]: unknown\n}\n\n/**\n * The blog paths a start's layout links and must not publish (AGL-3660): its\n * header, phone menu and footer link the blog by path while the posts part is\n * still owed, so a start whose posts part then failed or was skipped drops\n * every address the blog could have taken. None while the blog was written.\n */\nexport function aiSiteUnwrittenBlogHrefs(blogUnwritten: boolean | undefined): string[] {\n return blogUnwritten ? AI_SITE_BLOG_SLUGS.map((slug) => `/${slug}`) : []\n}\n\n/**\n * The layout's nodes with its links to the retired starter home pointed at\n * the new home, and a link for each proposed entry the header does not\n * already carry, placed in the header's toolbar after its existing links.\n * `null` when nothing changes, or when the layout has no toolbar to hold an\n * entry and no link to repoint.\n */\nexport function aiLayoutWithNavigation(\n nodes: NodesMap,\n input: {\n entries: readonly NavEntry[]\n retiredHomeId?: string | null\n homeId?: string | null\n /** Paths whose links come out: a blog the start linked and never wrote (AGL-3660). */\n droppedHrefs?: readonly string[]\n },\n): NodesMap | null {\n const map: Record<string, MutableNode> = {}\n for (const [id, node] of Object.entries(nodes as unknown as Record<string, MutableNode>)) {\n map[id] = { ...node, ...(node.props ? { props: { ...node.props } } : {}), ...(node.nodes ? { nodes: [...node.nodes] } : {}) }\n }\n let changed = false\n // The header, the phone menu and the footer link a blog by its path before\n // its posts are written (AGL-3660); a start whose posts were not written\n // publishes none of those links to a page that does not exist.\n const dropped = new Set(input.droppedHrefs ?? [])\n if (dropped.size) {\n const gone = new Set(\n Object.entries(map)\n .filter(([, node]) => node.componentId === 'muiScreenLink' && dropped.has(String(node.props?.['href'] ?? '')))\n .map(([id]) => id),\n )\n if (gone.size) {\n for (const id of gone) delete map[id]\n for (const node of Object.values(map)) {\n if (node.nodes?.some((child) => gone.has(child))) node.nodes = node.nodes.filter((child) => !gone.has(child))\n }\n changed = true\n }\n }\n const links = Object.values(map).filter((node) => node.componentId === 'muiScreenLink')\n if (input.retiredHomeId && input.homeId) {\n for (const link of links) {\n if (link.props?.['screenId'] === input.retiredHomeId) {\n link.props = { ...link.props, screenId: input.homeId }\n changed = true\n }\n }\n }\n const linked = new Set(links.map((link) => link.props?.['screenId']).filter(Boolean))\n const toolbar = Object.values(map).find((node) => node.componentId === 'muiToolbar')\n const missing = input.entries.filter((entry) => !linked.has(entry.screenId))\n if (toolbar && missing.length) {\n const children = toolbar.nodes ?? []\n // After the last link already in the toolbar (the brand), before the\n // search, the mode switch and the call to action.\n const lastLink = children.reduce(\n (at, id, index) => (map[id]?.componentId === 'muiScreenLink' ? index : at),\n -1,\n )\n const added = missing.map((entry) => {\n const id = `ai_nav_${createResourceUid()}`\n map[id] = {\n $id: id,\n componentId: 'muiScreenLink',\n pluginId: 'mui',\n parentId: toolbar.$id,\n props: { children: entry.label, screenId: entry.screenId, renderAs: 'link', color: 'inherit' },\n sx: { textDecoration: 'none', whiteSpace: 'nowrap' },\n nodes: [],\n }\n return id\n })\n toolbar.nodes = [...children.slice(0, lastLink + 1), ...added, ...children.slice(lastLink + 1)]\n changed = true\n }\n return changed ? (map as unknown as NodesMap) : null\n}\n\n/**\n * Publishes the pages a guided site start built. Never throws for a page; a\n * Firestore failure of the publish write itself reports every page as a\n * draft, for the same reason, and is logged.\n */\nexport async function aiPublishGuidedSite(\n firestore: Firestore,\n input: {\n job: Pick<AiJob, '$id' | 'orgId' | 'hostId'>\n outputs: readonly AiJobOutput[]\n now: Date\n /** The start owed its blog's posts and wrote none (AGL-3660): its links to the blog come out. */\n blogUnwritten?: boolean\n },\n deps: AiSitePublishDeps = {},\n): Promise<AiJobSitePublish> {\n const sendSitePublished = deps.sendSitePublished ?? sendGa4SitePublished\n const dropCache = deps.dropCache ?? dropPluginSiteCache\n const { job, now } = input\n const hostId = job.hostId ?? ''\n const pages = input.outputs.filter(\n (output, index, all) =>\n output.resource === 'screen' &&\n (output.hostId ?? hostId) === hostId &&\n all.findIndex((other) => other.resource === 'screen' && other.id === output.id) === index,\n )\n const hostRef = firestore.collection('hosts').doc(hostId)\n const host = await hostRef.get()\n const result: AiJobSitePublish = {\n liveUrl: aiSiteLiveUrl({ cname: host.get('cname'), subdomain: host.get('subdomain') }),\n published: [],\n drafts: [],\n }\n if (!host.exists || !pages.length) return result\n\n const before = { ...((host.get('screens') ?? {}) as Record<string, string>) }\n const placeholder = (host.get('defaultHomeScreenId') as string | undefined) || null\n const routing = { ...before }\n const entries: Record<string, string> = {}\n const accepted: Array<{ id: string; label: string; path: string; slug: string }> = []\n for (const page of pages) {\n const snapshot = await hostRef.collection('screens').doc(page.id).get()\n const label = String(snapshot.get('displayName') ?? page.label ?? '') || page.label\n if (!snapshot.exists || snapshot.get('deletedAt') != null) {\n result.drafts.push({ id: page.id, label, reason: AI_SITE_PUBLISH_DELETED })\n continue\n }\n const slug = normalizeScreenSlug(String(snapshot.get('slug') ?? ''))\n if (!slug) {\n result.drafts.push({ id: page.id, label, reason: AI_SITE_PUBLISH_NO_ADDRESS })\n continue\n }\n const owner = blockingRouteOwner(routing, slug, placeholder)\n if (owner && owner !== page.id) {\n result.drafts.push({ id: page.id, label, reason: aiSitePublishTakenCopy(slug) })\n continue\n }\n routing[page.id] = slug\n entries[page.id] = slug\n accepted.push({ id: page.id, label, path: slug, slug })\n }\n if (!accepted.length) return result\n\n // The first real home page replaces the placeholder in the same write.\n const home = accepted.find((page) => page.path === SCREEN_ROOT_PATH) ?? null\n const releases =\n home && placeholder && placeholder !== home.id && before[placeholder] === SCREEN_ROOT_PATH\n ? placeholder\n : null\n // An untouched starter home (still its one first version) is retired to the\n // trash; one its owner edited stays in Pages as their draft.\n const retire = releases\n ? (await hostRef.collection('screens').doc(releases).collection('versions').limit(2).get()).size <= 1\n : false\n\n // The header of the layout the pages render inside learns their entries.\n const layoutWrite = await aiNavigationLayoutWrite(firestore, {\n hostId,\n pageIds: accepted.map((page) => page.id),\n entries: navEntriesOf(pages).filter((entry) => entries[entry.screenId]),\n droppedHrefs: aiSiteUnwrittenBlogHrefs(input.blogUnwritten),\n retiredHomeId: releases,\n homeId: home?.id ?? null,\n jobId: job.$id,\n now,\n }).catch((error: unknown) => {\n console.error('ai site publish: navigation not applied', { hostId, jobId: job.$id, error })\n return null\n })\n\n const paths = [\n ...new Set(\n [\n ...Object.values(entries),\n ...(releases ? [SCREEN_ROOT_PATH] : []),\n ].map((path) => screenRoutePathToUrl(path)),\n ),\n ]\n const batch = firestore.batch()\n const hostUpdates: Record<string, unknown> = { updatedAt: now }\n for (const [id, path] of Object.entries(entries)) hostUpdates[`screens.${id}`] = path\n if (releases) {\n hostUpdates[`screens.${releases}`] = FieldValue.delete()\n hostUpdates['defaultHomeScreenId'] = FieldValue.delete()\n batch.set(\n hostRef.collection('screens').doc(releases),\n { publishedAt: FieldValue.delete(), ...(retire ? { deletedAt: now } : {}) },\n { merge: true },\n )\n } else if (placeholder && entries[placeholder]) {\n hostUpdates['defaultHomeScreenId'] = FieldValue.delete()\n }\n batch.update(hostRef, hostUpdates)\n for (const page of accepted) {\n batch.set(hostRef.collection('screens').doc(page.id), { slug: page.slug, publishedAt: now }, { merge: true })\n }\n if (layoutWrite) {\n batch.create(layoutWrite.versionRef, layoutWrite.version)\n batch.set(layoutWrite.layoutRef, { versionId: layoutWrite.versionRef.id, updatedAt: now }, { merge: true })\n }\n // The announce, written down in the publish's own batch (AGL-2575): a\n // layout change reaches every page, so it asks for the whole site.\n const outboxRef = firestore.collection('publishOutbox').doc(createResourceUid())\n batch.set(outboxRef, {\n hostId,\n paths: paths.length ? paths : [SCREEN_ROOT_PATH],\n createdAt: FieldValue.serverTimestamp(),\n attempts: 0,\n ...(layoutWrite ? { entireHost: true } : {}),\n })\n try {\n await batch.commit()\n } catch (error) {\n console.error('ai site publish failed', { hostId, jobId: job.$id, error })\n for (const page of accepted) result.drafts.push({ id: page.id, label: page.label, reason: AI_SITE_PUBLISH_FAILED })\n return result\n }\n result.published = accepted.map(({ id, label, path }) => ({ id, label, path: screenRoutePathToUrl(path) }))\n\n // The live site's cached pages go now; the outbox entry stays for the\n // drain unless the drop certainly happened.\n const dropped = await dropCache({\n hostIds: [hostId],\n reason: 'guided AI site start published',\n ...(layoutWrite ? {} : { paths: { [hostId]: paths } }),\n })\n if (dropped.complete) await outboxRef.delete().catch(() => undefined)\n // A site that came alive with nobody's browser on the publish (AGL-1589).\n await sendSitePublished({ hostId, firstPublish: isFirstPublishedRoute(before, placeholder) })\n return result\n}\n\ninterface LayoutWrite {\n layoutRef: FirebaseFirestore.DocumentReference\n versionRef: FirebaseFirestore.DocumentReference\n version: Record<string, unknown>\n}\n\n/**\n * The new live version of the layout the published pages render inside, with\n * their navigation entries and the home link moved off a retired starter;\n * `null` when there is nothing to change or no one layout to change.\n */\nasync function aiNavigationLayoutWrite(\n firestore: Firestore,\n input: {\n hostId: string\n pageIds: readonly string[]\n entries: readonly NavEntry[]\n droppedHrefs: readonly string[]\n retiredHomeId: string | null\n homeId: string | null\n jobId: string\n now: Date\n },\n): Promise<LayoutWrite | null> {\n if (!input.entries.length && !input.retiredHomeId && !input.droppedHrefs.length) return null\n const hostRef = firestore.collection('hosts').doc(input.hostId)\n const layoutIds = new Set<string>()\n for (const id of input.pageIds) {\n const screen = await hostRef.collection('screens').doc(id).get()\n const versionId = screen.get('versionId')\n const version =\n typeof versionId === 'string' && versionId\n ? await hostRef.collection('screens').doc(id).collection('versions').doc(versionId).get()\n : null\n const layoutId = version?.get('layoutId') ?? screen.get('layoutId')\n if (typeof layoutId === 'string' && layoutId) layoutIds.add(layoutId)\n }\n if (layoutIds.size !== 1) return null\n const [layoutId] = [...layoutIds]\n const layoutRef = hostRef.collection('layouts').doc(layoutId)\n const layout = await layoutRef.get()\n const currentVersionId = layout.get('versionId')\n if (!layout.exists || layout.get('deletedAt') != null || typeof currentVersionId !== 'string') return null\n const current = await layoutRef.collection('versions').doc(currentVersionId).get()\n const nodes = current.exists ? decodeStoredNodes<NodesMap>(current.get('nodes')) : null\n if (!nodes) return null\n const next = aiLayoutWithNavigation(nodes, input)\n const packed = next ? encodeStoredNodes(next) : null\n if (!next || !packed) return null\n const versionRef = layoutRef.collection('versions').doc(createResourceUid())\n return {\n layoutRef,\n versionRef,\n version: {\n layoutId,\n hostId: input.hostId,\n displayName: 'Navigation for the new pages',\n nodes: Buffer.from(packed),\n createdAt: input.now,\n updatedAt: input.now,\n aiJobId: input.jobId,\n },\n }\n}\n"],"names":["isFirstPublishedRoute","createResourceUid","hostPublicOrigin","blockingRouteOwner","normalizeScreenSlug","SCREEN_ROOT_PATH","screenRoutePathToUrl","decodeStoredNodes","encodeStoredNodes","dropPluginSiteCache","sendGa4SitePublished","FieldValue","AI_SITE_BLOG_SLUGS","aiJobPublishesBuild","job","kind","inputs","aiJobPublishesSite","aiSiteLiveUrl","host","text","value","trim","origin","cname","subdomain","AI_SITE_PUBLISH_DELETED","AI_SITE_PUBLISH_NO_ADDRESS","aiSitePublishTakenCopy","path","AI_SITE_PUBLISH_FAILED","navEntriesOf","outputs","entries","output","resource","navigation","proposal","label","some","entry","screenId","id","push","aiSiteUnwrittenBlogHrefs","blogUnwritten","map","slug","aiLayoutWithNavigation","nodes","input","node","Object","props","changed","dropped","Set","droppedHrefs","size","gone","filter","componentId","has","String","values","child","links","retiredHomeId","homeId","link","linked","Boolean","toolbar","find","missing","length","children","lastLink","reduce","at","index","added","$id","pluginId","parentId","renderAs","color","sx","textDecoration","whiteSpace","slice","aiPublishGuidedSite","firestore","deps","accepted","sendSitePublished","dropCache","now","hostId","pages","all","findIndex","other","hostRef","collection","doc","get","result","liveUrl","published","drafts","exists","before","placeholder","routing","page","snapshot","reason","owner","home","releases","retire","limit","layoutWrite","aiNavigationLayoutWrite","pageIds","jobId","catch","error","console","paths","batch","hostUpdates","updatedAt","delete","set","publishedAt","deletedAt","merge","update","create","versionRef","version","layoutRef","versionId","outboxRef","createdAt","serverTimestamp","attempts","entireHost","commit","hostIds","complete","undefined","firstPublish","layoutIds","screen","layoutId","add","layout","currentVersionId","current","next","packed","displayName","Buffer","from","aiJobId"],"mappings":"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"}
@@ -0,0 +1,151 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ /**
18
+ * RECORDED, NOT GOLDEN: the Portfolio page answer the local Free guided start
19
+ * of 2026-10-08 (job e6XQdRXvnJ, "Juniper Clay Studio", claude-sonnet-5-5)
20
+ * gave, copied verbatim from its replay recording (AGL-3660). Its plan named
21
+ * two sections — "Portfolio Hero" and "Works Gallery", the latter with no
22
+ * items — and the answer gave three: a hero, "Selected works" as six cards,
23
+ * and an inquiry beside the site's form. The reader kept the form and left
24
+ * the works out.
25
+ */
26
+ export declare const AI_JUNIPER_PORTFOLIO_PAGE_ANSWER: {
27
+ readonly sections: readonly [{
28
+ readonly band: "dark";
29
+ readonly cols: readonly [];
30
+ readonly align: "start";
31
+ readonly blocks: readonly [{
32
+ readonly kind: "eyebrow";
33
+ readonly col: -1;
34
+ readonly text: "Juniper Clay Studio";
35
+ readonly to: "";
36
+ readonly style: "none";
37
+ readonly icon: "";
38
+ readonly items: readonly [];
39
+ }, {
40
+ readonly kind: "heading";
41
+ readonly col: -1;
42
+ readonly text: "Handmade stoneware, shaped slowly.";
43
+ readonly to: "";
44
+ readonly style: "large";
45
+ readonly icon: "";
46
+ readonly items: readonly [];
47
+ }, {
48
+ readonly kind: "lede";
49
+ readonly col: -1;
50
+ readonly text: "A selection of recent work from Juniper Clay Studio, made for galleries, shops and collectors.";
51
+ readonly to: "";
52
+ readonly style: "none";
53
+ readonly icon: "";
54
+ readonly items: readonly [];
55
+ }, {
56
+ readonly kind: "button";
57
+ readonly col: -1;
58
+ readonly text: "Get in touch";
59
+ readonly to: "form";
60
+ readonly style: "primary";
61
+ readonly icon: "";
62
+ readonly items: readonly [];
63
+ }];
64
+ }, {
65
+ readonly band: "plain";
66
+ readonly cols: readonly [];
67
+ readonly align: "start";
68
+ readonly blocks: readonly [{
69
+ readonly kind: "heading";
70
+ readonly col: -1;
71
+ readonly text: "Selected works";
72
+ readonly to: "";
73
+ readonly style: "none";
74
+ readonly icon: "";
75
+ readonly items: readonly [];
76
+ }, {
77
+ readonly kind: "cards";
78
+ readonly col: -1;
79
+ readonly text: "";
80
+ readonly to: "";
81
+ readonly style: "none";
82
+ readonly icon: "";
83
+ readonly items: readonly [{
84
+ readonly title: "Stoneware vessels";
85
+ readonly text: "Thrown forms with quiet, matte glazes.";
86
+ readonly to: "";
87
+ readonly icon: "camera";
88
+ }, {
89
+ readonly title: "Tableware";
90
+ readonly text: "Bowls, plates and cups made for daily use.";
91
+ readonly to: "";
92
+ readonly icon: "camera";
93
+ }, {
94
+ readonly title: "Sculptural pieces";
95
+ readonly text: "Hand-built forms for the gallery wall and plinth.";
96
+ readonly to: "";
97
+ readonly icon: "camera";
98
+ }, {
99
+ readonly title: "Vases";
100
+ readonly text: "Tall and narrow shapes with textured surfaces.";
101
+ readonly to: "";
102
+ readonly icon: "camera";
103
+ }, {
104
+ readonly title: "Exhibition pieces";
105
+ readonly text: "Work shown in recent exhibitions.";
106
+ readonly to: "";
107
+ readonly icon: "camera";
108
+ }, {
109
+ readonly title: "Studio details";
110
+ readonly text: "Close views of glaze, clay and finish.";
111
+ readonly to: "";
112
+ readonly icon: "camera";
113
+ }];
114
+ }];
115
+ }, {
116
+ readonly band: "soft";
117
+ readonly cols: readonly [1, 1];
118
+ readonly align: "start";
119
+ readonly blocks: readonly [{
120
+ readonly kind: "heading";
121
+ readonly col: 0;
122
+ readonly text: "Interested in a piece or an exhibition?";
123
+ readonly to: "";
124
+ readonly style: "none";
125
+ readonly icon: "";
126
+ readonly items: readonly [];
127
+ }, {
128
+ readonly kind: "text";
129
+ readonly col: 0;
130
+ readonly text: "Galleries, shop buyers and collectors are welcome to send an inquiry. Tell us what you are looking for and Juniper Clay Studio will reply.";
131
+ readonly to: "";
132
+ readonly style: "none";
133
+ readonly icon: "";
134
+ readonly items: readonly [];
135
+ }, {
136
+ readonly kind: "form";
137
+ readonly col: 1;
138
+ readonly text: "";
139
+ readonly to: "VA0eQnc5VQ";
140
+ readonly style: "none";
141
+ readonly icon: "";
142
+ readonly items: readonly [];
143
+ }];
144
+ }];
145
+ };
146
+ /** The plan's sections for that page, as the job planned them. */
147
+ export declare const AI_JUNIPER_PORTFOLIO_PLANNED_SECTIONS: {
148
+ name: string;
149
+ uses: string[];
150
+ items: number;
151
+ }[];
@@ -0,0 +1,183 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */ /**
17
+ * RECORDED, NOT GOLDEN: the Portfolio page answer the local Free guided start
18
+ * of 2026-10-08 (job e6XQdRXvnJ, "Juniper Clay Studio", claude-sonnet-5-5)
19
+ * gave, copied verbatim from its replay recording (AGL-3660). Its plan named
20
+ * two sections — "Portfolio Hero" and "Works Gallery", the latter with no
21
+ * items — and the answer gave three: a hero, "Selected works" as six cards,
22
+ * and an inquiry beside the site's form. The reader kept the form and left
23
+ * the works out.
24
+ */ export const AI_JUNIPER_PORTFOLIO_PAGE_ANSWER = {
25
+ "sections": [
26
+ {
27
+ "band": "dark",
28
+ "cols": [],
29
+ "align": "start",
30
+ "blocks": [
31
+ {
32
+ "kind": "eyebrow",
33
+ "col": -1,
34
+ "text": "Juniper Clay Studio",
35
+ "to": "",
36
+ "style": "none",
37
+ "icon": "",
38
+ "items": []
39
+ },
40
+ {
41
+ "kind": "heading",
42
+ "col": -1,
43
+ "text": "Handmade stoneware, shaped slowly.",
44
+ "to": "",
45
+ "style": "large",
46
+ "icon": "",
47
+ "items": []
48
+ },
49
+ {
50
+ "kind": "lede",
51
+ "col": -1,
52
+ "text": "A selection of recent work from Juniper Clay Studio, made for galleries, shops and collectors.",
53
+ "to": "",
54
+ "style": "none",
55
+ "icon": "",
56
+ "items": []
57
+ },
58
+ {
59
+ "kind": "button",
60
+ "col": -1,
61
+ "text": "Get in touch",
62
+ "to": "form",
63
+ "style": "primary",
64
+ "icon": "",
65
+ "items": []
66
+ }
67
+ ]
68
+ },
69
+ {
70
+ "band": "plain",
71
+ "cols": [],
72
+ "align": "start",
73
+ "blocks": [
74
+ {
75
+ "kind": "heading",
76
+ "col": -1,
77
+ "text": "Selected works",
78
+ "to": "",
79
+ "style": "none",
80
+ "icon": "",
81
+ "items": []
82
+ },
83
+ {
84
+ "kind": "cards",
85
+ "col": -1,
86
+ "text": "",
87
+ "to": "",
88
+ "style": "none",
89
+ "icon": "",
90
+ "items": [
91
+ {
92
+ "title": "Stoneware vessels",
93
+ "text": "Thrown forms with quiet, matte glazes.",
94
+ "to": "",
95
+ "icon": "camera"
96
+ },
97
+ {
98
+ "title": "Tableware",
99
+ "text": "Bowls, plates and cups made for daily use.",
100
+ "to": "",
101
+ "icon": "camera"
102
+ },
103
+ {
104
+ "title": "Sculptural pieces",
105
+ "text": "Hand-built forms for the gallery wall and plinth.",
106
+ "to": "",
107
+ "icon": "camera"
108
+ },
109
+ {
110
+ "title": "Vases",
111
+ "text": "Tall and narrow shapes with textured surfaces.",
112
+ "to": "",
113
+ "icon": "camera"
114
+ },
115
+ {
116
+ "title": "Exhibition pieces",
117
+ "text": "Work shown in recent exhibitions.",
118
+ "to": "",
119
+ "icon": "camera"
120
+ },
121
+ {
122
+ "title": "Studio details",
123
+ "text": "Close views of glaze, clay and finish.",
124
+ "to": "",
125
+ "icon": "camera"
126
+ }
127
+ ]
128
+ }
129
+ ]
130
+ },
131
+ {
132
+ "band": "soft",
133
+ "cols": [
134
+ 1,
135
+ 1
136
+ ],
137
+ "align": "start",
138
+ "blocks": [
139
+ {
140
+ "kind": "heading",
141
+ "col": 0,
142
+ "text": "Interested in a piece or an exhibition?",
143
+ "to": "",
144
+ "style": "none",
145
+ "icon": "",
146
+ "items": []
147
+ },
148
+ {
149
+ "kind": "text",
150
+ "col": 0,
151
+ "text": "Galleries, shop buyers and collectors are welcome to send an inquiry. Tell us what you are looking for and Juniper Clay Studio will reply.",
152
+ "to": "",
153
+ "style": "none",
154
+ "icon": "",
155
+ "items": []
156
+ },
157
+ {
158
+ "kind": "form",
159
+ "col": 1,
160
+ "text": "",
161
+ "to": "VA0eQnc5VQ",
162
+ "style": "none",
163
+ "icon": "",
164
+ "items": []
165
+ }
166
+ ]
167
+ }
168
+ ]
169
+ };
170
+ /** The plan's sections for that page, as the job planned them. */ export const AI_JUNIPER_PORTFOLIO_PLANNED_SECTIONS = [
171
+ {
172
+ name: 'Portfolio Hero',
173
+ uses: [],
174
+ items: 0
175
+ },
176
+ {
177
+ name: 'Works Gallery',
178
+ uses: [],
179
+ items: 0
180
+ }
181
+ ];
182
+
183
+ //# sourceMappingURL=ai-juniper-portfolio-page-recording.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../../libs/plugins/ai/src/lib/jobs/fixtures/ai-juniper-portfolio-page-recording.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\n/**\n * RECORDED, NOT GOLDEN: the Portfolio page answer the local Free guided start\n * of 2026-10-08 (job e6XQdRXvnJ, \"Juniper Clay Studio\", claude-sonnet-5-5)\n * gave, copied verbatim from its replay recording (AGL-3660). Its plan named\n * two sections — \"Portfolio Hero\" and \"Works Gallery\", the latter with no\n * items — and the answer gave three: a hero, \"Selected works\" as six cards,\n * and an inquiry beside the site's form. The reader kept the form and left\n * the works out.\n */\nexport const AI_JUNIPER_PORTFOLIO_PAGE_ANSWER = {\n \"sections\": [\n {\n \"band\": \"dark\",\n \"cols\": [],\n \"align\": \"start\",\n \"blocks\": [\n {\n \"kind\": \"eyebrow\",\n \"col\": -1,\n \"text\": \"Juniper Clay Studio\",\n \"to\": \"\",\n \"style\": \"none\",\n \"icon\": \"\",\n \"items\": []\n },\n {\n \"kind\": \"heading\",\n \"col\": -1,\n \"text\": \"Handmade stoneware, shaped slowly.\",\n \"to\": \"\",\n \"style\": \"large\",\n \"icon\": \"\",\n \"items\": []\n },\n {\n \"kind\": \"lede\",\n \"col\": -1,\n \"text\": \"A selection of recent work from Juniper Clay Studio, made for galleries, shops and collectors.\",\n \"to\": \"\",\n \"style\": \"none\",\n \"icon\": \"\",\n \"items\": []\n },\n {\n \"kind\": \"button\",\n \"col\": -1,\n \"text\": \"Get in touch\",\n \"to\": \"form\",\n \"style\": \"primary\",\n \"icon\": \"\",\n \"items\": []\n }\n ]\n },\n {\n \"band\": \"plain\",\n \"cols\": [],\n \"align\": \"start\",\n \"blocks\": [\n {\n \"kind\": \"heading\",\n \"col\": -1,\n \"text\": \"Selected works\",\n \"to\": \"\",\n \"style\": \"none\",\n \"icon\": \"\",\n \"items\": []\n },\n {\n \"kind\": \"cards\",\n \"col\": -1,\n \"text\": \"\",\n \"to\": \"\",\n \"style\": \"none\",\n \"icon\": \"\",\n \"items\": [\n {\n \"title\": \"Stoneware vessels\",\n \"text\": \"Thrown forms with quiet, matte glazes.\",\n \"to\": \"\",\n \"icon\": \"camera\"\n },\n {\n \"title\": \"Tableware\",\n \"text\": \"Bowls, plates and cups made for daily use.\",\n \"to\": \"\",\n \"icon\": \"camera\"\n },\n {\n \"title\": \"Sculptural pieces\",\n \"text\": \"Hand-built forms for the gallery wall and plinth.\",\n \"to\": \"\",\n \"icon\": \"camera\"\n },\n {\n \"title\": \"Vases\",\n \"text\": \"Tall and narrow shapes with textured surfaces.\",\n \"to\": \"\",\n \"icon\": \"camera\"\n },\n {\n \"title\": \"Exhibition pieces\",\n \"text\": \"Work shown in recent exhibitions.\",\n \"to\": \"\",\n \"icon\": \"camera\"\n },\n {\n \"title\": \"Studio details\",\n \"text\": \"Close views of glaze, clay and finish.\",\n \"to\": \"\",\n \"icon\": \"camera\"\n }\n ]\n }\n ]\n },\n {\n \"band\": \"soft\",\n \"cols\": [\n 1,\n 1\n ],\n \"align\": \"start\",\n \"blocks\": [\n {\n \"kind\": \"heading\",\n \"col\": 0,\n \"text\": \"Interested in a piece or an exhibition?\",\n \"to\": \"\",\n \"style\": \"none\",\n \"icon\": \"\",\n \"items\": []\n },\n {\n \"kind\": \"text\",\n \"col\": 0,\n \"text\": \"Galleries, shop buyers and collectors are welcome to send an inquiry. Tell us what you are looking for and Juniper Clay Studio will reply.\",\n \"to\": \"\",\n \"style\": \"none\",\n \"icon\": \"\",\n \"items\": []\n },\n {\n \"kind\": \"form\",\n \"col\": 1,\n \"text\": \"\",\n \"to\": \"VA0eQnc5VQ\",\n \"style\": \"none\",\n \"icon\": \"\",\n \"items\": []\n }\n ]\n }\n ]\n} as const\n\n/** The plan's sections for that page, as the job planned them. */\nexport const AI_JUNIPER_PORTFOLIO_PLANNED_SECTIONS = [\n { name: 'Portfolio Hero', uses: [] as string[], items: 0 },\n { name: 'Works Gallery', uses: [] as string[], items: 0 },\n]\n"],"names":["AI_JUNIPER_PORTFOLIO_PAGE_ANSWER","AI_JUNIPER_PORTFOLIO_PLANNED_SECTIONS","name","uses","items"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;CAQC,GACD,OAAO,MAAMA,mCAAmC;IAC9C,YAAY;QACV;YACE,QAAQ;YACR,QAAQ,EAAE;YACV,SAAS;YACT,UAAU;gBACR;oBACE,QAAQ;oBACR,OAAO,CAAC;oBACR,QAAQ;oBACR,MAAM;oBACN,SAAS;oBACT,QAAQ;oBACR,SAAS,EAAE;gBACb;gBACA;oBACE,QAAQ;oBACR,OAAO,CAAC;oBACR,QAAQ;oBACR,MAAM;oBACN,SAAS;oBACT,QAAQ;oBACR,SAAS,EAAE;gBACb;gBACA;oBACE,QAAQ;oBACR,OAAO,CAAC;oBACR,QAAQ;oBACR,MAAM;oBACN,SAAS;oBACT,QAAQ;oBACR,SAAS,EAAE;gBACb;gBACA;oBACE,QAAQ;oBACR,OAAO,CAAC;oBACR,QAAQ;oBACR,MAAM;oBACN,SAAS;oBACT,QAAQ;oBACR,SAAS,EAAE;gBACb;aACD;QACH;QACA;YACE,QAAQ;YACR,QAAQ,EAAE;YACV,SAAS;YACT,UAAU;gBACR;oBACE,QAAQ;oBACR,OAAO,CAAC;oBACR,QAAQ;oBACR,MAAM;oBACN,SAAS;oBACT,QAAQ;oBACR,SAAS,EAAE;gBACb;gBACA;oBACE,QAAQ;oBACR,OAAO,CAAC;oBACR,QAAQ;oBACR,MAAM;oBACN,SAAS;oBACT,QAAQ;oBACR,SAAS;wBACP;4BACE,SAAS;4BACT,QAAQ;4BACR,MAAM;4BACN,QAAQ;wBACV;wBACA;4BACE,SAAS;4BACT,QAAQ;4BACR,MAAM;4BACN,QAAQ;wBACV;wBACA;4BACE,SAAS;4BACT,QAAQ;4BACR,MAAM;4BACN,QAAQ;wBACV;wBACA;4BACE,SAAS;4BACT,QAAQ;4BACR,MAAM;4BACN,QAAQ;wBACV;wBACA;4BACE,SAAS;4BACT,QAAQ;4BACR,MAAM;4BACN,QAAQ;wBACV;wBACA;4BACE,SAAS;4BACT,QAAQ;4BACR,MAAM;4BACN,QAAQ;wBACV;qBACD;gBACH;aACD;QACH;QACA;YACE,QAAQ;YACR,QAAQ;gBACN;gBACA;aACD;YACD,SAAS;YACT,UAAU;gBACR;oBACE,QAAQ;oBACR,OAAO;oBACP,QAAQ;oBACR,MAAM;oBACN,SAAS;oBACT,QAAQ;oBACR,SAAS,EAAE;gBACb;gBACA;oBACE,QAAQ;oBACR,OAAO;oBACP,QAAQ;oBACR,MAAM;oBACN,SAAS;oBACT,QAAQ;oBACR,SAAS,EAAE;gBACb;gBACA;oBACE,QAAQ;oBACR,OAAO;oBACP,QAAQ;oBACR,MAAM;oBACN,SAAS;oBACT,QAAQ;oBACR,SAAS,EAAE;gBACb;aACD;QACH;KACD;AACH,EAAU;AAEV,gEAAgE,GAChE,OAAO,MAAMC,wCAAwC;IACnD;QAAEC,MAAM;QAAkBC,MAAM,EAAE;QAAcC,OAAO;IAAE;IACzD;QAAEF,MAAM;QAAiBC,MAAM,EAAE;QAAcC,OAAO;IAAE;CACzD,CAAA"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ import type { AiBuildPlan } from '../../model/ai-build-plan';
18
+ /**
19
+ * RECORDED, NOT GOLDEN: the first plan answer of the local paid (Pro) blog
20
+ * guided start of 2026-10-08, "Slow Roads, a personal travel blog about long
21
+ * train journeys through Europe, written by one person" (job cvS0nF0rWP,
22
+ * claude-sonnet-5-5), copied by hand from its replay recording (AGL-3660).
23
+ * Its start wrote three posts at /blog, and this plan also planned an
24
+ * "Articles" page of featured cards at /articles, which the header linked
25
+ * while it never linked the blog.
26
+ */
27
+ export declare const AI_SLOW_ROADS_SITE_PLAN_FIRST_ANSWER: AiBuildPlan;