@aglyn/plugins-ai 1.0.0-beta.257 → 1.0.0-beta.258
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.
- package/package.json +17 -17
- package/src/lib/components/assist-panel.component.js +21 -8
- package/src/lib/components/assist-panel.component.js.map +1 -1
- package/src/lib/insights/ai-figure-readers.js +15 -8
- package/src/lib/insights/ai-figure-readers.js.map +1 -1
- package/src/lib/jobs/ai-job-build-step.d.ts +9 -0
- package/src/lib/jobs/ai-job-build-step.js +19 -3
- package/src/lib/jobs/ai-job-build-step.js.map +1 -1
- package/src/lib/jobs/ai-job-insight-step.js +1 -1
- package/src/lib/jobs/ai-job-insight-step.js.map +1 -1
- package/src/lib/jobs/ai-job-page-language.d.ts +19 -0
- package/src/lib/jobs/ai-job-page-language.js +24 -4
- package/src/lib/jobs/ai-job-page-language.js.map +1 -1
- package/src/lib/jobs/ai-job-page-sections.js +12 -1
- package/src/lib/jobs/ai-job-page-sections.js.map +1 -1
- package/src/lib/jobs/ai-job-page-step.d.ts +14 -0
- package/src/lib/jobs/ai-job-page-step.js +69 -20
- package/src/lib/jobs/ai-job-page-step.js.map +1 -1
- package/src/lib/jobs/ai-job-seo-step.js +3 -2
- package/src/lib/jobs/ai-job-seo-step.js.map +1 -1
- package/src/lib/jobs/ai-layout-stock-photos.d.ts +10 -5
- package/src/lib/jobs/ai-layout-stock-photos.js +89 -7
- package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
- package/src/lib/jobs/fixtures/ai-stock-photo-copper-kettle-recordings.d.ts +21 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-copper-kettle-recordings.js +126 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-copper-kettle-recordings.js.map +1 -0
- package/src/lib/layout-language/ai-layout-compiler.js +52 -52
- package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
- package/src/lib/layout-language/ai-layout-copy.d.ts +9 -5
- package/src/lib/layout-language/ai-layout-copy.js +22 -14
- package/src/lib/layout-language/ai-layout-copy.js.map +1 -1
- package/src/lib/layout-language/ai-layout-frame.js +4 -4
- package/src/lib/layout-language/ai-layout-frame.js.map +1 -1
- package/src/lib/layout-language/ai-layout-pictures.d.ts +44 -15
- package/src/lib/layout-language/ai-layout-pictures.js +29 -8
- package/src/lib/layout-language/ai-layout-pictures.js.map +1 -1
- package/src/lib/model/ai-build-job.js +14 -0
- package/src/lib/model/ai-build-job.js.map +1 -1
- package/src/lib/model/ai-host-publish-context.d.ts +6 -0
- package/src/lib/model/ai-host-publish-context.js +15 -2
- package/src/lib/model/ai-host-publish-context.js.map +1 -1
- package/src/lib/model/assist-follow-up.d.ts +35 -0
- package/src/lib/model/assist-follow-up.js +44 -4
- package/src/lib/model/assist-follow-up.js.map +1 -1
- package/src/lib/providers/anthropic.js +17 -4
- package/src/lib/providers/anthropic.js.map +1 -1
- package/src/lib/providers/cache-write-hour.d.ts +37 -0
- package/src/lib/providers/cache-write-hour.js +46 -0
- package/src/lib/providers/cache-write-hour.js.map +1 -0
- package/src/lib/providers/contract.d.ts +16 -3
- package/src/lib/providers/contract.js.map +1 -1
- package/src/lib/providers/routing.d.ts +13 -0
- package/src/lib/providers/routing.js +27 -11
- package/src/lib/providers/routing.js.map +1 -1
- package/src/lib/runtime/ai-dev-env.d.ts +13 -3
- package/src/lib/runtime/ai-dev-env.js +17 -3
- package/src/lib/runtime/ai-dev-env.js.map +1 -1
- package/src/lib/runtime/ai-dev-replay.js +4 -2
- package/src/lib/runtime/ai-dev-replay.js.map +1 -1
- package/src/lib/runtime/ai-doctrine-validators.d.ts +41 -1
- package/src/lib/runtime/ai-doctrine-validators.js +263 -2
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-doctrine.js +7 -1
- package/src/lib/runtime/ai-doctrine.js.map +1 -1
- package/src/lib/runtime/ai-eval-live.js +1 -1
- package/src/lib/runtime/ai-eval-live.js.map +1 -1
- package/src/lib/runtime/ai-eval.d.ts +1 -1
- package/src/lib/runtime/ai-eval.js +18 -1
- package/src/lib/runtime/ai-eval.js.map +1 -1
- package/src/lib/runtime/ai-placeholder-tokens.d.ts +70 -0
- package/src/lib/runtime/ai-placeholder-tokens.js +169 -0
- package/src/lib/runtime/ai-placeholder-tokens.js.map +1 -0
- package/src/lib/runtime/ai-repeated-items.js +5 -2
- package/src/lib/runtime/ai-repeated-items.js.map +1 -1
- package/src/lib/runtime/ai-runtime.js +2 -3
- package/src/lib/runtime/ai-runtime.js.map +1 -1
- package/src/lib/server/assist-chat.js +8 -3
- package/src/lib/server/assist-chat.js.map +1 -1
- package/src/lib/server/assist-view-context.d.ts +5 -1
- package/src/lib/server/assist-view-context.js +8 -3
- package/src/lib/server/assist-view-context.js.map +1 -1
- package/src/lib/usage/assist-usage.d.ts +2 -0
- package/src/lib/usage/assist-usage.js +8 -3
- package/src/lib/usage/assist-usage.js.map +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-seo-step.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { hostCollectionKind } from '@aglyn/aglyn/app-utils/collection-kind'\nimport { hostPublicOrigin } from '@aglyn/aglyn/app-utils/host-naming'\nimport {\n buildLlmsTxt,\n type LlmsTxtCollection,\n type LlmsTxtPage,\n} from '@aglyn/aglyn/app-utils/llms-txt'\nimport { screenRoutePathToUrl } from '@aglyn/aglyn/app-utils/screen-route'\nimport { SEO_FINDING_LABELS } from '@aglyn/aglyn/app-utils/seo-audit'\nimport {\n SEO_MAX_KEYWORDS,\n seoKeywordCoverage,\n seoKeywordSplit,\n seoQuotedKeywords,\n} from '@aglyn/aglyn/app-utils/seo-keywords'\nimport {\n SCREEN_SEO_LISTING_FIELDS,\n isSeoListingFieldKey,\n type SeoListingFieldKey,\n} from '@aglyn/aglyn/app-utils/seo-listing-fields'\nimport {\n SEO_PAGE_TEXT_MAX_CHARS,\n seoPageFacts,\n type SeoPageFacts,\n} from '@aglyn/aglyn/app-utils/seo-page-facts'\nimport {\n composeSeoPages,\n scanSeoSite,\n seoAuditSiteOf,\n type SeoRepeatRowsReader,\n type SeoSiteScan,\n} from '@aglyn/aglyn/app-utils/seo-site-scan'\n// By path, never through a barrel: only a server composition asks it.\nimport { readRepeatRows } from '@aglyn/aglyn/plugin-manager/repeat-rows'\nimport { decodeStoredNodes } from '@aglyn/aglyn/app-utils/stored-nodes'\nimport { getTemplateScreenIds } from '@aglyn/tenant-runtime/template-screens'\nimport type { AiJob, AiJobOutput } from '../model/ai-jobs.types'\nimport {\n AI_SEO_OUTPUT_IDS,\n aiSeoAuditView,\n aiSeoJobTarget,\n type AiSeoAuditReport,\n type AiSeoContentFix,\n type AiSeoFieldsProposal,\n type AiSeoFieldValues,\n type AiSeoPageFix,\n type AiSeoSiteFormField,\n type AiSeoSiteProposal,\n} from '../model/ai-seo'\nimport { aiModelForStep, aiRouteSettings } from '../providers/routing'\nimport type { AiSystemBlock, AiUsage } from '../runtime/ai-runtime'\nimport { aiSeoAudit, aiSeoAuditBatches, aiSeoHeadingDemotions } from '../runtime/seo-audit'\nimport {\n runValidatedGeneration,\n type AiGenerationSpend,\n type AiValidatedGeneration,\n} from '../runtime/ai-doctrine'\nimport { AI_SEO_FIELDS_MAX_TOKENS, generateSeoFields } from '../runtime/seo-fields'\nimport {\n AI_SEO_FIXES_TOOL_NAME,\n AI_SEO_SITE_TOOL_NAME,\n aiSeoFixImages,\n aiSeoFixesTool,\n aiSeoSiteTool,\n checkAiSeoFixes,\n checkAiSeoSite,\n type AiSeoBatchAnswer,\n type AiSeoBatchPage,\n} from '../tools/ai-seo-tool'\nimport {\n AI_SEO_FIXES_MAX_TOKENS,\n AI_SEO_SITE_MAX_TOKENS,\n aiSeoGenerationMaxTokens,\n} from './ai-job-seo-budget'\nimport type { AiJobStepContext, AiJobStepOutcome, AiJobStepRunner } from './ai-job-text-step'\n\n/**\n * The `seo` step (AGL-2910): search listings and a site audit, as proposals.\n *\n * `inputs.target` says what the job is about:\n *\n * - `screen` — one page's listing, written from the version the person is\n * looking at (`screenId`, `versionId`), for the page's SEO card;\n * - `product` — one product's listing, from the name and description the\n * product editor handed over (`name`, `text`), for the product editor;\n * - `site` — the audit of every published page the sitemap lists, with a\n * proposed fix per finding, structured data for the site's entity, and the\n * agent guidance `/llms.txt` leads with.\n *\n * ## It writes nothing\n *\n * Like every runner, this reads, asks and returns. A listing is served\n * straight from the screen document, the structured data and `/llms.txt`\n * from the host document, and a page's content from its published version,\n * so the values ride on the job's outputs and nothing else: a person puts\n * them in the page's SEO card or the site SEO form and saves them there, and\n * content fixes become a new version only through the apply door.\n *\n * ## An audit, a unit at a time\n *\n * The first pass reads the site — every published page's version and the\n * shared layouts, for the links between them — and scores it without a\n * model, through the platform's SEO check (the same findings the site's SEO\n * section lists to every owner); it records the report, and runs the first unit of generated work. Each later\n * pass runs one more unit: the site-wide proposal, then the page fixes in\n * batches. Every pass is one reservation and one provider exchange, and it\n * asks the machine to continue until nothing is left, so a large site is a\n * few beats of work rather than one request that cannot finish.\n */\n\nexport const AI_SEO_NO_SITE_COPY = 'This SEO job names a site this workspace does not have.'\nexport const AI_SEO_NO_PAGE_COPY = 'This page is no longer on the site.'\nexport const AI_SEO_NO_TARGET_COPY = 'This SEO job does not say what to write about.'\nexport const AI_SEO_NO_PRODUCT_COPY = 'Give the product a name before writing its listing.'\n\n/** How much of each page a batch of fixes carries. */\nexport const AI_SEO_BATCH_PAGE_TEXT_CHARS = 700\n\n/**\n * Output budgets, declared with the step's time (`ai-job-seo-budget.ts`,\n * AGL-3035). A batch's largest answer — every page with a title, description,\n * heading and image descriptions at their limits — and the site proposal's are\n * measured in `ai-job-seo-step.spec.ts`.\n */\nexport { AI_SEO_FIXES_MAX_TOKENS, AI_SEO_SITE_MAX_TOKENS }\n\n/** Pages the site prompt and the llms.txt preview list. */\nconst SITE_PAGES_LISTED = 25\n\n/** The fields a product's listing holds. */\nconst PRODUCT_LISTING_FIELDS: readonly SeoListingFieldKey[] = ['title', 'description']\n\nconst ZERO_USAGE: AiUsage = {\n inputTokens: 0,\n outputTokens: 0,\n cacheReadTokens: 0,\n cacheWriteTokens: 0,\n}\n\ntype Firestore = FirebaseFirestore.Firestore\ntype DocumentReference = FirebaseFirestore.DocumentReference\ntype Spent = Pick<AiJobStepOutcome, 'usage' | 'estCostUsd' | 'model' | 'stopReason'>\n\n/** The host document fields this step reads. */\nexport interface SeoHostDocument {\n orgId?: string\n displayName?: string\n subdomain?: string | null\n cname?: string | null\n screens?: Record<string, string>\n errorScreens?: Record<string, string>\n notFoundScreenId?: string | null\n seo?: {\n title?: string\n description?: string\n discourageSearchEngines?: boolean\n entity?: {\n type?: unknown\n name?: string\n description?: string\n email?: string\n telephone?: string\n contactType?: string\n }\n agent?: { whenToUse?: string; howToUse?: string }\n }\n}\n\n/** The screen document fields this step reads. */\ninterface SeoScreenDocument {\n displayName?: string\n description?: string\n versionId?: string\n visibility?: unknown\n deletedAt?: unknown\n seo?: {\n title?: string\n description?: string\n breadcrumb?: string\n image?: string\n imageAlt?: string\n }\n}\n\nconst str = (value: unknown): string => (typeof value === 'string' ? value.trim() : '')\n\nconst round6 = (value: number): number => Math.round(value * 1_000_000) / 1_000_000\n\nfunction modelOf(context: AiJobStepContext): string {\n return context.modelFor?.('job.seo') ?? aiModelForStep('job.seo')\n}\n\nfunction spentNothing(model: string): Spent {\n return { usage: ZERO_USAGE, estCostUsd: 0, model, stopReason: null }\n}\n\nfunction spentBy(generation: AiGenerationSpend): Spent {\n return {\n usage: generation.usage,\n estCostUsd: generation.estCostUsd,\n model: generation.model,\n stopReason: generation.stopReason,\n }\n}\n\nfunction addSpent(a: Spent, b: Spent): Spent {\n return {\n usage: {\n inputTokens: a.usage.inputTokens + b.usage.inputTokens,\n outputTokens: a.usage.outputTokens + b.usage.outputTokens,\n cacheReadTokens: a.usage.cacheReadTokens + b.usage.cacheReadTokens,\n cacheWriteTokens: a.usage.cacheWriteTokens + b.usage.cacheWriteTokens,\n },\n estCostUsd: round6(a.estCostUsd + b.estCostUsd),\n model: b.model || a.model,\n stopReason: b.stopReason ?? a.stopReason,\n }\n}\n\n/** Through JSON, so no `undefined` reaches the job document's write. */\nconst plain = <T>(value: T): Record<string, unknown> =>\n JSON.parse(JSON.stringify(value)) as Record<string, unknown>\n\n/**\n * The fields a listing job writes: the ones the editor named (`inputs.fields`,\n * comma-separated) that the target's editor holds, or all of those.\n */\nexport function aiSeoRequestedFields(\n raw: unknown,\n allowed: readonly SeoListingFieldKey[],\n): SeoListingFieldKey[] {\n const named = String(raw ?? '')\n .split(',')\n .map((part) => part.trim())\n .filter(isSeoListingFieldKey)\n const wanted = new Set<SeoListingFieldKey>(named.length ? named : allowed)\n return allowed.filter((key) => wanted.has(key))\n}\n\n/** The site's name, as its visitors read it. */\nfunction brandOf(host: SeoHostDocument): string {\n return str(host.seo?.title) || str(host.displayName) || str(host.subdomain)\n}\n\n/** The site a job names, read and checked against the job's org. */\nasync function loadSite(\n firestore: Firestore,\n job: AiJob,\n): Promise<{ host: SeoHostDocument } | { failure: string }> {\n if (!job.hostId) return { failure: AI_SEO_NO_SITE_COPY }\n const snapshot = await firestore.collection('hosts').doc(job.hostId).get()\n const host = snapshot.exists ? (snapshot.data() as SeoHostDocument) : null\n // The route checks the caller's membership of the org the job is metered\n // against, not that the site belongs to it. A site of another org is\n // refused here, before anything about it reaches a prompt.\n if (!host || host.orgId !== job.orgId) return { failure: AI_SEO_NO_SITE_COPY }\n return { host }\n}\n\n/** The rows a site's repeats render, through the platform's repeat-rows contract. */\nconst siteRepeatRows =\n (hostId: string): SeoRepeatRowsReader =>\n (keys) =>\n readRepeatRows({ hostId, keys })\n\n/**\n * Pages' facts as the SEO check reads them (AGL-3501): each version composed\n * as it publishes — components grafted, repeats expanded — and read against\n * the page's own map, so a fix is only ever offered for what the page holds.\n */\nasync function composedPageFacts(\n firestore: Firestore,\n hostId: string,\n versions: readonly ({ nodes: Record<string, unknown>; rootId?: string } | null)[],\n): Promise<SeoPageFacts[]> {\n const { composed } = await composeSeoPages(\n firestore,\n hostId,\n versions.map((version) => version?.nodes ?? null),\n { readRepeatRows: siteRepeatRows(hostId) },\n )\n return versions.map((version, index) =>\n seoPageFacts(composed[index] as never, { rootId: version?.rootId, pageNodes: version?.nodes as never }),\n )\n}\n\n/** A version's node map and root, or `null` when it cannot be read. */\nasync function readVersionNodes(\n hostRef: DocumentReference,\n screenId: string,\n versionId: string,\n): Promise<{ nodes: Record<string, unknown>; rootId?: string } | null> {\n if (!versionId) return null\n const snapshot = await hostRef\n .collection('screens')\n .doc(screenId)\n .collection('versions')\n .doc(versionId)\n .get()\n if (!snapshot.exists) return null\n const nodes = decodeStoredNodes<Record<string, unknown>>(snapshot.get('nodes'))\n if (!nodes) return null\n const rootId = snapshot.get('rootId')\n return { nodes, ...(typeof rootId === 'string' ? { rootId } : {}) }\n}\n\n/** Every other page's search title, for a proposal not to repeat. */\nasync function siteTitles(hostRef: DocumentReference, except: ReadonlySet<string>): Promise<string[]> {\n const snapshot = await hostRef.collection('screens').select('seo', 'deletedAt').limit(1000).get()\n return snapshot.docs\n .filter((doc) => !except.has(doc.id) && !doc.get('deletedAt'))\n .map((doc) => str((doc.get('seo') as SeoScreenDocument['seo'])?.title))\n .filter(Boolean)\n}\n\n/* ------------------------------------------------------------------------ *\n * One listing: a page or a product\n * ------------------------------------------------------------------------ */\n\nfunction listingOutcome(\n generation: AiValidatedGeneration<AiSeoFieldValues>,\n job: AiJob,\n subject: AiSeoFieldsProposal['subject'],\n context: {\n keywords: readonly string[]\n /** Keywords past the limit, which the proposal did not aim for. */\n unchecked: readonly string[]\n facts: SeoPageFacts | null\n fields: readonly SeoListingFieldKey[]\n hasImage: boolean\n },\n): AiJobStepOutcome {\n const spent = spentBy(generation)\n if (generation.status === 'refused') return { outputs: [], ...spent, refused: true }\n if (generation.status === 'needs_input') return { outputs: [], ...spent, failure: generation.message }\n const values = generation.value\n const notes: string[] = []\n if (context.fields.includes('imageAlt') && context.hasImage && !values.imageAlt) {\n notes.push('No image description was proposed: nothing on the page says what the picture shows.')\n }\n if (context.unchecked.length) {\n notes.push(\n `Only the first ${SEO_MAX_KEYWORDS} keywords were used, so ${seoQuotedKeywords(context.unchecked)} ${context.unchecked.length === 1 ? 'was' : 'were'} not.`,\n )\n }\n const proposal: AiSeoFieldsProposal = {\n kind: 'fields',\n subject,\n values,\n keywords: seoKeywordCoverage(context.keywords, {\n title: values.title,\n description: values.description,\n h1: context.facts?.h1s[0]?.text,\n body: context.facts?.text,\n }),\n notes,\n }\n const output: AiJobOutput = {\n resource: 'seo',\n id: AI_SEO_OUTPUT_IDS.fields(subject.kind, subject.id),\n hostId: job.hostId ?? null,\n label: `SEO proposal · ${subject.name}`,\n proposal: plain(proposal),\n }\n return { outputs: [output], ...spent }\n}\n\nasync function runScreenListing(\n context: AiJobStepContext & { firestore: Firestore },\n host: SeoHostDocument,\n): Promise<AiJobStepOutcome> {\n const { job, firestore, signal } = context\n const model = modelOf(context)\n const hostRef = firestore.collection('hosts').doc(job.hostId as string)\n const screenId = str(job.inputs?.['screenId'])\n const snapshot = screenId ? await hostRef.collection('screens').doc(screenId).get() : null\n const screen = snapshot?.exists ? (snapshot.data() as SeoScreenDocument) : null\n if (!screen || screen.deletedAt) {\n return { outputs: [], ...spentNothing(model), failure: AI_SEO_NO_PAGE_COPY }\n }\n\n const versionId = str(job.inputs?.['versionId']) || str(screen.versionId)\n const version = await readVersionNodes(hostRef, screenId, versionId)\n const [facts] = await composedPageFacts(firestore, job.hostId as string, [version])\n const fields = aiSeoRequestedFields(job.inputs?.['fields'], SCREEN_SEO_LISTING_FIELDS)\n const seo = screen.seo ?? {}\n const hasImage = Boolean(str(seo.image))\n // The share image is often a picture the page shows too: its description\n // and the text beside it there are what is known about it.\n const onPage = hasImage ? facts.images.find((image) => image.src === str(seo.image)) : undefined\n const route = host.screens?.[screenId]\n const path = typeof route === 'string' ? screenRoutePathToUrl(route) : null\n const name = str(screen.displayName) || path || 'Untitled page'\n const { keywords, unchecked } = seoKeywordSplit(job.inputs?.['keywords'])\n const generation = await generateSeoFields({\n subject: { kind: 'screen', name, path },\n brand: brandOf(host),\n text: facts.text || str(screen.description),\n fields,\n current: {\n title: str(seo.title),\n description: str(seo.description),\n breadcrumb: str(seo.breadcrumb),\n imageAlt: str(seo.imageAlt),\n },\n image: hasImage ? { alt: str(seo.imageAlt) || onPage?.alt || null, context: onPage?.context || null } : null,\n keywords,\n otherTitles: await siteTitles(hostRef, new Set([screenId])),\n model,\n maxTokens: aiSeoGenerationMaxTokens(model, AI_SEO_FIELDS_MAX_TOKENS),\n ...(signal ? { signal } : {}),\n })\n return listingOutcome(generation, job, { kind: 'screen', id: screenId, name, path }, {\n keywords,\n unchecked,\n facts,\n fields,\n hasImage,\n })\n}\n\nasync function runProductListing(\n context: AiJobStepContext & { firestore: Firestore },\n host: SeoHostDocument,\n): Promise<AiJobStepOutcome> {\n const { job, signal } = context\n const model = modelOf(context)\n const name = str(job.inputs?.['name']).slice(0, 200)\n if (!name) return { outputs: [], ...spentNothing(model), failure: AI_SEO_NO_PRODUCT_COPY }\n const fields = aiSeoRequestedFields(job.inputs?.['fields'], PRODUCT_LISTING_FIELDS)\n const { keywords, unchecked } = seoKeywordSplit(job.inputs?.['keywords'])\n const productId = str(job.inputs?.['productId']) || null\n const generation = await generateSeoFields({\n subject: { kind: 'product', name },\n brand: brandOf(host),\n // The product's own words, as its editor handed them over: the product\n // document is the commerce plugin's, and this step never reads it.\n text: str(job.inputs?.['text']).slice(0, SEO_PAGE_TEXT_MAX_CHARS),\n fields,\n current: {\n title: str(job.inputs?.['currentTitle']),\n description: str(job.inputs?.['currentDescription']),\n },\n image: null,\n keywords,\n model,\n maxTokens: aiSeoGenerationMaxTokens(model, AI_SEO_FIELDS_MAX_TOKENS),\n ...(signal ? { signal } : {}),\n })\n return listingOutcome(generation, job, { kind: 'product', id: productId, name }, {\n keywords,\n unchecked,\n facts: null,\n fields,\n hasImage: false,\n })\n}\n\n/* ------------------------------------------------------------------------ *\n * The site audit\n * ------------------------------------------------------------------------ */\n\n/**\n * Read the site for an audit: the platform SEO check's scan\n * (`@aglyn/aglyn/app-utils/seo-site-scan`), which the site's SEO section\n * reads through too, so a job audits exactly the pages the check lists.\n */\nasync function scanSite(\n firestore: Firestore,\n hostId: string,\n host: SeoHostDocument,\n keywords: unknown,\n): Promise<SeoSiteScan> {\n return scanSeoSite(firestore, hostId, host, {\n keywords,\n templateScreenIds: await getTemplateScreenIds({ hostId }),\n readRepeatRows: siteRepeatRows(hostId),\n })\n}\n\n/** One unit of generated work an audit still owes. */\nexport type AiSeoAuditUnit =\n | { kind: 'site' }\n | { kind: 'batch'; batch: number; of: number; screenIds: string[] }\n\n/** The units an audit owes after the outputs recorded so far, in the order they run. */\nexport function aiSeoPendingUnits(\n report: AiSeoAuditReport,\n outputs: readonly Pick<AiJobOutput, 'id'>[],\n): AiSeoAuditUnit[] {\n const done = new Set(outputs.map((output) => output.id))\n const units: AiSeoAuditUnit[] = []\n if (report.siteProposal && !done.has(AI_SEO_OUTPUT_IDS.site)) units.push({ kind: 'site' })\n const batches = aiSeoAuditBatches(report)\n batches.forEach((screenIds, index) => {\n if (!done.has(AI_SEO_OUTPUT_IDS.fixes(index + 1))) {\n units.push({ kind: 'batch', batch: index + 1, of: batches.length, screenIds })\n }\n })\n return units\n}\n\ninterface UnitResult {\n output: AiJobOutput\n spent: Spent\n}\n\n/* ---- The site-wide proposal ---------------------------------------------- */\n\nexport const AI_SEO_SITE_INSTRUCTIONS: AiSystemBlock[] = [\n {\n text:\n 'You propose two things for a website: the structured data that says who publishes it, and ' +\n 'the guidance an AI agent reads first in its /llms.txt.\\n\\n' +\n `Answer by calling ${AI_SEO_SITE_TOOL_NAME} exactly once. A reply in prose cannot be used.\\n\\n` +\n 'Rules:\\n' +\n '- Fill only the fields you are told are empty; answer null for every other one.\\n' +\n '- Use only what the pages you are given state. A contact email or telephone number goes in ' +\n 'only when a page shows it exactly; never compose one.\\n' +\n '- The entity description says what the publisher is, in one plain sentence, not what one page ' +\n 'offers.\\n' +\n '- The agent guidance is checkable and specific: the questions this site answers and which ' +\n 'pages answer them. No marketing language: an agent discounts a claim it cannot check.\\n' +\n '- Keep every length the tool states, and write in the language of the pages.',\n cacheBreakpoint: true,\n },\n]\n\n/** The site SEO form fields blank on the site now. */\nexport function aiSeoBlankSiteFields(host: SeoHostDocument): Set<AiSeoSiteFormField> {\n const entity = host.seo?.entity ?? {}\n const agent = host.seo?.agent ?? {}\n const blank = new Set<AiSeoSiteFormField>()\n if (entity.type === undefined || entity.type === null || entity.type === '') blank.add('seo.entity.type')\n if (!str(entity.name)) blank.add('seo.entity.name')\n if (!str(entity.description)) blank.add('seo.entity.description')\n if (!str(entity.email)) blank.add('seo.entity.email')\n if (!str(entity.telephone)) blank.add('seo.entity.telephone')\n if (!str(entity.contactType)) blank.add('seo.entity.contactType')\n if (!str(agent.whenToUse)) blank.add('seo.agent.whenToUse')\n if (!str(agent.howToUse)) blank.add('seo.agent.howToUse')\n return blank\n}\n\n/** The site's content collections, for the llms.txt preview. */\nasync function readContentCollections(hostRef: DocumentReference): Promise<LlmsTxtCollection[]> {\n try {\n const snapshot = await hostRef.collection('collections').select('slug', 'displayName', 'kind').limit(50).get()\n return snapshot.docs\n .filter((doc) => hostCollectionKind(doc.data()) === 'content' && str(doc.get('slug')))\n .map((doc) => ({ slug: str(doc.get('slug')), name: str(doc.get('displayName')) || undefined }))\n } catch {\n // A preview without the feeds is still a preview of the guidance.\n return []\n }\n}\n\n/** `/llms.txt` as it reads with `values` saved over what the site stores now. */\nexport function aiSeoLlmsPreview(\n host: SeoHostDocument,\n values: Partial<Record<AiSeoSiteFormField, string>>,\n pages: readonly LlmsTxtPage[],\n collections: readonly LlmsTxtCollection[],\n): string {\n return buildLlmsTxt({\n siteName: str(host.seo?.title) || str(host.displayName) || 'Site',\n origin: hostPublicOrigin(host) ?? '',\n description: str(host.seo?.description) || undefined,\n agent: {\n whenToUse: values['seo.agent.whenToUse'] ?? host.seo?.agent?.whenToUse,\n howToUse: values['seo.agent.howToUse'] ?? host.seo?.agent?.howToUse,\n },\n pages,\n collections,\n hasSearch: true,\n contactEmail: values['seo.entity.email'] ?? (str(host.seo?.entity?.email) || undefined),\n })\n}\n\nasync function proposeSite(\n context: AiJobStepContext & { firestore: Firestore },\n host: SeoHostDocument,\n scan: SeoSiteScan,\n): Promise<UnitResult> {\n const { job, firestore, signal } = context\n const model = modelOf(context)\n const hostRef = firestore.collection('hosts').doc(job.hostId as string)\n const blank = aiSeoBlankSiteFields(host)\n const collections = await readContentCollections(hostRef)\n const topLevel: LlmsTxtPage[] = scan.pages\n .filter((page) => !page.path.replace(/^\\//, '').includes('/'))\n .slice(0, SITE_PAGES_LISTED)\n .map((page) => ({ path: page.path, ...(page.name ? { title: page.name } : {}) }))\n // The pages that say who publishes a site: the home page, and whatever is\n // about the company or how to reach it.\n const telling = scan.pages\n .filter((page) => page.path === '/' || /about|contact|company|team|who/i.test(page.path))\n .slice(0, 4)\n const prompt = [\n `Site: ${brandOf(host) || 'untitled site'}`,\n host.seo?.description ? `Site description: ${str(host.seo.description)}` : '',\n `Empty fields you may fill: ${[...blank].join(', ') || 'none'}`,\n 'Pages:',\n ...scan.pages\n .slice(0, SITE_PAGES_LISTED)\n .map((page) => `- ${page.path} — ${page.name}${str(page.seo.title) ? ` — ${str(page.seo.title)}` : ''}`),\n collections.length\n ? `Content collections: ${collections.map((entry) => entry.name || entry.slug).join(', ')}`\n : '',\n ...telling.flatMap((page) => ['', `Text of ${page.path}:`, page.facts.text.slice(0, 1_200) || '(no text)']),\n ]\n .filter((line) => line !== '')\n .join('\\n')\n const siteText = scan.pages.map((page) => page.facts.text).join('\\n')\n const generation = await runValidatedGeneration('seo-site', {\n step: 'job.seo',\n ...aiRouteSettings('job.seo'),\n model,\n instructions: AI_SEO_SITE_INSTRUCTIONS,\n messages: [{ role: 'user', content: prompt }],\n tool: aiSeoSiteTool(),\n maxTokens: aiSeoGenerationMaxTokens(model, AI_SEO_SITE_MAX_TOKENS),\n ...(signal ? { signal } : {}),\n check: (answer) => checkAiSeoSite(answer, { blank, siteText }),\n })\n const proposed = generation.status === 'ok' ? generation.value : { values: {}, notes: [] }\n const notes =\n generation.status === 'ok'\n ? proposed.notes\n : [\n generation.status === 'refused'\n ? 'The AI declined to propose structured data for this site.'\n : generation.message,\n ]\n const site: AiSeoSiteProposal = {\n values: proposed.values,\n llmsPreview: aiSeoLlmsPreview(host, proposed.values, topLevel, collections),\n notes,\n }\n return {\n output: {\n resource: 'seo',\n id: AI_SEO_OUTPUT_IDS.site,\n hostId: job.hostId ?? null,\n label: 'SEO audit · structured data and agent guidance',\n proposal: plain({ kind: 'site', site }),\n },\n spent: spentBy(generation),\n }\n}\n\n/* ---- A batch of page fixes ------------------------------------------------ */\n\nexport const AI_SEO_FIXES_INSTRUCTIONS: AiSystemBlock[] = [\n {\n text:\n 'You fix the search listings and headings of a website’s pages. For each page you are given ' +\n 'what an audit found, what the page shows now, and its text.\\n\\n' +\n `Answer by calling ${AI_SEO_FIXES_TOOL_NAME} exactly once, with one entry per page. A reply in ` +\n 'prose cannot be used.\\n\\n' +\n 'Rules:\\n' +\n '- Fix only what the audit found. Answer null for a value it did not find a problem with.\\n' +\n '- Write only from the page’s own text. Never invent a fact, a price, a name, a place or a claim.\\n' +\n '- A title says what the page is, specifically. No two pages share a title, and none repeats a ' +\n 'title you are told another page uses.\\n' +\n '- A description is one or two sentences telling a searcher what they will find.\\n' +\n '- The main heading names what the page is about in a few plain words.\\n' +\n '- An image description says what the picture most likely shows, from the text beside it. ' +\n 'Leave an image out when the text gives no idea.\\n' +\n '- A target keyword goes in only where the page is about it and it reads naturally: at most ' +\n 'once in a title and once in a description. Never list keywords or repeat a word to rank.\\n' +\n '- Keep every length the tool states, and write in the language of each page.',\n cacheBreakpoint: true,\n },\n]\n\n/** The fixes one page gets: the model's values, its content fixes, and the guidance no fix can give. */\nexport function aiSeoPageFixFor(page: AiSeoBatchPage, answer: AiSeoBatchAnswer | null): AiSeoPageFix {\n const values: AiSeoFieldValues = {}\n if (answer?.title) values.title = answer.title\n if (answer?.description) values.description = answer.description\n const content: AiSeoContentFix[] = []\n for (const alt of answer?.imageAlts ?? []) content.push({ kind: 'image-alt', nodeId: alt.nodeId, alt: alt.alt })\n if (answer?.h1) {\n const first = page.facts.h1s[0]\n const promotable = page.facts.headings.find((heading) => heading.editable && heading.nodeId)\n if (page.codes.has('h1-thin') && first?.editable && first.nodeId) {\n content.push({ kind: 'h1-set', nodeId: first.nodeId, text: answer.h1 })\n } else if (page.codes.has('h1-missing') && promotable?.nodeId) {\n content.push({ kind: 'h1-set', nodeId: promotable.nodeId, text: answer.h1 })\n } else if (page.codes.has('h1-missing')) {\n content.push({ kind: 'h1-insert', text: answer.h1 })\n }\n }\n if (page.codes.has('h1-multiple')) content.push(...aiSeoHeadingDemotions(page.facts))\n const guidance: string[] = []\n if (page.codes.has('orphan')) {\n guidance.push('Link to this page from your navigation or a related page. Where it belongs is yours to choose.')\n }\n return { screenId: page.screenId, values, content, guidance }\n}\n\n/** The user turn for one batch: each page's findings, what it shows now, and its text. */\nexport function aiSeoFixesPrompt(pages: readonly AiSeoBatchPage[], titlesElsewhere: readonly string[]): string {\n const titles = titlesElsewhere.length\n ? `Titles other pages use:\\n${titlesElsewhere.slice(0, 40).map((title) => `- ${title}`).join('\\n')}\\n\\n`\n : ''\n const blocks = pages.map((page) => {\n const lines = [\n `### Page ${page.screenId}: ${page.name} (${page.path})`,\n `Found: ${[...page.codes].map((code) => SEO_FINDING_LABELS[code]).join('; ')}`,\n `Title now: ${str(page.seo?.title) || '(none)'}`,\n `Description now: ${str(page.seo?.description) || '(none)'}`,\n `Main heading now: ${page.facts.h1s[0]?.text || '(none)'}`,\n ]\n if (page.keywords.length) lines.push(`Target keywords: ${page.keywords.join(', ')}`)\n const images = aiSeoFixImages(page)\n if (images.length) {\n lines.push(\n 'Images without a description:',\n ...images.map((image) => `- ${image.nodeId}: beside \"${image.context || 'no text'}\"`),\n )\n }\n lines.push('Text:', page.facts.text.slice(0, AI_SEO_BATCH_PAGE_TEXT_CHARS) || '(no text)')\n return lines.join('\\n')\n })\n return `${titles}${blocks.join('\\n\\n')}`\n}\n\nasync function proposeFixes(\n context: AiJobStepContext & { firestore: Firestore },\n report: AiSeoAuditReport,\n unit: Extract<AiSeoAuditUnit, { kind: 'batch' }>,\n): Promise<UnitResult> {\n const { job, firestore, signal } = context\n const model = modelOf(context)\n const hostRef = firestore.collection('hosts').doc(job.hostId as string)\n const reports = new Map(report.pages.map((page) => [page.screenId, page]))\n // Read now, not as audited: a page edited since keeps its edits, and one\n // deleted since is skipped.\n const read = await Promise.all(\n unit.screenIds.map(async (screenId) => {\n const pageReport = reports.get(screenId)\n const snapshot = await hostRef.collection('screens').doc(screenId).get()\n const screen = snapshot.exists ? (snapshot.data() as SeoScreenDocument) : null\n if (!pageReport || !screen || screen.deletedAt) return null\n const version = await readVersionNodes(hostRef, screenId, str(screen.versionId))\n return { screenId, pageReport, screen, version }\n }),\n )\n const live = read.filter((entry): entry is NonNullable<typeof entry> => entry !== null)\n // Read as the audit read them, composed: a heading a component renders is\n // the page's, and is never offered to a fix that would rewrite the page.\n const facts = await composedPageFacts(\n firestore,\n job.hostId as string,\n live.map((entry) => entry.version),\n )\n const pages: AiSeoBatchPage[] = live.map(({ screenId, pageReport, screen }, index) => ({\n screenId,\n path: pageReport.path,\n name: pageReport.name,\n codes: new Set(pageReport.findings.map((entry) => entry.code)),\n keywords: pageReport.keywords.map((entry) => entry.keyword),\n seo: screen.seo ?? {},\n facts: facts[index],\n }))\n const titlesElsewhere = await siteTitles(hostRef, new Set(pages.map((page) => page.screenId)))\n\n let spent = spentNothing(model)\n let answers: Record<string, AiSeoBatchAnswer> = {}\n const notes: string[] = []\n if (pages.length) {\n const generation = await runValidatedGeneration('seo-fixes', {\n step: 'job.seo',\n ...aiRouteSettings('job.seo'),\n model,\n instructions: AI_SEO_FIXES_INSTRUCTIONS,\n messages: [{ role: 'user', content: aiSeoFixesPrompt(pages, titlesElsewhere) }],\n tool: aiSeoFixesTool(pages.map((page) => page.screenId)),\n // Lowered on a tier too slow to answer a whole batch and ask again\n // inside the least time the step registers.\n maxTokens: aiSeoGenerationMaxTokens(model, AI_SEO_FIXES_MAX_TOKENS),\n ...(signal ? { signal } : {}),\n check: (answer) => checkAiSeoFixes(answer, pages, titlesElsewhere),\n })\n spent = spentBy(generation)\n if (generation.status === 'ok') {\n answers = generation.value\n } else {\n notes.push(\n generation.status === 'refused'\n ? 'The AI declined to propose fixes for these pages.'\n : generation.message,\n )\n }\n }\n const fixes = pages.map((page) => aiSeoPageFixFor(page, answers[page.screenId] ?? null))\n const skipped = unit.screenIds.length - pages.length\n if (skipped > 0) {\n notes.push(`${skipped} ${skipped === 1 ? 'page was' : 'pages were'} deleted since the audit and left out.`)\n }\n return {\n output: {\n resource: 'seo',\n id: AI_SEO_OUTPUT_IDS.fixes(unit.batch),\n hostId: job.hostId ?? null,\n label: `SEO audit · fixes ${unit.batch} of ${unit.of}`,\n proposal: plain({ kind: 'fixes', batch: unit.batch, fixes, notes }),\n },\n spent,\n }\n}\n\nasync function runSiteAuditPass(\n context: AiJobStepContext & { firestore: Firestore },\n host: SeoHostDocument,\n): Promise<AiJobStepOutcome> {\n const { job, firestore } = context\n const model = modelOf(context)\n const recorded = aiSeoAuditView(job.outputs ?? [])\n const outputs: AiJobOutput[] = []\n let spent = spentNothing(model)\n let report: AiSeoAuditReport\n let scan: SeoSiteScan | null = null\n if (recorded) {\n report = recorded.report\n } else {\n scan = await scanSite(firestore, job.hostId as string, host, job.inputs?.['keywords'])\n report = aiSeoAudit(scan.pages, seoAuditSiteOf(host), { skipped: scan.skipped, notes: scan.notes })\n const findings = report.pages.reduce((sum, page) => sum + page.findings.length, report.site.length)\n outputs.push({\n resource: 'seo',\n id: AI_SEO_OUTPUT_IDS.report,\n hostId: job.hostId ?? null,\n label: `SEO audit · ${report.pages.length} ${report.pages.length === 1 ? 'page' : 'pages'} · ${findings} ${findings === 1 ? 'finding' : 'findings'}`,\n proposal: plain(report),\n })\n }\n const units = aiSeoPendingUnits(report, [...(job.outputs ?? []), ...outputs])\n if (!units.length) return { outputs, ...spent }\n const [unit] = units\n const result =\n unit.kind === 'site'\n ? await proposeSite(\n context,\n host,\n scan ?? (await scanSite(firestore, job.hostId as string, host, job.inputs?.['keywords'])),\n )\n : await proposeFixes(context, report, unit)\n outputs.push(result.output)\n spent = addSpent(spent, result.spent)\n return { outputs, ...spent, ...(units.length > 1 ? { continue: true } : {}) }\n}\n\nexport const runAiJobSeoStep: AiJobStepRunner = async (context) => {\n const { job, firestore } = context\n if (!firestore) {\n // The machine always hands one in; a runner called without it is a\n // wiring fault, not a customer path.\n throw new Error('the SEO step reads the site it writes about and was given no Firestore')\n }\n const target = aiSeoJobTarget(job.inputs)\n if (!target) return { outputs: [], ...spentNothing(modelOf(context)), failure: AI_SEO_NO_TARGET_COPY }\n const site = await loadSite(firestore, job)\n if ('failure' in site) return { outputs: [], ...spentNothing(modelOf(context)), failure: site.failure }\n const withFirestore = { ...context, firestore }\n switch (target) {\n case 'screen':\n return runScreenListing(withFirestore, site.host)\n case 'product':\n return runProductListing(withFirestore, site.host)\n case 'site':\n return runSiteAuditPass(withFirestore, site.host)\n }\n}\n"],"names":["hostCollectionKind","hostPublicOrigin","buildLlmsTxt","screenRoutePathToUrl","SEO_FINDING_LABELS","SEO_MAX_KEYWORDS","seoKeywordCoverage","seoKeywordSplit","seoQuotedKeywords","SCREEN_SEO_LISTING_FIELDS","isSeoListingFieldKey","SEO_PAGE_TEXT_MAX_CHARS","seoPageFacts","composeSeoPages","scanSeoSite","seoAuditSiteOf","readRepeatRows","decodeStoredNodes","getTemplateScreenIds","AI_SEO_OUTPUT_IDS","aiSeoAuditView","aiSeoJobTarget","aiModelForStep","aiRouteSettings","aiSeoAudit","aiSeoAuditBatches","aiSeoHeadingDemotions","runValidatedGeneration","AI_SEO_FIELDS_MAX_TOKENS","generateSeoFields","AI_SEO_FIXES_TOOL_NAME","AI_SEO_SITE_TOOL_NAME","aiSeoFixImages","aiSeoFixesTool","aiSeoSiteTool","checkAiSeoFixes","checkAiSeoSite","AI_SEO_FIXES_MAX_TOKENS","AI_SEO_SITE_MAX_TOKENS","aiSeoGenerationMaxTokens","AI_SEO_NO_SITE_COPY","AI_SEO_NO_PAGE_COPY","AI_SEO_NO_TARGET_COPY","AI_SEO_NO_PRODUCT_COPY","AI_SEO_BATCH_PAGE_TEXT_CHARS","SITE_PAGES_LISTED","PRODUCT_LISTING_FIELDS","ZERO_USAGE","inputTokens","outputTokens","cacheReadTokens","cacheWriteTokens","str","value","trim","round6","Math","round","modelOf","context","modelFor","spentNothing","model","usage","estCostUsd","stopReason","spentBy","generation","addSpent","a","b","plain","JSON","parse","stringify","aiSeoRequestedFields","raw","allowed","named","String","split","map","part","filter","wanted","Set","length","key","has","brandOf","host","seo","title","displayName","subdomain","loadSite","firestore","job","hostId","failure","snapshot","collection","doc","get","exists","data","orgId","siteRepeatRows","keys","composedPageFacts","versions","composed","version","nodes","index","rootId","pageNodes","readVersionNodes","hostRef","screenId","versionId","siteTitles","except","select","limit","docs","id","Boolean","listingOutcome","subject","spent","status","outputs","refused","message","values","notes","fields","includes","hasImage","imageAlt","push","unchecked","proposal","kind","keywords","description","h1","facts","h1s","text","body","output","resource","label","name","runScreenListing","screen","signal","inputs","deletedAt","image","onPage","images","find","src","undefined","route","screens","path","brand","current","breadcrumb","alt","otherTitles","maxTokens","runProductListing","slice","productId","scanSite","templateScreenIds","aiSeoPendingUnits","report","done","units","siteProposal","site","batches","forEach","screenIds","fixes","batch","of","AI_SEO_SITE_INSTRUCTIONS","cacheBreakpoint","aiSeoBlankSiteFields","entity","agent","blank","type","add","email","telephone","contactType","whenToUse","howToUse","readContentCollections","slug","aiSeoLlmsPreview","pages","collections","siteName","origin","hasSearch","contactEmail","proposeSite","scan","topLevel","page","replace","telling","test","prompt","join","entry","flatMap","line","siteText","step","instructions","messages","role","content","tool","check","answer","proposed","llmsPreview","AI_SEO_FIXES_INSTRUCTIONS","aiSeoPageFixFor","imageAlts","nodeId","first","promotable","headings","heading","editable","codes","guidance","aiSeoFixesPrompt","titlesElsewhere","titles","blocks","lines","code","proposeFixes","unit","reports","Map","read","Promise","all","pageReport","live","findings","keyword","answers","skipped","runSiteAuditPass","recorded","reduce","sum","result","continue","runAiJobSeoStep","Error","target","withFirestore"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-seo-step.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { hostCollectionKind } from '@aglyn/aglyn/app-utils/collection-kind'\nimport { hostPublicOrigin } from '@aglyn/aglyn/app-utils/host-naming'\nimport {\n buildLlmsTxt,\n type LlmsTxtCollection,\n type LlmsTxtPage,\n} from '@aglyn/aglyn/app-utils/llms-txt'\nimport { screenRoutePathToUrl } from '@aglyn/aglyn/app-utils/screen-route'\nimport { SEO_FINDING_LABELS } from '@aglyn/aglyn/app-utils/seo-audit'\nimport {\n SEO_MAX_KEYWORDS,\n seoKeywordCoverage,\n seoKeywordSplit,\n seoQuotedKeywords,\n} from '@aglyn/aglyn/app-utils/seo-keywords'\nimport {\n SCREEN_SEO_LISTING_FIELDS,\n isSeoListingFieldKey,\n type SeoListingFieldKey,\n} from '@aglyn/aglyn/app-utils/seo-listing-fields'\nimport {\n SEO_PAGE_TEXT_MAX_CHARS,\n seoPageFacts,\n type SeoPageFacts,\n} from '@aglyn/aglyn/app-utils/seo-page-facts'\nimport {\n composeSeoPages,\n scanSeoSite,\n seoAuditSiteOf,\n type SeoRepeatRowsReader,\n type SeoSiteScan,\n} from '@aglyn/aglyn/app-utils/seo-site-scan'\n// By path, never through a barrel: only a server composition asks it.\nimport { readRepeatRows } from '@aglyn/aglyn/plugin-manager/repeat-rows'\nimport { decodeStoredNodes } from '@aglyn/aglyn/app-utils/stored-nodes'\nimport { getTemplateScreenIds } from '@aglyn/tenant-runtime/template-screens'\nimport type { AiJob, AiJobOutput } from '../model/ai-jobs.types'\nimport {\n AI_SEO_OUTPUT_IDS,\n aiSeoAuditView,\n aiSeoJobTarget,\n type AiSeoAuditReport,\n type AiSeoContentFix,\n type AiSeoFieldsProposal,\n type AiSeoFieldValues,\n type AiSeoPageFix,\n type AiSeoSiteFormField,\n type AiSeoSiteProposal,\n} from '../model/ai-seo'\nimport { aiModelForStep, aiRouteSettings } from '../providers/routing'\nimport type { AiSystemBlock, AiUsage } from '../runtime/ai-runtime'\nimport { aiSeoAudit, aiSeoAuditBatches, aiSeoHeadingDemotions } from '../runtime/seo-audit'\nimport {\n runValidatedGeneration,\n type AiGenerationSpend,\n type AiValidatedGeneration,\n} from '../runtime/ai-doctrine'\nimport { AI_SEO_FIELDS_MAX_TOKENS, generateSeoFields } from '../runtime/seo-fields'\nimport {\n AI_SEO_FIXES_TOOL_NAME,\n AI_SEO_SITE_TOOL_NAME,\n aiSeoFixImages,\n aiSeoFixesTool,\n aiSeoSiteTool,\n checkAiSeoFixes,\n checkAiSeoSite,\n type AiSeoBatchAnswer,\n type AiSeoBatchPage,\n} from '../tools/ai-seo-tool'\nimport {\n AI_SEO_FIXES_MAX_TOKENS,\n AI_SEO_SITE_MAX_TOKENS,\n aiSeoGenerationMaxTokens,\n} from './ai-job-seo-budget'\nimport { aiCacheWriteHourSum } from '../providers/cache-write-hour'\nimport type { AiJobStepContext, AiJobStepOutcome, AiJobStepRunner } from './ai-job-text-step'\n\n/**\n * The `seo` step (AGL-2910): search listings and a site audit, as proposals.\n *\n * `inputs.target` says what the job is about:\n *\n * - `screen` — one page's listing, written from the version the person is\n * looking at (`screenId`, `versionId`), for the page's SEO card;\n * - `product` — one product's listing, from the name and description the\n * product editor handed over (`name`, `text`), for the product editor;\n * - `site` — the audit of every published page the sitemap lists, with a\n * proposed fix per finding, structured data for the site's entity, and the\n * agent guidance `/llms.txt` leads with.\n *\n * ## It writes nothing\n *\n * Like every runner, this reads, asks and returns. A listing is served\n * straight from the screen document, the structured data and `/llms.txt`\n * from the host document, and a page's content from its published version,\n * so the values ride on the job's outputs and nothing else: a person puts\n * them in the page's SEO card or the site SEO form and saves them there, and\n * content fixes become a new version only through the apply door.\n *\n * ## An audit, a unit at a time\n *\n * The first pass reads the site — every published page's version and the\n * shared layouts, for the links between them — and scores it without a\n * model, through the platform's SEO check (the same findings the site's SEO\n * section lists to every owner); it records the report, and runs the first unit of generated work. Each later\n * pass runs one more unit: the site-wide proposal, then the page fixes in\n * batches. Every pass is one reservation and one provider exchange, and it\n * asks the machine to continue until nothing is left, so a large site is a\n * few beats of work rather than one request that cannot finish.\n */\n\nexport const AI_SEO_NO_SITE_COPY = 'This SEO job names a site this workspace does not have.'\nexport const AI_SEO_NO_PAGE_COPY = 'This page is no longer on the site.'\nexport const AI_SEO_NO_TARGET_COPY = 'This SEO job does not say what to write about.'\nexport const AI_SEO_NO_PRODUCT_COPY = 'Give the product a name before writing its listing.'\n\n/** How much of each page a batch of fixes carries. */\nexport const AI_SEO_BATCH_PAGE_TEXT_CHARS = 700\n\n/**\n * Output budgets, declared with the step's time (`ai-job-seo-budget.ts`,\n * AGL-3035). A batch's largest answer — every page with a title, description,\n * heading and image descriptions at their limits — and the site proposal's are\n * measured in `ai-job-seo-step.spec.ts`.\n */\nexport { AI_SEO_FIXES_MAX_TOKENS, AI_SEO_SITE_MAX_TOKENS }\n\n/** Pages the site prompt and the llms.txt preview list. */\nconst SITE_PAGES_LISTED = 25\n\n/** The fields a product's listing holds. */\nconst PRODUCT_LISTING_FIELDS: readonly SeoListingFieldKey[] = ['title', 'description']\n\nconst ZERO_USAGE: AiUsage = {\n inputTokens: 0,\n outputTokens: 0,\n cacheReadTokens: 0,\n cacheWriteTokens: 0,\n}\n\ntype Firestore = FirebaseFirestore.Firestore\ntype DocumentReference = FirebaseFirestore.DocumentReference\ntype Spent = Pick<AiJobStepOutcome, 'usage' | 'estCostUsd' | 'model' | 'stopReason'>\n\n/** The host document fields this step reads. */\nexport interface SeoHostDocument {\n orgId?: string\n displayName?: string\n subdomain?: string | null\n cname?: string | null\n screens?: Record<string, string>\n errorScreens?: Record<string, string>\n notFoundScreenId?: string | null\n seo?: {\n title?: string\n description?: string\n discourageSearchEngines?: boolean\n entity?: {\n type?: unknown\n name?: string\n description?: string\n email?: string\n telephone?: string\n contactType?: string\n }\n agent?: { whenToUse?: string; howToUse?: string }\n }\n}\n\n/** The screen document fields this step reads. */\ninterface SeoScreenDocument {\n displayName?: string\n description?: string\n versionId?: string\n visibility?: unknown\n deletedAt?: unknown\n seo?: {\n title?: string\n description?: string\n breadcrumb?: string\n image?: string\n imageAlt?: string\n }\n}\n\nconst str = (value: unknown): string => (typeof value === 'string' ? value.trim() : '')\n\nconst round6 = (value: number): number => Math.round(value * 1_000_000) / 1_000_000\n\nfunction modelOf(context: AiJobStepContext): string {\n return context.modelFor?.('job.seo') ?? aiModelForStep('job.seo')\n}\n\nfunction spentNothing(model: string): Spent {\n return { usage: ZERO_USAGE, estCostUsd: 0, model, stopReason: null }\n}\n\nfunction spentBy(generation: AiGenerationSpend): Spent {\n return {\n usage: generation.usage,\n estCostUsd: generation.estCostUsd,\n model: generation.model,\n stopReason: generation.stopReason,\n }\n}\n\nfunction addSpent(a: Spent, b: Spent): Spent {\n return {\n usage: {\n inputTokens: a.usage.inputTokens + b.usage.inputTokens,\n outputTokens: a.usage.outputTokens + b.usage.outputTokens,\n cacheReadTokens: a.usage.cacheReadTokens + b.usage.cacheReadTokens,\n cacheWriteTokens: a.usage.cacheWriteTokens + b.usage.cacheWriteTokens,\n ...aiCacheWriteHourSum(a.usage, b.usage),\n },\n estCostUsd: round6(a.estCostUsd + b.estCostUsd),\n model: b.model || a.model,\n stopReason: b.stopReason ?? a.stopReason,\n }\n}\n\n/** Through JSON, so no `undefined` reaches the job document's write. */\nconst plain = <T>(value: T): Record<string, unknown> =>\n JSON.parse(JSON.stringify(value)) as Record<string, unknown>\n\n/**\n * The fields a listing job writes: the ones the editor named (`inputs.fields`,\n * comma-separated) that the target's editor holds, or all of those.\n */\nexport function aiSeoRequestedFields(\n raw: unknown,\n allowed: readonly SeoListingFieldKey[],\n): SeoListingFieldKey[] {\n const named = String(raw ?? '')\n .split(',')\n .map((part) => part.trim())\n .filter(isSeoListingFieldKey)\n const wanted = new Set<SeoListingFieldKey>(named.length ? named : allowed)\n return allowed.filter((key) => wanted.has(key))\n}\n\n/** The site's name, as its visitors read it. */\nfunction brandOf(host: SeoHostDocument): string {\n return str(host.seo?.title) || str(host.displayName) || str(host.subdomain)\n}\n\n/** The site a job names, read and checked against the job's org. */\nasync function loadSite(\n firestore: Firestore,\n job: AiJob,\n): Promise<{ host: SeoHostDocument } | { failure: string }> {\n if (!job.hostId) return { failure: AI_SEO_NO_SITE_COPY }\n const snapshot = await firestore.collection('hosts').doc(job.hostId).get()\n const host = snapshot.exists ? (snapshot.data() as SeoHostDocument) : null\n // The route checks the caller's membership of the org the job is metered\n // against, not that the site belongs to it. A site of another org is\n // refused here, before anything about it reaches a prompt.\n if (!host || host.orgId !== job.orgId) return { failure: AI_SEO_NO_SITE_COPY }\n return { host }\n}\n\n/** The rows a site's repeats render, through the platform's repeat-rows contract. */\nconst siteRepeatRows =\n (hostId: string): SeoRepeatRowsReader =>\n (keys) =>\n readRepeatRows({ hostId, keys })\n\n/**\n * Pages' facts as the SEO check reads them (AGL-3501): each version composed\n * as it publishes — components grafted, repeats expanded — and read against\n * the page's own map, so a fix is only ever offered for what the page holds.\n */\nasync function composedPageFacts(\n firestore: Firestore,\n hostId: string,\n versions: readonly ({ nodes: Record<string, unknown>; rootId?: string } | null)[],\n): Promise<SeoPageFacts[]> {\n const { composed } = await composeSeoPages(\n firestore,\n hostId,\n versions.map((version) => version?.nodes ?? null),\n { readRepeatRows: siteRepeatRows(hostId) },\n )\n return versions.map((version, index) =>\n seoPageFacts(composed[index] as never, { rootId: version?.rootId, pageNodes: version?.nodes as never }),\n )\n}\n\n/** A version's node map and root, or `null` when it cannot be read. */\nasync function readVersionNodes(\n hostRef: DocumentReference,\n screenId: string,\n versionId: string,\n): Promise<{ nodes: Record<string, unknown>; rootId?: string } | null> {\n if (!versionId) return null\n const snapshot = await hostRef\n .collection('screens')\n .doc(screenId)\n .collection('versions')\n .doc(versionId)\n .get()\n if (!snapshot.exists) return null\n const nodes = decodeStoredNodes<Record<string, unknown>>(snapshot.get('nodes'))\n if (!nodes) return null\n const rootId = snapshot.get('rootId')\n return { nodes, ...(typeof rootId === 'string' ? { rootId } : {}) }\n}\n\n/** Every other page's search title, for a proposal not to repeat. */\nasync function siteTitles(hostRef: DocumentReference, except: ReadonlySet<string>): Promise<string[]> {\n const snapshot = await hostRef.collection('screens').select('seo', 'deletedAt').limit(1000).get()\n return snapshot.docs\n .filter((doc) => !except.has(doc.id) && !doc.get('deletedAt'))\n .map((doc) => str((doc.get('seo') as SeoScreenDocument['seo'])?.title))\n .filter(Boolean)\n}\n\n/* ------------------------------------------------------------------------ *\n * One listing: a page or a product\n * ------------------------------------------------------------------------ */\n\nfunction listingOutcome(\n generation: AiValidatedGeneration<AiSeoFieldValues>,\n job: AiJob,\n subject: AiSeoFieldsProposal['subject'],\n context: {\n keywords: readonly string[]\n /** Keywords past the limit, which the proposal did not aim for. */\n unchecked: readonly string[]\n facts: SeoPageFacts | null\n fields: readonly SeoListingFieldKey[]\n hasImage: boolean\n },\n): AiJobStepOutcome {\n const spent = spentBy(generation)\n if (generation.status === 'refused') return { outputs: [], ...spent, refused: true }\n if (generation.status === 'needs_input') return { outputs: [], ...spent, failure: generation.message }\n const values = generation.value\n const notes: string[] = []\n if (context.fields.includes('imageAlt') && context.hasImage && !values.imageAlt) {\n notes.push('No image description was proposed: nothing on the page says what the picture shows.')\n }\n if (context.unchecked.length) {\n notes.push(\n `Only the first ${SEO_MAX_KEYWORDS} keywords were used, so ${seoQuotedKeywords(context.unchecked)} ${context.unchecked.length === 1 ? 'was' : 'were'} not.`,\n )\n }\n const proposal: AiSeoFieldsProposal = {\n kind: 'fields',\n subject,\n values,\n keywords: seoKeywordCoverage(context.keywords, {\n title: values.title,\n description: values.description,\n h1: context.facts?.h1s[0]?.text,\n body: context.facts?.text,\n }),\n notes,\n }\n const output: AiJobOutput = {\n resource: 'seo',\n id: AI_SEO_OUTPUT_IDS.fields(subject.kind, subject.id),\n hostId: job.hostId ?? null,\n label: `SEO proposal · ${subject.name}`,\n proposal: plain(proposal),\n }\n return { outputs: [output], ...spent }\n}\n\nasync function runScreenListing(\n context: AiJobStepContext & { firestore: Firestore },\n host: SeoHostDocument,\n): Promise<AiJobStepOutcome> {\n const { job, firestore, signal } = context\n const model = modelOf(context)\n const hostRef = firestore.collection('hosts').doc(job.hostId as string)\n const screenId = str(job.inputs?.['screenId'])\n const snapshot = screenId ? await hostRef.collection('screens').doc(screenId).get() : null\n const screen = snapshot?.exists ? (snapshot.data() as SeoScreenDocument) : null\n if (!screen || screen.deletedAt) {\n return { outputs: [], ...spentNothing(model), failure: AI_SEO_NO_PAGE_COPY }\n }\n\n const versionId = str(job.inputs?.['versionId']) || str(screen.versionId)\n const version = await readVersionNodes(hostRef, screenId, versionId)\n const [facts] = await composedPageFacts(firestore, job.hostId as string, [version])\n const fields = aiSeoRequestedFields(job.inputs?.['fields'], SCREEN_SEO_LISTING_FIELDS)\n const seo = screen.seo ?? {}\n const hasImage = Boolean(str(seo.image))\n // The share image is often a picture the page shows too: its description\n // and the text beside it there are what is known about it.\n const onPage = hasImage ? facts.images.find((image) => image.src === str(seo.image)) : undefined\n const route = host.screens?.[screenId]\n const path = typeof route === 'string' ? screenRoutePathToUrl(route) : null\n const name = str(screen.displayName) || path || 'Untitled page'\n const { keywords, unchecked } = seoKeywordSplit(job.inputs?.['keywords'])\n const generation = await generateSeoFields({\n subject: { kind: 'screen', name, path },\n brand: brandOf(host),\n text: facts.text || str(screen.description),\n fields,\n current: {\n title: str(seo.title),\n description: str(seo.description),\n breadcrumb: str(seo.breadcrumb),\n imageAlt: str(seo.imageAlt),\n },\n image: hasImage ? { alt: str(seo.imageAlt) || onPage?.alt || null, context: onPage?.context || null } : null,\n keywords,\n otherTitles: await siteTitles(hostRef, new Set([screenId])),\n model,\n maxTokens: aiSeoGenerationMaxTokens(model, AI_SEO_FIELDS_MAX_TOKENS),\n ...(signal ? { signal } : {}),\n })\n return listingOutcome(generation, job, { kind: 'screen', id: screenId, name, path }, {\n keywords,\n unchecked,\n facts,\n fields,\n hasImage,\n })\n}\n\nasync function runProductListing(\n context: AiJobStepContext & { firestore: Firestore },\n host: SeoHostDocument,\n): Promise<AiJobStepOutcome> {\n const { job, signal } = context\n const model = modelOf(context)\n const name = str(job.inputs?.['name']).slice(0, 200)\n if (!name) return { outputs: [], ...spentNothing(model), failure: AI_SEO_NO_PRODUCT_COPY }\n const fields = aiSeoRequestedFields(job.inputs?.['fields'], PRODUCT_LISTING_FIELDS)\n const { keywords, unchecked } = seoKeywordSplit(job.inputs?.['keywords'])\n const productId = str(job.inputs?.['productId']) || null\n const generation = await generateSeoFields({\n subject: { kind: 'product', name },\n brand: brandOf(host),\n // The product's own words, as its editor handed them over: the product\n // document is the commerce plugin's, and this step never reads it.\n text: str(job.inputs?.['text']).slice(0, SEO_PAGE_TEXT_MAX_CHARS),\n fields,\n current: {\n title: str(job.inputs?.['currentTitle']),\n description: str(job.inputs?.['currentDescription']),\n },\n image: null,\n keywords,\n model,\n maxTokens: aiSeoGenerationMaxTokens(model, AI_SEO_FIELDS_MAX_TOKENS),\n ...(signal ? { signal } : {}),\n })\n return listingOutcome(generation, job, { kind: 'product', id: productId, name }, {\n keywords,\n unchecked,\n facts: null,\n fields,\n hasImage: false,\n })\n}\n\n/* ------------------------------------------------------------------------ *\n * The site audit\n * ------------------------------------------------------------------------ */\n\n/**\n * Read the site for an audit: the platform SEO check's scan\n * (`@aglyn/aglyn/app-utils/seo-site-scan`), which the site's SEO section\n * reads through too, so a job audits exactly the pages the check lists.\n */\nasync function scanSite(\n firestore: Firestore,\n hostId: string,\n host: SeoHostDocument,\n keywords: unknown,\n): Promise<SeoSiteScan> {\n return scanSeoSite(firestore, hostId, host, {\n keywords,\n templateScreenIds: await getTemplateScreenIds({ hostId }),\n readRepeatRows: siteRepeatRows(hostId),\n })\n}\n\n/** One unit of generated work an audit still owes. */\nexport type AiSeoAuditUnit =\n | { kind: 'site' }\n | { kind: 'batch'; batch: number; of: number; screenIds: string[] }\n\n/** The units an audit owes after the outputs recorded so far, in the order they run. */\nexport function aiSeoPendingUnits(\n report: AiSeoAuditReport,\n outputs: readonly Pick<AiJobOutput, 'id'>[],\n): AiSeoAuditUnit[] {\n const done = new Set(outputs.map((output) => output.id))\n const units: AiSeoAuditUnit[] = []\n if (report.siteProposal && !done.has(AI_SEO_OUTPUT_IDS.site)) units.push({ kind: 'site' })\n const batches = aiSeoAuditBatches(report)\n batches.forEach((screenIds, index) => {\n if (!done.has(AI_SEO_OUTPUT_IDS.fixes(index + 1))) {\n units.push({ kind: 'batch', batch: index + 1, of: batches.length, screenIds })\n }\n })\n return units\n}\n\ninterface UnitResult {\n output: AiJobOutput\n spent: Spent\n}\n\n/* ---- The site-wide proposal ---------------------------------------------- */\n\nexport const AI_SEO_SITE_INSTRUCTIONS: AiSystemBlock[] = [\n {\n text:\n 'You propose two things for a website: the structured data that says who publishes it, and ' +\n 'the guidance an AI agent reads first in its /llms.txt.\\n\\n' +\n `Answer by calling ${AI_SEO_SITE_TOOL_NAME} exactly once. A reply in prose cannot be used.\\n\\n` +\n 'Rules:\\n' +\n '- Fill only the fields you are told are empty; answer null for every other one.\\n' +\n '- Use only what the pages you are given state. A contact email or telephone number goes in ' +\n 'only when a page shows it exactly; never compose one.\\n' +\n '- The entity description says what the publisher is, in one plain sentence, not what one page ' +\n 'offers.\\n' +\n '- The agent guidance is checkable and specific: the questions this site answers and which ' +\n 'pages answer them. No marketing language: an agent discounts a claim it cannot check.\\n' +\n '- Keep every length the tool states, and write in the language of the pages.',\n cacheBreakpoint: true,\n },\n]\n\n/** The site SEO form fields blank on the site now. */\nexport function aiSeoBlankSiteFields(host: SeoHostDocument): Set<AiSeoSiteFormField> {\n const entity = host.seo?.entity ?? {}\n const agent = host.seo?.agent ?? {}\n const blank = new Set<AiSeoSiteFormField>()\n if (entity.type === undefined || entity.type === null || entity.type === '') blank.add('seo.entity.type')\n if (!str(entity.name)) blank.add('seo.entity.name')\n if (!str(entity.description)) blank.add('seo.entity.description')\n if (!str(entity.email)) blank.add('seo.entity.email')\n if (!str(entity.telephone)) blank.add('seo.entity.telephone')\n if (!str(entity.contactType)) blank.add('seo.entity.contactType')\n if (!str(agent.whenToUse)) blank.add('seo.agent.whenToUse')\n if (!str(agent.howToUse)) blank.add('seo.agent.howToUse')\n return blank\n}\n\n/** The site's content collections, for the llms.txt preview. */\nasync function readContentCollections(hostRef: DocumentReference): Promise<LlmsTxtCollection[]> {\n try {\n const snapshot = await hostRef.collection('collections').select('slug', 'displayName', 'kind').limit(50).get()\n return snapshot.docs\n .filter((doc) => hostCollectionKind(doc.data()) === 'content' && str(doc.get('slug')))\n .map((doc) => ({ slug: str(doc.get('slug')), name: str(doc.get('displayName')) || undefined }))\n } catch {\n // A preview without the feeds is still a preview of the guidance.\n return []\n }\n}\n\n/** `/llms.txt` as it reads with `values` saved over what the site stores now. */\nexport function aiSeoLlmsPreview(\n host: SeoHostDocument,\n values: Partial<Record<AiSeoSiteFormField, string>>,\n pages: readonly LlmsTxtPage[],\n collections: readonly LlmsTxtCollection[],\n): string {\n return buildLlmsTxt({\n siteName: str(host.seo?.title) || str(host.displayName) || 'Site',\n origin: hostPublicOrigin(host) ?? '',\n description: str(host.seo?.description) || undefined,\n agent: {\n whenToUse: values['seo.agent.whenToUse'] ?? host.seo?.agent?.whenToUse,\n howToUse: values['seo.agent.howToUse'] ?? host.seo?.agent?.howToUse,\n },\n pages,\n collections,\n hasSearch: true,\n contactEmail: values['seo.entity.email'] ?? (str(host.seo?.entity?.email) || undefined),\n })\n}\n\nasync function proposeSite(\n context: AiJobStepContext & { firestore: Firestore },\n host: SeoHostDocument,\n scan: SeoSiteScan,\n): Promise<UnitResult> {\n const { job, firestore, signal } = context\n const model = modelOf(context)\n const hostRef = firestore.collection('hosts').doc(job.hostId as string)\n const blank = aiSeoBlankSiteFields(host)\n const collections = await readContentCollections(hostRef)\n const topLevel: LlmsTxtPage[] = scan.pages\n .filter((page) => !page.path.replace(/^\\//, '').includes('/'))\n .slice(0, SITE_PAGES_LISTED)\n .map((page) => ({ path: page.path, ...(page.name ? { title: page.name } : {}) }))\n // The pages that say who publishes a site: the home page, and whatever is\n // about the company or how to reach it.\n const telling = scan.pages\n .filter((page) => page.path === '/' || /about|contact|company|team|who/i.test(page.path))\n .slice(0, 4)\n const prompt = [\n `Site: ${brandOf(host) || 'untitled site'}`,\n host.seo?.description ? `Site description: ${str(host.seo.description)}` : '',\n `Empty fields you may fill: ${[...blank].join(', ') || 'none'}`,\n 'Pages:',\n ...scan.pages\n .slice(0, SITE_PAGES_LISTED)\n .map((page) => `- ${page.path} — ${page.name}${str(page.seo.title) ? ` — ${str(page.seo.title)}` : ''}`),\n collections.length\n ? `Content collections: ${collections.map((entry) => entry.name || entry.slug).join(', ')}`\n : '',\n ...telling.flatMap((page) => ['', `Text of ${page.path}:`, page.facts.text.slice(0, 1_200) || '(no text)']),\n ]\n .filter((line) => line !== '')\n .join('\\n')\n const siteText = scan.pages.map((page) => page.facts.text).join('\\n')\n const generation = await runValidatedGeneration('seo-site', {\n step: 'job.seo',\n ...aiRouteSettings('job.seo'),\n model,\n instructions: AI_SEO_SITE_INSTRUCTIONS,\n messages: [{ role: 'user', content: prompt }],\n tool: aiSeoSiteTool(),\n maxTokens: aiSeoGenerationMaxTokens(model, AI_SEO_SITE_MAX_TOKENS),\n ...(signal ? { signal } : {}),\n check: (answer) => checkAiSeoSite(answer, { blank, siteText }),\n })\n const proposed = generation.status === 'ok' ? generation.value : { values: {}, notes: [] }\n const notes =\n generation.status === 'ok'\n ? proposed.notes\n : [\n generation.status === 'refused'\n ? 'The AI declined to propose structured data for this site.'\n : generation.message,\n ]\n const site: AiSeoSiteProposal = {\n values: proposed.values,\n llmsPreview: aiSeoLlmsPreview(host, proposed.values, topLevel, collections),\n notes,\n }\n return {\n output: {\n resource: 'seo',\n id: AI_SEO_OUTPUT_IDS.site,\n hostId: job.hostId ?? null,\n label: 'SEO audit · structured data and agent guidance',\n proposal: plain({ kind: 'site', site }),\n },\n spent: spentBy(generation),\n }\n}\n\n/* ---- A batch of page fixes ------------------------------------------------ */\n\nexport const AI_SEO_FIXES_INSTRUCTIONS: AiSystemBlock[] = [\n {\n text:\n 'You fix the search listings and headings of a website’s pages. For each page you are given ' +\n 'what an audit found, what the page shows now, and its text.\\n\\n' +\n `Answer by calling ${AI_SEO_FIXES_TOOL_NAME} exactly once, with one entry per page. A reply in ` +\n 'prose cannot be used.\\n\\n' +\n 'Rules:\\n' +\n '- Fix only what the audit found. Answer null for a value it did not find a problem with.\\n' +\n '- Write only from the page’s own text. Never invent a fact, a price, a name, a place or a claim.\\n' +\n '- A title says what the page is, specifically. No two pages share a title, and none repeats a ' +\n 'title you are told another page uses.\\n' +\n '- A description is one or two sentences telling a searcher what they will find.\\n' +\n '- The main heading names what the page is about in a few plain words.\\n' +\n '- An image description says what the picture most likely shows, from the text beside it. ' +\n 'Leave an image out when the text gives no idea.\\n' +\n '- A target keyword goes in only where the page is about it and it reads naturally: at most ' +\n 'once in a title and once in a description. Never list keywords or repeat a word to rank.\\n' +\n '- Keep every length the tool states, and write in the language of each page.',\n cacheBreakpoint: true,\n },\n]\n\n/** The fixes one page gets: the model's values, its content fixes, and the guidance no fix can give. */\nexport function aiSeoPageFixFor(page: AiSeoBatchPage, answer: AiSeoBatchAnswer | null): AiSeoPageFix {\n const values: AiSeoFieldValues = {}\n if (answer?.title) values.title = answer.title\n if (answer?.description) values.description = answer.description\n const content: AiSeoContentFix[] = []\n for (const alt of answer?.imageAlts ?? []) content.push({ kind: 'image-alt', nodeId: alt.nodeId, alt: alt.alt })\n if (answer?.h1) {\n const first = page.facts.h1s[0]\n const promotable = page.facts.headings.find((heading) => heading.editable && heading.nodeId)\n if (page.codes.has('h1-thin') && first?.editable && first.nodeId) {\n content.push({ kind: 'h1-set', nodeId: first.nodeId, text: answer.h1 })\n } else if (page.codes.has('h1-missing') && promotable?.nodeId) {\n content.push({ kind: 'h1-set', nodeId: promotable.nodeId, text: answer.h1 })\n } else if (page.codes.has('h1-missing')) {\n content.push({ kind: 'h1-insert', text: answer.h1 })\n }\n }\n if (page.codes.has('h1-multiple')) content.push(...aiSeoHeadingDemotions(page.facts))\n const guidance: string[] = []\n if (page.codes.has('orphan')) {\n guidance.push('Link to this page from your navigation or a related page. Where it belongs is yours to choose.')\n }\n return { screenId: page.screenId, values, content, guidance }\n}\n\n/** The user turn for one batch: each page's findings, what it shows now, and its text. */\nexport function aiSeoFixesPrompt(pages: readonly AiSeoBatchPage[], titlesElsewhere: readonly string[]): string {\n const titles = titlesElsewhere.length\n ? `Titles other pages use:\\n${titlesElsewhere.slice(0, 40).map((title) => `- ${title}`).join('\\n')}\\n\\n`\n : ''\n const blocks = pages.map((page) => {\n const lines = [\n `### Page ${page.screenId}: ${page.name} (${page.path})`,\n `Found: ${[...page.codes].map((code) => SEO_FINDING_LABELS[code]).join('; ')}`,\n `Title now: ${str(page.seo?.title) || '(none)'}`,\n `Description now: ${str(page.seo?.description) || '(none)'}`,\n `Main heading now: ${page.facts.h1s[0]?.text || '(none)'}`,\n ]\n if (page.keywords.length) lines.push(`Target keywords: ${page.keywords.join(', ')}`)\n const images = aiSeoFixImages(page)\n if (images.length) {\n lines.push(\n 'Images without a description:',\n ...images.map((image) => `- ${image.nodeId}: beside \"${image.context || 'no text'}\"`),\n )\n }\n lines.push('Text:', page.facts.text.slice(0, AI_SEO_BATCH_PAGE_TEXT_CHARS) || '(no text)')\n return lines.join('\\n')\n })\n return `${titles}${blocks.join('\\n\\n')}`\n}\n\nasync function proposeFixes(\n context: AiJobStepContext & { firestore: Firestore },\n report: AiSeoAuditReport,\n unit: Extract<AiSeoAuditUnit, { kind: 'batch' }>,\n): Promise<UnitResult> {\n const { job, firestore, signal } = context\n const model = modelOf(context)\n const hostRef = firestore.collection('hosts').doc(job.hostId as string)\n const reports = new Map(report.pages.map((page) => [page.screenId, page]))\n // Read now, not as audited: a page edited since keeps its edits, and one\n // deleted since is skipped.\n const read = await Promise.all(\n unit.screenIds.map(async (screenId) => {\n const pageReport = reports.get(screenId)\n const snapshot = await hostRef.collection('screens').doc(screenId).get()\n const screen = snapshot.exists ? (snapshot.data() as SeoScreenDocument) : null\n if (!pageReport || !screen || screen.deletedAt) return null\n const version = await readVersionNodes(hostRef, screenId, str(screen.versionId))\n return { screenId, pageReport, screen, version }\n }),\n )\n const live = read.filter((entry): entry is NonNullable<typeof entry> => entry !== null)\n // Read as the audit read them, composed: a heading a component renders is\n // the page's, and is never offered to a fix that would rewrite the page.\n const facts = await composedPageFacts(\n firestore,\n job.hostId as string,\n live.map((entry) => entry.version),\n )\n const pages: AiSeoBatchPage[] = live.map(({ screenId, pageReport, screen }, index) => ({\n screenId,\n path: pageReport.path,\n name: pageReport.name,\n codes: new Set(pageReport.findings.map((entry) => entry.code)),\n keywords: pageReport.keywords.map((entry) => entry.keyword),\n seo: screen.seo ?? {},\n facts: facts[index],\n }))\n const titlesElsewhere = await siteTitles(hostRef, new Set(pages.map((page) => page.screenId)))\n\n let spent = spentNothing(model)\n let answers: Record<string, AiSeoBatchAnswer> = {}\n const notes: string[] = []\n if (pages.length) {\n const generation = await runValidatedGeneration('seo-fixes', {\n step: 'job.seo',\n ...aiRouteSettings('job.seo'),\n model,\n instructions: AI_SEO_FIXES_INSTRUCTIONS,\n messages: [{ role: 'user', content: aiSeoFixesPrompt(pages, titlesElsewhere) }],\n tool: aiSeoFixesTool(pages.map((page) => page.screenId)),\n // Lowered on a tier too slow to answer a whole batch and ask again\n // inside the least time the step registers.\n maxTokens: aiSeoGenerationMaxTokens(model, AI_SEO_FIXES_MAX_TOKENS),\n ...(signal ? { signal } : {}),\n check: (answer) => checkAiSeoFixes(answer, pages, titlesElsewhere),\n })\n spent = spentBy(generation)\n if (generation.status === 'ok') {\n answers = generation.value\n } else {\n notes.push(\n generation.status === 'refused'\n ? 'The AI declined to propose fixes for these pages.'\n : generation.message,\n )\n }\n }\n const fixes = pages.map((page) => aiSeoPageFixFor(page, answers[page.screenId] ?? null))\n const skipped = unit.screenIds.length - pages.length\n if (skipped > 0) {\n notes.push(`${skipped} ${skipped === 1 ? 'page was' : 'pages were'} deleted since the audit and left out.`)\n }\n return {\n output: {\n resource: 'seo',\n id: AI_SEO_OUTPUT_IDS.fixes(unit.batch),\n hostId: job.hostId ?? null,\n label: `SEO audit · fixes ${unit.batch} of ${unit.of}`,\n proposal: plain({ kind: 'fixes', batch: unit.batch, fixes, notes }),\n },\n spent,\n }\n}\n\nasync function runSiteAuditPass(\n context: AiJobStepContext & { firestore: Firestore },\n host: SeoHostDocument,\n): Promise<AiJobStepOutcome> {\n const { job, firestore } = context\n const model = modelOf(context)\n const recorded = aiSeoAuditView(job.outputs ?? [])\n const outputs: AiJobOutput[] = []\n let spent = spentNothing(model)\n let report: AiSeoAuditReport\n let scan: SeoSiteScan | null = null\n if (recorded) {\n report = recorded.report\n } else {\n scan = await scanSite(firestore, job.hostId as string, host, job.inputs?.['keywords'])\n report = aiSeoAudit(scan.pages, seoAuditSiteOf(host), { skipped: scan.skipped, notes: scan.notes })\n const findings = report.pages.reduce((sum, page) => sum + page.findings.length, report.site.length)\n outputs.push({\n resource: 'seo',\n id: AI_SEO_OUTPUT_IDS.report,\n hostId: job.hostId ?? null,\n label: `SEO audit · ${report.pages.length} ${report.pages.length === 1 ? 'page' : 'pages'} · ${findings} ${findings === 1 ? 'finding' : 'findings'}`,\n proposal: plain(report),\n })\n }\n const units = aiSeoPendingUnits(report, [...(job.outputs ?? []), ...outputs])\n if (!units.length) return { outputs, ...spent }\n const [unit] = units\n const result =\n unit.kind === 'site'\n ? await proposeSite(\n context,\n host,\n scan ?? (await scanSite(firestore, job.hostId as string, host, job.inputs?.['keywords'])),\n )\n : await proposeFixes(context, report, unit)\n outputs.push(result.output)\n spent = addSpent(spent, result.spent)\n return { outputs, ...spent, ...(units.length > 1 ? { continue: true } : {}) }\n}\n\nexport const runAiJobSeoStep: AiJobStepRunner = async (context) => {\n const { job, firestore } = context\n if (!firestore) {\n // The machine always hands one in; a runner called without it is a\n // wiring fault, not a customer path.\n throw new Error('the SEO step reads the site it writes about and was given no Firestore')\n }\n const target = aiSeoJobTarget(job.inputs)\n if (!target) return { outputs: [], ...spentNothing(modelOf(context)), failure: AI_SEO_NO_TARGET_COPY }\n const site = await loadSite(firestore, job)\n if ('failure' in site) return { outputs: [], ...spentNothing(modelOf(context)), failure: site.failure }\n const withFirestore = { ...context, firestore }\n switch (target) {\n case 'screen':\n return runScreenListing(withFirestore, site.host)\n case 'product':\n return runProductListing(withFirestore, site.host)\n case 'site':\n return runSiteAuditPass(withFirestore, site.host)\n }\n}\n"],"names":["hostCollectionKind","hostPublicOrigin","buildLlmsTxt","screenRoutePathToUrl","SEO_FINDING_LABELS","SEO_MAX_KEYWORDS","seoKeywordCoverage","seoKeywordSplit","seoQuotedKeywords","SCREEN_SEO_LISTING_FIELDS","isSeoListingFieldKey","SEO_PAGE_TEXT_MAX_CHARS","seoPageFacts","composeSeoPages","scanSeoSite","seoAuditSiteOf","readRepeatRows","decodeStoredNodes","getTemplateScreenIds","AI_SEO_OUTPUT_IDS","aiSeoAuditView","aiSeoJobTarget","aiModelForStep","aiRouteSettings","aiSeoAudit","aiSeoAuditBatches","aiSeoHeadingDemotions","runValidatedGeneration","AI_SEO_FIELDS_MAX_TOKENS","generateSeoFields","AI_SEO_FIXES_TOOL_NAME","AI_SEO_SITE_TOOL_NAME","aiSeoFixImages","aiSeoFixesTool","aiSeoSiteTool","checkAiSeoFixes","checkAiSeoSite","AI_SEO_FIXES_MAX_TOKENS","AI_SEO_SITE_MAX_TOKENS","aiSeoGenerationMaxTokens","aiCacheWriteHourSum","AI_SEO_NO_SITE_COPY","AI_SEO_NO_PAGE_COPY","AI_SEO_NO_TARGET_COPY","AI_SEO_NO_PRODUCT_COPY","AI_SEO_BATCH_PAGE_TEXT_CHARS","SITE_PAGES_LISTED","PRODUCT_LISTING_FIELDS","ZERO_USAGE","inputTokens","outputTokens","cacheReadTokens","cacheWriteTokens","str","value","trim","round6","Math","round","modelOf","context","modelFor","spentNothing","model","usage","estCostUsd","stopReason","spentBy","generation","addSpent","a","b","plain","JSON","parse","stringify","aiSeoRequestedFields","raw","allowed","named","String","split","map","part","filter","wanted","Set","length","key","has","brandOf","host","seo","title","displayName","subdomain","loadSite","firestore","job","hostId","failure","snapshot","collection","doc","get","exists","data","orgId","siteRepeatRows","keys","composedPageFacts","versions","composed","version","nodes","index","rootId","pageNodes","readVersionNodes","hostRef","screenId","versionId","siteTitles","except","select","limit","docs","id","Boolean","listingOutcome","subject","spent","status","outputs","refused","message","values","notes","fields","includes","hasImage","imageAlt","push","unchecked","proposal","kind","keywords","description","h1","facts","h1s","text","body","output","resource","label","name","runScreenListing","screen","signal","inputs","deletedAt","image","onPage","images","find","src","undefined","route","screens","path","brand","current","breadcrumb","alt","otherTitles","maxTokens","runProductListing","slice","productId","scanSite","templateScreenIds","aiSeoPendingUnits","report","done","units","siteProposal","site","batches","forEach","screenIds","fixes","batch","of","AI_SEO_SITE_INSTRUCTIONS","cacheBreakpoint","aiSeoBlankSiteFields","entity","agent","blank","type","add","email","telephone","contactType","whenToUse","howToUse","readContentCollections","slug","aiSeoLlmsPreview","pages","collections","siteName","origin","hasSearch","contactEmail","proposeSite","scan","topLevel","page","replace","telling","test","prompt","join","entry","flatMap","line","siteText","step","instructions","messages","role","content","tool","check","answer","proposed","llmsPreview","AI_SEO_FIXES_INSTRUCTIONS","aiSeoPageFixFor","imageAlts","nodeId","first","promotable","headings","heading","editable","codes","guidance","aiSeoFixesPrompt","titlesElsewhere","titles","blocks","lines","code","proposeFixes","unit","reports","Map","read","Promise","all","pageReport","live","findings","keyword","answers","skipped","runSiteAuditPass","recorded","reduce","sum","result","continue","runAiJobSeoStep","Error","target","withFirestore"],"mappings":"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"}
|
|
@@ -118,11 +118,16 @@ import type { AiBuildPlan } from '../model/ai-build-plan';
|
|
|
118
118
|
* its kin shuns (a pump for a bar) unless it names it; "bath" in a bath
|
|
119
119
|
* gift set is what the set is for; a size is never the product.
|
|
120
120
|
*
|
|
121
|
-
* A slot
|
|
122
|
-
* of time
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
121
|
+
* A slot the library could not fill because it never answered — down, or
|
|
122
|
+
* out of time before its first answer — or because the site's library
|
|
123
|
+
* refused to store, is left `null` for the starter photos. A slot the
|
|
124
|
+
* library answered and nothing acceptable came back for is answered
|
|
125
|
+
* {@link AI_LAYOUT_NO_PHOTO} and keeps its no-photo treatment: the beta.257
|
|
126
|
+
* Copper Kettle Catering hero, held to "caterer" while catering photos are
|
|
127
|
+
* tagged "catering", took the starters' pottery workbench (AGL-3831). A
|
|
128
|
+
* named object is never given one while the library answers anything of the
|
|
129
|
+
* site's world: the broad searches come first. Nothing here throws, and none
|
|
130
|
+
* of it is an AI model call: photos cost no AI credits.
|
|
126
131
|
*/
|
|
127
132
|
/** Wall clock the whole page's photos may take, inside the step's own signal. */
|
|
128
133
|
export declare const AI_LAYOUT_STOCK_PHOTOS_BUDGET_MS = 30000;
|
|
@@ -16,7 +16,7 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
|
16
16
|
* limitations under the License.
|
|
17
17
|
*/ import { pluginMediaIngest } from "@aglyn/aglyn/plugin-manager/plugin-media-ingest";
|
|
18
18
|
import { STOCK_PHOTO_QUERY_MAX_CHARS, stockPhotoProvider, stockPhotoSourceKey } from "@aglyn/aglyn/plugin-manager/stock-photo-provider";
|
|
19
|
-
import { aiLayoutSeedNumber } from "../layout-language/ai-layout-pictures.js";
|
|
19
|
+
import { AI_LAYOUT_NO_PHOTO, aiLayoutSeedNumber } from "../layout-language/ai-layout-pictures.js";
|
|
20
20
|
import { aiStorefrontSays } from "../layout-language/ai-layout-storefront.js";
|
|
21
21
|
import { aiLayoutLabel, aiLayoutSpeaksOtherThanEnglish } from "../layout-language/ai-layout-labels.js";
|
|
22
22
|
import { aiSiteKindOfInputs } from "../model/ai-site-kinds.js";
|
|
@@ -120,11 +120,16 @@ import { aiStockBusinessNoun } from "./ai-stock-business-nouns.js";
|
|
|
120
120
|
* its kin shuns (a pump for a bar) unless it names it; "bath" in a bath
|
|
121
121
|
* gift set is what the set is for; a size is never the product.
|
|
122
122
|
*
|
|
123
|
-
* A slot
|
|
124
|
-
* of time
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
123
|
+
* A slot the library could not fill because it never answered — down, or
|
|
124
|
+
* out of time before its first answer — or because the site's library
|
|
125
|
+
* refused to store, is left `null` for the starter photos. A slot the
|
|
126
|
+
* library answered and nothing acceptable came back for is answered
|
|
127
|
+
* {@link AI_LAYOUT_NO_PHOTO} and keeps its no-photo treatment: the beta.257
|
|
128
|
+
* Copper Kettle Catering hero, held to "caterer" while catering photos are
|
|
129
|
+
* tagged "catering", took the starters' pottery workbench (AGL-3831). A
|
|
130
|
+
* named object is never given one while the library answers anything of the
|
|
131
|
+
* site's world: the broad searches come first. Nothing here throws, and none
|
|
132
|
+
* of it is an AI model call: photos cost no AI credits.
|
|
128
133
|
*/ /** Wall clock the whole page's photos may take, inside the step's own signal. */ export const AI_LAYOUT_STOCK_PHOTOS_BUDGET_MS = 30000;
|
|
129
134
|
/** Searches one page may send to the provider; a repeated query is answered from the page's own memory. */ export const AI_LAYOUT_STOCK_SEARCHES_PER_PAGE = 18;
|
|
130
135
|
/** The hits a pick is made among. */ export const AI_LAYOUT_STOCK_PICK_WINDOW = 40;
|
|
@@ -924,6 +929,78 @@ const words = (list)=>new Set(list.trim().split(/\s+/));
|
|
|
924
929
|
'barista making coffee'
|
|
925
930
|
]
|
|
926
931
|
},
|
|
932
|
+
{
|
|
933
|
+
// A caterer is named "caterer" and its photos "catering", "buffet",
|
|
934
|
+
// "food": the beta.257 Copper Kettle Catering hero, held to "caterer"
|
|
935
|
+
// alone, accepted none of forty hits (AGL-3831).
|
|
936
|
+
match: [
|
|
937
|
+
'caterer',
|
|
938
|
+
'catering',
|
|
939
|
+
'cater'
|
|
940
|
+
],
|
|
941
|
+
kin: [
|
|
942
|
+
'catering',
|
|
943
|
+
'caterer',
|
|
944
|
+
'cater',
|
|
945
|
+
'buffet',
|
|
946
|
+
'banquet',
|
|
947
|
+
'platter',
|
|
948
|
+
'food',
|
|
949
|
+
'meal',
|
|
950
|
+
'cuisine'
|
|
951
|
+
],
|
|
952
|
+
broad: [
|
|
953
|
+
'caterer',
|
|
954
|
+
'catering buffet'
|
|
955
|
+
],
|
|
956
|
+
making: [
|
|
957
|
+
'chef cooking',
|
|
958
|
+
'catering chef'
|
|
959
|
+
],
|
|
960
|
+
// An airline's meal cart is catering too, never a caterer's: the same
|
|
961
|
+
// start's search answered a plane at Düsseldorf tagged "catering" first.
|
|
962
|
+
rivals: {
|
|
963
|
+
words: [
|
|
964
|
+
'airport',
|
|
965
|
+
'airplane',
|
|
966
|
+
'plane',
|
|
967
|
+
'aircraft',
|
|
968
|
+
'aviation',
|
|
969
|
+
'airline',
|
|
970
|
+
'airfield'
|
|
971
|
+
],
|
|
972
|
+
core: []
|
|
973
|
+
}
|
|
974
|
+
},
|
|
975
|
+
{
|
|
976
|
+
match: [
|
|
977
|
+
'barbecue',
|
|
978
|
+
'barbeque',
|
|
979
|
+
'bbq',
|
|
980
|
+
'smokehouse',
|
|
981
|
+
'pitmaster'
|
|
982
|
+
],
|
|
983
|
+
kin: [
|
|
984
|
+
'barbecue',
|
|
985
|
+
'barbeque',
|
|
986
|
+
'bbq',
|
|
987
|
+
'grill',
|
|
988
|
+
'grilled',
|
|
989
|
+
'brisket',
|
|
990
|
+
'smoked',
|
|
991
|
+
'smoker',
|
|
992
|
+
'ribs',
|
|
993
|
+
'meat'
|
|
994
|
+
],
|
|
995
|
+
broad: [
|
|
996
|
+
'barbecue',
|
|
997
|
+
'bbq meat'
|
|
998
|
+
],
|
|
999
|
+
making: [
|
|
1000
|
+
'pitmaster',
|
|
1001
|
+
'grilling meat'
|
|
1002
|
+
]
|
|
1003
|
+
},
|
|
927
1004
|
{
|
|
928
1005
|
match: [
|
|
929
1006
|
'florist',
|
|
@@ -1919,6 +1996,7 @@ const EXTENSIONS = {
|
|
|
1919
1996
|
const answers = new Map();
|
|
1920
1997
|
let searches = 0;
|
|
1921
1998
|
let storing = true;
|
|
1999
|
+
/** Whether the library answered any search of this page: a slot left empty then found nothing fitting. */ let answered = false;
|
|
1922
2000
|
const remember = (sourceKey, src)=>{
|
|
1923
2001
|
placedSrcs.add(src);
|
|
1924
2002
|
job == null ? void 0 : job.set(sourceKey, scope);
|
|
@@ -1949,6 +2027,7 @@ const EXTENSIONS = {
|
|
|
1949
2027
|
signal
|
|
1950
2028
|
}).then((found)=>{
|
|
1951
2029
|
var _ref;
|
|
2030
|
+
if (found) answered = true;
|
|
1952
2031
|
return (_ref = found == null ? void 0 : found.photos) != null ? _ref : [];
|
|
1953
2032
|
}).catch(()=>[]);
|
|
1954
2033
|
answers.set(key, asked);
|
|
@@ -2246,7 +2325,10 @@ const EXTENSIONS = {
|
|
|
2246
2325
|
length: Math.min(AI_LAYOUT_STOCK_COPIES_AT_ONCE, firsts.length)
|
|
2247
2326
|
}, worker));
|
|
2248
2327
|
for (const [index, choice] of choices.entries())if (choice == null ? void 0 : choice.repeat) await fill(index);
|
|
2249
|
-
|
|
2328
|
+
// The library answered and nothing in it fits: no photo rather than a
|
|
2329
|
+
// starter that shows another business (AGL-3831). A library that never
|
|
2330
|
+
// answered, or a site that refused to store, leaves the starters.
|
|
2331
|
+
return answered && storing ? photos.map((photo)=>photo != null ? photo : AI_LAYOUT_NO_PHOTO) : photos;
|
|
2250
2332
|
};
|
|
2251
2333
|
}
|
|
2252
2334
|
|