@aglyn/plugins-ai 1.0.0-beta.256 → 1.0.0-beta.258

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/package.json +17 -17
  2. package/src/lib/components/assist-panel.component.js +21 -8
  3. package/src/lib/components/assist-panel.component.js.map +1 -1
  4. package/src/lib/insights/ai-figure-readers.js +15 -8
  5. package/src/lib/insights/ai-figure-readers.js.map +1 -1
  6. package/src/lib/jobs/ai-job-build-step.d.ts +9 -0
  7. package/src/lib/jobs/ai-job-build-step.js +19 -3
  8. package/src/lib/jobs/ai-job-build-step.js.map +1 -1
  9. package/src/lib/jobs/ai-job-form-step.js +3 -3
  10. package/src/lib/jobs/ai-job-form-step.js.map +1 -1
  11. package/src/lib/jobs/ai-job-insight-step.js +1 -1
  12. package/src/lib/jobs/ai-job-insight-step.js.map +1 -1
  13. package/src/lib/jobs/ai-job-page-language.d.ts +19 -0
  14. package/src/lib/jobs/ai-job-page-language.js +24 -4
  15. package/src/lib/jobs/ai-job-page-language.js.map +1 -1
  16. package/src/lib/jobs/ai-job-page-sections.js +12 -1
  17. package/src/lib/jobs/ai-job-page-sections.js.map +1 -1
  18. package/src/lib/jobs/ai-job-page-step.d.ts +14 -0
  19. package/src/lib/jobs/ai-job-page-step.js +69 -20
  20. package/src/lib/jobs/ai-job-page-step.js.map +1 -1
  21. package/src/lib/jobs/ai-job-seo-step.js +3 -2
  22. package/src/lib/jobs/ai-job-seo-step.js.map +1 -1
  23. package/src/lib/jobs/ai-layout-stock-photos.d.ts +10 -5
  24. package/src/lib/jobs/ai-layout-stock-photos.js +89 -7
  25. package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
  26. package/src/lib/jobs/fixtures/ai-stock-photo-copper-kettle-recordings.d.ts +21 -0
  27. package/src/lib/jobs/fixtures/ai-stock-photo-copper-kettle-recordings.js +126 -0
  28. package/src/lib/jobs/fixtures/ai-stock-photo-copper-kettle-recordings.js.map +1 -0
  29. package/src/lib/layout-language/ai-layout-compiler.js +52 -52
  30. package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
  31. package/src/lib/layout-language/ai-layout-copy.d.ts +9 -5
  32. package/src/lib/layout-language/ai-layout-copy.js +22 -14
  33. package/src/lib/layout-language/ai-layout-copy.js.map +1 -1
  34. package/src/lib/layout-language/ai-layout-frame.js +4 -4
  35. package/src/lib/layout-language/ai-layout-frame.js.map +1 -1
  36. package/src/lib/layout-language/ai-layout-pictures.d.ts +44 -15
  37. package/src/lib/layout-language/ai-layout-pictures.js +29 -8
  38. package/src/lib/layout-language/ai-layout-pictures.js.map +1 -1
  39. package/src/lib/model/ai-build-job.js +14 -0
  40. package/src/lib/model/ai-build-job.js.map +1 -1
  41. package/src/lib/model/ai-host-publish-context.d.ts +6 -0
  42. package/src/lib/model/ai-host-publish-context.js +15 -2
  43. package/src/lib/model/ai-host-publish-context.js.map +1 -1
  44. package/src/lib/model/assist-follow-up.d.ts +35 -0
  45. package/src/lib/model/assist-follow-up.js +44 -4
  46. package/src/lib/model/assist-follow-up.js.map +1 -1
  47. package/src/lib/providers/anthropic.js +17 -4
  48. package/src/lib/providers/anthropic.js.map +1 -1
  49. package/src/lib/providers/cache-write-hour.d.ts +37 -0
  50. package/src/lib/providers/cache-write-hour.js +46 -0
  51. package/src/lib/providers/cache-write-hour.js.map +1 -0
  52. package/src/lib/providers/contract.d.ts +16 -3
  53. package/src/lib/providers/contract.js.map +1 -1
  54. package/src/lib/providers/routing.d.ts +13 -0
  55. package/src/lib/providers/routing.js +27 -11
  56. package/src/lib/providers/routing.js.map +1 -1
  57. package/src/lib/runtime/ai-dev-env.d.ts +13 -3
  58. package/src/lib/runtime/ai-dev-env.js +17 -3
  59. package/src/lib/runtime/ai-dev-env.js.map +1 -1
  60. package/src/lib/runtime/ai-dev-replay.js +4 -2
  61. package/src/lib/runtime/ai-dev-replay.js.map +1 -1
  62. package/src/lib/runtime/ai-doctrine-validators.d.ts +41 -1
  63. package/src/lib/runtime/ai-doctrine-validators.js +263 -2
  64. package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
  65. package/src/lib/runtime/ai-doctrine.js +7 -1
  66. package/src/lib/runtime/ai-doctrine.js.map +1 -1
  67. package/src/lib/runtime/ai-eval-live.js +1 -1
  68. package/src/lib/runtime/ai-eval-live.js.map +1 -1
  69. package/src/lib/runtime/ai-eval.d.ts +1 -1
  70. package/src/lib/runtime/ai-eval.js +18 -1
  71. package/src/lib/runtime/ai-eval.js.map +1 -1
  72. package/src/lib/runtime/ai-palette.generated.js +162 -2
  73. package/src/lib/runtime/ai-palette.generated.js.map +1 -1
  74. package/src/lib/runtime/ai-placeholder-tokens.d.ts +70 -0
  75. package/src/lib/runtime/ai-placeholder-tokens.js +169 -0
  76. package/src/lib/runtime/ai-placeholder-tokens.js.map +1 -0
  77. package/src/lib/runtime/ai-repeated-items.js +5 -2
  78. package/src/lib/runtime/ai-repeated-items.js.map +1 -1
  79. package/src/lib/runtime/ai-runtime.js +2 -3
  80. package/src/lib/runtime/ai-runtime.js.map +1 -1
  81. package/src/lib/server/assist-chat.js +8 -3
  82. package/src/lib/server/assist-chat.js.map +1 -1
  83. package/src/lib/server/assist-view-context.d.ts +5 -1
  84. package/src/lib/server/assist-view-context.js +8 -3
  85. package/src/lib/server/assist-view-context.js.map +1 -1
  86. package/src/lib/usage/assist-usage.d.ts +2 -0
  87. package/src/lib/usage/assist-usage.js +8 -3
  88. package/src/lib/usage/assist-usage.js.map +1 -1
@@ -15,6 +15,7 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
15
15
  * See the License for the specific language governing permissions and
16
16
  * limitations under the License.
17
17
  */ import { DEFAULT_SITE_IMAGES } from "@aglyn/aglyn/app-utils/default-site";
18
+ import { aiLayoutLabel, aiLayoutSpeaksOtherThanEnglish } from "./ai-layout-labels.js";
18
19
  /** Words that say a picture shows people, or a section is about them. */ const PEOPLE = /\b(about|team|story|owner|owners|founder|founders|staff|crew|people|person|portrait|meet|family|families|who we are|our (?:team|crew|people|story)|therapist|counsel(?:l)?or|coach|doctor|dentist|stylist|trainer|smil(?:e|es|ing)|headshot)\b/i;
