@aglyn/plugins-ai 1.0.0-beta.271 → 1.0.0-beta.273

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 (132) hide show
  1. package/package.json +17 -17
  2. package/src/lib/activity/ai-activity-actions.d.ts +4 -3
  3. package/src/lib/activity/ai-activity-actions.js +3 -0
  4. package/src/lib/activity/ai-activity-actions.js.map +1 -1
  5. package/src/lib/components/ai-site-build-page.component.js +18 -0
  6. package/src/lib/components/ai-site-build-page.component.js.map +1 -1
  7. package/src/lib/components/ai-site-import-job.component.d.ts +41 -0
  8. package/src/lib/components/ai-site-import-job.component.js +707 -0
  9. package/src/lib/components/ai-site-import-job.component.js.map +1 -0
  10. package/src/lib/components/ai-site-import-start.component.d.ts +64 -0
  11. package/src/lib/components/ai-site-import-start.component.js +335 -0
  12. package/src/lib/components/ai-site-import-start.component.js.map +1 -0
  13. package/src/lib/components/ai-site-ownership-proof.component.d.ts +42 -0
  14. package/src/lib/components/ai-site-ownership-proof.component.js +353 -0
  15. package/src/lib/components/ai-site-ownership-proof.component.js.map +1 -0
  16. package/src/lib/components/ai-site-start-card.component.js +329 -252
  17. package/src/lib/components/ai-site-start-card.component.js.map +1 -1
  18. package/src/lib/jobs/ai-empty-site-sweep.d.ts +12 -1
  19. package/src/lib/jobs/ai-empty-site-sweep.js +15 -3
  20. package/src/lib/jobs/ai-empty-site-sweep.js.map +1 -1
  21. package/src/lib/jobs/ai-job-drafts.d.ts +14 -0
  22. package/src/lib/jobs/ai-job-drafts.js +24 -0
  23. package/src/lib/jobs/ai-job-drafts.js.map +1 -1
  24. package/src/lib/jobs/ai-job-import-step.d.ts +136 -0
  25. package/src/lib/jobs/ai-job-import-step.js +809 -0
  26. package/src/lib/jobs/ai-job-import-step.js.map +1 -0
  27. package/src/lib/jobs/ai-job-page-language.d.ts +39 -1
  28. package/src/lib/jobs/ai-job-page-language.js +62 -4
  29. package/src/lib/jobs/ai-job-page-language.js.map +1 -1
  30. package/src/lib/jobs/ai-job-page-step.d.ts +2 -2
  31. package/src/lib/jobs/ai-job-page-step.js +26 -4
  32. package/src/lib/jobs/ai-job-page-step.js.map +1 -1
  33. package/src/lib/jobs/ai-job-seo-step.js +8 -4
  34. package/src/lib/jobs/ai-job-seo-step.js.map +1 -1
  35. package/src/lib/jobs/ai-job-site-datasets.d.ts +39 -1
  36. package/src/lib/jobs/ai-job-site-datasets.js +111 -15
  37. package/src/lib/jobs/ai-job-site-datasets.js.map +1 -1
  38. package/src/lib/jobs/ai-job-site-step.d.ts +12 -3
  39. package/src/lib/jobs/ai-job-site-step.js +85 -11
  40. package/src/lib/jobs/ai-job-site-step.js.map +1 -1
  41. package/src/lib/jobs/ai-job-text-step.d.ts +15 -0
  42. package/src/lib/jobs/ai-job-text-step.js.map +1 -1
  43. package/src/lib/jobs/ai-jobs.d.ts +8 -0
  44. package/src/lib/jobs/ai-jobs.js +19 -3
  45. package/src/lib/jobs/ai-jobs.js.map +1 -1
  46. package/src/lib/jobs/ai-layout-owner-photos.d.ts +16 -1
  47. package/src/lib/jobs/ai-layout-owner-photos.js +49 -2
  48. package/src/lib/jobs/ai-layout-owner-photos.js.map +1 -1
  49. package/src/lib/jobs/ai-layout-stock-photos.d.ts +35 -0
  50. package/src/lib/jobs/ai-layout-stock-photos.js +114 -43
  51. package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
  52. package/src/lib/jobs/ai-site-late-photos.d.ts +143 -0
  53. package/src/lib/jobs/ai-site-late-photos.js +486 -0
  54. package/src/lib/jobs/ai-site-late-photos.js.map +1 -0
  55. package/src/lib/jobs/ai-site-publish.d.ts +18 -0
  56. package/src/lib/jobs/ai-site-publish.js +123 -5
  57. package/src/lib/jobs/ai-site-publish.js.map +1 -1
  58. package/src/lib/jobs/ai-theme-brand-inputs.d.ts +61 -3
  59. package/src/lib/jobs/ai-theme-brand-inputs.js +117 -27
  60. package/src/lib/jobs/ai-theme-brand-inputs.js.map +1 -1
  61. package/src/lib/jobs/fixtures/ai-site-late-photos-laurel-fork.d.ts +83 -0
  62. package/src/lib/jobs/fixtures/ai-site-late-photos-laurel-fork.js +1553 -0
  63. package/src/lib/jobs/fixtures/ai-site-late-photos-laurel-fork.js.map +1 -0
  64. package/src/lib/layout-language/ai-layout-pictures.d.ts +50 -13
  65. package/src/lib/layout-language/ai-layout-pictures.js +81 -19
  66. package/src/lib/layout-language/ai-layout-pictures.js.map +1 -1
  67. package/src/lib/model/ai-credit-estimate.d.ts +20 -0
  68. package/src/lib/model/ai-credit-estimate.js +13 -0
  69. package/src/lib/model/ai-credit-estimate.js.map +1 -1
  70. package/src/lib/model/ai-job-activity.d.ts +2 -2
  71. package/src/lib/model/ai-job-activity.js +6 -4
  72. package/src/lib/model/ai-job-activity.js.map +1 -1
  73. package/src/lib/model/ai-job-notice.js +18 -4
  74. package/src/lib/model/ai-job-notice.js.map +1 -1
  75. package/src/lib/model/ai-jobs.types.d.ts +39 -2
  76. package/src/lib/model/ai-jobs.types.js +2 -1
  77. package/src/lib/model/ai-jobs.types.js.map +1 -1
  78. package/src/lib/model/ai-site-import-view.d.ts +130 -0
  79. package/src/lib/model/ai-site-import-view.js +182 -0
  80. package/src/lib/model/ai-site-import-view.js.map +1 -0
  81. package/src/lib/model/ai-site-import.d.ts +302 -0
  82. package/src/lib/model/ai-site-import.js +526 -0
  83. package/src/lib/model/ai-site-import.js.map +1 -0
  84. package/src/lib/model/ai-site-photos.d.ts +119 -0
  85. package/src/lib/model/ai-site-photos.js +128 -0
  86. package/src/lib/model/ai-site-photos.js.map +1 -0
  87. package/src/lib/model/ai-site-start.d.ts +22 -9
  88. package/src/lib/model/ai-site-start.js +64 -23
  89. package/src/lib/model/ai-site-start.js.map +1 -1
  90. package/src/lib/runtime/ai-doctrine-validators.d.ts +3 -1
  91. package/src/lib/runtime/ai-doctrine-validators.js +3 -1
  92. package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
  93. package/src/lib/runtime/seo-fields.d.ts +29 -4
  94. package/src/lib/runtime/seo-fields.js +58 -11
  95. package/src/lib/runtime/seo-fields.js.map +1 -1
  96. package/src/lib/server/ai-jobs-route.js +11 -0
  97. package/src/lib/server/ai-jobs-route.js.map +1 -1
  98. package/src/lib/server/ai-site-import-route.d.ts +89 -0
  99. package/src/lib/server/ai-site-import-route.js +356 -0
  100. package/src/lib/server/ai-site-import-route.js.map +1 -0
  101. package/src/lib/server.js +14 -0
  102. package/src/lib/server.js.map +1 -1
  103. package/src/lib/site-import/fixtures/site-import-crawl-recordings.d.ts +45 -0
  104. package/src/lib/site-import/fixtures/site-import-crawl-recordings.js +313 -0
  105. package/src/lib/site-import/fixtures/site-import-crawl-recordings.js.map +1 -0
  106. package/src/lib/site-import/fixtures/site-import-wave-recordings.d.ts +50 -0
  107. package/src/lib/site-import/fixtures/site-import-wave-recordings.js +217 -0
  108. package/src/lib/site-import/fixtures/site-import-wave-recordings.js.map +1 -0
  109. package/src/lib/site-import/site-import-crawl.d.ts +163 -0
  110. package/src/lib/site-import/site-import-crawl.js +467 -0
  111. package/src/lib/site-import/site-import-crawl.js.map +1 -0
  112. package/src/lib/site-import/site-import-flag.d.ts +23 -0
  113. package/src/lib/site-import/site-import-flag.js +23 -0
  114. package/src/lib/site-import/site-import-flag.js.map +1 -0
  115. package/src/lib/site-import/site-import-html.d.ts +94 -0
  116. package/src/lib/site-import/site-import-html.js +462 -0
  117. package/src/lib/site-import/site-import-html.js.map +1 -0
  118. package/src/lib/site-import/site-import-images.d.ts +54 -0
  119. package/src/lib/site-import/site-import-images.js +204 -0
  120. package/src/lib/site-import/site-import-images.js.map +1 -0
  121. package/src/lib/site-import/site-import-page.d.ts +48 -0
  122. package/src/lib/site-import/site-import-page.js +150 -0
  123. package/src/lib/site-import/site-import-page.js.map +1 -0
  124. package/src/lib/site-import/site-import-review.d.ts +40 -0
  125. package/src/lib/site-import/site-import-review.js +149 -0
  126. package/src/lib/site-import/site-import-review.js.map +1 -0
  127. package/src/lib/site-import/site-import-robots.d.ts +52 -0
  128. package/src/lib/site-import/site-import-robots.js +126 -0
  129. package/src/lib/site-import/site-import-robots.js.map +1 -0
  130. package/src/lib/tools/ai-seo-tool.d.ts +25 -1
  131. package/src/lib/tools/ai-seo-tool.js +61 -0
  132. package/src/lib/tools/ai-seo-tool.js.map +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-import-step.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*==========================================\n * THE `import` JOB: BRING MY SITE, IN REVIEWED WAVES (AGL-3976).\n *\n * One step, many passes, each one unit of work (`aiSiteImportNextWork`):\n *\n * 1. READ the proved site (no model): the crawler reads it as AglynBot,\n * main pages first, up to the plan's page count; the inventory is cut\n * into waves (`model/ai-site-import.ts`), every page gets a ledger row,\n * and Trust & Safety gets its row. The job then WAITS (`needs_review`,\n * reason `wave`): nothing is brought in until the person starts wave 1.\n * 2. START A WAVE (no model), on the person's say-so: the proof is still\n * current, the day's limit has room, the plan still has room; the\n * wave's pages are read again, fresh, and a page that became a sign-in\n * page, went away or is now off limits to AglynBot is left out.\n * 3. THE HEADER AND FOOTER, in the first wave: the layout step's own pass,\n * linking the wave's pages.\n * 4. EACH PAGE: its photos into the media library (no model, deduped by\n * source key), then the page step's own passes in the layout language,\n * told the page's text FENCED as untrusted data.\n * 5. CLOSE THE WAVE: the job waits again, with the wave's pages as drafts\n * to review, until the person starts the next one. The last wave ends\n * the job.\n *\n * Every page is an item of the job's ledger, so a page that fails on our\n * side gives back its own spend (the machine's `refundBuildCredits`) and the\n * wave goes on. Nothing here publishes: every page is a draft, and the site\n * stays unpublished until a member publishes it, which they can only reach\n * once the import, behind its ownership proof, has written something.\n *=========================================*/\n\nimport { resolveEffectivePlan } from '@aglyn/aglyn/app-utils/plan-entitlements'\nimport { createResourceUid } from '@aglyn/aglyn/app-utils/create-resource-uid'\nimport { pluginMediaIngest, type PluginMediaIngest } from '@aglyn/aglyn/plugin-manager/plugin-media-ingest'\nimport {\n listDomainOwnershipClaims,\n normalizeOwnershipDomain,\n} from '@aglyn/tenant-data-admin/server/domain-ownership'\nimport { getOrgForUser } from '@aglyn/tenant-data-admin/server/organizations'\nimport { consumeRateLimit } from '@aglyn/tenant-data-admin/server/rate-limit-store'\nimport { isServerReleaseFlagOnForOrg } from '@aglyn/tenant-data-admin/server/release-flags'\nimport type { AiBuildPlanCreate, AiBuildPlanScreen } from '../model/ai-build-plan'\nimport type { AiJob, AiJobItemLedger, AiJobPlan, AiJobReview } from '../model/ai-jobs.types'\nimport {\n AI_SITE_IMPORT_DOMAIN_INPUT,\n AI_SITE_IMPORT_FRAME_SLOT,\n AI_SITE_IMPORT_LEFT_OUT_COPY,\n AI_SITE_IMPORT_MAX_PAGES,\n AI_SITE_IMPORT_MIN_WORDS,\n AI_SITE_IMPORT_WAVE_PAGES,\n aiSiteImportInventory,\n aiSiteImportItemOpen,\n aiSiteImportLedger,\n aiSiteImportNextWork,\n aiSiteImportPageSettled,\n aiSiteImportPagesRead,\n aiSiteImportPhotosIn,\n aiSiteImportWaveClosed,\n aiSiteImportWaveMessage,\n aiSiteImportWaveStarted,\n type AiSiteImportLeftOutReason,\n type AiSiteImportPage,\n type AiSiteImportPageRead,\n type AiSiteImportState,\n type AiSiteImportWave,\n} from '../model/ai-site-import'\nimport { aiModelForStep } from '../providers/routing'\nimport { SITE_IMPORT_RELEASE_FLAG } from '../site-import/site-import-flag'\nimport { crawlSiteForImport, type SiteImportCrawlSeams, type SiteImportInventory } from '../site-import/site-import-crawl'\nimport { bringInSiteImportPhotos, type SiteImportPhotosDeps } from '../site-import/site-import-images'\nimport { siteImportLeftOutReason, siteImportListedPage, siteImportPageRead, siteImportPageWords } from '../site-import/site-import-page'\nimport { fileSiteImportReview } from '../site-import/site-import-review'\nimport { aiUnitErrorRetryable, aiUnitFailure, aiUnitSpend } from './ai-build-unit-outcome'\nimport { registerAiJobAdmission, type AiJobAdmission } from './ai-job-admission'\nimport { AI_JOB_STEP_OVERHEAD_MS } from './ai-job-budget'\nimport { readAiScreenRoom } from './ai-job-drafts'\nimport { aiUnspentOutcome } from './ai-job-generation'\nimport { AI_LAYOUT_PAGE_PHOTOS_INPUT } from './ai-layout-owner-photos'\nimport { AI_LAYOUT_SITE_PAGES_INPUT, AI_LAYOUT_SITE_PAGES_MAX, type AiLayoutSitePage } from './ai-job-layout-site-pages'\nimport { AI_JOB_PAGE_STEP_MINIMUM_MS } from './ai-job-page-budget'\nimport { AI_LAYOUT_IMPORTED_PAGE_INPUT, AI_LAYOUT_LANGUAGE_INPUT } from './ai-job-page-language'\nimport { aiSitePageWritten } from './ai-job-site-step'\nimport { AI_STOCK_PHOTO_PAGES_INPUT } from './ai-layout-stock-photos'\nimport { AI_JOB_BRIEF_MAX_CHARS, type AiJobItemOutcome, type AiJobStepContext, type AiJobStepOutcome, type AiJobStepRunner } from './ai-job-text-step'\nimport {\n aiApplyJobItemRecord,\n aiJobStepRunMinimumMs,\n aiJobStepRunnerFor,\n registerAiJobStep,\n registerAiJobStepPasses,\n} from './ai-jobs'\n\n/** Waves (the read included) one workspace may start in a day. */\nexport const SITE_IMPORT_WAVES_PER_ORG_PER_DAY = 12\n\nconst DAY_MS = 24 * 60 * 60 * 1000\n\n/** The rate-limit key a workspace's imports share. */\nexport function siteImportRateKey(orgId: string): string {\n return `ai-import:${orgId}`\n}\n\n/** The crawl's clock when the import reads the whole site. */\nexport const AI_SITE_IMPORT_READ_MS = 150_000\n\n/** The crawl's clock when a wave reads its own pages again. */\nexport const AI_SITE_IMPORT_WAVE_READ_MS = 90_000\n\n/** The clock for one page's photos: each is fetched and stored through the library's door. */\nexport const AI_SITE_IMPORT_PHOTOS_MS = 90_000\n\n/** What the step's own reads and writes around a pass take: the claims, the room, the row. */\nconst OWN_READS_MS = 10_000\n\n/** The least time each pass that asks no model needs. */\nexport const AI_SITE_IMPORT_PASS_MINIMUM_MS = {\n read: AI_SITE_IMPORT_READ_MS + OWN_READS_MS + AI_JOB_STEP_OVERHEAD_MS,\n wave: AI_SITE_IMPORT_WAVE_READ_MS + OWN_READS_MS + AI_JOB_STEP_OVERHEAD_MS,\n photos: AI_SITE_IMPORT_PHOTOS_MS + OWN_READS_MS + AI_JOB_STEP_OVERHEAD_MS,\n} as const\n\n/**\n * The passes one import may take: a read, then for every page its photos,\n * its answer and its listing, every wave's start and the header and footer,\n * with room for re-asks.\n */\nexport const AI_SITE_IMPORT_MAX_PASSES = AI_SITE_IMPORT_MAX_PAGES * 4 + 60\n\n/** Customer-safe sentences. */\nexport const AI_SITE_IMPORT_COPY = {\n noSite: 'Open the site to bring your website into.',\n badDomain: 'This website’s address cannot be brought in.',\n ownership: (domain: string) =>\n `Verify that you own ${domain} before bringing it in. A proof is good for 30 days; verify again, then start this wave again.`,\n limit: `This workspace has started as many site waves as it can today (${SITE_IMPORT_WAVES_PER_ORG_PER_DAY}). Start this wave again tomorrow.`,\n robots: (domain: string) =>\n `The robots.txt file of ${domain} could not be read just now, so nothing was read. Try again in a few minutes.`,\n nothing: (domain: string) => `No page of ${domain} could be read. Check that the site is up, then try again.`,\n unavailable: 'Bringing a site in is not available on this workspace right now.',\n frameUnavailable: 'The header and footer could not be built here; your pages use the site’s own layout.',\n pageUnavailable: 'Pages cannot be built here right now.',\n pageNotWritten: 'This page could not be written from its old page. Its spend is given back.',\n plan: 'Bringing an existing site in is included on every paid plan.',\n role: 'Only the workspace owner or an admin can bring a site in.',\n} as const\n\n/*------------------------------------------\n * Seams\n *-----------------------------------------*/\n\nexport interface AiSiteImportOwnership {\n current: boolean\n method: string | null\n provenAt: number | null\n detail: string | null\n}\n\nexport interface AiSiteImportStepDeps {\n crawl?: typeof crawlSiteForImport\n crawlSeams?: SiteImportCrawlSeams\n runnerFor?: typeof aiJobStepRunnerFor\n ownership?: (orgId: string, domain: string) => Promise<AiSiteImportOwnership | null>\n rateLimit?: (key: string, options: { limit: number; windowMs: number }) => Promise<{ allowed: boolean }>\n releaseFlagOn?: (orgId: string) => Promise<boolean>\n ingest?: () => PluginMediaIngest | null\n fetchImage?: SiteImportPhotosDeps['fetch']\n readRoom?: typeof readAiScreenRoom\n readSiteName?: (firestore: FirebaseFirestore.Firestore, hostId: string) => Promise<string>\n fileReview?: typeof fileSiteImportReview\n pageWritten?: typeof aiSitePageWritten\n mintId?: () => string\n clock?: () => number\n}\n\nasync function readOwnership(orgId: string, domain: string): Promise<AiSiteImportOwnership | null> {\n const claims = await listDomainOwnershipClaims(orgId)\n const claim = claims.find((entry) => entry.domain === domain)\n if (!claim) return null\n return { current: claim.current, method: claim.provenMethod, provenAt: claim.provenAt, detail: claim.provenDetail }\n}\n\nasync function readSiteName(firestore: FirebaseFirestore.Firestore, hostId: string): Promise<string> {\n const host = await firestore.collection('hosts').doc(hostId).get()\n const name = host.get('displayName')\n return typeof name === 'string' ? name.trim() : ''\n}\n\n/** The proved domain an import job is for, or `null`. */\nexport function aiSiteImportDomainOf(job: Pick<AiJob, 'inputs'>): string | null {\n return normalizeOwnershipDomain(String(job.inputs?.[AI_SITE_IMPORT_DOMAIN_INPUT] ?? '')).domain\n}\n\n/*------------------------------------------\n * The jobs a unit is handed to\n *-----------------------------------------*/\n\nconst pathOf = (slug: string) => (slug === '/' ? '/' : `/${slug}`)\n\n/** A plan as a delegated unit reads it: confirmed, with what it builds and nothing else. */\nfunction unitPlan(job: AiJob, input: { create?: AiBuildPlanCreate[]; screens?: AiBuildPlanScreen[] }): AiJobPlan {\n return {\n reuse: [],\n create: input.create ?? [],\n screens: input.screens ?? [],\n labels: {},\n status: 'confirmed',\n proposedAt: job.createdAt,\n confirmedAt: job.createdAt,\n confirmedBy: job.createdBy,\n }\n}\n\nconst delivered = (items: readonly AiJobItemLedger[], slot: string) =>\n items.some((row) => row.slot === slot && (row.status === 'succeeded' || row.status === 'degraded'))\n\n/**\n * The pages the header and a page may link: the ones of this wave and those\n * before it that are in or still coming, home first, at most the header's\n * seven. A page of a later wave is not linked: it may never be brought in.\n */\nexport function aiSiteImportLinkedPages(\n state: AiSiteImportState,\n items: readonly AiJobItemLedger[],\n wave: number,\n): AiLayoutSitePage[] {\n const rows = new Map(items.map((row) => [row.slot, row]))\n const pages = state.pages\n .filter((page) => page.wave <= wave && !page.refused)\n .filter((page) => {\n const row = rows.get(page.slot)\n return !row || row.status === 'pending' || row.status === 'running' || row.status === 'succeeded' || row.status === 'degraded'\n })\n .map((page) => ({ id: page.draftId, label: page.title, slug: pathOf(page.slug) }))\n return [...pages.filter((page) => page.slug === '/'), ...pages.filter((page) => page.slug !== '/')].slice(0, AI_LAYOUT_SITE_PAGES_MAX)\n}\n\n/** The header and footer's job: a layout of the site's name, linking the wave's pages, in the layout language. */\nexport function aiSiteImportFrameJob(job: AiJob, state: AiSiteImportState, wave: AiSiteImportWave, items: readonly AiJobItemLedger[]): AiJob {\n const draftId = state.frame?.draftId ?? `${job.$id}-${AI_SITE_IMPORT_FRAME_SLOT}`\n const creation: AiBuildPlanCreate = {\n kind: 'layout',\n name: 'Header and footer',\n why: `The header and footer every page brought in from ${state.domain} renders inside.`,\n duplicateOf: null,\n fields: ['header', 'nav', 'main', 'footer'],\n id: draftId,\n }\n return {\n ...job,\n $id: draftId,\n kind: 'layout',\n steps: [],\n outputs: [],\n items: null,\n siteImport: null,\n plan: unitPlan(job, { create: [creation] }),\n inputs: {\n originJobId: job.$id,\n businessName: state.siteName,\n [AI_LAYOUT_LANGUAGE_INPUT]: true,\n [AI_LAYOUT_SITE_PAGES_INPUT]: aiSiteImportLinkedPages(state, items, wave.index),\n },\n brief: `Build the header and footer of “${state.siteName}”, a website brought in from ${state.domain}.`.slice(0, AI_JOB_BRIEF_MAX_CHARS),\n }\n}\n\n/** One page's job: a page job's plan of one screen, its sections from the old page's headings, its text fenced. */\nexport function aiSiteImportPageJob(\n job: AiJob,\n state: AiSiteImportState,\n page: AiSiteImportPage,\n items: readonly AiJobItemLedger[],\n): AiJob {\n const sections = (page.read?.sections.length ? page.read.sections : ['Introduction']).map((name) => ({\n name,\n uses: [],\n items: 0,\n }))\n const screen: AiBuildPlanScreen = {\n title: page.title,\n slug: pathOf(page.slug),\n // Inside the header and footer this import built, once it is in.\n layout: state.frame && delivered(items, AI_SITE_IMPORT_FRAME_SLOT) ? state.frame.draftId : null,\n template: null,\n duplicateOf: null,\n nav: false,\n seoTitle: page.title,\n seoDescription: '',\n sections,\n record: null,\n id: page.draftId,\n }\n // The other pages already in, whose stock photos this page does not repeat.\n const others = state.pages.filter((entry) => entry.slot !== page.slot && delivered(items, entry.slot)).map((entry) => entry.draftId)\n return {\n ...job,\n $id: page.draftId,\n kind: 'page',\n steps: [],\n outputs: [],\n items: null,\n siteImport: null,\n plan: unitPlan(job, { screens: [screen] }),\n inputs: {\n originJobId: job.$id,\n businessName: state.siteName,\n [AI_LAYOUT_LANGUAGE_INPUT]: true,\n [AI_LAYOUT_SITE_PAGES_INPUT]: aiSiteImportLinkedPages(state, items, page.wave),\n [AI_LAYOUT_IMPORTED_PAGE_INPUT]: { url: page.url, text: page.read?.text ?? '' },\n [AI_LAYOUT_PAGE_PHOTOS_INPUT]: page.photos?.assets ?? [],\n ...(others.length ? { [AI_STOCK_PHOTO_PAGES_INPUT]: others } : {}),\n },\n brief: `Bring in the page “${page.title}” of ${state.domain} as a page of “${state.siteName}”.`.slice(0, AI_JOB_BRIEF_MAX_CHARS),\n }\n}\n\n/** The least time the import's next pass needs: a read's, a photo pass's, or the time of the step its unit is handed to. */\nexport function aiSiteImportRunMinimumMs(job: AiJob, runnerFor: typeof aiJobStepRunnerFor = aiJobStepRunnerFor): number {\n const state = job.siteImport ?? null\n const items = job.items ?? []\n const work = aiSiteImportNextWork(state, items)\n switch (work.kind) {\n case 'inventory':\n return AI_SITE_IMPORT_PASS_MINIMUM_MS.read\n case 'start-wave':\n case 'read':\n return AI_SITE_IMPORT_PASS_MINIMUM_MS.wave\n case 'images':\n return AI_SITE_IMPORT_PASS_MINIMUM_MS.photos\n case 'frame':\n return runnerFor('layout') && state ? aiJobStepRunMinimumMs(aiSiteImportFrameJob(job, state, work.wave, items)) : 0\n case 'page':\n return runnerFor('page') && state ? aiJobStepRunMinimumMs(aiSiteImportPageJob(job, state, work.page, items)) : 0\n default:\n return 0\n }\n}\n\n/*------------------------------------------\n * The step\n *-----------------------------------------*/\n\nconst waveReview = (message: string): AiJobReview => ({ reason: 'wave', message, findings: [] })\n\n/** The rows a wave's read left out, settled as not brought in, with the reason. */\nfunction withLeftOutRows(\n items: readonly AiJobItemLedger[],\n reads: ReadonlyMap<string, AiSiteImportPageRead | AiSiteImportLeftOutReason>,\n): AiJobItemLedger[] {\n return items.map((row) => {\n const read = reads.get(row.slot)\n return typeof read === 'string' && (row.status === 'pending' || row.status === 'running')\n ? { ...row, status: 'skipped', note: AI_SITE_IMPORT_LEFT_OUT_COPY[read] }\n : row\n })\n}\n\n/** Whether every row of the wave is settled, its header and footer included. */\nfunction waveSettled(state: AiSiteImportState, wave: AiSiteImportWave, items: readonly AiJobItemLedger[]): boolean {\n if (wave.frame && state.frame && aiSiteImportItemOpen(items, AI_SITE_IMPORT_FRAME_SLOT)) return false\n return wave.slots.every((slot) => !aiSiteImportItemOpen(items, slot))\n}\n\nexport function createAiJobImportStep(deps: AiSiteImportStepDeps = {}): AiJobStepRunner {\n const crawl = deps.crawl ?? crawlSiteForImport\n const runnerFor = deps.runnerFor ?? aiJobStepRunnerFor\n const ownershipOf = deps.ownership ?? readOwnership\n const rateLimit = deps.rateLimit ?? consumeRateLimit\n const flagOn = deps.releaseFlagOn ?? ((orgId: string) => isServerReleaseFlagOnForOrg(SITE_IMPORT_RELEASE_FLAG, orgId))\n const ingestOf = deps.ingest ?? pluginMediaIngest\n const readRoom = deps.readRoom ?? readAiScreenRoom\n const siteNameOf = deps.readSiteName ?? readSiteName\n const fileReview = deps.fileReview ?? fileSiteImportReview\n const pageWritten = deps.pageWritten ?? aiSitePageWritten\n const mintId = deps.mintId ?? createResourceUid\n const clock = deps.clock ?? Date.now\n\n return async (context: AiJobStepContext): Promise<AiJobStepOutcome> => {\n const { job, firestore, now, signal } = context\n const model = aiModelForStep('job.page')\n const unspent = (rest: Partial<AiJobStepOutcome> = {}): AiJobStepOutcome => ({ ...aiUnspentOutcome(model), ...rest })\n const hostId = job.hostId\n if (!hostId) return unspent({ failure: AI_SITE_IMPORT_COPY.noSite })\n const domain = aiSiteImportDomainOf(job)\n if (!domain) return unspent({ failure: AI_SITE_IMPORT_COPY.badDomain })\n const state = job.siteImport ?? null\n const items = job.items ?? []\n const work = aiSiteImportNextWork(state, items)\n const nowIso = now.toISOString()\n const review = (event: 'read' | 'wave-started' | 'wave-finished', next: AiSiteImportState, ledger: readonly AiJobItemLedger[], wave?: number) =>\n fileReview(firestore, { orgId: job.orgId, hostId, jobId: job.$id, state: next, items: ledger, event, nowMs: now.getTime(), ...(wave !== undefined ? { wave } : {}) })\n\n /** Closes the wave when this pass settled its last row: the review, or the job's end after the last wave. */\n const closing = async (\n next: AiSiteImportState,\n ledger: readonly AiJobItemLedger[],\n wave: AiSiteImportWave,\n outcome: AiJobStepOutcome,\n ): Promise<AiJobStepOutcome> => {\n if (!waveSettled(next, wave, ledger)) return { ...outcome, siteImport: next, continue: true }\n const closed = aiSiteImportWaveClosed(next, wave.index, nowIso)\n await review('wave-finished', closed, ledger, wave.index)\n const waiting = closed.waves.find((entry) => entry.index === wave.index)?.status === 'review'\n return waiting\n ? { ...outcome, siteImport: closed, review: waveReview(aiSiteImportWaveMessage(closed, ledger)) }\n : { ...outcome, siteImport: closed }\n }\n\n /** The proof, the flag and the day's limit, asked before anything is read. */\n const gate = async (): Promise<{ ownership: AiSiteImportOwnership } | AiJobStepOutcome> => {\n if (!(await flagOn(job.orgId))) return unspent({ review: waveReview(AI_SITE_IMPORT_COPY.unavailable) })\n const ownership = await ownershipOf(job.orgId, domain)\n if (!ownership?.current) {\n return unspent({ review: { reason: 'ownership', message: AI_SITE_IMPORT_COPY.ownership(domain), findings: [] } })\n }\n const window = await rateLimit(siteImportRateKey(job.orgId), { limit: SITE_IMPORT_WAVES_PER_ORG_PER_DAY, windowMs: DAY_MS })\n if (!window.allowed) return unspent({ review: { reason: 'limit', message: AI_SITE_IMPORT_COPY.limit, findings: [] } })\n return { ownership }\n }\n\n // ── 1. Read the site ──\n if (work.kind === 'inventory') {\n const passed = await gate()\n if (!('ownership' in passed)) return passed\n const room = await readRoom(firestore, { hostId, org: (context.org ?? null) as never })\n const left = room.limit === null ? AI_SITE_IMPORT_MAX_PAGES : Math.max(0, room.limit - room.used)\n const crawled = await crawl(\n {\n domain,\n // A wave's worth past the room, so what the plan leaves out is said.\n maxPages: Math.min(AI_SITE_IMPORT_MAX_PAGES, left + AI_SITE_IMPORT_WAVE_PAGES),\n timeBudgetMs: AI_SITE_IMPORT_READ_MS,\n shallowFirst: true,\n ...(signal ? { signal } : {}),\n },\n deps.crawlSeams,\n )\n if (crawled.refused) return unspent({ failure: AI_SITE_IMPORT_COPY.badDomain })\n if (crawled.stoppedBy === 'robots-unreachable') return unspent({ review: waveReview(AI_SITE_IMPORT_COPY.robots(domain)) })\n if (!crawled.pages.length) return unspent({ review: waveReview(AI_SITE_IMPORT_COPY.nothing(domain)) })\n const inventory = aiSiteImportInventory({\n domain,\n siteName: await siteNameOf(firestore, hostId),\n ownership: { method: passed.ownership.method, provenAt: passed.ownership.provenAt, detail: passed.ownership.detail },\n readAt: nowIso,\n pages: crawled.pages.map(siteImportListedPage),\n menu: siteImportMenu(crawled),\n skipped: crawled.skipped.map((entry) => ({ url: entry.url, reason: siteImportLeftOutReason(entry.reason) })),\n skippedOverflow: crawled.skippedOverflow,\n stoppedBy: crawled.stoppedBy,\n pageLimit: room.limit,\n existingPages: room.used,\n mintId,\n })\n const ledger = aiSiteImportLedger(inventory)\n const reviewId = await review('read', inventory, ledger)\n const next = { ...inventory, reviewId }\n return unspent({ items: ledger, siteImport: next, review: waveReview(aiSiteImportWaveMessage(next, ledger)) })\n }\n\n if (!state) return unspent({ failure: AI_SITE_IMPORT_COPY.badDomain })\n if (work.kind === 'done') return unspent()\n\n // ── 2. Start a wave ──\n if (work.kind === 'start-wave') {\n const passed = await gate()\n if (!('ownership' in passed)) return passed\n const wave = work.wave\n const pages = wave.slots\n .map((slot) => state.pages.find((page) => page.slot === slot))\n .filter((page): page is AiSiteImportPage => !!page)\n const reads = new Map<string, AiSiteImportPageRead | AiSiteImportLeftOutReason>()\n // Never past the plan: the room is read again, since pages may have been added since the site was read.\n const room = await readRoom(firestore, { hostId, org: (context.org ?? null) as never })\n let left = room.limit === null ? Number.POSITIVE_INFINITY : Math.max(0, room.limit - room.used)\n const toRead: AiSiteImportPage[] = []\n for (const page of pages) {\n if (left > 0) {\n toRead.push(page)\n left -= 1\n } else {\n reads.set(page.slot, 'plan-limit')\n }\n }\n if (toRead.length) {\n const crawled = await crawl(\n {\n domain,\n maxPages: toRead.length,\n only: toRead.map((page) => page.url),\n timeBudgetMs: AI_SITE_IMPORT_WAVE_READ_MS,\n ...(signal ? { signal } : {}),\n },\n deps.crawlSeams,\n )\n if (crawled.stoppedBy === 'robots-unreachable') return unspent({ review: waveReview(AI_SITE_IMPORT_COPY.robots(domain)) })\n // A read the clock cut short leaves its other pages for the next pass, never \"unreadable\".\n for (const [slot, read] of siteImportWaveReads(toRead, crawled, nowIso, { cutShort: crawled.stoppedBy === 'time' })) {\n reads.set(slot, read)\n }\n }\n let ledger = withLeftOutRows(items, reads)\n // A wave none of whose pages could be read builds no header for them.\n const anyRead = [...reads.values()].some((read) => typeof read !== 'string')\n if (wave.frame && !anyRead && reads.size === pages.length) {\n ledger = ledger.map((row) =>\n row.slot === AI_SITE_IMPORT_FRAME_SLOT && (row.status === 'pending' || row.status === 'running')\n ? { ...row, status: 'skipped', note: 'Not built: no page of this wave could be read.' }\n : row,\n )\n }\n const started = aiSiteImportWaveStarted(state, wave.index, nowIso, reads)\n await review('wave-started', started, ledger, wave.index)\n const running = started.waves.find((entry) => entry.index === wave.index) as AiSiteImportWave\n return closing(started, ledger, running, unspent({ items: ledger }))\n }\n\n const wave = work.wave\n\n // ── 2b. The rest of a wave's read, where the clock cut it short ──\n if (work.kind === 'read') {\n const crawled = await crawl(\n {\n domain,\n maxPages: work.pages.length,\n only: work.pages.map((page) => page.url),\n timeBudgetMs: AI_SITE_IMPORT_WAVE_READ_MS,\n ...(signal ? { signal } : {}),\n },\n deps.crawlSeams,\n )\n if (crawled.stoppedBy === 'robots-unreachable') return unspent({ review: waveReview(AI_SITE_IMPORT_COPY.robots(domain)) })\n const reads = siteImportWaveReads(work.pages, crawled, nowIso, { cutShort: crawled.stoppedBy === 'time' })\n const ledger = withLeftOutRows(items, reads)\n return closing(aiSiteImportPagesRead(state, reads), ledger, wave, unspent({ items: ledger }))\n }\n\n // ── 3. The header and footer ──\n if (work.kind === 'frame') {\n const runner = runnerFor('layout')\n if (!runner) {\n const item: AiJobItemOutcome = { slot: AI_SITE_IMPORT_FRAME_SLOT, status: 'skipped', note: AI_SITE_IMPORT_COPY.frameUnavailable }\n return closing(state, aiApplyJobItemRecord(items, item, 0, now), wave, unspent({ item }))\n }\n const derived = aiSiteImportFrameJob(job, state, wave, items)\n return runUnit(AI_SITE_IMPORT_FRAME_SLOT, runner, derived, state, null)\n }\n\n // ── 4a. A page's photos ──\n if (work.kind === 'images') {\n const photos = signal?.aborted\n ? { brought: 0, reused: 0, skipped: work.page.read?.images.length ?? 0, assets: [] }\n : await bringInSiteImportPhotos(\n {\n hostId,\n uid: job.createdBy,\n domain,\n images: work.page.read?.images ?? [],\n deadlineAt: clock() + AI_SITE_IMPORT_PHOTOS_MS,\n ...(signal ? { signal } : {}),\n },\n { ingest: ingestOf(), ...(deps.fetchImage ? { fetch: deps.fetchImage } : {}) },\n )\n return unspent({\n item: { slot: work.page.slot, status: 'running' },\n siteImport: aiSiteImportPhotosIn(state, work.page.slot, photos),\n continue: true,\n })\n }\n\n // ── 4b. The page ──\n if (work.kind === 'page') {\n const runner = runnerFor('page')\n if (!runner) {\n const item: AiJobItemOutcome = {\n slot: work.page.slot,\n status: 'failed',\n failure: { ours: true, reason: 'unavailable', message: AI_SITE_IMPORT_COPY.pageUnavailable },\n }\n return closing(state, aiApplyJobItemRecord(items, item, 0, now), wave, unspent({ item }))\n }\n return runUnit(work.page.slot, runner, aiSiteImportPageJob(job, state, work.page, items), state, work.page)\n }\n\n // ── 5. Close the wave ──\n return closing(state, items, wave, unspent())\n\n /** One delegated pass: the unit's spend is the pass's, what it came to is its row. */\n async function runUnit(\n slot: string,\n runner: AiJobStepRunner,\n derived: AiJob,\n current: AiSiteImportState,\n page: AiSiteImportPage | null,\n ): Promise<AiJobStepOutcome> {\n let outcome: AiJobStepOutcome\n try {\n outcome = await runner({ ...context, job: derived })\n } catch (error) {\n // A provider that will answer later, a budget that ended, a cancel: the machine's.\n if (aiUnitErrorRetryable(error) || (signal?.aborted ?? false)) throw error\n console.error('ai site import unit threw', { orgId: job.orgId, jobId: job.$id, slot, error })\n const item: AiJobItemOutcome = {\n slot,\n status: 'failed',\n failure: { ours: true, reason: 'provider', message: AI_SITE_IMPORT_COPY.pageNotWritten },\n }\n return closing(current, aiApplyJobItemRecord(items, item, 0, now), wave, unspent({ item }))\n }\n const spent = aiUnitSpend(outcome)\n const stopped = aiUnitFailure(slot, outcome)\n if (stopped?.status === 'running') return { ...spent, outputs: [], item: stopped, siteImport: current, continue: true }\n let item: AiJobItemOutcome\n if (stopped) {\n item = stopped\n } else if (page && derived.plan) {\n // A page counts as in only when its sections are in it (AGL-3596).\n const screen = derived.plan.screens[0]\n const written = await pageWritten(firestore, {\n hostId: hostId as string,\n unitJobId: derived.$id,\n screen,\n draftId: page.draftId,\n layoutLanguage: true,\n })\n item = written\n ? { slot, status: 'succeeded', outputs: spent.outputs.map((output) => output.id) }\n : { slot, status: 'failed', failure: { ours: true, reason: 'step-failure', message: AI_SITE_IMPORT_COPY.pageNotWritten } }\n } else {\n item = { slot, status: 'succeeded', outputs: spent.outputs.map((output) => output.id) }\n }\n const settledState = page ? aiSiteImportPageSettled(current, slot) : current\n return closing(\n settledState,\n aiApplyJobItemRecord(items, item, 0, now),\n wave,\n { ...spent, outputs: item.status === 'succeeded' ? spent.outputs : [], item },\n )\n }\n }\n}\n\n/** The home page's menu, as same-site paths in its order. */\nexport function siteImportMenu(crawled: Pick<SiteImportInventory, 'pages' | 'origins'>): string[] {\n const home = crawled.pages.find((page) => page.foundBy === 'home') ?? crawled.pages[0]\n const paths: string[] = []\n for (const link of home?.navLinks ?? []) {\n try {\n const url = new URL(link.url)\n if (!crawled.origins.includes(url.origin)) continue\n const path = url.pathname.replace(/\\/+$/, '') || '/'\n if (!paths.includes(path)) paths.push(path)\n } catch {\n // A menu entry that is not an address names no page.\n }\n }\n return paths\n}\n\n/**\n * What a wave's read came to for each page: the page read again, or why it\n * is left out now. Matched by address; a page the site sent to another of its\n * own addresses is matched in the order the wave asked for it.\n */\nexport function siteImportWaveReads(\n asked: readonly AiSiteImportPage[],\n crawled: Pick<SiteImportInventory, 'pages' | 'skipped'>,\n at: string,\n options: {\n /** The read's clock ran out: a page it did not reach is left for the next pass, not left out. */\n cutShort?: boolean\n } = {},\n): Map<string, AiSiteImportPageRead | AiSiteImportLeftOutReason> {\n const reads = new Map<string, AiSiteImportPageRead | AiSiteImportLeftOutReason>()\n const pathKey = (url: string) => {\n try {\n const parsed = new URL(url)\n return `${parsed.pathname.replace(/\\/+$/, '') || '/'}${parsed.search}`\n } catch {\n return url\n }\n }\n const pages = new Map(crawled.pages.map((page) => [pathKey(page.url), page]))\n const skipped = new Map(crawled.skipped.map((entry) => [pathKey(entry.url), entry.reason]))\n const unmatched = [...crawled.pages]\n const take = (page: (typeof crawled.pages)[number]) => {\n const index = unmatched.indexOf(page)\n if (index !== -1) unmatched.splice(index, 1)\n }\n const pending: AiSiteImportPage[] = []\n for (const page of asked) {\n const found = pages.get(pathKey(page.url))\n if (found) {\n take(found)\n reads.set(page.slot, siteImportReadOrEmpty(found, at))\n continue\n }\n const reason = skipped.get(pathKey(page.url))\n if (reason) {\n reads.set(page.slot, siteImportLeftOutReason(reason))\n continue\n }\n pending.push(page)\n }\n for (const page of pending) {\n const found = unmatched.shift()\n if (found) reads.set(page.slot, siteImportReadOrEmpty(found, at))\n else if (!options.cutShort) reads.set(page.slot, 'unreadable')\n }\n return reads\n}\n\nfunction siteImportReadOrEmpty(page: SiteImportInventory['pages'][number], at: string): AiSiteImportPageRead | 'empty' {\n if (siteImportPageWords(page) < AI_SITE_IMPORT_MIN_WORDS && !page.images.length) return 'empty'\n return siteImportPageRead(page, at)\n}\n\n/*------------------------------------------\n * Admission, and registration\n *-----------------------------------------*/\n\nexport interface AiSiteImportAdmissionDeps {\n releaseFlagOn?: (orgId: string) => Promise<boolean>\n ownership?: (orgId: string, domain: string) => Promise<AiSiteImportOwnership | null>\n memberRole?: (uid: string, orgId: string) => Promise<string | null>\n}\n\n/**\n * What an import asks before it exists and before each wave (AGL-3976): the\n * release flag, a site to bring it into, a paid plan, the owner or an admin,\n * and a CURRENT proof that the workspace owns the domain. Nothing is spent on\n * a refusal, and the day's limit is the step's to count, once a wave runs.\n */\nexport function createAiSiteImportAdmission(deps: AiSiteImportAdmissionDeps = {}): AiJobAdmission {\n const flagOn = deps.releaseFlagOn ?? ((orgId: string) => isServerReleaseFlagOnForOrg(SITE_IMPORT_RELEASE_FLAG, orgId))\n const ownershipOf = deps.ownership ?? readOwnership\n const memberRole =\n deps.memberRole ??\n (async (uid: string, orgId: string) => (await getOrgForUser(uid, orgId))?.member?.role ?? null)\n return async (context) => {\n if (!(await flagOn(context.orgId))) return { status: 404, error: 'Not found' }\n if (!context.hostId && !context.siteToCome) return { status: 400, error: AI_SITE_IMPORT_COPY.noSite }\n if (resolveEffectivePlan(context.org as never) === 'free') return { status: 403, error: AI_SITE_IMPORT_COPY.plan }\n const role = context.uid ? await memberRole(context.uid, context.orgId) : null\n if (role !== 'owner' && role !== 'admin') return { status: 403, error: AI_SITE_IMPORT_COPY.role }\n const { domain, error } = normalizeOwnershipDomain(String(context.inputs[AI_SITE_IMPORT_DOMAIN_INPUT] ?? ''))\n if (!domain) return { status: 400, error: error ?? AI_SITE_IMPORT_COPY.badDomain }\n const ownership = await ownershipOf(context.orgId, domain)\n if (!ownership?.current) return { status: 403, error: AI_SITE_IMPORT_COPY.ownership(domain) }\n return null\n }\n}\n\n/**\n * The import kind's registrations (AGL-3976), by a call from `server.ts`,\n * never by an import: its runner with each pass's least time, its pass bound\n * and its admission.\n */\nexport function registerAiSiteImportJob(deps: AiSiteImportStepDeps = {}): void {\n const runnerFor = deps.runnerFor ?? aiJobStepRunnerFor\n registerAiJobStep('import', createAiJobImportStep(deps), {\n minimumMs: AI_JOB_PAGE_STEP_MINIMUM_MS,\n minimumMsFor: (job) => aiSiteImportRunMinimumMs(job, runnerFor),\n })\n registerAiJobStepPasses('import', AI_SITE_IMPORT_MAX_PASSES)\n registerAiJobAdmission('import', createAiSiteImportAdmission())\n}\n"],"names":["resolveEffectivePlan","createResourceUid","pluginMediaIngest","listDomainOwnershipClaims","normalizeOwnershipDomain","getOrgForUser","consumeRateLimit","isServerReleaseFlagOnForOrg","AI_SITE_IMPORT_DOMAIN_INPUT","AI_SITE_IMPORT_FRAME_SLOT","AI_SITE_IMPORT_LEFT_OUT_COPY","AI_SITE_IMPORT_MAX_PAGES","AI_SITE_IMPORT_MIN_WORDS","AI_SITE_IMPORT_WAVE_PAGES","aiSiteImportInventory","aiSiteImportItemOpen","aiSiteImportLedger","aiSiteImportNextWork","aiSiteImportPageSettled","aiSiteImportPagesRead","aiSiteImportPhotosIn","aiSiteImportWaveClosed","aiSiteImportWaveMessage","aiSiteImportWaveStarted","aiModelForStep","SITE_IMPORT_RELEASE_FLAG","crawlSiteForImport","bringInSiteImportPhotos","siteImportLeftOutReason","siteImportListedPage","siteImportPageRead","siteImportPageWords","fileSiteImportReview","aiUnitErrorRetryable","aiUnitFailure","aiUnitSpend","registerAiJobAdmission","AI_JOB_STEP_OVERHEAD_MS","readAiScreenRoom","aiUnspentOutcome","AI_LAYOUT_PAGE_PHOTOS_INPUT","AI_LAYOUT_SITE_PAGES_INPUT","AI_LAYOUT_SITE_PAGES_MAX","AI_JOB_PAGE_STEP_MINIMUM_MS","AI_LAYOUT_IMPORTED_PAGE_INPUT","AI_LAYOUT_LANGUAGE_INPUT","aiSitePageWritten","AI_STOCK_PHOTO_PAGES_INPUT","AI_JOB_BRIEF_MAX_CHARS","aiApplyJobItemRecord","aiJobStepRunMinimumMs","aiJobStepRunnerFor","registerAiJobStep","registerAiJobStepPasses","SITE_IMPORT_WAVES_PER_ORG_PER_DAY","DAY_MS","siteImportRateKey","orgId","AI_SITE_IMPORT_READ_MS","AI_SITE_IMPORT_WAVE_READ_MS","AI_SITE_IMPORT_PHOTOS_MS","OWN_READS_MS","AI_SITE_IMPORT_PASS_MINIMUM_MS","read","wave","photos","AI_SITE_IMPORT_MAX_PASSES","AI_SITE_IMPORT_COPY","noSite","badDomain","ownership","domain","limit","robots","nothing","unavailable","frameUnavailable","pageUnavailable","pageNotWritten","plan","role","readOwnership","claims","claim","find","entry","current","method","provenMethod","provenAt","detail","provenDetail","readSiteName","firestore","hostId","host","collection","doc","get","name","trim","aiSiteImportDomainOf","job","String","inputs","pathOf","slug","unitPlan","input","reuse","create","screens","labels","status","proposedAt","createdAt","confirmedAt","confirmedBy","createdBy","delivered","items","slot","some","row","aiSiteImportLinkedPages","state","rows","Map","map","pages","filter","page","refused","id","draftId","label","title","slice","aiSiteImportFrameJob","frame","$id","creation","kind","why","duplicateOf","fields","steps","outputs","siteImport","originJobId","businessName","siteName","index","brief","aiSiteImportPageJob","sections","length","uses","screen","layout","template","nav","seoTitle","seoDescription","record","others","url","text","assets","aiSiteImportRunMinimumMs","runnerFor","work","waveReview","message","reason","findings","withLeftOutRows","reads","note","waveSettled","slots","every","createAiJobImportStep","deps","crawl","ownershipOf","rateLimit","flagOn","releaseFlagOn","ingestOf","ingest","readRoom","siteNameOf","fileReview","pageWritten","mintId","clock","Date","now","context","signal","model","unspent","rest","failure","nowIso","toISOString","review","event","next","ledger","jobId","nowMs","getTime","undefined","closing","outcome","closed","continue","waiting","waves","gate","window","windowMs","allowed","passed","room","org","left","Math","max","used","crawled","maxPages","min","timeBudgetMs","shallowFirst","crawlSeams","stoppedBy","inventory","readAt","menu","siteImportMenu","skipped","skippedOverflow","pageLimit","existingPages","reviewId","Number","POSITIVE_INFINITY","toRead","push","set","only","siteImportWaveReads","cutShort","anyRead","values","size","started","running","runner","item","derived","runUnit","aborted","brought","reused","images","uid","deadlineAt","fetchImage","fetch","ours","error","console","spent","stopped","written","unitJobId","layoutLanguage","output","settledState","home","foundBy","paths","link","navLinks","URL","origins","includes","origin","path","pathname","replace","asked","at","options","pathKey","parsed","search","unmatched","take","indexOf","splice","pending","found","siteImportReadOrEmpty","shift","createAiSiteImportAdmission","memberRole","member","siteToCome","registerAiSiteImportJob","minimumMs","minimumMsFor"],"mappings":"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"}
@@ -137,7 +137,11 @@ export declare const AI_JOB_PAGE_LANGUAGE_INSTRUCTIONS: readonly AiSystemBlock[]
137
137
  * header's call to action home the same way, six of them with such a page.
138
138
  */
139
139
  export declare function aiLayoutFormPageOfPlan(plan: Pick<AiJobPlan, 'create' | 'screens'>, formIds?: readonly string[]): string | null;
140
- /** Every word the job was given, which a page's contact details are held to. */
140
+ /**
141
+ * Every word the job was given, which a page's contact details are held to:
142
+ * the brief, its scalar inputs, and the text of a page brought in from the
143
+ * owner's own site (AGL-3976), whose phone and address are the owner's.
144
+ */
141
145
  export declare function aiLayoutFacts(job: Pick<AiJob, 'brief' | 'inputs'>): string;
142
146
  /** What a page's links resolve against: the site's pages, built and planned, its forms and components. */
143
147
  export declare function aiLayoutPageTargets(input: {
@@ -179,6 +183,40 @@ export interface AiLayoutStartPage {
179
183
  * the model. Empty when the page has no words to show.
180
184
  */
181
185
  export declare function aiLayoutStartPageLines(start: AiLayoutStartPage | null | undefined): string[];
186
+ /**
187
+ * The unit input carrying a page a site import brings in (AGL-3976): its
188
+ * address on the owner's own website and its text as that site shows it. It
189
+ * reaches the model only fenced as untrusted data, under
190
+ * `UNTRUSTED_TEXT_RULE`, and only on a job the import step derives — a
191
+ * member's own job carries none, because the create door's inputs are
192
+ * scalars and this is an object.
193
+ */
194
+ export declare const AI_LAYOUT_IMPORTED_PAGE_INPUT = "importedPage";
195
+ /** A page brought in, as the import step hands it to the page step. */
196
+ export interface AiLayoutImportedPage {
197
+ /** Where the page is on the owner's website. */
198
+ url: string;
199
+ /** Its title, headings and text in order, as the crawler extracted them. */
200
+ text: string;
201
+ }
202
+ /** The most characters of an imported page's text a prompt shows. */
203
+ export declare const AI_LAYOUT_IMPORTED_PAGE_MAX_CHARS = 6000;
204
+ /** The imported page a job's inputs carry, read defensively; `null` on every other job. */
205
+ export declare function aiLayoutImportedPageOf(inputs: Readonly<Record<string, unknown>> | null | undefined): AiLayoutImportedPage | null;
206
+ /**
207
+ * What a page brought in from the owner's site is told (AGL-3976): rewrite
208
+ * it into the planned sections, keeping its facts, and the text itself,
209
+ * fenced. The fence and the system block's rule are what keep the page's own
210
+ * words — "ignore your instructions" on a hostile page — data.
211
+ */
212
+ export declare function aiLayoutImportedPageLines(page: AiLayoutImportedPage | null): string[];
213
+ /**
214
+ * The page door's system blocks for this job: the language's cached
215
+ * instructions, and for a page brought in from the owner's site the rule
216
+ * that fenced text is data (AGL-3976), a static block of its own so every
217
+ * imported page shares one cached prefix and no other page's changes.
218
+ */
219
+ export declare function aiJobPageLanguageInstructions(job: Pick<AiJob, 'inputs'>): readonly AiSystemBlock[];
182
220
  /**
183
221
  * What a page is told about the site's form (AGL-3899). A page whose plan
184
222
  * places the form, or a site whose form has no page of its own, is told the
@@ -15,6 +15,7 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
15
15
  * See the License for the specific language governing permissions and
16
16
  * limitations under the License.
17
17
  */ import { CANVAS_ROOT_ELEMENT_ID } from "@aglyn/aglyn/foundation/constants/canvas";
18
+ import { fenceUntrustedText, UNTRUSTED_TEXT_RULE } from "@aglyn/shared-util-tools/untrusted-text";
18
19
  import { isAiPlanNewRef } from "../model/ai-build-plan.js";
19
20
  import { aiHomeScreenIds } from "../model/ai-site-inventory.js";
20
21
  import { AI_LAYOUT_POST_ADDRESS_TOKENS, aiCompileLayoutPage } from "../layout-language/ai-layout-compiler.js";
@@ -166,12 +167,21 @@ export const AI_JOB_PAGE_LANGUAGE_BUDGET = aiJobStepBudget({
166
167
  const screen = (_placing_find = placing.find((entry)=>!aiLayoutIsHomeSlug(entry.slug))) != null ? _placing_find : placing[0];
167
168
  return (_ref = screen == null ? void 0 : screen.id) != null ? _ref : null;
168
169
  }
169
- /** Every word the job was given, which a page's contact details are held to. */ export function aiLayoutFacts(job) {
170
- var _job_inputs;
170
+ /**
171
+ * Every word the job was given, which a page's contact details are held to:
172
+ * the brief, its scalar inputs, and the text of a page brought in from the
173
+ * owner's own site (AGL-3976), whose phone and address are the owner's.
174
+ */ export function aiLayoutFacts(job) {
175
+ var _job_inputs, _ref;
176
+ var _aiLayoutImportedPageOf;
171
177
  const values = Object.values((_job_inputs = job.inputs) != null ? _job_inputs : {}).filter((value)=>typeof value === 'string');
178
+ const imported = (_ref = (_aiLayoutImportedPageOf = aiLayoutImportedPageOf(job.inputs)) == null ? void 0 : _aiLayoutImportedPageOf.text) != null ? _ref : '';
172
179
  return [
173
180
  job.brief,
174
- ...values
181
+ ...values,
182
+ ...imported ? [
183
+ imported
184
+ ] : []
175
185
  ].join('\n');
176
186
  }
177
187
  /** What a page's links resolve against: the site's pages, built and planned, its forms and components. */ export function aiLayoutPageTargets(input) {
@@ -312,6 +322,52 @@ export const AI_JOB_PAGE_LANGUAGE_BUDGET = aiJobStepBudget({
312
322
  shown
313
323
  ];
314
324
  }
325
+ /**
326
+ * The unit input carrying a page a site import brings in (AGL-3976): its
327
+ * address on the owner's own website and its text as that site shows it. It
328
+ * reaches the model only fenced as untrusted data, under
329
+ * `UNTRUSTED_TEXT_RULE`, and only on a job the import step derives — a
330
+ * member's own job carries none, because the create door's inputs are
331
+ * scalars and this is an object.
332
+ */ export const AI_LAYOUT_IMPORTED_PAGE_INPUT = 'importedPage';
333
+ /** The most characters of an imported page's text a prompt shows. */ export const AI_LAYOUT_IMPORTED_PAGE_MAX_CHARS = 6000;
334
+ /** The imported page a job's inputs carry, read defensively; `null` on every other job. */ export function aiLayoutImportedPageOf(inputs) {
335
+ const raw = inputs == null ? void 0 : inputs[AI_LAYOUT_IMPORTED_PAGE_INPUT];
336
+ if (!raw || typeof raw !== 'object') return null;
337
+ const { url, text } = raw;
338
+ if (typeof url !== 'string' || typeof text !== 'string' || !text.trim()) return null;
339
+ return {
340
+ url: url.slice(0, 2048),
341
+ text
342
+ };
343
+ }
344
+ /**
345
+ * What a page brought in from the owner's site is told (AGL-3976): rewrite
346
+ * it into the planned sections, keeping its facts, and the text itself,
347
+ * fenced. The fence and the system block's rule are what keep the page's own
348
+ * words — "ignore your instructions" on a hostile page — data.
349
+ */ export function aiLayoutImportedPageLines(page) {
350
+ if (!page) return [];
351
+ return [
352
+ 'This page is the site owner\'s own page, brought in from their current website. Rewrite its content into the sections below, in its own language: keep its facts, names, prices, hours, services and details wherever a section covers them, and leave a gap in square brackets for a fact it does not give rather than invent one. Leave out cookie notices, sign-in, carts, the old site\'s own tools and anything about the website builder it was made with. Draw no form, embed or script of its own, and design every section anew. Its text, as that site shows it:',
353
+ fenceUntrustedText(`The owner's page ${page.url}`, page.text, {
354
+ maxChars: AI_LAYOUT_IMPORTED_PAGE_MAX_CHARS
355
+ })
356
+ ];
357
+ }
358
+ /**
359
+ * The page door's system blocks for this job: the language's cached
360
+ * instructions, and for a page brought in from the owner's site the rule
361
+ * that fenced text is data (AGL-3976), a static block of its own so every
362
+ * imported page shares one cached prefix and no other page's changes.
363
+ */ export function aiJobPageLanguageInstructions(job) {
364
+ return aiLayoutImportedPageOf(job.inputs) ? [
365
+ ...AI_JOB_PAGE_LANGUAGE_INSTRUCTIONS,
366
+ {
367
+ text: UNTRUSTED_TEXT_RULE
368
+ }
369
+ ] : AI_JOB_PAGE_LANGUAGE_INSTRUCTIONS;
370
+ }
315
371
  /**
316
372
  * What a page is told about the site's form (AGL-3899). A page whose plan
317
373
  * places the form, or a site whose form has no page of its own, is told the
@@ -376,6 +432,8 @@ export const AI_JOB_PAGE_LANGUAGE_BUDGET = aiJobStepBudget({
376
432
  ...aiSiteKindDesignLines(job.inputs),
377
433
  ...aiPlanReferenceLines(plan),
378
434
  ...aiLayoutStartPageLines(input.startsFrom),
435
+ // A page brought in from the owner's own site (AGL-3976), fenced.
436
+ ...aiLayoutImportedPageLines(aiLayoutImportedPageOf(job.inputs)),
379
437
  `Sections to design, in order:`,
380
438
  ...sections,
381
439
  pages.length ? `Other pages of this site: ${pages.join(', ')}.` : 'This site has no other page to link.',
@@ -745,7 +803,7 @@ export const AI_JOB_PAGE_LANGUAGE_BUDGET = aiJobStepBudget({
745
803
  return runValidatedGeneration(AI_LAYOUT_PAGE_KIND, _extends({
746
804
  step: 'job.page',
747
805
  model: input.model,
748
- instructions: AI_JOB_PAGE_LANGUAGE_INSTRUCTIONS,
806
+ instructions: aiJobPageLanguageInstructions(input.job),
749
807
  inventory: input.inventory,
750
808
  messages: [
751
809
  {