@aglyn/plugins-ai 1.0.0-beta.257 → 1.0.0-beta.259
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 +33 -0
- package/src/lib/jobs/ai-job-page-language.js +46 -6
- 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 +79 -39
- 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-job-template-step.js +7 -1
- package/src/lib/jobs/ai-job-template-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-motion.d.ts +75 -0
- package/src/lib/layout-language/ai-layout-motion.js +172 -0
- package/src/lib/layout-language/ai-layout-motion.js.map +1 -0
- 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/operator-alerts.js +1 -1
- package/src/lib/operator-alerts.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 +70 -1
- package/src/lib/runtime/ai-doctrine-validators.js +332 -2
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-doctrine.js +12 -4
- 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-edit.d.ts +21 -0
- package/src/lib/server/assist-edit.js +99 -21
- package/src/lib/server/assist-edit.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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"}
|
|
@@ -29,6 +29,8 @@ import { aiTemplateExamplesSystemBlock } from "../runtime/ai-template-examples.j
|
|
|
29
29
|
import { readSiteInventory } from "../runtime/site-inventory.js";
|
|
30
30
|
import { registerAiJobAdmission } from "./ai-job-admission.js";
|
|
31
31
|
import { aiJobDraftId } from "./ai-job-draft-ids.js";
|
|
32
|
+
import { aiLayoutMotionSiteOf } from "./ai-job-page-language.js";
|
|
33
|
+
import { aiWithSiteMotion } from "../layout-language/ai-layout-motion.js";
|
|
32
34
|
import { aiDraftAdmissionRefusal, aiDraftAllowanceRefusal, aiSiteSubdomain, readAiDraft, readAiTemplateDraftNodes, writeAiDraft } from "./ai-job-drafts.js";
|
|
33
35
|
import { aiConfirmedPlan, aiCopySourceLines, aiDoctrineReview, aiGenerationSpent, aiJobBriefLine, aiLimitReview, aiModelNodeIds, aiPlacedComponentIds, aiPlanCreation, aiPlanReferenceLines, aiPlanReuseViolations, aiUnspentOutcome } from "./ai-job-generation.js";
|
|
34
36
|
import { aiPlannedTemplateTokens, aiPlanTemplateTokenViolations } from "./ai-job-plan-conformance.js";
|
|
@@ -493,7 +495,11 @@ export function createAiJobTemplateStep(deps = {}) {
|
|
|
493
495
|
uid: job.createdBy,
|
|
494
496
|
org,
|
|
495
497
|
name,
|
|
496
|
-
|
|
498
|
+
// The site's entrance motion, as its pages have (AGL-3838): a post or a
|
|
499
|
+
// record opens the way the rest of the site does.
|
|
500
|
+
nodes: aiWithSiteMotion(result.value.nodes, aiLayoutMotionSiteOf(job, ''), {
|
|
501
|
+
rootId: result.value.rootId
|
|
502
|
+
}),
|
|
497
503
|
slug: aiTemplateDraftSlug(definition, collection),
|
|
498
504
|
now
|
|
499
505
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-template-step.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { throwIfAiJobCanceled } from './ai-job-cancel'\nimport { hostCollectionKind } from '@aglyn/aglyn/app-utils/collection-kind'\nimport { normalizeScreenSlug } from '@aglyn/aglyn/app-utils/screen-route'\nimport type { AglynOrgBilling } from '@aglyn/aglyn/foundation/definitions/org-billing.types'\nimport { duplicateResource } from '@aglyn/tenant-data-admin/server/duplicate-resource'\nimport { CANVAS_ROOT_ELEMENT_ID } from '@aglyn/aglyn/foundation/constants/canvas'\nimport type { AiJob, AiJobOutput, AiJobPlan, AiJobReview } from '../model/ai-jobs.types'\nimport {\n AI_TEMPLATE_SUBJECT_DEFINITIONS,\n AI_TEMPLATE_SUBJECTS,\n aiBindingTokensIn,\n aiTemplateAddressTokens,\n isAiAddressToken,\n isAiUnreadToken,\n parseAiTemplateJobInputs,\n type AiTemplateSubjectDefinition,\n} from '../model/ai-template-subjects'\nimport { AI_STEP_TIERS } from '../providers/catalog'\nimport { AI_ROUTING_TABLE, aiModelForStep } from '../providers/routing'\nimport {\n aiDoctrineTreeTool,\n runValidatedGeneration,\n type AiValidatedTree,\n} from '../runtime/ai-doctrine'\nimport {\n walkTree,\n type AiDoctrineNode,\n type AiDoctrineViolation,\n} from '../runtime/ai-doctrine-validators'\nimport type { AiLoadEstimate } from '../runtime/ai-palette'\nimport { AI_PALETTE } from '../runtime/ai-palette.generated'\nimport type { AiSystemBlock } from '../runtime/ai-runtime'\nimport { aiTemplateExamplesSystemBlock } from '../runtime/ai-template-examples'\nimport { readSiteInventory } from '../runtime/site-inventory'\nimport { registerAiJobAdmission, type AiJobAdmission } from './ai-job-admission'\nimport { aiJobDraftId } from './ai-job-draft-ids'\nimport {\n aiDraftAdmissionRefusal,\n aiDraftAllowanceRefusal,\n aiSiteSubdomain,\n readAiDraft,\n readAiTemplateDraftNodes,\n writeAiDraft,\n type AiDraftRecord,\n} from './ai-job-drafts'\nimport {\n aiConfirmedPlan,\n aiCopySourceLines,\n aiDoctrineReview,\n aiGenerationSpent,\n aiJobBriefLine,\n aiLimitReview,\n aiModelNodeIds,\n aiPlacedComponentIds,\n aiPlanCreation,\n aiPlanReferenceLines,\n aiPlanReuseViolations,\n aiUnspentOutcome,\n} from './ai-job-generation'\nimport { aiPlannedTemplateTokens, aiPlanTemplateTokenViolations } from './ai-job-plan-conformance'\nimport {\n aiBuildingPlanCreations,\n aiJobPlanCreationsRefusal,\n aiPlanCreationsRunMinimumMs,\n} from './ai-job-plan-creations'\nimport type { AiJobStepRunner } from './ai-job-text-step'\nimport { aiJobStepBudget } from './ai-job-budget'\nimport { registerAiJobStep } from './ai-jobs'\n\n/**\n * The template step (AGL-2909): the generation step of a `template` job, run\n * once a member confirmed the job's plan.\n *\n * It builds ONE page template — the page a site renders once per record: a\n * content collection's entry, a product, an author — through\n * `runValidatedGeneration('template', …)`. Everything that differs per\n * record is a binding token from the subject's vocabulary\n * (`model/ai-template-subjects.ts`), which is the list the besigner's insert\n * picker offers for that page; a link or media prop may hold one of those\n * tokens whole, which the palette validator admits only because this door\n * names them. The door adds one check of its own through `extend` (rule 8):\n * no token the page does not fill, an h1 bound to the subject's title, no\n * block that fills itself only on another subject's page, and no address,\n * slug or timestamp printed in its copy.\n *\n * The model is shown the platform's starter pages as examples, in a cached\n * block (`runtime/ai-template-examples.ts`), and writes against them.\n *\n * The template lands in the site's library as a new draft\n * (`ai-job-drafts.ts`), inert until a member uses it: bound to no\n * collection, set as no product or author page, served nowhere. A plan that\n * starts from a copy of a template the site has gets that copy instead.\n */\n\n/** The longest answer a template may run to; the tree is held to the template budget either way. */\nexport const AI_JOB_TEMPLATE_MAX_TOKENS = AI_ROUTING_TABLE['job.template'].maxTokens\n\n/**\n * The template step's time (AGL-3035, AGL-3036): its two inventory-lookup\n * rounds, its answer and its re-ask at the routing table's ceiling on the tier\n * `job.template` is served from, fitted to what a beat can give a step. It\n * runs on the job beat, never at an inline door, and asks for no more of the\n * ceiling than fits that time on the model it runs.\n */\nexport const AI_JOB_TEMPLATE_STEP_BUDGET = aiJobStepBudget({\n tier: AI_STEP_TIERS['job.template'],\n maxTokens: AI_JOB_TEMPLATE_MAX_TOKENS,\n})\n\n/** The least time one template step needs before it starts. */\nexport const AI_JOB_TEMPLATE_STEP_MINIMUM_MS = AI_JOB_TEMPLATE_STEP_BUDGET.minimumMs\n\n/** The template step's own instructions, cached after the doctrine and before the examples. */\nexport const AI_JOB_TEMPLATE_INSTRUCTIONS: readonly AiSystemBlock[] = [\n {\n text: [\n 'You build one page template: the page a website renders once for each record it keeps, such as each entry of a content collection, each product or each author. Answer with submit_template: the whole page as one flat node map.',\n 'Everything that differs from one record to the next is a binding token, never typed copy. The request names what the page is for, the tokens that page fills and the blocks that fill themselves there; bind only those tokens.',\n 'The page’s one h1 holds the title token the request names. Copy that reads the same on every record, such as a section heading, is written in the site’s voice.',\n 'A link or media prop holds exactly one token from the request’s link and media tokens, such as an Image whose src is {{entry.coverImage}}; its alt text may combine words and tokens. Copy never prints a link or media token, a slug or a timestamp.',\n 'A template carries no third-party player. It plays a video token only when the confirmed plan lists it, in a Video (video) whose src is that token and whose poster is the cover image token.',\n 'The page renders inside the site’s layout, so it carries no header, navigation or footer of its own.',\n ].join('\\n'),\n },\n]\n\n/** A content collection an entry template is for, as the prompt and the draft name it. */\nexport interface AiTemplateCollection {\n id: string\n name: string\n slug: string\n}\n\n/** The content collection by that id on the site, or `null` when there is none. */\nexport async function readAiTemplateCollection(\n firestore: FirebaseFirestore.Firestore,\n hostId: string,\n collectionId: string,\n): Promise<AiTemplateCollection | null> {\n const snapshot = await firestore\n .collection('hosts')\n .doc(hostId)\n .collection('collections')\n .doc(collectionId)\n .get()\n if (!snapshot.exists) return null\n const data = (snapshot.data() ?? {}) as Record<string, unknown>\n if (hostCollectionKind(data) !== 'content') return null\n const text = (value: unknown) => (typeof value === 'string' ? value.trim() : '')\n const slug = text(data['slug'])\n return {\n id: collectionId,\n name: text(data['displayName']) || text(data['name']) || slug || collectionId,\n slug,\n }\n}\n\n/** The template's name: the plan's, else the collection's or the subject's. */\nexport function aiTemplateDraftName(\n definition: AiTemplateSubjectDefinition,\n collection: AiTemplateCollection | null,\n planned: string | null | undefined,\n): string {\n return planned || (collection ? `${collection.name} entry template` : definition.defaultName)\n}\n\n/** The address Use template offers for the page it creates. */\nexport function aiTemplateDraftSlug(\n definition: AiTemplateSubjectDefinition,\n collection: AiTemplateCollection | null,\n): string | null {\n return (\n normalizeScreenSlug(\n collection\n ? `${collection.slug || collection.id}-entry-template`\n : `${definition.subject}-page-template`,\n ) ?? null\n )\n}\n\n/** The template job as the generation's user turn, with the template a short copy would have started from (AGL-3143 §14). */\nexport function aiJobTemplatePrompt(\n job: Pick<AiJob, 'brief'>,\n definition: AiTemplateSubjectDefinition,\n collection: AiTemplateCollection | null,\n plan: AiJobPlan | null,\n name: string,\n source: readonly string[] = [],\n): string {\n const of = collection\n ? ` of the \"${collection.name}\" collection${collection.slug ? ` (/${collection.slug})` : ''}`\n : ''\n return [\n `Template name: ${name}`,\n `The page is for ${definition.noun}${of}.`,\n `Title token: ${definition.titleToken}`,\n `Tokens this page fills: ${definition.tokens.map((entry) => `${entry.token} ${entry.label}`).join('; ')}`,\n `Link and media tokens: ${aiTemplateAddressTokens(definition).join(', ')}`,\n definition.blocks.length\n ? `Blocks that fill themselves on this page: ${definition.blocks.join(', ')}`\n : 'No block fills itself on this page: bind the tokens.',\n aiJobBriefLine(job),\n ...aiPlanReferenceLines(plan),\n ...source,\n ].join('\\n')\n}\n\n/** Which subject's page each self-filling block belongs to. */\nconst BLOCK_SUBJECTS = new Map<string, AiTemplateSubjectDefinition>(\n AI_TEMPLATE_SUBJECTS.flatMap((subject) =>\n AI_TEMPLATE_SUBJECT_DEFINITIONS[subject].blocks.map(\n (block) => [block, AI_TEMPLATE_SUBJECT_DEFINITIONS[subject]] as const,\n ),\n ),\n)\n\n/** Every string a prop value holds, however deeply. */\nfunction stringsIn(value: unknown, out: string[] = []): string[] {\n if (typeof value === 'string') out.push(value)\n else if (Array.isArray(value)) for (const inner of value) stringsIn(inner, out)\n else if (value && typeof value === 'object') {\n for (const inner of Object.values(value as Record<string, unknown>)) stringsIn(inner, out)\n }\n return out\n}\n\n/** The page's title: a Typography that renders as an h1. */\nfunction isPageTitle(node: AiDoctrineNode): boolean {\n if (node.componentId !== 'muiTypography') return false\n const element = typeof node.props?.['component'] === 'string' ? String(node.props['component']) : ''\n return (element || String(node.props?.['variant'] ?? '')) === 'h1'\n}\n\n/**\n * The tokens a prop prints for a reader that no reader reads: a link or\n * picture token, a slug or a timestamp, in copy (AGL-3143 §16). Copy is a\n * prop the palette gives the text role, read as words where it holds more\n * than one token or is the element's children. A block handed one token\n * whole reads it as a value, as Entry Meta's avatar takes the author's\n * portrait and a Video's publication date takes the timestamp, and a\n * markdown link's target is followed, not read.\n */\nfunction printedTokens(node: AiDoctrineNode): string[] {\n const roles = AI_PALETTE[node.componentId]?.propRoles ?? {}\n return Object.entries(node.props ?? {}).flatMap(([key, value]) => {\n if (typeof value !== 'string' || roles[key] !== 'text') return []\n const tokens = aiBindingTokensIn(value.replace(/\\]\\([^)]*\\)/g, ']'))\n const unread = tokens.filter((token) => isAiAddressToken(token) || isAiUnreadToken(token))\n const whole = tokens.length === 1 && value.trim() === tokens[0]\n return unread.length && (key === 'children' || !whole) ? unread : []\n })\n}\n\n/**\n * The template door's check on a tree the doctrine admitted (rule 8): every\n * token is one the page fills, the h1 binds the subject's title, no block\n * that fills itself on another subject's page is placed — except inside a\n * Collection Entries card, where each fills from the card's own entry — and\n * no copy prints an address, a slug or a timestamp.\n */\nexport function aiTemplateBindingCheck(\n definition: AiTemplateSubjectDefinition,\n): (tree: AiValidatedTree) => AiDoctrineViolation[] {\n const allowed = new Set(definition.tokens.map((entry) => entry.token))\n return (tree) => {\n const nodes = tree.nodes as unknown as Record<string, AiDoctrineNode>\n const unknown = new Set<string>()\n const unknownAt: string[] = []\n const foreign: Array<{ id: string; componentId: string; owner: AiTemplateSubjectDefinition }> = []\n const titles: string[] = []\n const printed = new Set<string>()\n const printedAt: string[] = []\n let titleBound = false\n for (const { id, node, ancestors } of walkTree({ rootId: tree.rootId, nodes })) {\n const tokens = stringsIn(node.props).flatMap(aiBindingTokensIn)\n const strays = tokens.filter((token) => !allowed.has(token))\n if (strays.length) {\n for (const token of strays) unknown.add(token)\n unknownAt.push(id)\n }\n const shown = printedTokens(node).filter((token) => allowed.has(token))\n if (shown.length) {\n for (const token of shown) printed.add(token)\n printedAt.push(id)\n }\n const owner = BLOCK_SUBJECTS.get(node.componentId)\n const inCard = ancestors.some((ancestor) => nodes[ancestor]?.componentId === 'collectionEntries')\n if (owner && owner.subject !== definition.subject && !inCard) {\n foreign.push({ id, componentId: node.componentId, owner })\n }\n if (isPageTitle(node)) {\n titles.push(id)\n if (tokens.includes(definition.titleToken)) titleBound = true\n }\n }\n const violations: AiDoctrineViolation[] = []\n if (unknown.size) {\n violations.push({\n rule: 8,\n code: 'unknown-binding',\n message: `This binds ${[...unknown].slice(0, 3).join(', ')}, which ${definition.noun}’s page does not fill. Bind only the tokens listed for this page.`,\n nodeIds: aiModelNodeIds(unknownAt, tree.sourceIds),\n })\n }\n if (printed.size) {\n violations.push({\n rule: 8,\n code: 'printed-token',\n message: `This prints ${[...printed].slice(0, 3).join(', ')} in the page's copy, where a reader sees an address, a slug or a timestamp. A link or picture token goes whole in a link or media prop, and a slug or a timestamp stays off the page.`,\n nodeIds: aiModelNodeIds(printedAt, tree.sourceIds),\n })\n }\n // The doctrine already refuses a page with no h1 or with several.\n if (titles.length === 1 && !titleBound) {\n violations.push({\n rule: 8,\n code: 'typed-title',\n message: `The page title is typed, so every record would show the same one. Put ${definition.titleToken} in the h1.`,\n nodeIds: aiModelNodeIds(titles, tree.sourceIds),\n })\n }\n if (foreign.length) {\n const [first] = foreign\n violations.push({\n rule: 8,\n code: 'foreign-block',\n message: `The ${AI_PALETTE[first.componentId]?.displayName ?? first.componentId} block fills itself only on ${first.owner.noun}’s page, and this template is for ${definition.noun}. Remove it.`,\n nodeIds: aiModelNodeIds(\n foreign.map((entry) => entry.id),\n tree.sourceIds,\n ),\n })\n }\n return violations\n }\n}\n\nexport interface AiTemplateJobAdmissionDeps {\n readCollection?: typeof readAiTemplateCollection\n}\n\n/**\n * A template job is admitted with inputs that say what the page is for, for\n * a site of the job's own org that holds the collection it names, with a\n * template to spare.\n */\nexport function createAiTemplateJobAdmission(deps: AiTemplateJobAdmissionDeps = {}): AiJobAdmission {\n const readCollection = deps.readCollection ?? readAiTemplateCollection\n return async (context) => {\n const inputs = parseAiTemplateJobInputs(context.inputs)\n if (typeof inputs === 'string') return { status: 400, error: inputs }\n return aiDraftAdmissionRefusal(context.firestore, {\n orgId: context.orgId,\n hostId: context.hostId,\n kind: 'template',\n org: context.org,\n ownCheck: async (hostId) =>\n inputs.collectionId && !(await readCollection(context.firestore, hostId, inputs.collectionId))\n ? { status: 404, error: 'That content collection is not on this site' }\n : aiJobPlanCreationsRefusal('template', context, hostId),\n })\n }\n}\n\nexport interface AiJobTemplateStepDeps {\n /** The inventory reader; specs hand in a fake. */\n readInventory?: typeof readSiteInventory\n /** The platform's duplicate module, for a plan that starts from a copy. */\n duplicate?: typeof duplicateResource\n readCollection?: typeof readAiTemplateCollection\n /** The starter examples block; built from the starters once per process otherwise. */\n examples?: () => AiSystemBlock\n}\n\n/**\n * The binding tokens the confirmed plan gives the template, against the\n * template a COPY actually produced (AGL-3024); `null` when the copy keeps\n * the plan, or when the plan promised no token to keep.\n *\n * The same hole as `aiCopiedLayoutReview` and `aiCopiedPageReview`, on the\n * third kind that can start from a duplicate. The generate path already holds\n * a built template to `aiPlanTemplateTokenViolations`; the copy path reached\n * none of it, so a plan reading \"duplicate it and bind the author's photo\"\n * got the duplicate and reported Done.\n *\n * A copy that cannot be read back settles nothing, and a promise this step\n * cannot settle is reviewed by a person rather than reported as kept.\n */\nexport async function aiCopiedTemplateReview(\n firestore: FirebaseFirestore.Firestore,\n input: {\n hostId: string\n id: string\n name: string\n plan: AiJobPlan | null\n definition: AiTemplateSubjectDefinition\n },\n): Promise<AiJobReview | null> {\n const { tokens, unreadable } = aiPlannedTemplateTokens(input.plan, input.definition)\n if (!tokens.length && !unreadable.length) return null\n const stored = await readAiTemplateDraftNodes(firestore, {\n hostId: input.hostId,\n id: input.id,\n })\n const nodes = (stored ?? {}) as unknown as Record<string, AiDoctrineNode>\n if (!nodes[CANVAS_ROOT_ELEMENT_ID]) {\n return {\n reason: 'doctrine',\n message: `\"${input.name}\" was copied from the template the plan names, and this job could not read the copy back to check it shows what the plan gives it. Open the template and check it before you apply it.`,\n findings: [],\n }\n }\n const violations = aiPlanTemplateTokenViolations(input.plan, input.definition, {\n rootId: CANVAS_ROOT_ELEMENT_ID,\n nodes,\n })\n if (!violations.length) return null\n return aiDoctrineReview({\n message: `\"${input.name}\" is a copy of the template the plan names, and a copy is all it is: ${violations[0].message}`,\n violations,\n })\n}\n\nexport function createAiJobTemplateStep(deps: AiJobTemplateStepDeps = {}): AiJobStepRunner {\n const readInventory = deps.readInventory ?? readSiteInventory\n const duplicate = deps.duplicate ?? duplicateResource\n const readCollection = deps.readCollection ?? readAiTemplateCollection\n const examples = deps.examples ?? aiTemplateExamplesSystemBlock\n return async ({ job, now, signal, firestore, modelFor }) => {\n const hostId = job.hostId\n if (!hostId) throw new Error('a template job names no site, and its admission refuses one')\n const inputs = parseAiTemplateJobInputs(job.inputs)\n if (typeof inputs === 'string') {\n throw new Error(`a template job's inputs no longer read as they were admitted: ${inputs}`)\n }\n const definition = AI_TEMPLATE_SUBJECT_DEFINITIONS[inputs.subject]\n const output = (draft: AiDraftRecord, load?: AiLoadEstimate | null): AiJobOutput => ({\n resource: 'template',\n id: draft.id,\n versionId: null,\n hostId,\n hostSubdomain: draft.hostSubdomain,\n label: draft.name,\n ...(load ? { load } : {}),\n })\n // The switch's answer for this job, else the routing table's (AGL-2942).\n const model = modelFor?.('job.template') ?? aiModelForStep('job.template')\n\n // A run cut off after its draft was written reports that draft, found by the id the job recorded.\n const draftId = aiJobDraftId(job, 'template')\n const written = await readAiDraft(firestore, { kind: 'template', hostId, id: draftId })\n if (written) return aiUnspentOutcome(model, { outputs: [output(written)] })\n\n const [inventory, orgSnapshot, collection] = await Promise.all([\n readInventory(job.orgId, hostId, { firestore }),\n firestore.collection('orgs').doc(job.orgId).get(),\n inputs.collectionId\n ? readCollection(firestore, hostId, inputs.collectionId)\n : Promise.resolve(null),\n ])\n if (inputs.collectionId && !collection) {\n throw new Error(`collection ${inputs.collectionId} left site ${hostId} after its template job was admitted`)\n }\n const org = (orgSnapshot.data() ?? null) as Partial<AglynOrgBilling> | null\n const plan = aiConfirmedPlan(job)\n const creation = aiPlanCreation(plan, 'template')\n const name = aiTemplateDraftName(definition, collection, creation?.name)\n\n // The plan starts from a copy of a template the site has (rule 15): the\n // copy is the draft, and nothing is generated. A source gone since the\n // plan was made is built from the brief instead, and one that cannot keep\n // the plan's tokens is built FROM (AGL-3143 §14): copying it could only end\n // in the review below. A source this step cannot read still copies.\n const source = creation?.duplicateOf\n ? inventory.templates.find((template) => template.id === creation.duplicateOf)\n : undefined\n const sourceReview = source\n ? await aiCopiedTemplateReview(firestore, { hostId, id: source.id, name: source.name, plan, definition })\n : null\n const sourceShort = Boolean(sourceReview?.findings.length)\n const sourceLines =\n source && sourceShort\n ? aiCopySourceLines(\n 'template',\n source.name,\n (await readAiTemplateDraftNodes(firestore, { hostId, id: source.id })) as Record<string, unknown> | null,\n )\n : []\n if (creation?.duplicateOf && source && !sourceShort) {\n const copy = await duplicate('template', {\n orgId: job.orgId,\n hostId,\n sourceId: creation.duplicateOf,\n name,\n uid: job.createdBy,\n org: org as Record<string, unknown> | null,\n // The same claim the page step makes (AGL-3024): a copy core mints is\n // written under core's own id, never `draftId`, so a step that runs\n // again — a pass resumed, a run cut off after the copy — would mint a\n // second. The job's recorded id makes core replay the one it made.\n attemptKey: draftId,\n })\n if (copy.ok) {\n const hostSubdomain = await aiSiteSubdomain(firestore, hostId)\n // The copy is reported with what the plan promised of it read back\n // (AGL-3024): this branch generates nothing, so nothing else can.\n const review = await aiCopiedTemplateReview(firestore, {\n hostId,\n id: copy.id,\n name: copy.name,\n plan,\n definition,\n })\n if (review) return aiUnspentOutcome(model, { review })\n return aiUnspentOutcome(model, {\n outputs: [output({ id: copy.id, versionId: null, name: copy.name, hostSubdomain })],\n })\n }\n if (copy.ok === false && copy.status === 403) {\n return aiUnspentOutcome(model, { review: aiLimitReview(copy.error) })\n }\n }\n\n const allowance = await aiDraftAllowanceRefusal(firestore, { kind: 'template', hostId, org })\n if (allowance) return aiUnspentOutcome(model, { review: aiLimitReview(allowance) })\n\n const result = await runValidatedGeneration('template', {\n step: 'job.template',\n model,\n instructions: [...AI_JOB_TEMPLATE_INSTRUCTIONS, examples()],\n inventory,\n messages: [\n {\n role: 'user',\n content: aiJobTemplatePrompt(job, definition, collection, plan, name, sourceLines),\n },\n ],\n tool: aiDoctrineTreeTool('template'),\n maxTokens: AI_JOB_TEMPLATE_STEP_BUDGET.maxTokens(model),\n ...(AI_ROUTING_TABLE['job.template'].thinking ? { thinking: AI_ROUTING_TABLE['job.template'].thinking } : {}),\n ...(AI_ROUTING_TABLE['job.template'].effort ? { effort: AI_ROUTING_TABLE['job.template'].effort } : {}),\n context: { bindingTokens: aiTemplateAddressTokens(definition) },\n // Rule 8's binding check, and the confirmed plan kept in both halves: a\n // token the plan promised that the template does not bind (AGL-3143\n // §11), and a component the plan reuses that it does not place. The\n // layout and component doors hold their builds to that reuse; the\n // template door did not, so it could drop a promised component and\n // still answer `Done` (AGL-3024).\n extend: (tree) => [\n ...aiTemplateBindingCheck(definition)(tree),\n ...aiPlanTemplateTokenViolations(plan, definition, {\n rootId: tree.rootId,\n nodes: tree.nodes as unknown as Record<string, AiDoctrineNode>,\n }),\n ...aiPlanReuseViolations(inventory, plan, aiPlacedComponentIds(tree), 'template'),\n ],\n ...(signal ? { signal } : {}),\n })\n const spent = aiGenerationSpent(result)\n if (result.status === 'refused') return { ...spent, refused: true }\n if (result.status === 'needs_input') return { ...spent, review: aiDoctrineReview(result) }\n\n // Never written once the job is canceled (AGL-3616).\n throwIfAiJobCanceled(signal)\n const draft = await writeAiDraft(firestore, {\n kind: 'template',\n hostId,\n id: draftId,\n uid: job.createdBy,\n org,\n name,\n nodes: result.value.nodes,\n slug: aiTemplateDraftSlug(definition, collection),\n now,\n })\n if (draft.ok === false) {\n if (draft.status === 404) throw new Error(`site ${hostId} vanished while its template was generated`)\n return { ...spent, review: aiLimitReview(draft.error) }\n }\n return { ...spent, outputs: [output(draft, result.value.load)] }\n }\n}\n\n/** The template step, building the components and forms its plan creates before the template (AGL-3143 §15). */\nexport const runAiJobTemplateStep = aiBuildingPlanCreations('template', 'job.template', createAiJobTemplateStep())\n\n/** Registers the template step and the check a template job passes before it is created or resumed. */\nexport function registerAiTemplateJob(): void {\n registerAiJobStep('template', runAiJobTemplateStep, {\n minimumMs: AI_JOB_TEMPLATE_STEP_MINIMUM_MS,\n minimumMsFor: aiPlanCreationsRunMinimumMs('template'),\n })\n registerAiJobAdmission('template', createAiTemplateJobAdmission())\n}\n"],"names":["throwIfAiJobCanceled","hostCollectionKind","normalizeScreenSlug","duplicateResource","CANVAS_ROOT_ELEMENT_ID","AI_TEMPLATE_SUBJECT_DEFINITIONS","AI_TEMPLATE_SUBJECTS","aiBindingTokensIn","aiTemplateAddressTokens","isAiAddressToken","isAiUnreadToken","parseAiTemplateJobInputs","AI_STEP_TIERS","AI_ROUTING_TABLE","aiModelForStep","aiDoctrineTreeTool","runValidatedGeneration","walkTree","AI_PALETTE","aiTemplateExamplesSystemBlock","readSiteInventory","registerAiJobAdmission","aiJobDraftId","aiDraftAdmissionRefusal","aiDraftAllowanceRefusal","aiSiteSubdomain","readAiDraft","readAiTemplateDraftNodes","writeAiDraft","aiConfirmedPlan","aiCopySourceLines","aiDoctrineReview","aiGenerationSpent","aiJobBriefLine","aiLimitReview","aiModelNodeIds","aiPlacedComponentIds","aiPlanCreation","aiPlanReferenceLines","aiPlanReuseViolations","aiUnspentOutcome","aiPlannedTemplateTokens","aiPlanTemplateTokenViolations","aiBuildingPlanCreations","aiJobPlanCreationsRefusal","aiPlanCreationsRunMinimumMs","aiJobStepBudget","registerAiJobStep","AI_JOB_TEMPLATE_MAX_TOKENS","maxTokens","AI_JOB_TEMPLATE_STEP_BUDGET","tier","AI_JOB_TEMPLATE_STEP_MINIMUM_MS","minimumMs","AI_JOB_TEMPLATE_INSTRUCTIONS","text","join","readAiTemplateCollection","firestore","hostId","collectionId","snapshot","collection","doc","get","exists","data","value","trim","slug","id","name","aiTemplateDraftName","definition","planned","defaultName","aiTemplateDraftSlug","subject","aiJobTemplatePrompt","job","plan","source","of","noun","titleToken","tokens","map","entry","token","label","blocks","length","BLOCK_SUBJECTS","Map","flatMap","block","stringsIn","out","push","Array","isArray","inner","Object","values","isPageTitle","node","componentId","element","props","String","printedTokens","roles","propRoles","entries","key","replace","unread","filter","whole","aiTemplateBindingCheck","allowed","Set","tree","nodes","unknown","unknownAt","foreign","titles","printed","printedAt","titleBound","ancestors","rootId","strays","has","add","shown","owner","inCard","some","ancestor","includes","violations","size","rule","code","message","slice","nodeIds","sourceIds","first","displayName","createAiTemplateJobAdmission","deps","readCollection","context","inputs","status","error","orgId","kind","org","ownCheck","aiCopiedTemplateReview","input","unreadable","stored","reason","findings","createAiJobTemplateStep","readInventory","duplicate","examples","now","signal","modelFor","orgSnapshot","Error","output","draft","load","resource","versionId","hostSubdomain","model","draftId","written","outputs","inventory","Promise","all","resolve","creation","duplicateOf","templates","find","template","undefined","sourceReview","sourceShort","Boolean","sourceLines","copy","sourceId","uid","createdBy","attemptKey","ok","review","allowance","result","step","instructions","messages","role","content","tool","thinking","effort","bindingTokens","extend","spent","refused","runAiJobTemplateStep","registerAiTemplateJob","minimumMsFor"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-template-step.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { throwIfAiJobCanceled } from './ai-job-cancel'\nimport { hostCollectionKind } from '@aglyn/aglyn/app-utils/collection-kind'\nimport { normalizeScreenSlug } from '@aglyn/aglyn/app-utils/screen-route'\nimport type { AglynOrgBilling } from '@aglyn/aglyn/foundation/definitions/org-billing.types'\nimport { duplicateResource } from '@aglyn/tenant-data-admin/server/duplicate-resource'\nimport { CANVAS_ROOT_ELEMENT_ID } from '@aglyn/aglyn/foundation/constants/canvas'\nimport type { AiJob, AiJobOutput, AiJobPlan, AiJobReview } from '../model/ai-jobs.types'\nimport {\n AI_TEMPLATE_SUBJECT_DEFINITIONS,\n AI_TEMPLATE_SUBJECTS,\n aiBindingTokensIn,\n aiTemplateAddressTokens,\n isAiAddressToken,\n isAiUnreadToken,\n parseAiTemplateJobInputs,\n type AiTemplateSubjectDefinition,\n} from '../model/ai-template-subjects'\nimport { AI_STEP_TIERS } from '../providers/catalog'\nimport { AI_ROUTING_TABLE, aiModelForStep } from '../providers/routing'\nimport {\n aiDoctrineTreeTool,\n runValidatedGeneration,\n type AiValidatedTree,\n} from '../runtime/ai-doctrine'\nimport {\n walkTree,\n type AiDoctrineNode,\n type AiDoctrineViolation,\n} from '../runtime/ai-doctrine-validators'\nimport type { AiLoadEstimate } from '../runtime/ai-palette'\nimport { AI_PALETTE } from '../runtime/ai-palette.generated'\nimport type { AiSystemBlock } from '../runtime/ai-runtime'\nimport { aiTemplateExamplesSystemBlock } from '../runtime/ai-template-examples'\nimport { readSiteInventory } from '../runtime/site-inventory'\nimport { registerAiJobAdmission, type AiJobAdmission } from './ai-job-admission'\nimport { aiJobDraftId } from './ai-job-draft-ids'\nimport { aiLayoutMotionSiteOf } from './ai-job-page-language'\nimport { aiWithSiteMotion } from '../layout-language/ai-layout-motion'\nimport {\n aiDraftAdmissionRefusal,\n aiDraftAllowanceRefusal,\n aiSiteSubdomain,\n readAiDraft,\n readAiTemplateDraftNodes,\n writeAiDraft,\n type AiDraftRecord,\n} from './ai-job-drafts'\nimport {\n aiConfirmedPlan,\n aiCopySourceLines,\n aiDoctrineReview,\n aiGenerationSpent,\n aiJobBriefLine,\n aiLimitReview,\n aiModelNodeIds,\n aiPlacedComponentIds,\n aiPlanCreation,\n aiPlanReferenceLines,\n aiPlanReuseViolations,\n aiUnspentOutcome,\n} from './ai-job-generation'\nimport { aiPlannedTemplateTokens, aiPlanTemplateTokenViolations } from './ai-job-plan-conformance'\nimport {\n aiBuildingPlanCreations,\n aiJobPlanCreationsRefusal,\n aiPlanCreationsRunMinimumMs,\n} from './ai-job-plan-creations'\nimport type { AiJobStepRunner } from './ai-job-text-step'\nimport { aiJobStepBudget } from './ai-job-budget'\nimport { registerAiJobStep } from './ai-jobs'\n\n/**\n * The template step (AGL-2909): the generation step of a `template` job, run\n * once a member confirmed the job's plan.\n *\n * It builds ONE page template — the page a site renders once per record: a\n * content collection's entry, a product, an author — through\n * `runValidatedGeneration('template', …)`. Everything that differs per\n * record is a binding token from the subject's vocabulary\n * (`model/ai-template-subjects.ts`), which is the list the besigner's insert\n * picker offers for that page; a link or media prop may hold one of those\n * tokens whole, which the palette validator admits only because this door\n * names them. The door adds one check of its own through `extend` (rule 8):\n * no token the page does not fill, an h1 bound to the subject's title, no\n * block that fills itself only on another subject's page, and no address,\n * slug or timestamp printed in its copy.\n *\n * The model is shown the platform's starter pages as examples, in a cached\n * block (`runtime/ai-template-examples.ts`), and writes against them.\n *\n * The template lands in the site's library as a new draft\n * (`ai-job-drafts.ts`), inert until a member uses it: bound to no\n * collection, set as no product or author page, served nowhere. A plan that\n * starts from a copy of a template the site has gets that copy instead.\n */\n\n/** The longest answer a template may run to; the tree is held to the template budget either way. */\nexport const AI_JOB_TEMPLATE_MAX_TOKENS = AI_ROUTING_TABLE['job.template'].maxTokens\n\n/**\n * The template step's time (AGL-3035, AGL-3036): its two inventory-lookup\n * rounds, its answer and its re-ask at the routing table's ceiling on the tier\n * `job.template` is served from, fitted to what a beat can give a step. It\n * runs on the job beat, never at an inline door, and asks for no more of the\n * ceiling than fits that time on the model it runs.\n */\nexport const AI_JOB_TEMPLATE_STEP_BUDGET = aiJobStepBudget({\n tier: AI_STEP_TIERS['job.template'],\n maxTokens: AI_JOB_TEMPLATE_MAX_TOKENS,\n})\n\n/** The least time one template step needs before it starts. */\nexport const AI_JOB_TEMPLATE_STEP_MINIMUM_MS = AI_JOB_TEMPLATE_STEP_BUDGET.minimumMs\n\n/** The template step's own instructions, cached after the doctrine and before the examples. */\nexport const AI_JOB_TEMPLATE_INSTRUCTIONS: readonly AiSystemBlock[] = [\n {\n text: [\n 'You build one page template: the page a website renders once for each record it keeps, such as each entry of a content collection, each product or each author. Answer with submit_template: the whole page as one flat node map.',\n 'Everything that differs from one record to the next is a binding token, never typed copy. The request names what the page is for, the tokens that page fills and the blocks that fill themselves there; bind only those tokens.',\n 'The page’s one h1 holds the title token the request names. Copy that reads the same on every record, such as a section heading, is written in the site’s voice.',\n 'A link or media prop holds exactly one token from the request’s link and media tokens, such as an Image whose src is {{entry.coverImage}}; its alt text may combine words and tokens. Copy never prints a link or media token, a slug or a timestamp.',\n 'A template carries no third-party player. It plays a video token only when the confirmed plan lists it, in a Video (video) whose src is that token and whose poster is the cover image token.',\n 'The page renders inside the site’s layout, so it carries no header, navigation or footer of its own.',\n ].join('\\n'),\n },\n]\n\n/** A content collection an entry template is for, as the prompt and the draft name it. */\nexport interface AiTemplateCollection {\n id: string\n name: string\n slug: string\n}\n\n/** The content collection by that id on the site, or `null` when there is none. */\nexport async function readAiTemplateCollection(\n firestore: FirebaseFirestore.Firestore,\n hostId: string,\n collectionId: string,\n): Promise<AiTemplateCollection | null> {\n const snapshot = await firestore\n .collection('hosts')\n .doc(hostId)\n .collection('collections')\n .doc(collectionId)\n .get()\n if (!snapshot.exists) return null\n const data = (snapshot.data() ?? {}) as Record<string, unknown>\n if (hostCollectionKind(data) !== 'content') return null\n const text = (value: unknown) => (typeof value === 'string' ? value.trim() : '')\n const slug = text(data['slug'])\n return {\n id: collectionId,\n name: text(data['displayName']) || text(data['name']) || slug || collectionId,\n slug,\n }\n}\n\n/** The template's name: the plan's, else the collection's or the subject's. */\nexport function aiTemplateDraftName(\n definition: AiTemplateSubjectDefinition,\n collection: AiTemplateCollection | null,\n planned: string | null | undefined,\n): string {\n return planned || (collection ? `${collection.name} entry template` : definition.defaultName)\n}\n\n/** The address Use template offers for the page it creates. */\nexport function aiTemplateDraftSlug(\n definition: AiTemplateSubjectDefinition,\n collection: AiTemplateCollection | null,\n): string | null {\n return (\n normalizeScreenSlug(\n collection\n ? `${collection.slug || collection.id}-entry-template`\n : `${definition.subject}-page-template`,\n ) ?? null\n )\n}\n\n/** The template job as the generation's user turn, with the template a short copy would have started from (AGL-3143 §14). */\nexport function aiJobTemplatePrompt(\n job: Pick<AiJob, 'brief'>,\n definition: AiTemplateSubjectDefinition,\n collection: AiTemplateCollection | null,\n plan: AiJobPlan | null,\n name: string,\n source: readonly string[] = [],\n): string {\n const of = collection\n ? ` of the \"${collection.name}\" collection${collection.slug ? ` (/${collection.slug})` : ''}`\n : ''\n return [\n `Template name: ${name}`,\n `The page is for ${definition.noun}${of}.`,\n `Title token: ${definition.titleToken}`,\n `Tokens this page fills: ${definition.tokens.map((entry) => `${entry.token} ${entry.label}`).join('; ')}`,\n `Link and media tokens: ${aiTemplateAddressTokens(definition).join(', ')}`,\n definition.blocks.length\n ? `Blocks that fill themselves on this page: ${definition.blocks.join(', ')}`\n : 'No block fills itself on this page: bind the tokens.',\n aiJobBriefLine(job),\n ...aiPlanReferenceLines(plan),\n ...source,\n ].join('\\n')\n}\n\n/** Which subject's page each self-filling block belongs to. */\nconst BLOCK_SUBJECTS = new Map<string, AiTemplateSubjectDefinition>(\n AI_TEMPLATE_SUBJECTS.flatMap((subject) =>\n AI_TEMPLATE_SUBJECT_DEFINITIONS[subject].blocks.map(\n (block) => [block, AI_TEMPLATE_SUBJECT_DEFINITIONS[subject]] as const,\n ),\n ),\n)\n\n/** Every string a prop value holds, however deeply. */\nfunction stringsIn(value: unknown, out: string[] = []): string[] {\n if (typeof value === 'string') out.push(value)\n else if (Array.isArray(value)) for (const inner of value) stringsIn(inner, out)\n else if (value && typeof value === 'object') {\n for (const inner of Object.values(value as Record<string, unknown>)) stringsIn(inner, out)\n }\n return out\n}\n\n/** The page's title: a Typography that renders as an h1. */\nfunction isPageTitle(node: AiDoctrineNode): boolean {\n if (node.componentId !== 'muiTypography') return false\n const element = typeof node.props?.['component'] === 'string' ? String(node.props['component']) : ''\n return (element || String(node.props?.['variant'] ?? '')) === 'h1'\n}\n\n/**\n * The tokens a prop prints for a reader that no reader reads: a link or\n * picture token, a slug or a timestamp, in copy (AGL-3143 §16). Copy is a\n * prop the palette gives the text role, read as words where it holds more\n * than one token or is the element's children. A block handed one token\n * whole reads it as a value, as Entry Meta's avatar takes the author's\n * portrait and a Video's publication date takes the timestamp, and a\n * markdown link's target is followed, not read.\n */\nfunction printedTokens(node: AiDoctrineNode): string[] {\n const roles = AI_PALETTE[node.componentId]?.propRoles ?? {}\n return Object.entries(node.props ?? {}).flatMap(([key, value]) => {\n if (typeof value !== 'string' || roles[key] !== 'text') return []\n const tokens = aiBindingTokensIn(value.replace(/\\]\\([^)]*\\)/g, ']'))\n const unread = tokens.filter((token) => isAiAddressToken(token) || isAiUnreadToken(token))\n const whole = tokens.length === 1 && value.trim() === tokens[0]\n return unread.length && (key === 'children' || !whole) ? unread : []\n })\n}\n\n/**\n * The template door's check on a tree the doctrine admitted (rule 8): every\n * token is one the page fills, the h1 binds the subject's title, no block\n * that fills itself on another subject's page is placed — except inside a\n * Collection Entries card, where each fills from the card's own entry — and\n * no copy prints an address, a slug or a timestamp.\n */\nexport function aiTemplateBindingCheck(\n definition: AiTemplateSubjectDefinition,\n): (tree: AiValidatedTree) => AiDoctrineViolation[] {\n const allowed = new Set(definition.tokens.map((entry) => entry.token))\n return (tree) => {\n const nodes = tree.nodes as unknown as Record<string, AiDoctrineNode>\n const unknown = new Set<string>()\n const unknownAt: string[] = []\n const foreign: Array<{ id: string; componentId: string; owner: AiTemplateSubjectDefinition }> = []\n const titles: string[] = []\n const printed = new Set<string>()\n const printedAt: string[] = []\n let titleBound = false\n for (const { id, node, ancestors } of walkTree({ rootId: tree.rootId, nodes })) {\n const tokens = stringsIn(node.props).flatMap(aiBindingTokensIn)\n const strays = tokens.filter((token) => !allowed.has(token))\n if (strays.length) {\n for (const token of strays) unknown.add(token)\n unknownAt.push(id)\n }\n const shown = printedTokens(node).filter((token) => allowed.has(token))\n if (shown.length) {\n for (const token of shown) printed.add(token)\n printedAt.push(id)\n }\n const owner = BLOCK_SUBJECTS.get(node.componentId)\n const inCard = ancestors.some((ancestor) => nodes[ancestor]?.componentId === 'collectionEntries')\n if (owner && owner.subject !== definition.subject && !inCard) {\n foreign.push({ id, componentId: node.componentId, owner })\n }\n if (isPageTitle(node)) {\n titles.push(id)\n if (tokens.includes(definition.titleToken)) titleBound = true\n }\n }\n const violations: AiDoctrineViolation[] = []\n if (unknown.size) {\n violations.push({\n rule: 8,\n code: 'unknown-binding',\n message: `This binds ${[...unknown].slice(0, 3).join(', ')}, which ${definition.noun}’s page does not fill. Bind only the tokens listed for this page.`,\n nodeIds: aiModelNodeIds(unknownAt, tree.sourceIds),\n })\n }\n if (printed.size) {\n violations.push({\n rule: 8,\n code: 'printed-token',\n message: `This prints ${[...printed].slice(0, 3).join(', ')} in the page's copy, where a reader sees an address, a slug or a timestamp. A link or picture token goes whole in a link or media prop, and a slug or a timestamp stays off the page.`,\n nodeIds: aiModelNodeIds(printedAt, tree.sourceIds),\n })\n }\n // The doctrine already refuses a page with no h1 or with several.\n if (titles.length === 1 && !titleBound) {\n violations.push({\n rule: 8,\n code: 'typed-title',\n message: `The page title is typed, so every record would show the same one. Put ${definition.titleToken} in the h1.`,\n nodeIds: aiModelNodeIds(titles, tree.sourceIds),\n })\n }\n if (foreign.length) {\n const [first] = foreign\n violations.push({\n rule: 8,\n code: 'foreign-block',\n message: `The ${AI_PALETTE[first.componentId]?.displayName ?? first.componentId} block fills itself only on ${first.owner.noun}’s page, and this template is for ${definition.noun}. Remove it.`,\n nodeIds: aiModelNodeIds(\n foreign.map((entry) => entry.id),\n tree.sourceIds,\n ),\n })\n }\n return violations\n }\n}\n\nexport interface AiTemplateJobAdmissionDeps {\n readCollection?: typeof readAiTemplateCollection\n}\n\n/**\n * A template job is admitted with inputs that say what the page is for, for\n * a site of the job's own org that holds the collection it names, with a\n * template to spare.\n */\nexport function createAiTemplateJobAdmission(deps: AiTemplateJobAdmissionDeps = {}): AiJobAdmission {\n const readCollection = deps.readCollection ?? readAiTemplateCollection\n return async (context) => {\n const inputs = parseAiTemplateJobInputs(context.inputs)\n if (typeof inputs === 'string') return { status: 400, error: inputs }\n return aiDraftAdmissionRefusal(context.firestore, {\n orgId: context.orgId,\n hostId: context.hostId,\n kind: 'template',\n org: context.org,\n ownCheck: async (hostId) =>\n inputs.collectionId && !(await readCollection(context.firestore, hostId, inputs.collectionId))\n ? { status: 404, error: 'That content collection is not on this site' }\n : aiJobPlanCreationsRefusal('template', context, hostId),\n })\n }\n}\n\nexport interface AiJobTemplateStepDeps {\n /** The inventory reader; specs hand in a fake. */\n readInventory?: typeof readSiteInventory\n /** The platform's duplicate module, for a plan that starts from a copy. */\n duplicate?: typeof duplicateResource\n readCollection?: typeof readAiTemplateCollection\n /** The starter examples block; built from the starters once per process otherwise. */\n examples?: () => AiSystemBlock\n}\n\n/**\n * The binding tokens the confirmed plan gives the template, against the\n * template a COPY actually produced (AGL-3024); `null` when the copy keeps\n * the plan, or when the plan promised no token to keep.\n *\n * The same hole as `aiCopiedLayoutReview` and `aiCopiedPageReview`, on the\n * third kind that can start from a duplicate. The generate path already holds\n * a built template to `aiPlanTemplateTokenViolations`; the copy path reached\n * none of it, so a plan reading \"duplicate it and bind the author's photo\"\n * got the duplicate and reported Done.\n *\n * A copy that cannot be read back settles nothing, and a promise this step\n * cannot settle is reviewed by a person rather than reported as kept.\n */\nexport async function aiCopiedTemplateReview(\n firestore: FirebaseFirestore.Firestore,\n input: {\n hostId: string\n id: string\n name: string\n plan: AiJobPlan | null\n definition: AiTemplateSubjectDefinition\n },\n): Promise<AiJobReview | null> {\n const { tokens, unreadable } = aiPlannedTemplateTokens(input.plan, input.definition)\n if (!tokens.length && !unreadable.length) return null\n const stored = await readAiTemplateDraftNodes(firestore, {\n hostId: input.hostId,\n id: input.id,\n })\n const nodes = (stored ?? {}) as unknown as Record<string, AiDoctrineNode>\n if (!nodes[CANVAS_ROOT_ELEMENT_ID]) {\n return {\n reason: 'doctrine',\n message: `\"${input.name}\" was copied from the template the plan names, and this job could not read the copy back to check it shows what the plan gives it. Open the template and check it before you apply it.`,\n findings: [],\n }\n }\n const violations = aiPlanTemplateTokenViolations(input.plan, input.definition, {\n rootId: CANVAS_ROOT_ELEMENT_ID,\n nodes,\n })\n if (!violations.length) return null\n return aiDoctrineReview({\n message: `\"${input.name}\" is a copy of the template the plan names, and a copy is all it is: ${violations[0].message}`,\n violations,\n })\n}\n\nexport function createAiJobTemplateStep(deps: AiJobTemplateStepDeps = {}): AiJobStepRunner {\n const readInventory = deps.readInventory ?? readSiteInventory\n const duplicate = deps.duplicate ?? duplicateResource\n const readCollection = deps.readCollection ?? readAiTemplateCollection\n const examples = deps.examples ?? aiTemplateExamplesSystemBlock\n return async ({ job, now, signal, firestore, modelFor }) => {\n const hostId = job.hostId\n if (!hostId) throw new Error('a template job names no site, and its admission refuses one')\n const inputs = parseAiTemplateJobInputs(job.inputs)\n if (typeof inputs === 'string') {\n throw new Error(`a template job's inputs no longer read as they were admitted: ${inputs}`)\n }\n const definition = AI_TEMPLATE_SUBJECT_DEFINITIONS[inputs.subject]\n const output = (draft: AiDraftRecord, load?: AiLoadEstimate | null): AiJobOutput => ({\n resource: 'template',\n id: draft.id,\n versionId: null,\n hostId,\n hostSubdomain: draft.hostSubdomain,\n label: draft.name,\n ...(load ? { load } : {}),\n })\n // The switch's answer for this job, else the routing table's (AGL-2942).\n const model = modelFor?.('job.template') ?? aiModelForStep('job.template')\n\n // A run cut off after its draft was written reports that draft, found by the id the job recorded.\n const draftId = aiJobDraftId(job, 'template')\n const written = await readAiDraft(firestore, { kind: 'template', hostId, id: draftId })\n if (written) return aiUnspentOutcome(model, { outputs: [output(written)] })\n\n const [inventory, orgSnapshot, collection] = await Promise.all([\n readInventory(job.orgId, hostId, { firestore }),\n firestore.collection('orgs').doc(job.orgId).get(),\n inputs.collectionId\n ? readCollection(firestore, hostId, inputs.collectionId)\n : Promise.resolve(null),\n ])\n if (inputs.collectionId && !collection) {\n throw new Error(`collection ${inputs.collectionId} left site ${hostId} after its template job was admitted`)\n }\n const org = (orgSnapshot.data() ?? null) as Partial<AglynOrgBilling> | null\n const plan = aiConfirmedPlan(job)\n const creation = aiPlanCreation(plan, 'template')\n const name = aiTemplateDraftName(definition, collection, creation?.name)\n\n // The plan starts from a copy of a template the site has (rule 15): the\n // copy is the draft, and nothing is generated. A source gone since the\n // plan was made is built from the brief instead, and one that cannot keep\n // the plan's tokens is built FROM (AGL-3143 §14): copying it could only end\n // in the review below. A source this step cannot read still copies.\n const source = creation?.duplicateOf\n ? inventory.templates.find((template) => template.id === creation.duplicateOf)\n : undefined\n const sourceReview = source\n ? await aiCopiedTemplateReview(firestore, { hostId, id: source.id, name: source.name, plan, definition })\n : null\n const sourceShort = Boolean(sourceReview?.findings.length)\n const sourceLines =\n source && sourceShort\n ? aiCopySourceLines(\n 'template',\n source.name,\n (await readAiTemplateDraftNodes(firestore, { hostId, id: source.id })) as Record<string, unknown> | null,\n )\n : []\n if (creation?.duplicateOf && source && !sourceShort) {\n const copy = await duplicate('template', {\n orgId: job.orgId,\n hostId,\n sourceId: creation.duplicateOf,\n name,\n uid: job.createdBy,\n org: org as Record<string, unknown> | null,\n // The same claim the page step makes (AGL-3024): a copy core mints is\n // written under core's own id, never `draftId`, so a step that runs\n // again — a pass resumed, a run cut off after the copy — would mint a\n // second. The job's recorded id makes core replay the one it made.\n attemptKey: draftId,\n })\n if (copy.ok) {\n const hostSubdomain = await aiSiteSubdomain(firestore, hostId)\n // The copy is reported with what the plan promised of it read back\n // (AGL-3024): this branch generates nothing, so nothing else can.\n const review = await aiCopiedTemplateReview(firestore, {\n hostId,\n id: copy.id,\n name: copy.name,\n plan,\n definition,\n })\n if (review) return aiUnspentOutcome(model, { review })\n return aiUnspentOutcome(model, {\n outputs: [output({ id: copy.id, versionId: null, name: copy.name, hostSubdomain })],\n })\n }\n if (copy.ok === false && copy.status === 403) {\n return aiUnspentOutcome(model, { review: aiLimitReview(copy.error) })\n }\n }\n\n const allowance = await aiDraftAllowanceRefusal(firestore, { kind: 'template', hostId, org })\n if (allowance) return aiUnspentOutcome(model, { review: aiLimitReview(allowance) })\n\n const result = await runValidatedGeneration('template', {\n step: 'job.template',\n model,\n instructions: [...AI_JOB_TEMPLATE_INSTRUCTIONS, examples()],\n inventory,\n messages: [\n {\n role: 'user',\n content: aiJobTemplatePrompt(job, definition, collection, plan, name, sourceLines),\n },\n ],\n tool: aiDoctrineTreeTool('template'),\n maxTokens: AI_JOB_TEMPLATE_STEP_BUDGET.maxTokens(model),\n ...(AI_ROUTING_TABLE['job.template'].thinking ? { thinking: AI_ROUTING_TABLE['job.template'].thinking } : {}),\n ...(AI_ROUTING_TABLE['job.template'].effort ? { effort: AI_ROUTING_TABLE['job.template'].effort } : {}),\n context: { bindingTokens: aiTemplateAddressTokens(definition) },\n // Rule 8's binding check, and the confirmed plan kept in both halves: a\n // token the plan promised that the template does not bind (AGL-3143\n // §11), and a component the plan reuses that it does not place. The\n // layout and component doors hold their builds to that reuse; the\n // template door did not, so it could drop a promised component and\n // still answer `Done` (AGL-3024).\n extend: (tree) => [\n ...aiTemplateBindingCheck(definition)(tree),\n ...aiPlanTemplateTokenViolations(plan, definition, {\n rootId: tree.rootId,\n nodes: tree.nodes as unknown as Record<string, AiDoctrineNode>,\n }),\n ...aiPlanReuseViolations(inventory, plan, aiPlacedComponentIds(tree), 'template'),\n ],\n ...(signal ? { signal } : {}),\n })\n const spent = aiGenerationSpent(result)\n if (result.status === 'refused') return { ...spent, refused: true }\n if (result.status === 'needs_input') return { ...spent, review: aiDoctrineReview(result) }\n\n // Never written once the job is canceled (AGL-3616).\n throwIfAiJobCanceled(signal)\n const draft = await writeAiDraft(firestore, {\n kind: 'template',\n hostId,\n id: draftId,\n uid: job.createdBy,\n org,\n name,\n // The site's entrance motion, as its pages have (AGL-3838): a post or a\n // record opens the way the rest of the site does.\n nodes: aiWithSiteMotion(result.value.nodes, aiLayoutMotionSiteOf(job, ''), { rootId: result.value.rootId }),\n slug: aiTemplateDraftSlug(definition, collection),\n now,\n })\n if (draft.ok === false) {\n if (draft.status === 404) throw new Error(`site ${hostId} vanished while its template was generated`)\n return { ...spent, review: aiLimitReview(draft.error) }\n }\n return { ...spent, outputs: [output(draft, result.value.load)] }\n }\n}\n\n/** The template step, building the components and forms its plan creates before the template (AGL-3143 §15). */\nexport const runAiJobTemplateStep = aiBuildingPlanCreations('template', 'job.template', createAiJobTemplateStep())\n\n/** Registers the template step and the check a template job passes before it is created or resumed. */\nexport function registerAiTemplateJob(): void {\n registerAiJobStep('template', runAiJobTemplateStep, {\n minimumMs: AI_JOB_TEMPLATE_STEP_MINIMUM_MS,\n minimumMsFor: aiPlanCreationsRunMinimumMs('template'),\n })\n registerAiJobAdmission('template', createAiTemplateJobAdmission())\n}\n"],"names":["throwIfAiJobCanceled","hostCollectionKind","normalizeScreenSlug","duplicateResource","CANVAS_ROOT_ELEMENT_ID","AI_TEMPLATE_SUBJECT_DEFINITIONS","AI_TEMPLATE_SUBJECTS","aiBindingTokensIn","aiTemplateAddressTokens","isAiAddressToken","isAiUnreadToken","parseAiTemplateJobInputs","AI_STEP_TIERS","AI_ROUTING_TABLE","aiModelForStep","aiDoctrineTreeTool","runValidatedGeneration","walkTree","AI_PALETTE","aiTemplateExamplesSystemBlock","readSiteInventory","registerAiJobAdmission","aiJobDraftId","aiLayoutMotionSiteOf","aiWithSiteMotion","aiDraftAdmissionRefusal","aiDraftAllowanceRefusal","aiSiteSubdomain","readAiDraft","readAiTemplateDraftNodes","writeAiDraft","aiConfirmedPlan","aiCopySourceLines","aiDoctrineReview","aiGenerationSpent","aiJobBriefLine","aiLimitReview","aiModelNodeIds","aiPlacedComponentIds","aiPlanCreation","aiPlanReferenceLines","aiPlanReuseViolations","aiUnspentOutcome","aiPlannedTemplateTokens","aiPlanTemplateTokenViolations","aiBuildingPlanCreations","aiJobPlanCreationsRefusal","aiPlanCreationsRunMinimumMs","aiJobStepBudget","registerAiJobStep","AI_JOB_TEMPLATE_MAX_TOKENS","maxTokens","AI_JOB_TEMPLATE_STEP_BUDGET","tier","AI_JOB_TEMPLATE_STEP_MINIMUM_MS","minimumMs","AI_JOB_TEMPLATE_INSTRUCTIONS","text","join","readAiTemplateCollection","firestore","hostId","collectionId","snapshot","collection","doc","get","exists","data","value","trim","slug","id","name","aiTemplateDraftName","definition","planned","defaultName","aiTemplateDraftSlug","subject","aiJobTemplatePrompt","job","plan","source","of","noun","titleToken","tokens","map","entry","token","label","blocks","length","BLOCK_SUBJECTS","Map","flatMap","block","stringsIn","out","push","Array","isArray","inner","Object","values","isPageTitle","node","componentId","element","props","String","printedTokens","roles","propRoles","entries","key","replace","unread","filter","whole","aiTemplateBindingCheck","allowed","Set","tree","nodes","unknown","unknownAt","foreign","titles","printed","printedAt","titleBound","ancestors","rootId","strays","has","add","shown","owner","inCard","some","ancestor","includes","violations","size","rule","code","message","slice","nodeIds","sourceIds","first","displayName","createAiTemplateJobAdmission","deps","readCollection","context","inputs","status","error","orgId","kind","org","ownCheck","aiCopiedTemplateReview","input","unreadable","stored","reason","findings","createAiJobTemplateStep","readInventory","duplicate","examples","now","signal","modelFor","orgSnapshot","Error","output","draft","load","resource","versionId","hostSubdomain","model","draftId","written","outputs","inventory","Promise","all","resolve","creation","duplicateOf","templates","find","template","undefined","sourceReview","sourceShort","Boolean","sourceLines","copy","sourceId","uid","createdBy","attemptKey","ok","review","allowance","result","step","instructions","messages","role","content","tool","thinking","effort","bindingTokens","extend","spent","refused","runAiJobTemplateStep","registerAiTemplateJob","minimumMsFor"],"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;
|