19
20
  /**
20
21
  * An `aspectRatio` (`'16 / 9'`) as a number, or `null` where none is named.
@@ -109,7 +110,18 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
109
110
  visit(rootId, null, -1);
110
111
  return slots;
111
112
  }
112
- /** The starter photos, by name. */ export const AI_LAYOUT_STARTER_PHOTOS = DEFAULT_SITE_IMAGES;
113
+ /** The starter photos, by name, each with the words that describe it. */ export const AI_LAYOUT_STARTER_PHOTOS = DEFAULT_SITE_IMAGES;
114
+ /**
115
+ * A source's answer for a slot the library answered and nothing in it fits
116
+ * (AGL-3831): the slot keeps its no-photo treatment rather than take a
117
+ * starter. Its source is empty, so a caller reading `src` to fill something
118
+ * that must hold a photo (a product's, a record's) falls back as it would on
119
+ * `null`.
120
+ */ export const AI_LAYOUT_NO_PHOTO = Object.freeze({
121
+ src: '',
122
+ width: 0,
123
+ height: 0
124
+ });
113
125
  /**
114
126
  * The starter photos each role draws from, best first. A hero draws from the
115
127
  * wide photos that read as a page's opening, an about picture leads with the
@@ -205,7 +217,8 @@ function rotate(list, by) {
205
217
  return {
206
218
  src: photo.src,
207
219
  width: photo.width,
208
- height: photo.height
220
+ height: photo.height,
221
+ alt: photo.alt
209
222
  };
210
223
  });
211
224
  }
@@ -254,9 +267,10 @@ function rotate(list, by) {
254
267
  }
255
268
  /**
256
269
  * Fills a stored page's empty picture slots (AGL-3660): from `source` where
257
- * one is given and it answers, else from the starter photos. Never throws,
258
- * and never leaves a page worse than it came: a picture is decoration, and a
259
- * job never fails over one.
270
+ * one is given and it answers; a slot it answers with nothing fitting stays
271
+ * without a photo; every other slot takes a starter photo, described by its
272
+ * own words (AGL-3831). Never throws, and never leaves a page worse than it
273
+ * came: a picture is decoration, and a job never fails over one.
260
274
  */ export async function aiResolveLayoutPictures(nodes, input) {
261
275
  try {
262
276
  const slots = aiLayoutPictureSlots(nodes, input.rootId, {
@@ -275,10 +289,17 @@ function rotate(list, by) {
275
289
  found = [];
276
290
  }
277
291
  }
278
- const starters = aiLayoutStarterPhotos(slots, input.seed);
292
+ // The starters' words are English; a site in another language says it
293
+ // shows an illustrative photo, true of any photo, as a stock photo's does.
294
+ const otherLanguage = aiLayoutSpeaksOtherThanEnglish(input.language);
295
+ const starters = aiLayoutStarterPhotos(slots, input.seed).map((photo)=>otherLanguage ? _extends({}, photo, {
296
+ alt: aiLayoutLabel('illustrativePhoto', input.language)
297
+ }) : photo);
279
298
  return aiLayoutPlacePictures(nodes, slots, slots.map((_slot, index)=>{
280
- var _found_index;
281
- return (_found_index = found[index]) != null ? _found_index : starters[index];
299
+ var _starters_index;
300
+ const answer = found[index];
301
+ if (answer) return answer.src ? answer : null;
302
+ return (_starters_index = starters[index]) != null ? _starters_index : null;
282
303
  }));
283
304
  } catch (error) {
284
305
  console.error('ai layout pictures: the page keeps its empty picture slots', {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/layout-language/ai-layout-pictures.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 { DEFAULT_SITE_IMAGES } from '@aglyn/aglyn/app-utils/default-site'\nimport type { NodesMap } from '@aglyn/aglyn/types/nodes'\n\n/**\n * THE PICTURES OF A LANGUAGE PAGE (AGL-3660).\n *\n * The compiler writes a picture slot as a frame holding an icon and an\n * `image` with alt text and no source (`ai-layout-compiler.ts`), and stays\n * pure: it never looks a photo up. This module is what runs after it, on the\n * stored page — it finds each empty slot, decides what kind of picture the\n * slot is, and fills it with a photo the site can serve from its own origin.\n *\n * Two sources fill a slot, in order:\n *\n * 1. A stock photo searched for by the slot's own description and copied into\n * the site's media library (`jobs/ai-layout-stock-photos.ts`), when the\n * deployment has a stock-photo key.\n * 2. Otherwise, or when the search finds nothing fitting, one of the STARTER\n * PHOTOS every new site is born with (`DEFAULT_SITE_IMAGES`, served at\n * `/_static/starter/` by the tenant and the console alike, credited in the\n * CREDITS file beside them). They are the platform's own files, so a\n * visitor's browser asks no other host for them.\n *\n * A slot keeps the alt text the model wrote unless the photo filling it\n * brings its own words: the model wrote it before any photo was found, so a\n * stock photo that is not what it says is described by what it is\n * (`aiStockPhotoAlt`).\n */\n\n/** What a picture slot is for, read from where it sits and what it says. */\nexport type AiLayoutPictureRole = 'hero' | 'about' | 'gallery'\n\n/** One empty picture slot of a stored page, in document order. */\nexport interface AiLayoutPictureSlot {\n /** The `image` node. */\n imageId: string\n /** The frame around it, which sets the slot's shape. */\n frameId: string | null\n /** The placeholder icon sharing the frame, taken out once a photo fills it. */\n iconId: string | null\n alt: string\n /** The frame's shape as width over height; 1 when it names none. */\n aspect: number\n /** The plan index of the section it sits in, or -1. */\n sectionIndex: number\n role: AiLayoutPictureRole\n /**\n * A product's photo (AGL-3676): what it is of, most specific first — its\n * name, then what its photo should show — searched for with the shop's\n * category and held to it, so a wick trimmer is never a laptop on a desk.\n */\n product?: { subjects: readonly string[] }\n}\n\n/** A photo a slot is filled with. */\nexport interface AiLayoutPicturePhoto {\n /** The site-served address: a starter path or the media library's own. */\n src: string\n width: number\n height: number\n /**\n * What the photo shows, where the slot's own alt text does not say it: the\n * slot takes it in place of the words the model wrote before the photo was\n * found. Absent, the slot's words describe it.\n */\n alt?: string\n}\n\n/** Words that say a picture shows people, or a section is about them. */\nconst PEOPLE =\n /\\b(about|team|story|owner|owners|founder|founders|staff|crew|people|person|portrait|meet|family|families|who we are|our (?:team|crew|people|story)|therapist|counsel(?:l)?or|coach|doctor|dentist|stylist|trainer|smil(?:e|es|ing)|headshot)\\b/i\n\n/**\n * An `aspectRatio` (`'16 / 9'`) as a number, or `null` where none is named.\n * A responsive one (`{ xs: '4 / 3', md: '4 / 5' }`, as a designed page's\n * pictures carry, AGL-3660) reads at the desktop breakpoint first: one photo\n * serves every width, and the wide screen is where its shape shows most.\n */\nfunction aspectOf(value: unknown): number | null {\n if (value && typeof value === 'object') {\n const byBreakpoint = value as Record<string, unknown>\n for (const breakpoint of ['md', 'lg', 'xl', 'sm', 'xs']) {\n const ratio = aspectOf(byBreakpoint[breakpoint])\n if (ratio !== null) return ratio\n }\n return null\n }\n const match = /^\\s*(\\d+(?:\\.\\d+)?)\\s*\\/\\s*(\\d+(?:\\.\\d+)?)\\s*$/.exec(String(value ?? ''))\n if (!match) return null\n const ratio = Number(match[1]) / Number(match[2])\n return Number.isFinite(ratio) && ratio > 0 ? ratio : null\n}\n\n/** The shape a full-bleed cover's photo is searched for: it fills a wide band. */\nconst COVER_ASPECT = 16 / 9\n\n/**\n * A slot's shape (AGL-3660): the image's own `aspectRatio` (a designed\n * picture card sets it on the image), else its frame's, else — for a frame\n * laid absolutely over its whole section, a designed photo cover — a wide\n * band's. 1 where nothing says.\n */\nfunction slotAspect(image: { sx?: Record<string, unknown> } | undefined, frame: { sx?: Record<string, unknown> } | undefined): number {\n const own = aspectOf(image?.sx?.['aspectRatio']) ?? aspectOf(frame?.sx?.['aspectRatio'])\n if (own !== null) return own\n const sx = frame?.sx\n if (sx?.['position'] === 'absolute' && sx['width'] === '100%' && sx['height'] === '100%') return COVER_ASPECT\n return 1\n}\n\n/** A slot's role: the first section's picture is the hero; people make an about picture. */\nexport function aiLayoutPictureRole(input: {\n sectionIndex: number\n sectionName: string\n alt: string\n}): AiLayoutPictureRole {\n if (input.sectionIndex === 0) return 'hero'\n if (PEOPLE.test(input.sectionName) || PEOPLE.test(input.alt)) return 'about'\n return 'gallery'\n}\n\n/**\n * Every empty picture slot of a stored page, walked from its root in\n * document order. A slot that already has a source — one the owner placed, or\n * an earlier pass filled — is not a slot.\n */\nexport function aiLayoutPictureSlots(\n nodes: NodesMap,\n rootId: string,\n sections: { ids: readonly string[]; names: readonly string[] },\n): AiLayoutPictureSlot[] {\n const map = nodes as unknown as Record<\n string,\n { componentId?: string; props?: Record<string, unknown>; sx?: Record<string, unknown>; nodes?: string[] }\n >\n const slots: AiLayoutPictureSlot[] = []\n const seen = new Set<string>()\n const visit = (id: string, parentId: string | null, sectionIndex: number) => {\n if (seen.has(id)) return\n seen.add(id)\n const node = map[id]\n if (!node) return\n const index = sections.ids.indexOf(id)\n const here = index >= 0 ? index : sectionIndex\n if (node.componentId === 'image') {\n const src = node.props?.['src']\n const alt = typeof node.props?.['alt'] === 'string' ? (node.props['alt'] as string).trim() : ''\n if ((typeof src !== 'string' || !src.trim()) && alt) {\n const frame = parentId ? map[parentId] : undefined\n const iconId =\n frame?.nodes?.find((child) => child !== id && map[child]?.componentId === 'icon') ?? null\n slots.push({\n imageId: id,\n frameId: frame ? parentId : null,\n iconId,\n alt,\n aspect: slotAspect(node, frame),\n sectionIndex: here,\n role: aiLayoutPictureRole({\n sectionIndex: here,\n sectionName: here >= 0 ? (sections.names[here] ?? '') : '',\n alt,\n }),\n })\n }\n }\n for (const child of node.nodes ?? []) visit(child, id, here)\n }\n visit(rootId, null, -1)\n return slots\n}\n\n/** The starter photos, by name. */\nexport const AI_LAYOUT_STARTER_PHOTOS = DEFAULT_SITE_IMAGES\n\ntype StarterName = keyof typeof AI_LAYOUT_STARTER_PHOTOS\n\n/**\n * The starter photos each role draws from, best first. A hero draws from the\n * wide photos that read as a page's opening, an about picture leads with the\n * owner, and the rest are the gallery's.\n */\nconst STARTER_POOLS: Readonly<Record<AiLayoutPictureRole, readonly StarterName[]>> = {\n hero: ['hero', 'desk', 'craft'],\n about: ['about', 'hero'],\n gallery: ['desk', 'plants', 'craft'],\n}\n\nconst ALL_STARTERS = Object.keys(AI_LAYOUT_STARTER_PHOTOS) as StarterName[]\n\n/** A small stable number from a seed, so the same job always picks the same photos. */\nexport function aiLayoutSeedNumber(seed: string): number {\n let hash = 2166136261\n for (let index = 0; index < seed.length; index += 1) {\n hash ^= seed.charCodeAt(index)\n hash = Math.imul(hash, 16777619)\n }\n return hash >>> 0\n}\n\nfunction rotate<T>(list: readonly T[], by: number): T[] {\n if (!list.length) return []\n const start = by % list.length\n return [...list.slice(start), ...list.slice(0, start)]\n}\n\n/**\n * A starter photo for each slot (AGL-3660). No photo repeats on a page while\n * an unused one remains, and every pool turns by the seed, so sites do not\n * all open with the same photo while one job always picks the same.\n *\n * The slots are served by what they are for, not where they sit: the hero\n * first, then the pictures of people (an about picture leads with the owner),\n * then the rest. The live Juniper Clay start (2026-10-08) had six slots and\n * five starters, and the owner's photo went to a stoneware card before the\n * About section that described the artist asked for it. Only a page with more\n * slots than starters repeats one: the photo placed longest ago, never the\n * one in a slot beside it. The owner's portrait goes only to a picture of\n * people; a gallery picture never takes it, even to avoid a repeat.\n */\nexport function aiLayoutStarterPhotos(\n slots: readonly AiLayoutPictureSlot[],\n seed: string,\n): AiLayoutPicturePhoto[] {\n const turn = aiLayoutSeedNumber(seed)\n const order: AiLayoutPictureRole[] = ['hero', 'about', 'gallery']\n const served = slots\n .map((slot, index) => ({ slot, index }))\n .sort((a, b) => order.indexOf(a.slot.role) - order.indexOf(b.slot.role) || a.index - b.index)\n const names: Array<StarterName | undefined> = slots.map(() => undefined)\n /** When each starter was last placed, by its turn in the serving order. */\n const placedAt = new Map<StarterName, number>()\n served.forEach(({ slot, index }, step) => {\n // An about picture leads with the owner on every site; the others turn.\n const pool = slot.role === 'about' ? [...STARTER_POOLS.about] : rotate(STARTER_POOLS[slot.role], turn)\n const rest = rotate(\n ALL_STARTERS.filter((name) => !pool.includes(name)),\n turn,\n )\n // The owner's portrait is a picture of a person, and only a picture of\n // people takes it: never a vase in a gallery (AGL-3660).\n const candidates = [...pool, ...rest].filter((name) => slot.role === 'about' || name !== 'about')\n const unused = candidates.find((candidate) => !placedAt.has(candidate))\n const beside = new Set([names[index - 1], names[index + 1]].filter(Boolean))\n const name =\n unused ??\n [...candidates]\n .sort((a, b) => (placedAt.get(a) ?? -1) - (placedAt.get(b) ?? -1))\n .find((candidate) => !beside.has(candidate)) ??\n candidates[0]\n names[index] = name\n placedAt.set(name, step)\n })\n return names.map((name) => {\n const photo = AI_LAYOUT_STARTER_PHOTOS[name as StarterName]\n return { src: photo.src, width: photo.width, height: photo.height }\n })\n}\n\n/**\n * The page with each slot filled by its photo: the `image` given its source,\n * its intrinsic size and how it loads (the hero eagerly, as the starter home\n * page loads its own), and the placeholder icon taken out of the frame. The\n * alt text is kept as the model wrote it unless the photo brings its own\n * words. A slot given no photo is left as the compiler wrote it.\n */\nexport function aiLayoutPlacePictures(\n nodes: NodesMap,\n slots: readonly AiLayoutPictureSlot[],\n photos: ReadonlyArray<AiLayoutPicturePhoto | null>,\n): NodesMap {\n const next = { ...(nodes as unknown as Record<string, Record<string, unknown>>) }\n // Only the page's first picture loads eagerly (rule 16, AGL-3660): a hero\n // of several photos loaded them all eagerly, and a live Portfolio page was\n // refused for it (2026-10-10). The slots are in document order; every later\n // picture leaves `loading` unset, which the renderer loads when reached.\n const lead = slots.findIndex((_, index) => Boolean(photos[index] && next[slots[index].imageId]))\n slots.forEach((slot, index) => {\n const photo = photos[index]\n const node = next[slot.imageId]\n if (!photo || !node) return\n const props = { ...((node['props'] as Record<string, unknown> | undefined) ?? {}) }\n delete props['loading']\n next[slot.imageId] = {\n ...node,\n props: {\n ...props,\n src: photo.src,\n intrinsicWidth: photo.width,\n intrinsicHeight: photo.height,\n ...(photo.alt?.trim() ? { alt: photo.alt.trim() } : {}),\n ...(index === lead && slot.role === 'hero' ? { loading: 'eager' } : {}),\n },\n }\n if (slot.iconId && slot.frameId && next[slot.frameId]) {\n const frame = next[slot.frameId]\n next[slot.frameId] = {\n ...frame,\n nodes: ((frame['nodes'] as string[] | undefined) ?? []).filter((child) => child !== slot.iconId),\n }\n delete next[slot.iconId]\n }\n })\n return next as unknown as NodesMap\n}\n\n/**\n * A source of photos found for the slots themselves — a stock search copied\n * into the site's library — answering one photo or `null` per slot, in order.\n * It may throw; a slot it leaves unfilled takes a starter photo.\n */\nexport type AiLayoutPictureSource = (\n slots: readonly AiLayoutPictureSlot[],\n) => Promise<ReadonlyArray<AiLayoutPicturePhoto | null>>\n\n/**\n * Fills a stored page's empty picture slots (AGL-3660): from `source` where\n * one is given and it answers, else from the starter photos. Never throws,\n * and never leaves a page worse than it came: a picture is decoration, and a\n * job never fails over one.\n */\nexport async function aiResolveLayoutPictures(\n nodes: NodesMap,\n input: {\n rootId: string\n sectionIds: readonly string[]\n sectionNames: readonly string[]\n /** A per-job word, so two sites do not open with the same photo. */\n seed: string\n source?: AiLayoutPictureSource | null\n },\n): Promise<NodesMap> {\n try {\n const slots = aiLayoutPictureSlots(nodes, input.rootId, {\n ids: input.sectionIds,\n names: input.sectionNames,\n })\n if (!slots.length) return nodes\n let found: ReadonlyArray<AiLayoutPicturePhoto | null> = []\n if (input.source) {\n try {\n found = await input.source(slots)\n } catch (error) {\n console.warn('ai layout pictures: the stock source failed; starter photos fill the page', { error })\n found = []\n }\n }\n const starters = aiLayoutStarterPhotos(slots, input.seed)\n return aiLayoutPlacePictures(\n nodes,\n slots,\n slots.map((_slot, index) => found[index] ?? starters[index]),\n )\n } catch (error) {\n console.error('ai layout pictures: the page keeps its empty picture slots', { error })\n return nodes\n }\n}\n"],"names":["DEFAULT_SITE_IMAGES","PEOPLE","aspectOf","value","byBreakpoint","breakpoint","ratio","match","exec","String","Number","isFinite","COVER_ASPECT","slotAspect","image","frame","own","sx","aiLayoutPictureRole","input","sectionIndex","test","sectionName","alt","aiLayoutPictureSlots","nodes","rootId","sections","map","slots","seen","Set","visit","id","parentId","node","has","add","index","ids","indexOf","here","componentId","src","props","trim","undefined","iconId","find","child","push","imageId","frameId","aspect","role","names","AI_LAYOUT_STARTER_PHOTOS","STARTER_POOLS","hero","about","gallery","ALL_STARTERS","Object","keys","aiLayoutSeedNumber","seed","hash","length","charCodeAt","Math","imul","rotate","list","by","start","slice","aiLayoutStarterPhotos","turn","order","served","slot","sort","a","b","placedAt","Map","forEach","step","unused","pool","rest","filter","name","includes","candidates","candidate","beside","Boolean","get","set","photo","width","height","aiLayoutPlacePictures","photos","next","lead","findIndex","_","intrinsicWidth","intrinsicHeight","loading","aiResolveLayoutPictures","sectionIds","sectionNames","found","source","error","console","warn","starters","_slot"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/layout-language/ai-layout-pictures.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 { DEFAULT_SITE_IMAGES } from '@aglyn/aglyn/app-utils/default-site'\nimport type { NodesMap } from '@aglyn/aglyn/types/nodes'\nimport { aiLayoutLabel, aiLayoutSpeaksOtherThanEnglish } from './ai-layout-labels'\n\n/**\n * THE PICTURES OF A LANGUAGE PAGE (AGL-3660).\n *\n * The compiler writes a picture slot as a frame holding an icon and an\n * `image` with alt text and no source (`ai-layout-compiler.ts`), and stays\n * pure: it never looks a photo up. This module is what runs after it, on the\n * stored page — it finds each empty slot, decides what kind of picture the\n * slot is, and fills it with a photo the site can serve from its own origin.\n *\n * Two sources fill a slot, in order:\n *\n * 1. A stock photo searched for by the slot's own description and copied into\n * the site's media library (`jobs/ai-layout-stock-photos.ts`), when the\n * deployment has a stock-photo key.\n * 2. Where the library cannot be asked at all — no stock-photo key, the\n * library down, refusing or out of time before it answered — one of the\n * STARTER PHOTOS every new site is born with (`DEFAULT_SITE_IMAGES`,\n * served at `/_static/starter/` by the tenant and the console alike,\n * credited in the CREDITS file beside them). They are the platform's own\n * files, so a visitor's browser asks no other host for them, and a\n * starter reads better than no picture when there is no library.\n *\n * Where the library answered and nothing in it fits a slot\n * ({@link AI_LAYOUT_NO_PHOTO}), the slot keeps the compiler's no-photo\n * treatment — its frame with its icon, or its section's own band behind a\n * cover — and never a starter: the beta.257 Copper Kettle Catering start\n * (2026-10-15) opened a barbecue caterer's home page on the starters'\n * pottery workbench (AGL-3831). An empty picture draws nothing on the\n * published page, and the Besigner shows the owner where to choose one.\n *\n * A slot keeps the alt text the model wrote only when it describes the photo\n * placed: a stock photo that is not what it says is described by what it is\n * (`aiStockPhotoAlt`), and a starter photo always by its own words, since\n * the model never wrote for it (AGL-3831).\n */\n\n/** What a picture slot is for, read from where it sits and what it says. */\nexport type AiLayoutPictureRole = 'hero' | 'about' | 'gallery'\n\n/** One empty picture slot of a stored page, in document order. */\nexport interface AiLayoutPictureSlot {\n /** The `image` node. */\n imageId: string\n /** The frame around it, which sets the slot's shape. */\n frameId: string | null\n /** The placeholder icon sharing the frame, taken out once a photo fills it. */\n iconId: string | null\n alt: string\n /** The frame's shape as width over height; 1 when it names none. */\n aspect: number\n /** The plan index of the section it sits in, or -1. */\n sectionIndex: number\n role: AiLayoutPictureRole\n /**\n * A product's photo (AGL-3676): what it is of, most specific first — its\n * name, then what its photo should show — searched for with the shop's\n * category and held to it, so a wick trimmer is never a laptop on a desk.\n */\n product?: { subjects: readonly string[] }\n}\n\n/** A photo a slot is filled with. */\nexport interface AiLayoutPicturePhoto {\n /** The site-served address: a starter path or the media library's own. */\n src: string\n width: number\n height: number\n /**\n * What the photo shows, where the slot's own alt text does not say it: the\n * slot takes it in place of the words the model wrote before the photo was\n * found. Absent, the slot's words describe it.\n */\n alt?: string\n}\n\n/** Words that say a picture shows people, or a section is about them. */\nconst PEOPLE =\n /\\b(about|team|story|owner|owners|founder|founders|staff|crew|people|person|portrait|meet|family|families|who we are|our (?:team|crew|people|story)|therapist|counsel(?:l)?or|coach|doctor|dentist|stylist|trainer|smil(?:e|es|ing)|headshot)\\b/i\n\n/**\n * An `aspectRatio` (`'16 / 9'`) as a number, or `null` where none is named.\n * A responsive one (`{ xs: '4 / 3', md: '4 / 5' }`, as a designed page's\n * pictures carry, AGL-3660) reads at the desktop breakpoint first: one photo\n * serves every width, and the wide screen is where its shape shows most.\n */\nfunction aspectOf(value: unknown): number | null {\n if (value && typeof value === 'object') {\n const byBreakpoint = value as Record<string, unknown>\n for (const breakpoint of ['md', 'lg', 'xl', 'sm', 'xs']) {\n const ratio = aspectOf(byBreakpoint[breakpoint])\n if (ratio !== null) return ratio\n }\n return null\n }\n const match = /^\\s*(\\d+(?:\\.\\d+)?)\\s*\\/\\s*(\\d+(?:\\.\\d+)?)\\s*$/.exec(String(value ?? ''))\n if (!match) return null\n const ratio = Number(match[1]) / Number(match[2])\n return Number.isFinite(ratio) && ratio > 0 ? ratio : null\n}\n\n/** The shape a full-bleed cover's photo is searched for: it fills a wide band. */\nconst COVER_ASPECT = 16 / 9\n\n/**\n * A slot's shape (AGL-3660): the image's own `aspectRatio` (a designed\n * picture card sets it on the image), else its frame's, else — for a frame\n * laid absolutely over its whole section, a designed photo cover — a wide\n * band's. 1 where nothing says.\n */\nfunction slotAspect(image: { sx?: Record<string, unknown> } | undefined, frame: { sx?: Record<string, unknown> } | undefined): number {\n const own = aspectOf(image?.sx?.['aspectRatio']) ?? aspectOf(frame?.sx?.['aspectRatio'])\n if (own !== null) return own\n const sx = frame?.sx\n if (sx?.['position'] === 'absolute' && sx['width'] === '100%' && sx['height'] === '100%') return COVER_ASPECT\n return 1\n}\n\n/** A slot's role: the first section's picture is the hero; people make an about picture. */\nexport function aiLayoutPictureRole(input: {\n sectionIndex: number\n sectionName: string\n alt: string\n}): AiLayoutPictureRole {\n if (input.sectionIndex === 0) return 'hero'\n if (PEOPLE.test(input.sectionName) || PEOPLE.test(input.alt)) return 'about'\n return 'gallery'\n}\n\n/**\n * Every empty picture slot of a stored page, walked from its root in\n * document order. A slot that already has a source — one the owner placed, or\n * an earlier pass filled — is not a slot.\n */\nexport function aiLayoutPictureSlots(\n nodes: NodesMap,\n rootId: string,\n sections: { ids: readonly string[]; names: readonly string[] },\n): AiLayoutPictureSlot[] {\n const map = nodes as unknown as Record<\n string,\n { componentId?: string; props?: Record<string, unknown>; sx?: Record<string, unknown>; nodes?: string[] }\n >\n const slots: AiLayoutPictureSlot[] = []\n const seen = new Set<string>()\n const visit = (id: string, parentId: string | null, sectionIndex: number) => {\n if (seen.has(id)) return\n seen.add(id)\n const node = map[id]\n if (!node) return\n const index = sections.ids.indexOf(id)\n const here = index >= 0 ? index : sectionIndex\n if (node.componentId === 'image') {\n const src = node.props?.['src']\n const alt = typeof node.props?.['alt'] === 'string' ? (node.props['alt'] as string).trim() : ''\n if ((typeof src !== 'string' || !src.trim()) && alt) {\n const frame = parentId ? map[parentId] : undefined\n const iconId =\n frame?.nodes?.find((child) => child !== id && map[child]?.componentId === 'icon') ?? null\n slots.push({\n imageId: id,\n frameId: frame ? parentId : null,\n iconId,\n alt,\n aspect: slotAspect(node, frame),\n sectionIndex: here,\n role: aiLayoutPictureRole({\n sectionIndex: here,\n sectionName: here >= 0 ? (sections.names[here] ?? '') : '',\n alt,\n }),\n })\n }\n }\n for (const child of node.nodes ?? []) visit(child, id, here)\n }\n visit(rootId, null, -1)\n return slots\n}\n\n/** The starter photos, by name, each with the words that describe it. */\nexport const AI_LAYOUT_STARTER_PHOTOS = DEFAULT_SITE_IMAGES\n\n/**\n * A source's answer for a slot the library answered and nothing in it fits\n * (AGL-3831): the slot keeps its no-photo treatment rather than take a\n * starter. Its source is empty, so a caller reading `src` to fill something\n * that must hold a photo (a product's, a record's) falls back as it would on\n * `null`.\n */\nexport const AI_LAYOUT_NO_PHOTO: Readonly<AiLayoutPicturePhoto> = Object.freeze({ src: '', width: 0, height: 0 })\n\ntype StarterName = keyof typeof AI_LAYOUT_STARTER_PHOTOS\n\n/**\n * The starter photos each role draws from, best first. A hero draws from the\n * wide photos that read as a page's opening, an about picture leads with the\n * owner, and the rest are the gallery's.\n */\nconst STARTER_POOLS: Readonly<Record<AiLayoutPictureRole, readonly StarterName[]>> = {\n hero: ['hero', 'desk', 'craft'],\n about: ['about', 'hero'],\n gallery: ['desk', 'plants', 'craft'],\n}\n\nconst ALL_STARTERS = Object.keys(AI_LAYOUT_STARTER_PHOTOS) as StarterName[]\n\n/** A small stable number from a seed, so the same job always picks the same photos. */\nexport function aiLayoutSeedNumber(seed: string): number {\n let hash = 2166136261\n for (let index = 0; index < seed.length; index += 1) {\n hash ^= seed.charCodeAt(index)\n hash = Math.imul(hash, 16777619)\n }\n return hash >>> 0\n}\n\nfunction rotate<T>(list: readonly T[], by: number): T[] {\n if (!list.length) return []\n const start = by % list.length\n return [...list.slice(start), ...list.slice(0, start)]\n}\n\n/**\n * A starter photo for each slot (AGL-3660). No photo repeats on a page while\n * an unused one remains, and every pool turns by the seed, so sites do not\n * all open with the same photo while one job always picks the same.\n *\n * The slots are served by what they are for, not where they sit: the hero\n * first, then the pictures of people (an about picture leads with the owner),\n * then the rest. The live Juniper Clay start (2026-10-08) had six slots and\n * five starters, and the owner's photo went to a stoneware card before the\n * About section that described the artist asked for it. Only a page with more\n * slots than starters repeats one: the photo placed longest ago, never the\n * one in a slot beside it. The owner's portrait goes only to a picture of\n * people; a gallery picture never takes it, even to avoid a repeat.\n */\nexport function aiLayoutStarterPhotos(\n slots: readonly AiLayoutPictureSlot[],\n seed: string,\n): AiLayoutPicturePhoto[] {\n const turn = aiLayoutSeedNumber(seed)\n const order: AiLayoutPictureRole[] = ['hero', 'about', 'gallery']\n const served = slots\n .map((slot, index) => ({ slot, index }))\n .sort((a, b) => order.indexOf(a.slot.role) - order.indexOf(b.slot.role) || a.index - b.index)\n const names: Array<StarterName | undefined> = slots.map(() => undefined)\n /** When each starter was last placed, by its turn in the serving order. */\n const placedAt = new Map<StarterName, number>()\n served.forEach(({ slot, index }, step) => {\n // An about picture leads with the owner on every site; the others turn.\n const pool = slot.role === 'about' ? [...STARTER_POOLS.about] : rotate(STARTER_POOLS[slot.role], turn)\n const rest = rotate(\n ALL_STARTERS.filter((name) => !pool.includes(name)),\n turn,\n )\n // The owner's portrait is a picture of a person, and only a picture of\n // people takes it: never a vase in a gallery (AGL-3660).\n const candidates = [...pool, ...rest].filter((name) => slot.role === 'about' || name !== 'about')\n const unused = candidates.find((candidate) => !placedAt.has(candidate))\n const beside = new Set([names[index - 1], names[index + 1]].filter(Boolean))\n const name =\n unused ??\n [...candidates]\n .sort((a, b) => (placedAt.get(a) ?? -1) - (placedAt.get(b) ?? -1))\n .find((candidate) => !beside.has(candidate)) ??\n candidates[0]\n names[index] = name\n placedAt.set(name, step)\n })\n return names.map((name) => {\n const photo = AI_LAYOUT_STARTER_PHOTOS[name as StarterName]\n return { src: photo.src, width: photo.width, height: photo.height, alt: photo.alt }\n })\n}\n\n/**\n * The page with each slot filled by its photo: the `image` given its source,\n * its intrinsic size and how it loads (the hero eagerly, as the starter home\n * page loads its own), and the placeholder icon taken out of the frame. The\n * alt text is kept as the model wrote it unless the photo brings its own\n * words. A slot given no photo is left as the compiler wrote it.\n */\nexport function aiLayoutPlacePictures(\n nodes: NodesMap,\n slots: readonly AiLayoutPictureSlot[],\n photos: ReadonlyArray<AiLayoutPicturePhoto | null>,\n): NodesMap {\n const next = { ...(nodes as unknown as Record<string, Record<string, unknown>>) }\n // Only the page's first picture loads eagerly (rule 16, AGL-3660): a hero\n // of several photos loaded them all eagerly, and a live Portfolio page was\n // refused for it (2026-10-10). The slots are in document order; every later\n // picture leaves `loading` unset, which the renderer loads when reached.\n const lead = slots.findIndex((_, index) => Boolean(photos[index] && next[slots[index].imageId]))\n slots.forEach((slot, index) => {\n const photo = photos[index]\n const node = next[slot.imageId]\n if (!photo || !node) return\n const props = { ...((node['props'] as Record<string, unknown> | undefined) ?? {}) }\n delete props['loading']\n next[slot.imageId] = {\n ...node,\n props: {\n ...props,\n src: photo.src,\n intrinsicWidth: photo.width,\n intrinsicHeight: photo.height,\n ...(photo.alt?.trim() ? { alt: photo.alt.trim() } : {}),\n ...(index === lead && slot.role === 'hero' ? { loading: 'eager' } : {}),\n },\n }\n if (slot.iconId && slot.frameId && next[slot.frameId]) {\n const frame = next[slot.frameId]\n next[slot.frameId] = {\n ...frame,\n nodes: ((frame['nodes'] as string[] | undefined) ?? []).filter((child) => child !== slot.iconId),\n }\n delete next[slot.iconId]\n }\n })\n return next as unknown as NodesMap\n}\n\n/**\n * A source of photos found for the slots themselves — a stock search copied\n * into the site's library — answering per slot, in order: a photo; or\n * {@link AI_LAYOUT_NO_PHOTO} where the library answered and nothing fits,\n * which leaves the slot without a photo; or `null` where the library could\n * not be asked, which takes a starter photo. It may throw, and then every\n * slot takes a starter.\n */\nexport type AiLayoutPictureSource = (\n slots: readonly AiLayoutPictureSlot[],\n) => Promise<ReadonlyArray<AiLayoutPicturePhoto | null>>\n\n/**\n * Fills a stored page's empty picture slots (AGL-3660): from `source` where\n * one is given and it answers; a slot it answers with nothing fitting stays\n * without a photo; every other slot takes a starter photo, described by its\n * own words (AGL-3831). Never throws, and never leaves a page worse than it\n * came: a picture is decoration, and a job never fails over one.\n */\nexport async function aiResolveLayoutPictures(\n nodes: NodesMap,\n input: {\n rootId: string\n sectionIds: readonly string[]\n sectionNames: readonly string[]\n /** A per-job word, so two sites do not open with the same photo. */\n seed: string\n source?: AiLayoutPictureSource | null\n /** The site's language (AGL-3750); a starter photo's English words are not its own. */\n language?: string\n },\n): Promise<NodesMap> {\n try {\n const slots = aiLayoutPictureSlots(nodes, input.rootId, {\n ids: input.sectionIds,\n names: input.sectionNames,\n })\n if (!slots.length) return nodes\n let found: ReadonlyArray<AiLayoutPicturePhoto | null> = []\n if (input.source) {\n try {\n found = await input.source(slots)\n } catch (error) {\n console.warn('ai layout pictures: the stock source failed; starter photos fill the page', { error })\n found = []\n }\n }\n // The starters' words are English; a site in another language says it\n // shows an illustrative photo, true of any photo, as a stock photo's does.\n const otherLanguage = aiLayoutSpeaksOtherThanEnglish(input.language)\n const starters = aiLayoutStarterPhotos(slots, input.seed).map((photo) =>\n otherLanguage ? { ...photo, alt: aiLayoutLabel('illustrativePhoto', input.language) } : photo,\n )\n return aiLayoutPlacePictures(\n nodes,\n slots,\n slots.map((_slot, index) => {\n const answer = found[index]\n if (answer) return answer.src ? answer : null\n return starters[index] ?? null\n }),\n )\n } catch (error) {\n console.error('ai layout pictures: the page keeps its empty picture slots', { error })\n return nodes\n }\n}\n"],"names":["DEFAULT_SITE_IMAGES","aiLayoutLabel","aiLayoutSpeaksOtherThanEnglish","PEOPLE","aspectOf","value","byBreakpoint","breakpoint","ratio","match","exec","String","Number","isFinite","COVER_ASPECT","slotAspect","image","frame","own","sx","aiLayoutPictureRole","input","sectionIndex","test","sectionName","alt","aiLayoutPictureSlots","nodes","rootId","sections","map","slots","seen","Set","visit","id","parentId","node","has","add","index","ids","indexOf","here","componentId","src","props","trim","undefined","iconId","find","child","push","imageId","frameId","aspect","role","names","AI_LAYOUT_STARTER_PHOTOS","AI_LAYOUT_NO_PHOTO","Object","freeze","width","height","STARTER_POOLS","hero","about","gallery","ALL_STARTERS","keys","aiLayoutSeedNumber","seed","hash","length","charCodeAt","Math","imul","rotate","list","by","start","slice","aiLayoutStarterPhotos","turn","order","served","slot","sort","a","b","placedAt","Map","forEach","step","unused","pool","rest","filter","name","includes","candidates","candidate","beside","Boolean","get","set","photo","aiLayoutPlacePictures","photos","next","lead","findIndex","_","intrinsicWidth","intrinsicHeight","loading","aiResolveLayoutPictures","sectionIds","sectionNames","found","source","error","console","warn","otherLanguage","language","starters","_slot","answer"],"mappings":"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"}
@@ -529,6 +529,20 @@ const OPEN = [
529
529
  result.notes.push(`Its submissions arrive in the Inbox only: the dataset “${dep.label}” could not be created.`);
530
530
  continue;
531
531
  }
532
+ // An item that links pages this build makes (a menu's entries, AGL-3728)
533
+ // is built with the ones it made and without the ones it could not
534
+ // (AGL-3832), and skipped only when it made none of them.
535
+ const madeAPage = unit.deps.some((one)=>{
536
+ var _bySlot_get;
537
+ const made = rows.get(one);
538
+ return !!((_bySlot_get = bySlot.get(one)) == null ? void 0 : _bySlot_get.screen) && !!made && aiBuildItemDelivered(made);
539
+ });
540
+ if (!unit.screen && dep.screen && madeAPage) {
541
+ result.degradedBy.push(slot);
542
+ result.dropped.add(dep.label.toLowerCase());
543
+ result.notes.push(`Built without the ${noun} “${dep.label}”: it could not be created.`);
544
+ continue;
545
+ }
532
546
  if (!unit.screen && degrade === 'omit') {
533
547
  result.skip = `Not built: the ${noun} “${dep.label}” it needs could not be created.`;
534
548
  result.degradedBy.push(slot);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-build-job.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 {\n pluginAiCapabilityArgsProblems,\n type PluginAiCapability,\n} from '@aglyn/aglyn/plugin-manager/plugin-ai-capabilities'\nimport type { PluginDraftRecord } from '@aglyn/aglyn/plugin-manager/plugin-resource-drafts'\nimport {\n AI_PLAN_NEW_REF_PREFIX,\n isAiPlanNewRef,\n type AiBuildItem,\n type AiBuildPlan,\n type AiBuildPlanCreate,\n type AiBuildPlanCreateKind,\n type AiBuildPlanScreen,\n} from './ai-build-plan'\nimport type { AiJobItemLedger, AiJobItemStatus } from './ai-jobs.types'\nimport {\n AI_BUILD_STORE_OP,\n AI_BUILD_STORE_PAGES_LABEL,\n AI_BUILD_STORE_SLOT,\n aiBuildMakesStore,\n aiBuildStorePagesAdded,\n} from './ai-build-store-pages'\nimport {\n AI_SITE_HOME_FIRST_LABEL,\n AI_SITE_MAX_SECTIONS,\n AI_SITE_PASS_CREDITS,\n aiCreationMeasuredCredits,\n aiJobPlanCreditEstimate,\n aiJobPlanCreditRange,\n aiSitePlanIsHome,\n aiStandaloneScreenCreditRange,\n} from './ai-site-job'\nimport {\n AI_CREDIT_RANGE_ZERO,\n AI_MEASURED_PASS_CREDITS,\n aiCreditRangeAdd,\n aiCreditRangeOf,\n aiCreditRangeOrdered,\n type AiCreditRange,\n type AiCreditsSmaller,\n} from './ai-credit-estimate'\n\n/**\n * What a `build` job is (AGL-3616): one request — \"a few pages, a contact\n * form and a way to book me\" — planned as creations, pages and items, built\n * one UNIT a pass in dependency order, and settled ITEM BY ITEM.\n *\n * A build is a generalized site scaffold. Every unit is handed to what\n * already builds that kind of thing — a page to the page step, a form to the\n * form step, another plugin's resource to that plugin's draft writer — so a\n * build cannot drift from what it builds. What is new is the settlement: one\n * failed unit does not end the job. It gives back its own spend, the units\n * that depended on it are built without it (or skipped, when they cannot\n * stand without it), and the job is `done` when anything was delivered.\n *\n * This module is the build's MODEL — its units, their order, how a failure\n * travels to what depends on it, what it is estimated to cost, and the\n * ledger arithmetic — and does no I/O, so the step, the machine, the doors\n * and the console read one vocabulary.\n */\n\n/** How much one build holds: what one proposal can describe and one person can read. */\nexport const AI_BUILD_LIMITS = {\n /** Creations, pages and items together. */\n units: 16,\n pages: 8,\n /** A Free workspace's pages. */\n freePages: 2,\n} as const\n\n/**\n * What a Free build's plan card says when it holds as many pages as a Free\n * build makes (AGL-3722): the plan is told the cap and plans the first pages\n * a request asks for, so a request for three — \"About us, Custom cakes and\n * Catering\", 2026-10-10 — came back as two with nothing saying why. `null`\n * when the plan holds fewer, or on a paid workspace.\n */\nexport function aiBuildFreePageCapNote(plan: Pick<AiBuildPlan, 'screens'>, freeTaste: boolean): string | null {\n const cap = AI_BUILD_LIMITS.freePages\n if (!freeTaste || plan.screens.length < cap) return null\n return `A Free workspace’s build makes up to ${cap} pages at a time. If you asked for more, ask Assist for the rest once this build is done.`\n}\n\n/** What a build creates through its `create` list; everything else beyond pages is an item. */\nexport const AI_BUILD_CREATE_KINDS: readonly AiBuildPlanCreateKind[] = ['layout', 'form', 'component', 'email']\n\n/**\n * The operations a build plans through `create` and `screens` rather than\n * `items`: an item naming one is a plan that put it in the wrong list.\n */\nexport const AI_BUILD_STRUCTURAL_OPS: readonly string[] = ['page', ...AI_BUILD_CREATE_KINDS]\n\n/**\n * What a build hands an item it made before it (AGL-3728): its pages, by the\n * title the plan gave each and the id it was written under, and its layouts,\n * by name. An item's `new:<title>` destinations are read against it.\n */\nexport const AI_BUILD_MADE_INPUT = 'buildMade'\n\nexport interface AiBuildMade {\n pages: Array<{ title: string; id: string }>\n layouts: Array<{ name: string; id: string }>\n}\n\n/** The job input a build's Assist proposal sets when the brief asked to publish. */\nexport const AI_BUILD_PUBLISH_INPUT = 'publish'\n\n/** A unit of a build: a creation, a page, or an item. */\nexport interface AiBuildUnit {\n slot: string\n /** The capability operation that builds it. */\n op: string\n label: string\n creation?: AiBuildPlanCreate\n screen?: AiBuildPlanScreen\n item?: AiBuildItem\n /** The slots it is built after, in plan order. */\n deps: string[]\n}\n\n/** The operation a creation is built as. */\nfunction creationOp(creation: AiBuildPlanCreate): string {\n return creation.kind\n}\n\n/** A plan's `new:<name>` names, each to the slot that builds it. */\nfunction namedSlots(plan: AiBuildPlan): Map<string, string> {\n const slots = new Map<string, string>()\n plan.create.forEach((creation, index) => {\n if (AI_BUILD_CREATE_KINDS.includes(creation.kind)) slots.set(creation.name.toLowerCase(), `c${index}`)\n })\n for (const item of plan.items ?? []) slots.set(item.name.toLowerCase(), item.slot)\n return slots\n}\n\n/** The name a `new:<name>` reference gives, lowercased. */\nexport function aiBuildRefName(ref: string): string {\n return ref.slice(AI_PLAN_NEW_REF_PREFIX.length).trim().toLowerCase()\n}\n\n/**\n * The `new:<name>` references an item's arguments make (AGL-3728): an\n * argument that is one, or the destination after `=>` in an entry such as a\n * menu item's `About => new:About`. Read from the arguments, never from the\n * model's `dependsOn`, so what an item links is always built before it.\n */\nexport function aiBuildItemArgRefs(args: Readonly<Record<string, unknown>> | null | undefined): string[] {\n const refs: string[] = []\n const take = (value: unknown) => {\n if (typeof value !== 'string') return\n for (const part of [value, value.split('=>').slice(-1)[0] ?? '']) {\n const ref = part.trim()\n if (isAiPlanNewRef(ref) && !refs.includes(ref)) refs.push(ref)\n }\n }\n for (const value of Object.values(args ?? {})) {\n if (Array.isArray(value)) value.forEach(take)\n else take(value)\n }\n return refs\n}\n\n/** Every reference a page makes: its layout, template, starting page and what its sections place. */\nexport function aiBuildScreenRefs(screen: AiBuildPlanScreen): string[] {\n return [\n screen.layout,\n screen.template,\n screen.duplicateOf,\n ...screen.sections.flatMap((section) => section.uses),\n ].filter((ref): ref is string => typeof ref === 'string' && ref.length > 0)\n}\n\n/**\n * The units a plan implies, in plan order: creations, then items, then\n * pages — the order a member reads them in, and the one a page that places\n * a creation or an item is built after anyway. `aiBuildOrder` sorts them on\n * their dependencies.\n */\nexport function aiBuildUnits(plan: AiBuildPlan): AiBuildUnit[] {\n const slots = namedSlots(plan)\n // A page, by the title the plan gave it: what an item's arguments may link\n // as `new:<title>` (a menu item, AGL-3728), built before the item.\n const pageSlots = new Map<string, string>()\n plan.screens.forEach((screen, index) => {\n const title = screen.title.trim().toLowerCase()\n if (title && !pageSlots.has(title)) pageSlots.set(title, `p${index}`)\n })\n const depsOf = (refs: readonly (string | null)[], self: string, pages = false): string[] => {\n const deps: string[] = []\n for (const ref of refs) {\n if (!isAiPlanNewRef(ref)) continue\n const slot = slots.get(aiBuildRefName(ref)) ?? (pages ? pageSlots.get(aiBuildRefName(ref)) : undefined)\n if (slot && slot !== self && !deps.includes(slot)) deps.push(slot)\n }\n return deps\n }\n const units: AiBuildUnit[] = []\n plan.create.forEach((creation, index) => {\n if (!AI_BUILD_CREATE_KINDS.includes(creation.kind)) return\n const slot = `c${index}`\n // A form is built after the dataset it writes to (AGL-3616).\n units.push({\n slot,\n op: creationOp(creation),\n label: creation.name,\n creation,\n deps: depsOf([creation.duplicateOf, creation.kind === 'form' ? (creation.writesTo ?? null) : null], slot),\n })\n })\n for (const item of plan.items ?? []) {\n units.push({\n slot: item.slot,\n op: item.op,\n label: item.name,\n item,\n // What it depends on, and what its arguments link as `new:` (AGL-3728).\n deps: depsOf([...item.dependsOn, ...aiBuildItemArgRefs(item.args)], item.slot, true),\n })\n }\n plan.screens.forEach((screen, index) => {\n const slot = `p${index}`\n units.push({ slot, op: 'page', label: screen.title, screen, deps: depsOf(aiBuildScreenRefs(screen), slot) })\n })\n // A store's own pages (AGL-3676), added by the platform: last, so every\n // planned page keeps the first claim on the site's pages allowance.\n if (aiBuildStorePagesAdded(plan).length) {\n units.push({ slot: AI_BUILD_STORE_SLOT, op: AI_BUILD_STORE_OP, label: AI_BUILD_STORE_PAGES_LABEL, deps: [] })\n }\n return units\n}\n\n/** Whether a unit is one the platform adds rather than the plan (AGL-3676): a store's own pages. */\nexport function aiBuildUnitIsPlatform(unit: Pick<AiBuildUnit, 'op'>): boolean {\n return unit.op === AI_BUILD_STORE_OP\n}\n\n/**\n * The units in an order that builds every dependency before what depends on\n * it, plan order otherwise; `null` when the dependencies hold a cycle.\n */\nexport function aiBuildOrder(units: readonly AiBuildUnit[]): AiBuildUnit[] | null {\n const ordered: AiBuildUnit[] = []\n const placed = new Set<string>()\n const known = new Set(units.map((unit) => unit.slot))\n while (ordered.length < units.length) {\n const next = units.find(\n (unit) => !placed.has(unit.slot) && unit.deps.every((dep) => placed.has(dep) || !known.has(dep)),\n )\n if (!next) return null\n ordered.push(next)\n placed.add(next.slot)\n }\n return ordered\n}\n\n/**\n * What is wrong with a plan's references as a build reads them: a\n * `new:<name>` an item depends on that nothing in the plan makes, and a\n * cycle. Each is a sentence the plan's one re-ask carries.\n */\nexport function aiBuildGraphProblems(plan: AiBuildPlan): string[] {\n const problems: string[] = []\n const slots = namedSlots(plan)\n for (const item of plan.items ?? []) {\n for (const ref of item.dependsOn) {\n if (!isAiPlanNewRef(ref)) continue\n if (!slots.has(aiBuildRefName(ref))) {\n problems.push(`The item \"${item.name}\" depends on ${ref}, which the plan never makes. Declare it, or take it out of dependsOn.`)\n } else if (aiBuildRefName(ref) === item.name.toLowerCase()) {\n problems.push(`The item \"${item.name}\" depends on itself. Take it out of its own dependsOn.`)\n }\n }\n }\n if (!aiBuildOrder(aiBuildUnits(plan))) {\n problems.push('The plan’s dependencies go round in a circle, so nothing in the circle can be built first. Make one of them depend on nothing.')\n }\n return problems\n}\n\n/** The operations a build may plan, as the registry and the gates left them, by op. */\nexport type AiBuildOps = ReadonlyMap<string, PluginAiCapability>\n\n/** How many pages a build may hold on this workspace. */\nexport function aiBuildPageLimit(freeTaste: boolean | null | undefined): number {\n return freeTaste ? AI_BUILD_LIMITS.freePages : AI_BUILD_LIMITS.pages\n}\n\n/**\n * Why a build cannot build a plan of this shape, in a sentence a member\n * reads; `null` when it can. Held to the plan with the one re-ask (the plan\n * step's scope), and again by the doors before a confirmed plan runs.\n * `ops` is what may be planned on this site; absent, items are not judged\n * against the registry (a console reading a kept plan).\n */\nexport function aiBuildPlanShapeRefusal(\n plan: AiBuildPlan,\n options: { freeTaste?: boolean; ops?: AiBuildOps } = {},\n): string | null {\n // What the platform adds beside the plan (a store's own pages) is no part of its size.\n const units = aiBuildUnits(plan).filter((unit) => !aiBuildUnitIsPlatform(unit))\n if (!units.length) return 'This plan builds nothing. Describe what to build again.'\n if (units.length > AI_BUILD_LIMITS.units) {\n return `This plan builds ${units.length} things, and one build holds ${AI_BUILD_LIMITS.units}. Ask for the rest in a second request.`\n }\n const pages = aiBuildPageLimit(options.freeTaste)\n if (plan.screens.length > pages) {\n return options.freeTaste\n ? `This plan builds ${plan.screens.length} pages, and a Free workspace's build makes at most ${pages}. Paid plans can build more.`\n : `This plan builds ${plan.screens.length} pages, and one build makes at most ${pages}.`\n }\n const slugs = new Set<string>()\n for (const screen of plan.screens) {\n if (!screen.sections.length) return `The page “${screen.title}” has no sections to build.`\n if (screen.sections.length > AI_SITE_MAX_SECTIONS) {\n return `The page “${screen.title}” has ${screen.sections.length} sections, and a built page holds ${AI_SITE_MAX_SECTIONS}.`\n }\n const slug = screen.slug.trim().toLowerCase()\n if (slugs.has(slug)) return `Two pages in this plan share the address ${screen.slug}.`\n slugs.add(slug)\n }\n for (const creation of plan.create) {\n if (!AI_BUILD_CREATE_KINDS.includes(creation.kind)) {\n return `A build does not create ${creation.kind === 'theme-change' ? 'theme changes' : `${creation.kind}s`} (“${creation.name}”). Take it out of create.`\n }\n }\n const graph = aiBuildGraphProblems(plan)\n if (graph.length) return graph[0]\n const ops = options.ops\n const counts = new Map<string, number>()\n for (const unit of units) counts.set(unit.op, (counts.get(unit.op) ?? 0) + 1)\n for (const item of plan.items ?? []) {\n if (AI_BUILD_STRUCTURAL_OPS.includes(item.op)) {\n return item.op === 'page'\n ? `“${item.name}” is a page: plan it in screens, not items.`\n : `“${item.name}” is ${item.op === 'email' ? 'an' : 'a'} ${item.op}: plan it in create, not items.`\n }\n if (!ops) continue\n const capability = ops.get(item.op)\n if (!capability) return `Nothing on this site can make “${item.name}” (${item.op}). Take it out of items.`\n if (options.freeTaste && !capability.freeAllowed) {\n return `A Free workspace cannot have ${aiArticle(capability.noun)} made (“${item.name}”). Take it out of items.`\n }\n const problems = pluginAiCapabilityArgsProblems(capability.argsSchema, item.args)\n if (problems.length) return `The arguments of “${item.name}” do not read: ${problems.join('; ')}.`\n }\n if (ops) {\n for (const [op, count] of counts) {\n const capability = ops.get(op)\n if (capability && count > capability.maxPerPlan) {\n return `This plan makes ${count} of “${capability.noun}”, and one build makes at most ${capability.maxPerPlan}.`\n }\n }\n }\n return null\n}\n\n/**\n * What the person must still say before this plan can be built, as the\n * questions its operations ask (`missingInput`), or `null` when nothing is\n * missing: an item the planner kept with nothing it may build from — a\n * tracking change naming no id, AGL-3751 — is asked about, never built\n * empty or from a value the planner made up. Not a plan rule: a re-ask\n * cannot supply what only the person knows, so the plan is not kept and\n * the build answers with the question.\n */\nexport function aiBuildPlanMissingInput(plan: AiBuildPlan, ops: AiBuildOps | null | undefined): string | null {\n if (!ops) return null\n const questions: string[] = []\n for (const item of plan.items ?? []) {\n const question = ops.get(item.op)?.missingInput?.({ name: item.name, why: item.why, args: item.args })\n if (question && !questions.includes(question)) questions.push(question)\n }\n return questions.length ? questions.join(' ') : null\n}\n\n/** \"a booking service\", \"an email design\". */\nexport function aiArticle(noun: string): string {\n return `${/^[aeiou]/i.test(noun) ? 'an' : 'a'} ${noun}`\n}\n\n/** The passes one unit is estimated at: a page's sections and its listing, one for a creation. */\nfunction unitPasses(unit: AiBuildUnit): number {\n if (unit.screen) return unit.screen.sections.length + 1\n return 1\n}\n\n/**\n * About what one unit costs, in credits: a page or a creation by the passes\n * it implies at the nominal credits a pass is held against, as a site's are;\n * an item by its capability's own estimate, recorded on the plan when it was\n * kept.\n */\nexport function aiBuildUnitCreditEstimate(unit: AiBuildUnit, ops?: AiBuildOps): number {\n // A store's own pages are written by code, with no model: no credits (AGL-3676).\n if (aiBuildUnitIsPlatform(unit)) return 0\n if (unit.item) {\n if (typeof unit.item.credits === 'number') return unit.item.credits\n const capability = ops?.get(unit.item.op)\n if (capability) return Math.max(0, Math.floor(capability.estimateCredits(unit.item.args)))\n return AI_SITE_PASS_CREDITS\n }\n return unitPasses(unit) * AI_SITE_PASS_CREDITS\n}\n\n/**\n * About what a build costs, in credits: every unit's estimate, or only the\n * units named in `slots` — what Try again shows. The ceiling a member\n * confirms; the meter charges what actually runs.\n */\nexport function aiBuildCreditEstimate(\n plan: AiBuildPlan,\n options: { ops?: AiBuildOps; slots?: ReadonlySet<string> } = {},\n): number {\n return aiBuildUnits(plan)\n .filter((unit) => !options.slots || options.slots.has(unit.slot))\n .reduce((total, unit) => total + aiBuildUnitCreditEstimate(unit, options.ops), 0)\n}\n\n/**\n * About what a job of this kind costs to build from its plan: a build's\n * units, every other planned kind as `aiJobPlanCreditEstimate` counts it.\n */\nexport function aiJobCreditEstimate(kind: string, plan: AiBuildPlan): number {\n return kind === 'build' ? aiBuildCreditEstimate(plan) : aiJobPlanCreditEstimate(kind, plan)\n}\n\n/**\n * What one unit is likely to cost, its p90 and its ceiling (AGL-3722): a page\n * as a page written on its own measured (a fixed cost and one per section), a creation at what its kind\n * measured, an item at the measured pass times the passes its capability\n * estimates (none for a draft another plugin writes, which asks no model).\n * The ceiling is `aiBuildUnitCreditEstimate`, unchanged.\n */\nexport function aiBuildUnitCreditRange(unit: AiBuildUnit, ops?: AiBuildOps): AiCreditRange {\n const ceiling = aiBuildUnitCreditEstimate(unit, ops)\n // A build writes each page on its own, never in a site start's warm run (AGL-3722).\n if (unit.screen) return aiStandaloneScreenCreditRange(unit.screen.sections.length)\n if (unit.creation) return aiCreditRangeOf(aiCreationMeasuredCredits(unit.creation.kind, { standalone: true }), ceiling)\n const passes = Math.ceil(ceiling / AI_SITE_PASS_CREDITS)\n return aiCreditRangeOf(\n { median: passes * AI_MEASURED_PASS_CREDITS.median, p90: passes * AI_MEASURED_PASS_CREDITS.p90 },\n ceiling,\n )\n}\n\n/**\n * What a build is likely to cost, its p90 and its ceiling (AGL-3722): every\n * unit's range, or only the units named in `slots` — what Try again runs.\n * The plan card, Try again and the Free admission all read this; the meter\n * charges what actually runs.\n */\nexport function aiBuildCreditRange(\n plan: AiBuildPlan,\n options: { ops?: AiBuildOps; slots?: ReadonlySet<string> } = {},\n): AiCreditRange {\n return aiCreditRangeOrdered(\n aiBuildUnits(plan)\n .filter((unit) => !options.slots || options.slots.has(unit.slot))\n .reduce((total, unit) => aiCreditRangeAdd(total, aiBuildUnitCreditRange(unit, options.ops)), AI_CREDIT_RANGE_ZERO),\n )\n}\n\n/** `aiJobCreditEstimate` as a range (AGL-3722). */\nexport function aiJobCreditRange(kind: string, plan: AiBuildPlan): AiCreditRange {\n return kind === 'build' ? aiBuildCreditRange(plan) : aiJobPlanCreditRange(kind, plan)\n}\n\n/**\n * A smaller first build (AGL-3722): the plan's home page — its first page\n * where it has no home — and only what that page needs, transitively: the\n * layout it is drawn in, the form and anything else its sections place.\n * Every other page, and what only they needed, waits for a later request.\n * `null` when the plan has no page, or when the smaller plan is the whole one.\n */\nexport function aiBuildFirstPagePlan<T extends AiBuildPlan>(plan: T): T | null {\n if (!plan.screens.length) return null\n const index = Math.max(0, plan.screens.findIndex(aiSitePlanIsHome))\n const units = aiBuildUnits(plan).filter((unit) => !aiBuildUnitIsPlatform(unit))\n const bySlot = new Map(units.map((unit) => [unit.slot, unit]))\n const keep = new Set<string>()\n const queue = [`p${index}`]\n while (queue.length) {\n const slot = queue.shift() as string\n if (keep.has(slot)) continue\n keep.add(slot)\n for (const dep of bySlot.get(slot)?.deps ?? []) queue.push(dep)\n }\n if (keep.size === units.length) return null\n const screen = plan.screens[index]\n const create = plan.create.filter((creation, at) => !AI_BUILD_CREATE_KINDS.includes(creation.kind) || keep.has(`c${at}`))\n const items = (plan.items ?? []).filter((item) => keep.has(item.slot))\n const kept = new Set([screen.slug, ...create.map((entry) => `${AI_PLAN_NEW_REF_PREFIX}${entry.name}`)])\n const smaller: T = {\n ...plan,\n create,\n screens: [screen],\n ...(plan.items ? { items } : {}),\n ...(plan.embeds ? { embeds: plan.embeds.filter((embed) => kept.has(embed.where)) } : {}),\n }\n // The store's own pages stay only while the smaller build still makes a store (AGL-3676).\n if (smaller.storePages && !aiBuildMakesStore(smaller)) delete smaller.storePages\n return smaller\n}\n\n/** The smaller first build a build that does not fit is offered (AGL-3722), with its range; `null` when there is none. */\nexport function aiBuildSmaller(plan: AiBuildPlan, ops?: AiBuildOps): AiCreditsSmaller | null {\n const smaller = aiBuildFirstPagePlan(plan)\n if (!smaller) return null\n const screen = smaller.screens[0]\n const label = aiSitePlanIsHome(screen) ? AI_SITE_HOME_FIRST_LABEL : `Build the ${screen.title} page first`\n return { label, ...aiBuildCreditRange(smaller, { ops }) }\n}\n\n/** A fresh ledger: one pending row per unit, first attempt. */\nexport function aiBuildInitialLedger(units: readonly AiBuildUnit[]): AiJobItemLedger[] {\n return units.map((unit) => ({\n slot: unit.slot,\n op: unit.op,\n label: unit.label,\n status: 'pending',\n attempt: 1,\n creditsSpent: 0,\n attemptCredits: 0,\n creditsRefunded: 0,\n failure: null,\n outputs: [],\n }))\n}\n\nconst OPEN: readonly AiJobItemStatus[] = ['pending', 'running']\n\n/** Whether an item is still to be built. */\nexport function aiBuildItemOpen(row: Pick<AiJobItemLedger, 'status'>): boolean {\n return OPEN.includes(row.status)\n}\n\n/** Whether an item delivered: built as planned, or built without something that failed. */\nexport function aiBuildItemDelivered(row: Pick<AiJobItemLedger, 'status'>): boolean {\n return row.status === 'succeeded' || row.status === 'degraded'\n}\n\n/**\n * The next unit to build: the first in build order whose row is still open.\n * Its dependencies come before it, so by then each is settled one way or the\n * other. `null` when nothing is left.\n */\nexport function aiBuildNextUnit(\n ordered: readonly AiBuildUnit[],\n ledger: readonly AiJobItemLedger[],\n): AiBuildUnit | null {\n const rows = new Map(ledger.map((row) => [row.slot, row]))\n return ordered.find((unit) => {\n const row = rows.get(unit.slot)\n return !row || aiBuildItemOpen(row)\n }) ?? null\n}\n\n/** How a unit is built given what its dependencies came to. */\nexport interface AiBuildDegradation {\n /** The unit is not built at all, with the sentence why; nothing is spent. */\n skip: string | null\n /** The failed dependencies that change how it is built. */\n degradedBy: string[]\n /** What the person reads about the change; one per failed dependency. */\n notes: string[]\n /** `new:<name>` references (lowercased names) to leave out. */\n dropped: Set<string>\n /** Brief lines for the unit's own job: what to leave out, and what to place. */\n briefLines: string[]\n}\n\n/**\n * How a unit is built when some of what it depends on failed (AGL-3616).\n *\n * A PAGE is always built: a failed layout falls back to the site's own, a\n * failed form or component is left out of its section, and a failed item a\n * section places (a booking service) takes its block out of the page — each\n * with a note. Any OTHER unit whose dependency failed is skipped when that\n * dependency's capability says `omit` (a campaign whose email design failed\n * has nothing to send), and built without it when it says `fallback`.\n */\nexport function aiBuildDegradation(\n unit: AiBuildUnit,\n context: {\n units: readonly AiBuildUnit[]\n ledger: readonly AiJobItemLedger[]\n ops?: AiBuildOps\n },\n): AiBuildDegradation {\n const rows = new Map(context.ledger.map((row) => [row.slot, row]))\n const bySlot = new Map(context.units.map((one) => [one.slot, one]))\n const result: AiBuildDegradation = { skip: null, degradedBy: [], notes: [], dropped: new Set(), briefLines: [] }\n for (const slot of unit.deps) {\n const dep = bySlot.get(slot)\n const row = rows.get(slot)\n if (!dep || !row || aiBuildItemDelivered(row)) continue\n const noun = dep.creation\n ? aiBuildCreationNoun(dep.creation.kind)\n : (context.ops?.get(dep.op)?.noun ?? dep.op)\n const capability = dep.item ? context.ops?.get(dep.op) : undefined\n const degrade = dep.item?.degrade ?? capability?.degrade ?? (dep.creation?.kind === 'layout' ? 'fallback' : 'omit')\n // A form whose dataset could not be made is built without it (AGL-3616):\n // its submissions reach the Inbox, as any form's do.\n if (aiBuildUnitWritesTo(unit) === dep.label.toLowerCase()) {\n result.degradedBy.push(slot)\n result.notes.push(`Its submissions arrive in the Inbox only: the dataset “${dep.label}” could not be created.`)\n continue\n }\n if (!unit.screen && degrade === 'omit') {\n result.skip = `Not built: the ${noun} “${dep.label}” it needs could not be created.`\n result.degradedBy.push(slot)\n return result\n }\n result.degradedBy.push(slot)\n result.dropped.add(dep.label.toLowerCase())\n if (dep.creation?.kind === 'layout') {\n result.notes.push(`Built inside the site’s own layout: the layout “${dep.label}” could not be created.`)\n continue\n }\n if (unit.screen) {\n const section = unit.screen.sections.find((one) =>\n one.uses.some((ref) => isAiPlanNewRef(ref) && aiBuildRefName(ref) === dep.label.toLowerCase()),\n )\n // Another item this section places with the same block was built (one\n // of several events, AGL-3743): the block stays, and only this item is\n // missing from what it shows.\n const blockStays = Boolean(\n section &&\n capability?.pageBlocks?.length &&\n aiBuildSectionDeliveredBlocks(section, { ...context, rows, except: slot }).has(aiBuildBlocksKey(capability.pageBlocks)),\n )\n if (blockStays) {\n result.notes.push(`Built without the ${noun} “${dep.label}”: it could not be created.`)\n result.briefLines.push(`Leave the ${noun} “${dep.label}” out of the “${section?.name}” section: it could not be created.`)\n continue\n }\n const blocks = capability?.pageBlocks?.length ? ` the ${capability.pageBlocks.join(' or ')} block` : ''\n result.notes.push(\n `Built without${blocks || ` the ${noun}`}: the ${noun} “${dep.label}” could not be created.`,\n )\n result.briefLines.push(\n `${section ? `Build the “${section.name}” section` : 'Build the page'} without the ${noun} “${dep.label}”${blocks ? ` and without${blocks}` : ''}: it could not be created.`,\n )\n } else {\n result.notes.push(`Built without the ${noun} “${dep.label}”: it could not be created.`)\n }\n }\n return result\n}\n\n/** The dataset a form unit writes to, by its `new:` name lowercased; `null` for any other unit (AGL-3616). */\nexport function aiBuildUnitWritesTo(unit: Pick<AiBuildUnit, 'creation'>): string | null {\n const ref = unit.creation?.kind === 'form' ? unit.creation.writesTo : null\n return isAiPlanNewRef(ref) ? aiBuildRefName(ref) : null\n}\n\n/** What each creation is called in a note. */\nexport function aiBuildCreationNoun(kind: AiBuildPlanCreateKind): string {\n return kind === 'email' ? 'email design' : kind === 'theme-change' ? 'theme change' : kind\n}\n\n/**\n * The brief lines a page is told about the items its sections place and\n * that were built: the block to put there, in the owner's words.\n */\nexport function aiBuildPlacedItemLines(\n unit: AiBuildUnit,\n context: {\n units: readonly AiBuildUnit[]\n ledger: readonly AiJobItemLedger[]\n ops?: AiBuildOps\n /** Operations whose items the platform places itself (a listing), so the page is told nothing of them. */\n placedByCode?: readonly string[]\n },\n): string[] {\n if (!unit.screen) return []\n const rows = new Map(context.ledger.map((row) => [row.slot, row]))\n const lines: string[] = []\n for (const section of unit.screen.sections) {\n // One line per block a section places, naming every built item it shows:\n // a section listing four events places the Event List once (AGL-3743).\n const placed = new Map<string, { blocks: readonly string[]; noun: string; labels: string[] }>()\n for (const { dep, capability } of aiBuildSectionItems(section, context.units, context.ops)) {\n const row = rows.get(dep.slot)\n if (!row || !aiBuildItemDelivered(row)) continue\n if (context.placedByCode?.includes(dep.op)) continue\n const blocks = capability?.pageBlocks ?? []\n if (!blocks.length) continue\n const key = aiBuildBlocksKey(blocks)\n const entry = placed.get(key) ?? { blocks, noun: capability?.noun ?? dep.op, labels: [] }\n entry.labels.push(`“${dep.label}”`)\n placed.set(key, entry)\n }\n for (const { blocks, noun, labels } of placed.values()) {\n const named =\n labels.length === 1\n ? `the ${noun} ${labels[0]}`\n : `the ${noun}s ${labels.slice(0, -1).join(', ')} and ${labels[labels.length - 1]}`\n lines.push(`In the “${section.name}” section, place the ${blocks.join(' or ')} block for ${named}.`)\n }\n }\n return lines\n}\n\n/** The items a page section places, each with its capability, in the order its uses name them. */\nfunction aiBuildSectionItems(\n section: { uses: readonly string[] },\n units: readonly AiBuildUnit[],\n ops: AiBuildOps | undefined,\n): Array<{ dep: AiBuildUnit; capability: PluginAiCapability | undefined }> {\n const found: Array<{ dep: AiBuildUnit; capability: PluginAiCapability | undefined }> = []\n for (const ref of section.uses) {\n if (!isAiPlanNewRef(ref)) continue\n const dep = units.find((one) => one.item && one.item.name.toLowerCase() === aiBuildRefName(ref))\n if (dep?.item) found.push({ dep, capability: ops?.get(dep.op) })\n }\n return found\n}\n\n/** The blocks a section's built items place, each set as one key; `except` leaves one item out. */\nfunction aiBuildSectionDeliveredBlocks(\n section: { uses: readonly string[] },\n context: { units: readonly AiBuildUnit[]; rows: ReadonlyMap<string, AiJobItemLedger>; ops?: AiBuildOps; except: string },\n): Set<string> {\n const keys = new Set<string>()\n for (const { dep, capability } of aiBuildSectionItems(section, context.units, context.ops)) {\n if (dep.slot === context.except || !capability?.pageBlocks?.length) continue\n const row = context.rows.get(dep.slot)\n if (row && aiBuildItemDelivered(row)) keys.add(aiBuildBlocksKey(capability.pageBlocks))\n }\n return keys\n}\n\nfunction aiBuildBlocksKey(blocks: readonly string[]): string {\n return [...blocks].sort().join('|')\n}\n\n/** What a build came to: whether anything was delivered, and whether anything is left. */\nexport function aiBuildSettlement(ledger: readonly AiJobItemLedger[]): {\n delivered: boolean\n open: boolean\n failed: number\n} {\n return {\n delivered: ledger.some(aiBuildItemDelivered),\n open: ledger.some(aiBuildItemOpen),\n failed: ledger.filter((row) => row.status === 'failed').length,\n }\n}\n\n/**\n * The ledger Try again runs (AGL-3616): every failed item back to `pending`\n * on its next attempt, with the items left unbuilt because of it — skipped,\n * or degraded with no draft — and every item the build never reached. An\n * item that succeeded is never run again, and a degraded page that has its\n * draft keeps it, with a note that the page needs an edit. `retried` names\n * the slots that will run, which is what the estimate is shown for.\n */\nexport function aiBuildRetryLedger(\n ledger: readonly AiJobItemLedger[],\n units: readonly AiBuildUnit[],\n): { ledger: AiJobItemLedger[]; retried: string[] } {\n const failed = new Set(ledger.filter((row) => row.status === 'failed').map((row) => row.slot))\n const dependents = new Map<string, string[]>()\n for (const unit of units) {\n for (const dep of unit.deps) dependents.set(dep, [...(dependents.get(dep) ?? []), unit.slot])\n }\n // Everything downstream of a failed item, transitively.\n const downstream = new Set<string>()\n const queue = [...failed]\n while (queue.length) {\n const slot = queue.shift() as string\n for (const next of dependents.get(slot) ?? []) {\n if (downstream.has(next)) continue\n downstream.add(next)\n queue.push(next)\n }\n }\n const retried: string[] = []\n const next = ledger.map((row): AiJobItemLedger => {\n const again =\n row.status === 'failed' ||\n (row.status === 'skipped' && (downstream.has(row.slot) || !(row.degradedBy ?? []).length)) ||\n (row.status === 'degraded' && downstream.has(row.slot) && !row.outputs.length)\n if (again) {\n retried.push(row.slot)\n return {\n ...row,\n status: 'pending',\n attempt: row.attempt + 1,\n attemptCredits: 0,\n failure: null,\n note: null,\n degradedBy: [],\n settledAt: null,\n }\n }\n if (row.status === 'degraded' && downstream.has(row.slot) && row.outputs.length) {\n return {\n ...row,\n note: `${row.note ? `${row.note} ` : ''}It keeps its draft: once the rest is built, edit this page to add what it is missing.`,\n }\n }\n return row\n })\n return { ledger: next, retried }\n}\n\n/**\n * Units read back off a ledger alone (AGL-3616), for a job whose units are\n * not a build plan's — a site scaffold's: each row as its unit, and every\n * page built after the layouts and forms the ledger holds.\n */\nexport function aiLedgerUnits(ledger: readonly AiJobItemLedger[]): AiBuildUnit[] {\n const creations = ledger.filter((row) => row.op === 'layout' || row.op === 'form').map((row) => row.slot)\n return ledger.map((row) => ({\n slot: row.slot,\n op: row.op,\n label: row.label,\n deps: row.op === 'page' ? creations : [],\n }))\n}\n\n/**\n * What the job drawer says under an item another plugin's writer made: a\n * draft, or, for setup its writer saves live (`live`, a CRM pipeline or\n * field, AGL-3746), that it was saved. A writer that went live only when the\n * member may (an event the publishing role publishes, AGL-3755) reports\n * `live: false` for one it kept as a draft, with its `notice` saying why.\n */\nexport function aiBuildItemOutputNote(\n capability: Pick<PluginAiCapability, 'where' | 'live'>,\n written?: Pick<PluginDraftRecord, 'live' | 'notice'>,\n): string {\n const live = written?.live ?? capability.live\n const said = live ? `Live now, in ${capability.where}.` : `A draft, in ${capability.where}.`\n const notice = written?.notice?.trim()\n return notice ? `${said} ${notice}` : said\n}\n"],"names":["pluginAiCapabilityArgsProblems","AI_PLAN_NEW_REF_PREFIX","isAiPlanNewRef","AI_BUILD_STORE_OP","AI_BUILD_STORE_PAGES_LABEL","AI_BUILD_STORE_SLOT","aiBuildMakesStore","aiBuildStorePagesAdded","AI_SITE_HOME_FIRST_LABEL","AI_SITE_MAX_SECTIONS","AI_SITE_PASS_CREDITS","aiCreationMeasuredCredits","aiJobPlanCreditEstimate","aiJobPlanCreditRange","aiSitePlanIsHome","aiStandaloneScreenCreditRange","AI_CREDIT_RANGE_ZERO","AI_MEASURED_PASS_CREDITS","aiCreditRangeAdd","aiCreditRangeOf","aiCreditRangeOrdered","AI_BUILD_LIMITS","units","pages","freePages","aiBuildFreePageCapNote","plan","freeTaste","cap","screens","length","AI_BUILD_CREATE_KINDS","AI_BUILD_STRUCTURAL_OPS","AI_BUILD_MADE_INPUT","AI_BUILD_PUBLISH_INPUT","creationOp","creation","kind","namedSlots","slots","Map","create","forEach","index","includes","set","name","toLowerCase","item","items","slot","aiBuildRefName","ref","slice","trim","aiBuildItemArgRefs","args","refs","take","value","part","split","push","Object","values","Array","isArray","aiBuildScreenRefs","screen","layout","template","duplicateOf","sections","flatMap","section","uses","filter","aiBuildUnits","pageSlots","title","has","depsOf","self","deps","get","undefined","op","label","writesTo","dependsOn","aiBuildUnitIsPlatform","unit","aiBuildOrder","ordered","placed","Set","known","map","next","find","every","dep","add","aiBuildGraphProblems","problems","aiBuildPageLimit","aiBuildPlanShapeRefusal","options","counts","slugs","slug","graph","ops","capability","freeAllowed","aiArticle","noun","argsSchema","join","count","maxPerPlan","aiBuildPlanMissingInput","questions","question","missingInput","why","test","unitPasses","aiBuildUnitCreditEstimate","credits","Math","max","floor","estimateCredits","aiBuildCreditEstimate","reduce","total","aiJobCreditEstimate","aiBuildUnitCreditRange","ceiling","standalone","passes","ceil","median","p90","aiBuildCreditRange","aiJobCreditRange","aiBuildFirstPagePlan","findIndex","bySlot","keep","queue","shift","size","at","kept","entry","smaller","embeds","embed","where","storePages","aiBuildSmaller","aiBuildInitialLedger","status","attempt","creditsSpent","attemptCredits","creditsRefunded","failure","outputs","OPEN","aiBuildItemOpen","row","aiBuildItemDelivered","aiBuildNextUnit","ledger","rows","aiBuildDegradation","context","one","result","skip","degradedBy","notes","dropped","briefLines","aiBuildCreationNoun","degrade","aiBuildUnitWritesTo","some","blockStays","Boolean","pageBlocks","aiBuildSectionDeliveredBlocks","except","aiBuildBlocksKey","blocks","aiBuildPlacedItemLines","lines","aiBuildSectionItems","placedByCode","key","labels","named","found","keys","sort","aiBuildSettlement","delivered","open","failed","aiBuildRetryLedger","dependents","downstream","retried","again","note","settledAt","aiLedgerUnits","creations","aiBuildItemOutputNote","written","live","said","notice"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-build-job.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 {\n pluginAiCapabilityArgsProblems,\n type PluginAiCapability,\n} from '@aglyn/aglyn/plugin-manager/plugin-ai-capabilities'\nimport type { PluginDraftRecord } from '@aglyn/aglyn/plugin-manager/plugin-resource-drafts'\nimport {\n AI_PLAN_NEW_REF_PREFIX,\n isAiPlanNewRef,\n type AiBuildItem,\n type AiBuildPlan,\n type AiBuildPlanCreate,\n type AiBuildPlanCreateKind,\n type AiBuildPlanScreen,\n} from './ai-build-plan'\nimport type { AiJobItemLedger, AiJobItemStatus } from './ai-jobs.types'\nimport {\n AI_BUILD_STORE_OP,\n AI_BUILD_STORE_PAGES_LABEL,\n AI_BUILD_STORE_SLOT,\n aiBuildMakesStore,\n aiBuildStorePagesAdded,\n} from './ai-build-store-pages'\nimport {\n AI_SITE_HOME_FIRST_LABEL,\n AI_SITE_MAX_SECTIONS,\n AI_SITE_PASS_CREDITS,\n aiCreationMeasuredCredits,\n aiJobPlanCreditEstimate,\n aiJobPlanCreditRange,\n aiSitePlanIsHome,\n aiStandaloneScreenCreditRange,\n} from './ai-site-job'\nimport {\n AI_CREDIT_RANGE_ZERO,\n AI_MEASURED_PASS_CREDITS,\n aiCreditRangeAdd,\n aiCreditRangeOf,\n aiCreditRangeOrdered,\n type AiCreditRange,\n type AiCreditsSmaller,\n} from './ai-credit-estimate'\n\n/**\n * What a `build` job is (AGL-3616): one request — \"a few pages, a contact\n * form and a way to book me\" — planned as creations, pages and items, built\n * one UNIT a pass in dependency order, and settled ITEM BY ITEM.\n *\n * A build is a generalized site scaffold. Every unit is handed to what\n * already builds that kind of thing — a page to the page step, a form to the\n * form step, another plugin's resource to that plugin's draft writer — so a\n * build cannot drift from what it builds. What is new is the settlement: one\n * failed unit does not end the job. It gives back its own spend, the units\n * that depended on it are built without it (or skipped, when they cannot\n * stand without it), and the job is `done` when anything was delivered.\n *\n * This module is the build's MODEL — its units, their order, how a failure\n * travels to what depends on it, what it is estimated to cost, and the\n * ledger arithmetic — and does no I/O, so the step, the machine, the doors\n * and the console read one vocabulary.\n */\n\n/** How much one build holds: what one proposal can describe and one person can read. */\nexport const AI_BUILD_LIMITS = {\n /** Creations, pages and items together. */\n units: 16,\n pages: 8,\n /** A Free workspace's pages. */\n freePages: 2,\n} as const\n\n/**\n * What a Free build's plan card says when it holds as many pages as a Free\n * build makes (AGL-3722): the plan is told the cap and plans the first pages\n * a request asks for, so a request for three — \"About us, Custom cakes and\n * Catering\", 2026-10-10 — came back as two with nothing saying why. `null`\n * when the plan holds fewer, or on a paid workspace.\n */\nexport function aiBuildFreePageCapNote(plan: Pick<AiBuildPlan, 'screens'>, freeTaste: boolean): string | null {\n const cap = AI_BUILD_LIMITS.freePages\n if (!freeTaste || plan.screens.length < cap) return null\n return `A Free workspace’s build makes up to ${cap} pages at a time. If you asked for more, ask Assist for the rest once this build is done.`\n}\n\n/** What a build creates through its `create` list; everything else beyond pages is an item. */\nexport const AI_BUILD_CREATE_KINDS: readonly AiBuildPlanCreateKind[] = ['layout', 'form', 'component', 'email']\n\n/**\n * The operations a build plans through `create` and `screens` rather than\n * `items`: an item naming one is a plan that put it in the wrong list.\n */\nexport const AI_BUILD_STRUCTURAL_OPS: readonly string[] = ['page', ...AI_BUILD_CREATE_KINDS]\n\n/**\n * What a build hands an item it made before it (AGL-3728): its pages, by the\n * title the plan gave each and the id it was written under, and its layouts,\n * by name. An item's `new:<title>` destinations are read against it.\n */\nexport const AI_BUILD_MADE_INPUT = 'buildMade'\n\nexport interface AiBuildMade {\n pages: Array<{ title: string; id: string }>\n layouts: Array<{ name: string; id: string }>\n}\n\n/** The job input a build's Assist proposal sets when the brief asked to publish. */\nexport const AI_BUILD_PUBLISH_INPUT = 'publish'\n\n/** A unit of a build: a creation, a page, or an item. */\nexport interface AiBuildUnit {\n slot: string\n /** The capability operation that builds it. */\n op: string\n label: string\n creation?: AiBuildPlanCreate\n screen?: AiBuildPlanScreen\n item?: AiBuildItem\n /** The slots it is built after, in plan order. */\n deps: string[]\n}\n\n/** The operation a creation is built as. */\nfunction creationOp(creation: AiBuildPlanCreate): string {\n return creation.kind\n}\n\n/** A plan's `new:<name>` names, each to the slot that builds it. */\nfunction namedSlots(plan: AiBuildPlan): Map<string, string> {\n const slots = new Map<string, string>()\n plan.create.forEach((creation, index) => {\n if (AI_BUILD_CREATE_KINDS.includes(creation.kind)) slots.set(creation.name.toLowerCase(), `c${index}`)\n })\n for (const item of plan.items ?? []) slots.set(item.name.toLowerCase(), item.slot)\n return slots\n}\n\n/** The name a `new:<name>` reference gives, lowercased. */\nexport function aiBuildRefName(ref: string): string {\n return ref.slice(AI_PLAN_NEW_REF_PREFIX.length).trim().toLowerCase()\n}\n\n/**\n * The `new:<name>` references an item's arguments make (AGL-3728): an\n * argument that is one, or the destination after `=>` in an entry such as a\n * menu item's `About => new:About`. Read from the arguments, never from the\n * model's `dependsOn`, so what an item links is always built before it.\n */\nexport function aiBuildItemArgRefs(args: Readonly<Record<string, unknown>> | null | undefined): string[] {\n const refs: string[] = []\n const take = (value: unknown) => {\n if (typeof value !== 'string') return\n for (const part of [value, value.split('=>').slice(-1)[0] ?? '']) {\n const ref = part.trim()\n if (isAiPlanNewRef(ref) && !refs.includes(ref)) refs.push(ref)\n }\n }\n for (const value of Object.values(args ?? {})) {\n if (Array.isArray(value)) value.forEach(take)\n else take(value)\n }\n return refs\n}\n\n/** Every reference a page makes: its layout, template, starting page and what its sections place. */\nexport function aiBuildScreenRefs(screen: AiBuildPlanScreen): string[] {\n return [\n screen.layout,\n screen.template,\n screen.duplicateOf,\n ...screen.sections.flatMap((section) => section.uses),\n ].filter((ref): ref is string => typeof ref === 'string' && ref.length > 0)\n}\n\n/**\n * The units a plan implies, in plan order: creations, then items, then\n * pages — the order a member reads them in, and the one a page that places\n * a creation or an item is built after anyway. `aiBuildOrder` sorts them on\n * their dependencies.\n */\nexport function aiBuildUnits(plan: AiBuildPlan): AiBuildUnit[] {\n const slots = namedSlots(plan)\n // A page, by the title the plan gave it: what an item's arguments may link\n // as `new:<title>` (a menu item, AGL-3728), built before the item.\n const pageSlots = new Map<string, string>()\n plan.screens.forEach((screen, index) => {\n const title = screen.title.trim().toLowerCase()\n if (title && !pageSlots.has(title)) pageSlots.set(title, `p${index}`)\n })\n const depsOf = (refs: readonly (string | null)[], self: string, pages = false): string[] => {\n const deps: string[] = []\n for (const ref of refs) {\n if (!isAiPlanNewRef(ref)) continue\n const slot = slots.get(aiBuildRefName(ref)) ?? (pages ? pageSlots.get(aiBuildRefName(ref)) : undefined)\n if (slot && slot !== self && !deps.includes(slot)) deps.push(slot)\n }\n return deps\n }\n const units: AiBuildUnit[] = []\n plan.create.forEach((creation, index) => {\n if (!AI_BUILD_CREATE_KINDS.includes(creation.kind)) return\n const slot = `c${index}`\n // A form is built after the dataset it writes to (AGL-3616).\n units.push({\n slot,\n op: creationOp(creation),\n label: creation.name,\n creation,\n deps: depsOf([creation.duplicateOf, creation.kind === 'form' ? (creation.writesTo ?? null) : null], slot),\n })\n })\n for (const item of plan.items ?? []) {\n units.push({\n slot: item.slot,\n op: item.op,\n label: item.name,\n item,\n // What it depends on, and what its arguments link as `new:` (AGL-3728).\n deps: depsOf([...item.dependsOn, ...aiBuildItemArgRefs(item.args)], item.slot, true),\n })\n }\n plan.screens.forEach((screen, index) => {\n const slot = `p${index}`\n units.push({ slot, op: 'page', label: screen.title, screen, deps: depsOf(aiBuildScreenRefs(screen), slot) })\n })\n // A store's own pages (AGL-3676), added by the platform: last, so every\n // planned page keeps the first claim on the site's pages allowance.\n if (aiBuildStorePagesAdded(plan).length) {\n units.push({ slot: AI_BUILD_STORE_SLOT, op: AI_BUILD_STORE_OP, label: AI_BUILD_STORE_PAGES_LABEL, deps: [] })\n }\n return units\n}\n\n/** Whether a unit is one the platform adds rather than the plan (AGL-3676): a store's own pages. */\nexport function aiBuildUnitIsPlatform(unit: Pick<AiBuildUnit, 'op'>): boolean {\n return unit.op === AI_BUILD_STORE_OP\n}\n\n/**\n * The units in an order that builds every dependency before what depends on\n * it, plan order otherwise; `null` when the dependencies hold a cycle.\n */\nexport function aiBuildOrder(units: readonly AiBuildUnit[]): AiBuildUnit[] | null {\n const ordered: AiBuildUnit[] = []\n const placed = new Set<string>()\n const known = new Set(units.map((unit) => unit.slot))\n while (ordered.length < units.length) {\n const next = units.find(\n (unit) => !placed.has(unit.slot) && unit.deps.every((dep) => placed.has(dep) || !known.has(dep)),\n )\n if (!next) return null\n ordered.push(next)\n placed.add(next.slot)\n }\n return ordered\n}\n\n/**\n * What is wrong with a plan's references as a build reads them: a\n * `new:<name>` an item depends on that nothing in the plan makes, and a\n * cycle. Each is a sentence the plan's one re-ask carries.\n */\nexport function aiBuildGraphProblems(plan: AiBuildPlan): string[] {\n const problems: string[] = []\n const slots = namedSlots(plan)\n for (const item of plan.items ?? []) {\n for (const ref of item.dependsOn) {\n if (!isAiPlanNewRef(ref)) continue\n if (!slots.has(aiBuildRefName(ref))) {\n problems.push(`The item \"${item.name}\" depends on ${ref}, which the plan never makes. Declare it, or take it out of dependsOn.`)\n } else if (aiBuildRefName(ref) === item.name.toLowerCase()) {\n problems.push(`The item \"${item.name}\" depends on itself. Take it out of its own dependsOn.`)\n }\n }\n }\n if (!aiBuildOrder(aiBuildUnits(plan))) {\n problems.push('The plan’s dependencies go round in a circle, so nothing in the circle can be built first. Make one of them depend on nothing.')\n }\n return problems\n}\n\n/** The operations a build may plan, as the registry and the gates left them, by op. */\nexport type AiBuildOps = ReadonlyMap<string, PluginAiCapability>\n\n/** How many pages a build may hold on this workspace. */\nexport function aiBuildPageLimit(freeTaste: boolean | null | undefined): number {\n return freeTaste ? AI_BUILD_LIMITS.freePages : AI_BUILD_LIMITS.pages\n}\n\n/**\n * Why a build cannot build a plan of this shape, in a sentence a member\n * reads; `null` when it can. Held to the plan with the one re-ask (the plan\n * step's scope), and again by the doors before a confirmed plan runs.\n * `ops` is what may be planned on this site; absent, items are not judged\n * against the registry (a console reading a kept plan).\n */\nexport function aiBuildPlanShapeRefusal(\n plan: AiBuildPlan,\n options: { freeTaste?: boolean; ops?: AiBuildOps } = {},\n): string | null {\n // What the platform adds beside the plan (a store's own pages) is no part of its size.\n const units = aiBuildUnits(plan).filter((unit) => !aiBuildUnitIsPlatform(unit))\n if (!units.length) return 'This plan builds nothing. Describe what to build again.'\n if (units.length > AI_BUILD_LIMITS.units) {\n return `This plan builds ${units.length} things, and one build holds ${AI_BUILD_LIMITS.units}. Ask for the rest in a second request.`\n }\n const pages = aiBuildPageLimit(options.freeTaste)\n if (plan.screens.length > pages) {\n return options.freeTaste\n ? `This plan builds ${plan.screens.length} pages, and a Free workspace's build makes at most ${pages}. Paid plans can build more.`\n : `This plan builds ${plan.screens.length} pages, and one build makes at most ${pages}.`\n }\n const slugs = new Set<string>()\n for (const screen of plan.screens) {\n if (!screen.sections.length) return `The page “${screen.title}” has no sections to build.`\n if (screen.sections.length > AI_SITE_MAX_SECTIONS) {\n return `The page “${screen.title}” has ${screen.sections.length} sections, and a built page holds ${AI_SITE_MAX_SECTIONS}.`\n }\n const slug = screen.slug.trim().toLowerCase()\n if (slugs.has(slug)) return `Two pages in this plan share the address ${screen.slug}.`\n slugs.add(slug)\n }\n for (const creation of plan.create) {\n if (!AI_BUILD_CREATE_KINDS.includes(creation.kind)) {\n return `A build does not create ${creation.kind === 'theme-change' ? 'theme changes' : `${creation.kind}s`} (“${creation.name}”). Take it out of create.`\n }\n }\n const graph = aiBuildGraphProblems(plan)\n if (graph.length) return graph[0]\n const ops = options.ops\n const counts = new Map<string, number>()\n for (const unit of units) counts.set(unit.op, (counts.get(unit.op) ?? 0) + 1)\n for (const item of plan.items ?? []) {\n if (AI_BUILD_STRUCTURAL_OPS.includes(item.op)) {\n return item.op === 'page'\n ? `“${item.name}” is a page: plan it in screens, not items.`\n : `“${item.name}” is ${item.op === 'email' ? 'an' : 'a'} ${item.op}: plan it in create, not items.`\n }\n if (!ops) continue\n const capability = ops.get(item.op)\n if (!capability) return `Nothing on this site can make “${item.name}” (${item.op}). Take it out of items.`\n if (options.freeTaste && !capability.freeAllowed) {\n return `A Free workspace cannot have ${aiArticle(capability.noun)} made (“${item.name}”). Take it out of items.`\n }\n const problems = pluginAiCapabilityArgsProblems(capability.argsSchema, item.args)\n if (problems.length) return `The arguments of “${item.name}” do not read: ${problems.join('; ')}.`\n }\n if (ops) {\n for (const [op, count] of counts) {\n const capability = ops.get(op)\n if (capability && count > capability.maxPerPlan) {\n return `This plan makes ${count} of “${capability.noun}”, and one build makes at most ${capability.maxPerPlan}.`\n }\n }\n }\n return null\n}\n\n/**\n * What the person must still say before this plan can be built, as the\n * questions its operations ask (`missingInput`), or `null` when nothing is\n * missing: an item the planner kept with nothing it may build from — a\n * tracking change naming no id, AGL-3751 — is asked about, never built\n * empty or from a value the planner made up. Not a plan rule: a re-ask\n * cannot supply what only the person knows, so the plan is not kept and\n * the build answers with the question.\n */\nexport function aiBuildPlanMissingInput(plan: AiBuildPlan, ops: AiBuildOps | null | undefined): string | null {\n if (!ops) return null\n const questions: string[] = []\n for (const item of plan.items ?? []) {\n const question = ops.get(item.op)?.missingInput?.({ name: item.name, why: item.why, args: item.args })\n if (question && !questions.includes(question)) questions.push(question)\n }\n return questions.length ? questions.join(' ') : null\n}\n\n/** \"a booking service\", \"an email design\". */\nexport function aiArticle(noun: string): string {\n return `${/^[aeiou]/i.test(noun) ? 'an' : 'a'} ${noun}`\n}\n\n/** The passes one unit is estimated at: a page's sections and its listing, one for a creation. */\nfunction unitPasses(unit: AiBuildUnit): number {\n if (unit.screen) return unit.screen.sections.length + 1\n return 1\n}\n\n/**\n * About what one unit costs, in credits: a page or a creation by the passes\n * it implies at the nominal credits a pass is held against, as a site's are;\n * an item by its capability's own estimate, recorded on the plan when it was\n * kept.\n */\nexport function aiBuildUnitCreditEstimate(unit: AiBuildUnit, ops?: AiBuildOps): number {\n // A store's own pages are written by code, with no model: no credits (AGL-3676).\n if (aiBuildUnitIsPlatform(unit)) return 0\n if (unit.item) {\n if (typeof unit.item.credits === 'number') return unit.item.credits\n const capability = ops?.get(unit.item.op)\n if (capability) return Math.max(0, Math.floor(capability.estimateCredits(unit.item.args)))\n return AI_SITE_PASS_CREDITS\n }\n return unitPasses(unit) * AI_SITE_PASS_CREDITS\n}\n\n/**\n * About what a build costs, in credits: every unit's estimate, or only the\n * units named in `slots` — what Try again shows. The ceiling a member\n * confirms; the meter charges what actually runs.\n */\nexport function aiBuildCreditEstimate(\n plan: AiBuildPlan,\n options: { ops?: AiBuildOps; slots?: ReadonlySet<string> } = {},\n): number {\n return aiBuildUnits(plan)\n .filter((unit) => !options.slots || options.slots.has(unit.slot))\n .reduce((total, unit) => total + aiBuildUnitCreditEstimate(unit, options.ops), 0)\n}\n\n/**\n * About what a job of this kind costs to build from its plan: a build's\n * units, every other planned kind as `aiJobPlanCreditEstimate` counts it.\n */\nexport function aiJobCreditEstimate(kind: string, plan: AiBuildPlan): number {\n return kind === 'build' ? aiBuildCreditEstimate(plan) : aiJobPlanCreditEstimate(kind, plan)\n}\n\n/**\n * What one unit is likely to cost, its p90 and its ceiling (AGL-3722): a page\n * as a page written on its own measured (a fixed cost and one per section), a creation at what its kind\n * measured, an item at the measured pass times the passes its capability\n * estimates (none for a draft another plugin writes, which asks no model).\n * The ceiling is `aiBuildUnitCreditEstimate`, unchanged.\n */\nexport function aiBuildUnitCreditRange(unit: AiBuildUnit, ops?: AiBuildOps): AiCreditRange {\n const ceiling = aiBuildUnitCreditEstimate(unit, ops)\n // A build writes each page on its own, never in a site start's warm run (AGL-3722).\n if (unit.screen) return aiStandaloneScreenCreditRange(unit.screen.sections.length)\n if (unit.creation) return aiCreditRangeOf(aiCreationMeasuredCredits(unit.creation.kind, { standalone: true }), ceiling)\n const passes = Math.ceil(ceiling / AI_SITE_PASS_CREDITS)\n return aiCreditRangeOf(\n { median: passes * AI_MEASURED_PASS_CREDITS.median, p90: passes * AI_MEASURED_PASS_CREDITS.p90 },\n ceiling,\n )\n}\n\n/**\n * What a build is likely to cost, its p90 and its ceiling (AGL-3722): every\n * unit's range, or only the units named in `slots` — what Try again runs.\n * The plan card, Try again and the Free admission all read this; the meter\n * charges what actually runs.\n */\nexport function aiBuildCreditRange(\n plan: AiBuildPlan,\n options: { ops?: AiBuildOps; slots?: ReadonlySet<string> } = {},\n): AiCreditRange {\n return aiCreditRangeOrdered(\n aiBuildUnits(plan)\n .filter((unit) => !options.slots || options.slots.has(unit.slot))\n .reduce((total, unit) => aiCreditRangeAdd(total, aiBuildUnitCreditRange(unit, options.ops)), AI_CREDIT_RANGE_ZERO),\n )\n}\n\n/** `aiJobCreditEstimate` as a range (AGL-3722). */\nexport function aiJobCreditRange(kind: string, plan: AiBuildPlan): AiCreditRange {\n return kind === 'build' ? aiBuildCreditRange(plan) : aiJobPlanCreditRange(kind, plan)\n}\n\n/**\n * A smaller first build (AGL-3722): the plan's home page — its first page\n * where it has no home — and only what that page needs, transitively: the\n * layout it is drawn in, the form and anything else its sections place.\n * Every other page, and what only they needed, waits for a later request.\n * `null` when the plan has no page, or when the smaller plan is the whole one.\n */\nexport function aiBuildFirstPagePlan<T extends AiBuildPlan>(plan: T): T | null {\n if (!plan.screens.length) return null\n const index = Math.max(0, plan.screens.findIndex(aiSitePlanIsHome))\n const units = aiBuildUnits(plan).filter((unit) => !aiBuildUnitIsPlatform(unit))\n const bySlot = new Map(units.map((unit) => [unit.slot, unit]))\n const keep = new Set<string>()\n const queue = [`p${index}`]\n while (queue.length) {\n const slot = queue.shift() as string\n if (keep.has(slot)) continue\n keep.add(slot)\n for (const dep of bySlot.get(slot)?.deps ?? []) queue.push(dep)\n }\n if (keep.size === units.length) return null\n const screen = plan.screens[index]\n const create = plan.create.filter((creation, at) => !AI_BUILD_CREATE_KINDS.includes(creation.kind) || keep.has(`c${at}`))\n const items = (plan.items ?? []).filter((item) => keep.has(item.slot))\n const kept = new Set([screen.slug, ...create.map((entry) => `${AI_PLAN_NEW_REF_PREFIX}${entry.name}`)])\n const smaller: T = {\n ...plan,\n create,\n screens: [screen],\n ...(plan.items ? { items } : {}),\n ...(plan.embeds ? { embeds: plan.embeds.filter((embed) => kept.has(embed.where)) } : {}),\n }\n // The store's own pages stay only while the smaller build still makes a store (AGL-3676).\n if (smaller.storePages && !aiBuildMakesStore(smaller)) delete smaller.storePages\n return smaller\n}\n\n/** The smaller first build a build that does not fit is offered (AGL-3722), with its range; `null` when there is none. */\nexport function aiBuildSmaller(plan: AiBuildPlan, ops?: AiBuildOps): AiCreditsSmaller | null {\n const smaller = aiBuildFirstPagePlan(plan)\n if (!smaller) return null\n const screen = smaller.screens[0]\n const label = aiSitePlanIsHome(screen) ? AI_SITE_HOME_FIRST_LABEL : `Build the ${screen.title} page first`\n return { label, ...aiBuildCreditRange(smaller, { ops }) }\n}\n\n/** A fresh ledger: one pending row per unit, first attempt. */\nexport function aiBuildInitialLedger(units: readonly AiBuildUnit[]): AiJobItemLedger[] {\n return units.map((unit) => ({\n slot: unit.slot,\n op: unit.op,\n label: unit.label,\n status: 'pending',\n attempt: 1,\n creditsSpent: 0,\n attemptCredits: 0,\n creditsRefunded: 0,\n failure: null,\n outputs: [],\n }))\n}\n\nconst OPEN: readonly AiJobItemStatus[] = ['pending', 'running']\n\n/** Whether an item is still to be built. */\nexport function aiBuildItemOpen(row: Pick<AiJobItemLedger, 'status'>): boolean {\n return OPEN.includes(row.status)\n}\n\n/** Whether an item delivered: built as planned, or built without something that failed. */\nexport function aiBuildItemDelivered(row: Pick<AiJobItemLedger, 'status'>): boolean {\n return row.status === 'succeeded' || row.status === 'degraded'\n}\n\n/**\n * The next unit to build: the first in build order whose row is still open.\n * Its dependencies come before it, so by then each is settled one way or the\n * other. `null` when nothing is left.\n */\nexport function aiBuildNextUnit(\n ordered: readonly AiBuildUnit[],\n ledger: readonly AiJobItemLedger[],\n): AiBuildUnit | null {\n const rows = new Map(ledger.map((row) => [row.slot, row]))\n return ordered.find((unit) => {\n const row = rows.get(unit.slot)\n return !row || aiBuildItemOpen(row)\n }) ?? null\n}\n\n/** How a unit is built given what its dependencies came to. */\nexport interface AiBuildDegradation {\n /** The unit is not built at all, with the sentence why; nothing is spent. */\n skip: string | null\n /** The failed dependencies that change how it is built. */\n degradedBy: string[]\n /** What the person reads about the change; one per failed dependency. */\n notes: string[]\n /** `new:<name>` references (lowercased names) to leave out. */\n dropped: Set<string>\n /** Brief lines for the unit's own job: what to leave out, and what to place. */\n briefLines: string[]\n}\n\n/**\n * How a unit is built when some of what it depends on failed (AGL-3616).\n *\n * A PAGE is always built: a failed layout falls back to the site's own, a\n * failed form or component is left out of its section, and a failed item a\n * section places (a booking service) takes its block out of the page — each\n * with a note. Any OTHER unit whose dependency failed is skipped when that\n * dependency's capability says `omit` (a campaign whose email design failed\n * has nothing to send), and built without it when it says `fallback`.\n */\nexport function aiBuildDegradation(\n unit: AiBuildUnit,\n context: {\n units: readonly AiBuildUnit[]\n ledger: readonly AiJobItemLedger[]\n ops?: AiBuildOps\n },\n): AiBuildDegradation {\n const rows = new Map(context.ledger.map((row) => [row.slot, row]))\n const bySlot = new Map(context.units.map((one) => [one.slot, one]))\n const result: AiBuildDegradation = { skip: null, degradedBy: [], notes: [], dropped: new Set(), briefLines: [] }\n for (const slot of unit.deps) {\n const dep = bySlot.get(slot)\n const row = rows.get(slot)\n if (!dep || !row || aiBuildItemDelivered(row)) continue\n const noun = dep.creation\n ? aiBuildCreationNoun(dep.creation.kind)\n : (context.ops?.get(dep.op)?.noun ?? dep.op)\n const capability = dep.item ? context.ops?.get(dep.op) : undefined\n const degrade = dep.item?.degrade ?? capability?.degrade ?? (dep.creation?.kind === 'layout' ? 'fallback' : 'omit')\n // A form whose dataset could not be made is built without it (AGL-3616):\n // its submissions reach the Inbox, as any form's do.\n if (aiBuildUnitWritesTo(unit) === dep.label.toLowerCase()) {\n result.degradedBy.push(slot)\n result.notes.push(`Its submissions arrive in the Inbox only: the dataset “${dep.label}” could not be created.`)\n continue\n }\n // An item that links pages this build makes (a menu's entries, AGL-3728)\n // is built with the ones it made and without the ones it could not\n // (AGL-3832), and skipped only when it made none of them.\n const madeAPage = unit.deps.some((one) => {\n const made = rows.get(one)\n return !!bySlot.get(one)?.screen && !!made && aiBuildItemDelivered(made)\n })\n if (!unit.screen && dep.screen && madeAPage) {\n result.degradedBy.push(slot)\n result.dropped.add(dep.label.toLowerCase())\n result.notes.push(`Built without the ${noun} “${dep.label}”: it could not be created.`)\n continue\n }\n if (!unit.screen && degrade === 'omit') {\n result.skip = `Not built: the ${noun} “${dep.label}” it needs could not be created.`\n result.degradedBy.push(slot)\n return result\n }\n result.degradedBy.push(slot)\n result.dropped.add(dep.label.toLowerCase())\n if (dep.creation?.kind === 'layout') {\n result.notes.push(`Built inside the site’s own layout: the layout “${dep.label}” could not be created.`)\n continue\n }\n if (unit.screen) {\n const section = unit.screen.sections.find((one) =>\n one.uses.some((ref) => isAiPlanNewRef(ref) && aiBuildRefName(ref) === dep.label.toLowerCase()),\n )\n // Another item this section places with the same block was built (one\n // of several events, AGL-3743): the block stays, and only this item is\n // missing from what it shows.\n const blockStays = Boolean(\n section &&\n capability?.pageBlocks?.length &&\n aiBuildSectionDeliveredBlocks(section, { ...context, rows, except: slot }).has(aiBuildBlocksKey(capability.pageBlocks)),\n )\n if (blockStays) {\n result.notes.push(`Built without the ${noun} “${dep.label}”: it could not be created.`)\n result.briefLines.push(`Leave the ${noun} “${dep.label}” out of the “${section?.name}” section: it could not be created.`)\n continue\n }\n const blocks = capability?.pageBlocks?.length ? ` the ${capability.pageBlocks.join(' or ')} block` : ''\n result.notes.push(\n `Built without${blocks || ` the ${noun}`}: the ${noun} “${dep.label}” could not be created.`,\n )\n result.briefLines.push(\n `${section ? `Build the “${section.name}” section` : 'Build the page'} without the ${noun} “${dep.label}”${blocks ? ` and without${blocks}` : ''}: it could not be created.`,\n )\n } else {\n result.notes.push(`Built without the ${noun} “${dep.label}”: it could not be created.`)\n }\n }\n return result\n}\n\n/** The dataset a form unit writes to, by its `new:` name lowercased; `null` for any other unit (AGL-3616). */\nexport function aiBuildUnitWritesTo(unit: Pick<AiBuildUnit, 'creation'>): string | null {\n const ref = unit.creation?.kind === 'form' ? unit.creation.writesTo : null\n return isAiPlanNewRef(ref) ? aiBuildRefName(ref) : null\n}\n\n/** What each creation is called in a note. */\nexport function aiBuildCreationNoun(kind: AiBuildPlanCreateKind): string {\n return kind === 'email' ? 'email design' : kind === 'theme-change' ? 'theme change' : kind\n}\n\n/**\n * The brief lines a page is told about the items its sections place and\n * that were built: the block to put there, in the owner's words.\n */\nexport function aiBuildPlacedItemLines(\n unit: AiBuildUnit,\n context: {\n units: readonly AiBuildUnit[]\n ledger: readonly AiJobItemLedger[]\n ops?: AiBuildOps\n /** Operations whose items the platform places itself (a listing), so the page is told nothing of them. */\n placedByCode?: readonly string[]\n },\n): string[] {\n if (!unit.screen) return []\n const rows = new Map(context.ledger.map((row) => [row.slot, row]))\n const lines: string[] = []\n for (const section of unit.screen.sections) {\n // One line per block a section places, naming every built item it shows:\n // a section listing four events places the Event List once (AGL-3743).\n const placed = new Map<string, { blocks: readonly string[]; noun: string; labels: string[] }>()\n for (const { dep, capability } of aiBuildSectionItems(section, context.units, context.ops)) {\n const row = rows.get(dep.slot)\n if (!row || !aiBuildItemDelivered(row)) continue\n if (context.placedByCode?.includes(dep.op)) continue\n const blocks = capability?.pageBlocks ?? []\n if (!blocks.length) continue\n const key = aiBuildBlocksKey(blocks)\n const entry = placed.get(key) ?? { blocks, noun: capability?.noun ?? dep.op, labels: [] }\n entry.labels.push(`“${dep.label}”`)\n placed.set(key, entry)\n }\n for (const { blocks, noun, labels } of placed.values()) {\n const named =\n labels.length === 1\n ? `the ${noun} ${labels[0]}`\n : `the ${noun}s ${labels.slice(0, -1).join(', ')} and ${labels[labels.length - 1]}`\n lines.push(`In the “${section.name}” section, place the ${blocks.join(' or ')} block for ${named}.`)\n }\n }\n return lines\n}\n\n/** The items a page section places, each with its capability, in the order its uses name them. */\nfunction aiBuildSectionItems(\n section: { uses: readonly string[] },\n units: readonly AiBuildUnit[],\n ops: AiBuildOps | undefined,\n): Array<{ dep: AiBuildUnit; capability: PluginAiCapability | undefined }> {\n const found: Array<{ dep: AiBuildUnit; capability: PluginAiCapability | undefined }> = []\n for (const ref of section.uses) {\n if (!isAiPlanNewRef(ref)) continue\n const dep = units.find((one) => one.item && one.item.name.toLowerCase() === aiBuildRefName(ref))\n if (dep?.item) found.push({ dep, capability: ops?.get(dep.op) })\n }\n return found\n}\n\n/** The blocks a section's built items place, each set as one key; `except` leaves one item out. */\nfunction aiBuildSectionDeliveredBlocks(\n section: { uses: readonly string[] },\n context: { units: readonly AiBuildUnit[]; rows: ReadonlyMap<string, AiJobItemLedger>; ops?: AiBuildOps; except: string },\n): Set<string> {\n const keys = new Set<string>()\n for (const { dep, capability } of aiBuildSectionItems(section, context.units, context.ops)) {\n if (dep.slot === context.except || !capability?.pageBlocks?.length) continue\n const row = context.rows.get(dep.slot)\n if (row && aiBuildItemDelivered(row)) keys.add(aiBuildBlocksKey(capability.pageBlocks))\n }\n return keys\n}\n\nfunction aiBuildBlocksKey(blocks: readonly string[]): string {\n return [...blocks].sort().join('|')\n}\n\n/** What a build came to: whether anything was delivered, and whether anything is left. */\nexport function aiBuildSettlement(ledger: readonly AiJobItemLedger[]): {\n delivered: boolean\n open: boolean\n failed: number\n} {\n return {\n delivered: ledger.some(aiBuildItemDelivered),\n open: ledger.some(aiBuildItemOpen),\n failed: ledger.filter((row) => row.status === 'failed').length,\n }\n}\n\n/**\n * The ledger Try again runs (AGL-3616): every failed item back to `pending`\n * on its next attempt, with the items left unbuilt because of it — skipped,\n * or degraded with no draft — and every item the build never reached. An\n * item that succeeded is never run again, and a degraded page that has its\n * draft keeps it, with a note that the page needs an edit. `retried` names\n * the slots that will run, which is what the estimate is shown for.\n */\nexport function aiBuildRetryLedger(\n ledger: readonly AiJobItemLedger[],\n units: readonly AiBuildUnit[],\n): { ledger: AiJobItemLedger[]; retried: string[] } {\n const failed = new Set(ledger.filter((row) => row.status === 'failed').map((row) => row.slot))\n const dependents = new Map<string, string[]>()\n for (const unit of units) {\n for (const dep of unit.deps) dependents.set(dep, [...(dependents.get(dep) ?? []), unit.slot])\n }\n // Everything downstream of a failed item, transitively.\n const downstream = new Set<string>()\n const queue = [...failed]\n while (queue.length) {\n const slot = queue.shift() as string\n for (const next of dependents.get(slot) ?? []) {\n if (downstream.has(next)) continue\n downstream.add(next)\n queue.push(next)\n }\n }\n const retried: string[] = []\n const next = ledger.map((row): AiJobItemLedger => {\n const again =\n row.status === 'failed' ||\n (row.status === 'skipped' && (downstream.has(row.slot) || !(row.degradedBy ?? []).length)) ||\n (row.status === 'degraded' && downstream.has(row.slot) && !row.outputs.length)\n if (again) {\n retried.push(row.slot)\n return {\n ...row,\n status: 'pending',\n attempt: row.attempt + 1,\n attemptCredits: 0,\n failure: null,\n note: null,\n degradedBy: [],\n settledAt: null,\n }\n }\n if (row.status === 'degraded' && downstream.has(row.slot) && row.outputs.length) {\n return {\n ...row,\n note: `${row.note ? `${row.note} ` : ''}It keeps its draft: once the rest is built, edit this page to add what it is missing.`,\n }\n }\n return row\n })\n return { ledger: next, retried }\n}\n\n/**\n * Units read back off a ledger alone (AGL-3616), for a job whose units are\n * not a build plan's — a site scaffold's: each row as its unit, and every\n * page built after the layouts and forms the ledger holds.\n */\nexport function aiLedgerUnits(ledger: readonly AiJobItemLedger[]): AiBuildUnit[] {\n const creations = ledger.filter((row) => row.op === 'layout' || row.op === 'form').map((row) => row.slot)\n return ledger.map((row) => ({\n slot: row.slot,\n op: row.op,\n label: row.label,\n deps: row.op === 'page' ? creations : [],\n }))\n}\n\n/**\n * What the job drawer says under an item another plugin's writer made: a\n * draft, or, for setup its writer saves live (`live`, a CRM pipeline or\n * field, AGL-3746), that it was saved. A writer that went live only when the\n * member may (an event the publishing role publishes, AGL-3755) reports\n * `live: false` for one it kept as a draft, with its `notice` saying why.\n */\nexport function aiBuildItemOutputNote(\n capability: Pick<PluginAiCapability, 'where' | 'live'>,\n written?: Pick<PluginDraftRecord, 'live' | 'notice'>,\n): string {\n const live = written?.live ?? capability.live\n const said = live ? `Live now, in ${capability.where}.` : `A draft, in ${capability.where}.`\n const notice = written?.notice?.trim()\n return notice ? `${said} ${notice}` : said\n}\n"],"names":["pluginAiCapabilityArgsProblems","AI_PLAN_NEW_REF_PREFIX","isAiPlanNewRef","AI_BUILD_STORE_OP","AI_BUILD_STORE_PAGES_LABEL","AI_BUILD_STORE_SLOT","aiBuildMakesStore","aiBuildStorePagesAdded","AI_SITE_HOME_FIRST_LABEL","AI_SITE_MAX_SECTIONS","AI_SITE_PASS_CREDITS","aiCreationMeasuredCredits","aiJobPlanCreditEstimate","aiJobPlanCreditRange","aiSitePlanIsHome","aiStandaloneScreenCreditRange","AI_CREDIT_RANGE_ZERO","AI_MEASURED_PASS_CREDITS","aiCreditRangeAdd","aiCreditRangeOf","aiCreditRangeOrdered","AI_BUILD_LIMITS","units","pages","freePages","aiBuildFreePageCapNote","plan","freeTaste","cap","screens","length","AI_BUILD_CREATE_KINDS","AI_BUILD_STRUCTURAL_OPS","AI_BUILD_MADE_INPUT","AI_BUILD_PUBLISH_INPUT","creationOp","creation","kind","namedSlots","slots","Map","create","forEach","index","includes","set","name","toLowerCase","item","items","slot","aiBuildRefName","ref","slice","trim","aiBuildItemArgRefs","args","refs","take","value","part","split","push","Object","values","Array","isArray","aiBuildScreenRefs","screen","layout","template","duplicateOf","sections","flatMap","section","uses","filter","aiBuildUnits","pageSlots","title","has","depsOf","self","deps","get","undefined","op","label","writesTo","dependsOn","aiBuildUnitIsPlatform","unit","aiBuildOrder","ordered","placed","Set","known","map","next","find","every","dep","add","aiBuildGraphProblems","problems","aiBuildPageLimit","aiBuildPlanShapeRefusal","options","counts","slugs","slug","graph","ops","capability","freeAllowed","aiArticle","noun","argsSchema","join","count","maxPerPlan","aiBuildPlanMissingInput","questions","question","missingInput","why","test","unitPasses","aiBuildUnitCreditEstimate","credits","Math","max","floor","estimateCredits","aiBuildCreditEstimate","reduce","total","aiJobCreditEstimate","aiBuildUnitCreditRange","ceiling","standalone","passes","ceil","median","p90","aiBuildCreditRange","aiJobCreditRange","aiBuildFirstPagePlan","findIndex","bySlot","keep","queue","shift","size","at","kept","entry","smaller","embeds","embed","where","storePages","aiBuildSmaller","aiBuildInitialLedger","status","attempt","creditsSpent","attemptCredits","creditsRefunded","failure","outputs","OPEN","aiBuildItemOpen","row","aiBuildItemDelivered","aiBuildNextUnit","ledger","rows","aiBuildDegradation","context","one","result","skip","degradedBy","notes","dropped","briefLines","aiBuildCreationNoun","degrade","aiBuildUnitWritesTo","madeAPage","some","made","blockStays","Boolean","pageBlocks","aiBuildSectionDeliveredBlocks","except","aiBuildBlocksKey","blocks","aiBuildPlacedItemLines","lines","aiBuildSectionItems","placedByCode","key","labels","named","found","keys","sort","aiBuildSettlement","delivered","open","failed","aiBuildRetryLedger","dependents","downstream","retried","again","note","settledAt","aiLedgerUnits","creations","aiBuildItemOutputNote","written","live","said","notice"],"mappings":"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"}
@@ -35,6 +35,12 @@ export interface AiHostPublishContext {
35
35
  * `host.screens` is the routing map publishing writes — screen id to path —
36
36
  * so its entries are exactly the pages a visitor can reach. `screens`, when a
37
37
  * caller has the page documents, adds when the first one went live.
38
+ *
39
+ * The map holds routing paths, not URLs: `/` for the home page and `menu` or
40
+ * `services/pricing` for the rest. Each is read as the URL a visitor opens.
41
+ * Kept only when it already began with `/`, every page but the home was
42
+ * dropped, and a site publishing five pages was described as publishing
43
+ * only `/` (AGL-3834).
38
44
  */
39
45
  export declare function aiHostPublishContext(host: Record<string, unknown> | null | undefined, screens?: ReadonlyArray<{
40
46
  publishedAt?: unknown;
@@ -14,6 +14,7 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */ import { liveCustomDomain, TENANT_APEX } from "@aglyn/aglyn/app-utils/host-naming";
17
+ import { SCREEN_ROOT_PATH, screenRoutePathToUrl } from "@aglyn/aglyn/app-utils/screen-route";
17
18
  const millis = (value)=>{
18
19
  if (typeof value === 'number' && Number.isFinite(value)) return value;
19
20
  if (value && typeof value.toMillis === 'function') {
@@ -25,16 +26,28 @@ const millis = (value)=>{
25
26
  }
26
27
  return null;
27
28
  };
29
+ /** A routing-map path as the URL path a visitor opens, or `null` for an entry that names none. */ function routeUrl(path) {
30
+ if (typeof path !== 'string') return null;
31
+ const slug = path.trim().replace(/^\/+|\/+$/g, '');
32
+ if (slug) return screenRoutePathToUrl(slug);
33
+ return path.trim() === SCREEN_ROOT_PATH ? SCREEN_ROOT_PATH : null;
34
+ }
28
35
  /**
29
36
  * `host.screens` is the routing map publishing writes — screen id to path —
30
37
  * so its entries are exactly the pages a visitor can reach. `screens`, when a
31
38
  * caller has the page documents, adds when the first one went live.
39
+ *
40
+ * The map holds routing paths, not URLs: `/` for the home page and `menu` or
41
+ * `services/pricing` for the rest. Each is read as the URL a visitor opens.
42
+ * Kept only when it already began with `/`, every page but the home was
43
+ * dropped, and a site publishing five pages was described as publishing
44
+ * only `/` (AGL-3834).
32
45
  */ export function aiHostPublishContext(host, screens) {
33
46
  const routes = host == null ? void 0 : host['screens'];
34
47
  const paths = routes && typeof routes === 'object' ? [
35
- ...new Set(Object.values(routes).filter((path)=>typeof path === 'string' && path.startsWith('/')))
48
+ ...new Set(Object.values(routes).map(routeUrl).filter((path)=>path !== null))
36
49
  ] : [];
37
- paths.sort((a, b)=>a === '/' ? -1 : b === '/' ? 1 : a.localeCompare(b));
50
+ paths.sort((a, b)=>a === SCREEN_ROOT_PATH ? -1 : b === SCREEN_ROOT_PATH ? 1 : a.localeCompare(b));
38
51
  const domain = host ? liveCustomDomain(host) : undefined;
39
52
  const subdomain = typeof (host == null ? void 0 : host['subdomain']) === 'string' ? host['subdomain'] : '';
40
53
  const liveUrl = domain ? `https://${domain}` : subdomain ? `https://${subdomain}.${TENANT_APEX}` : null;