@aglyn/plugins-ai 1.0.0-beta.246 → 1.0.0-beta.249

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 (130) hide show
  1. package/package.json +17 -17
  2. package/src/lib/activity/ai-activity-actions.js +5 -1
  3. package/src/lib/activity/ai-activity-actions.js.map +1 -1
  4. package/src/lib/components/ai-job-links.js +14 -0
  5. package/src/lib/components/ai-job-links.js.map +1 -1
  6. package/src/lib/components/ai-site-start-card.component.d.ts +1 -1
  7. package/src/lib/components/ai-site-start-card.component.js +92 -19
  8. package/src/lib/components/ai-site-start-card.component.js.map +1 -1
  9. package/src/lib/declarations.server.js +12 -0
  10. package/src/lib/declarations.server.js.map +1 -1
  11. package/src/lib/jobs/ai-build-capabilities.js +56 -2
  12. package/src/lib/jobs/ai-build-capabilities.js.map +1 -1
  13. package/src/lib/jobs/ai-job-build-step.d.ts +3 -8
  14. package/src/lib/jobs/ai-job-build-step.js +49 -12
  15. package/src/lib/jobs/ai-job-build-step.js.map +1 -1
  16. package/src/lib/jobs/ai-job-form-dataset.d.ts +8 -5
  17. package/src/lib/jobs/ai-job-form-dataset.js +28 -10
  18. package/src/lib/jobs/ai-job-form-dataset.js.map +1 -1
  19. package/src/lib/jobs/ai-job-form-step.d.ts +20 -4
  20. package/src/lib/jobs/ai-job-form-step.js +98 -28
  21. package/src/lib/jobs/ai-job-form-step.js.map +1 -1
  22. package/src/lib/jobs/ai-job-layout-language.d.ts +6 -2
  23. package/src/lib/jobs/ai-job-layout-language.js +16 -5
  24. package/src/lib/jobs/ai-job-layout-language.js.map +1 -1
  25. package/src/lib/jobs/ai-job-layout-step.js +4 -1
  26. package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
  27. package/src/lib/jobs/ai-job-media-step.d.ts +239 -0
  28. package/src/lib/jobs/ai-job-media-step.js +610 -0
  29. package/src/lib/jobs/ai-job-media-step.js.map +1 -0
  30. package/src/lib/jobs/ai-job-page-language.d.ts +2 -0
  31. package/src/lib/jobs/ai-job-page-language.js +9 -0
  32. package/src/lib/jobs/ai-job-page-language.js.map +1 -1
  33. package/src/lib/jobs/ai-job-page-sections.js +4 -3
  34. package/src/lib/jobs/ai-job-page-sections.js.map +1 -1
  35. package/src/lib/jobs/ai-job-page-step.js +3 -2
  36. package/src/lib/jobs/ai-job-page-step.js.map +1 -1
  37. package/src/lib/jobs/ai-job-plan-step.d.ts +10 -1
  38. package/src/lib/jobs/ai-job-plan-step.js +73 -15
  39. package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
  40. package/src/lib/jobs/ai-job-settings-step.d.ts +0 -4
  41. package/src/lib/jobs/ai-job-settings-step.js +2 -7
  42. package/src/lib/jobs/ai-job-settings-step.js.map +1 -1
  43. package/src/lib/jobs/ai-job-site-content.d.ts +2 -2
  44. package/src/lib/jobs/ai-job-site-content.js +5 -3
  45. package/src/lib/jobs/ai-job-site-content.js.map +1 -1
  46. package/src/lib/jobs/ai-job-site-datasets.d.ts +1 -1
  47. package/src/lib/jobs/ai-job-site-datasets.js +3 -2
  48. package/src/lib/jobs/ai-job-site-datasets.js.map +1 -1
  49. package/src/lib/jobs/ai-job-site-events.d.ts +148 -0
  50. package/src/lib/jobs/ai-job-site-events.js +313 -0
  51. package/src/lib/jobs/ai-job-site-events.js.map +1 -0
  52. package/src/lib/jobs/ai-job-site-step.d.ts +9 -3
  53. package/src/lib/jobs/ai-job-site-step.js +63 -17
  54. package/src/lib/jobs/ai-job-site-step.js.map +1 -1
  55. package/src/lib/jobs/ai-job-tracking-step.d.ts +184 -0
  56. package/src/lib/jobs/ai-job-tracking-step.js +501 -0
  57. package/src/lib/jobs/ai-job-tracking-step.js.map +1 -0
  58. package/src/lib/jobs/ai-layout-stock-photos.d.ts +41 -0
  59. package/src/lib/jobs/ai-layout-stock-photos.js +71 -3
  60. package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
  61. package/src/lib/jobs/ai-site-publish.js +2 -3
  62. package/src/lib/jobs/ai-site-publish.js.map +1 -1
  63. package/src/lib/jobs/ai-stock-photo-picker.d.ts +24 -0
  64. package/src/lib/jobs/ai-stock-photo-picker.js +108 -0
  65. package/src/lib/jobs/ai-stock-photo-picker.js.map +1 -0
  66. package/src/lib/jobs/fixtures/ai-jazz-events-build-plan-recording.d.ts +37 -0
  67. package/src/lib/jobs/fixtures/ai-jazz-events-build-plan-recording.js +47 -0
  68. package/src/lib/jobs/fixtures/ai-jazz-events-build-plan-recording.js.map +1 -0
  69. package/src/lib/jobs/fixtures/ai-stock-photo-agl3760-recordings.d.ts +21 -0
  70. package/src/lib/jobs/fixtures/ai-stock-photo-agl3760-recordings.js +128 -0
  71. package/src/lib/jobs/fixtures/ai-stock-photo-agl3760-recordings.js.map +1 -0
  72. package/src/lib/jobs/fixtures/ai-stock-photo-agl3769-recordings.d.ts +21 -0
  73. package/src/lib/jobs/fixtures/ai-stock-photo-agl3769-recordings.js +126 -0
  74. package/src/lib/jobs/fixtures/ai-stock-photo-agl3769-recordings.js.map +1 -0
  75. package/src/lib/layout-language/ai-layout-compiler.d.ts +10 -4
  76. package/src/lib/layout-language/ai-layout-compiler.js +38 -19
  77. package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
  78. package/src/lib/layout-language/ai-layout-frame.d.ts +7 -5
  79. package/src/lib/layout-language/ai-layout-frame.js +12 -9
  80. package/src/lib/layout-language/ai-layout-frame.js.map +1 -1
  81. package/src/lib/layout-language/ai-layout-gaps.js +3 -1
  82. package/src/lib/layout-language/ai-layout-gaps.js.map +1 -1
  83. package/src/lib/layout-language/ai-layout-labels.d.ts +70 -0
  84. package/src/lib/layout-language/ai-layout-labels.js +162 -0
  85. package/src/lib/layout-language/ai-layout-labels.js.map +1 -0
  86. package/src/lib/layout-language/ai-layout-links.js +5 -2
  87. package/src/lib/layout-language/ai-layout-links.js.map +1 -1
  88. package/src/lib/layout-language/ai-layout-listings.d.ts +14 -1
  89. package/src/lib/layout-language/ai-layout-listings.js +32 -5
  90. package/src/lib/layout-language/ai-layout-listings.js.map +1 -1
  91. package/src/lib/model/ai-build-event-page.d.ts +70 -0
  92. package/src/lib/model/ai-build-event-page.js +145 -0
  93. package/src/lib/model/ai-build-event-page.js.map +1 -0
  94. package/src/lib/model/ai-build-job.d.ts +21 -0
  95. package/src/lib/model/ai-build-job.js +39 -0
  96. package/src/lib/model/ai-build-job.js.map +1 -1
  97. package/src/lib/model/ai-build-plan.d.ts +25 -14
  98. package/src/lib/model/ai-build-plan.js +33 -23
  99. package/src/lib/model/ai-build-plan.js.map +1 -1
  100. package/src/lib/model/ai-job-activity.js +3 -1
  101. package/src/lib/model/ai-job-activity.js.map +1 -1
  102. package/src/lib/model/ai-jobs.types.d.ts +10 -2
  103. package/src/lib/model/ai-jobs.types.js +3 -1
  104. package/src/lib/model/ai-jobs.types.js.map +1 -1
  105. package/src/lib/model/ai-plan-capabilities.d.ts +8 -0
  106. package/src/lib/model/ai-plan-capabilities.js.map +1 -1
  107. package/src/lib/model/ai-site-inventory.d.ts +2 -0
  108. package/src/lib/model/ai-site-inventory.js +5 -0
  109. package/src/lib/model/ai-site-inventory.js.map +1 -1
  110. package/src/lib/model/ai-site-job.d.ts +2 -2
  111. package/src/lib/model/ai-site-job.js +3 -3
  112. package/src/lib/model/ai-site-job.js.map +1 -1
  113. package/src/lib/runtime/ai-doctrine-validators.d.ts +25 -3
  114. package/src/lib/runtime/ai-doctrine-validators.js +53 -6
  115. package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
  116. package/src/lib/runtime/ai-doctrine.d.ts +6 -1
  117. package/src/lib/runtime/ai-doctrine.js +13 -9
  118. package/src/lib/runtime/ai-doctrine.js.map +1 -1
  119. package/src/lib/runtime/ai-eval-live.d.ts +1 -1
  120. package/src/lib/runtime/ai-eval-live.js +1 -1
  121. package/src/lib/runtime/ai-eval-live.js.map +1 -1
  122. package/src/lib/runtime/ai-palette.d.ts +3 -1
  123. package/src/lib/runtime/ai-palette.generated.js +498 -10
  124. package/src/lib/runtime/ai-palette.generated.js.map +1 -1
  125. package/src/lib/runtime/ai-palette.js +5 -3
  126. package/src/lib/runtime/ai-palette.js.map +1 -1
  127. package/src/lib/runtime/ai-template-examples.js +8 -2
  128. package/src/lib/runtime/ai-template-examples.js.map +1 -1
  129. package/src/lib/server.js +8 -0
  130. package/src/lib/server.js.map +1 -1
@@ -133,7 +133,9 @@ export const AI_OUTPUT_KINDS = [
133
133
  * A page is a few hundred elements and tens of kilobytes of stored map; a
134
134
  * reusable component is one repeated block; a layout is the chrome every
135
135
  * page loads, so it carries the least imagery; a form is fields and a
136
- * button; an email is held to the clipping threshold on its rendered HTML.
136
+ * button — each field a Form Control over a Form Label and its answer, a
137
+ * choice a Choice Label over its Radio or Checkbox, so a form of a few dozen
138
+ * questions is a hundred-odd elements; an email is held to the clipping threshold on its rendered HTML.
137
139
  * No kind names a font beyond the theme's.
138
140
  *
139
141
  * A third-party player is a page's or a component's to carry, and only where
@@ -171,8 +173,8 @@ export const AI_OUTPUT_KINDS = [
171
173
  fontFamilies: 0
172
174
  },
173
175
  form: {
174
- nodes: 60,
175
- bytes: 8000,
176
+ nodes: 150,
177
+ bytes: 15000,
176
178
  imageBytes: 0,
177
179
  embeds: 0,
178
180
  fontFamilies: 0
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/runtime/ai-palette.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 type { JsonLinealOrder } from '@aglyn/aglyn/app-utils/lineal-order'\n\n/**\n * The element palette as a model sees it (AGL-2905).\n *\n * `ai-palette.generated.ts` is produced from the registered plugin bundles by\n * `libs/plugins/ai/scripts/generate-ai-palette.mts` and holds the data; this module\n * holds the shapes that data is typed against and the small readers over it,\n * so a regeneration never rewrites a type.\n */\n\n/** The document kinds a model is asked to compose. */\nexport type AiSurface = 'screen' | 'email' | 'form' | 'layout' | 'component'\n\nexport const AI_SURFACE_NAMES: readonly AiSurface[] = [\n 'screen',\n 'email',\n 'form',\n 'layout',\n 'component',\n]\n\n/**\n * The subset of JSON Schema a prop declaration is expressed in. Enough to\n * validate what an attribute field can persist; nothing the field editor\n * cannot itself model.\n */\nexport interface AiPropSchema {\n type?: 'string' | 'number' | 'integer' | 'boolean'\n enum?: readonly string[]\n minLength?: number\n maxLength?: number\n minimum?: number\n maximum?: number\n pattern?: string\n description?: string\n}\n\nexport interface AiPropsSchema {\n type: 'object'\n properties: Record<string, AiPropSchema>\n required: readonly string[]\n additionalProperties: false\n}\n\n/**\n * What a string prop MEANS, which decides how the validator checks it.\n *\n * - `text` — copy the visitor reads; held to a length ceiling.\n * - `url` — a navigable address: an `https:` URL or a root-relative path.\n * - `screen` — a screen id, checked against the screens the host has.\n * - `media` — an image or video source: a DAM reference or an `https:` URL.\n */\nexport type AiPropRole = 'text' | 'url' | 'screen' | 'media'\n\n/**\n * A `restrictChildren` / `restrictParent` directive as generated data: the\n * same tuple the schema declares, with the flag spelled as the string the\n * enum holds so a JSON emitter can write it.\n */\nexport type AiLinealOrder = JsonLinealOrder\n\nexport interface AiPaletteEntry {\n pluginId: string\n kind: 'element' | 'plaintext' | 'markdown'\n category: string\n displayName: string\n /** One sentence, from the schema's own description. */\n summary: string\n /** Whether the editor lets anything be dropped inside this element. */\n acceptsChildren: boolean\n restrictChildren?: AiLinealOrder\n restrictParent?: AiLinealOrder\n propsSchema: AiPropsSchema\n /** Roles of the string props the validator treats specially. */\n propRoles: Record<string, AiPropRole>\n /**\n * The attribute field kind each declared prop is edited with, as its\n * `FieldComponentType` value (`switch`, `select`, `screen-select`, …): what\n * the editor reads to decide which component properties a field can be\n * bound to (AGL-2908).\n */\n propFields: Record<string, string>\n /** Length ceilings for `text` props, in characters. */\n textLimits: Record<string, number>\n /** Names of the presets that place this component. */\n presets: readonly string[]\n}\n\n/**\n * What every NODE can do, as against what one component declares (AGL-3156).\n *\n * Generated from the node capabilities core declares, beside the per-component\n * schemas rather than out of them: repeating is not a Stack's feature, so no\n * component schema names it and a palette built from schemas alone advertises\n * it on nothing. Declared ONCE for the whole palette — a model may write these\n * props on any element the surface allows.\n */\nexport interface AiNodeCapability {\n /** Stable id, as core declares it. */\n id: string\n displayName: string\n /** One sentence, for the prompt catalog. */\n summary: string\n propsSchema: AiPropsSchema\n propRoles: Record<string, AiPropRole>\n propFields: Record<string, string>\n textLimits: Record<string, number>\n /** Props offered only on a node that holds a child list. */\n childrenOnlyProps: readonly string[]\n /**\n * Declared attributes the palette deliberately does not offer: a picker\n * that chooses a record the site holds, which a model cannot name from a\n * description. Recorded so a field left off on purpose reads differently\n * from one that went missing.\n */\n omittedProps: readonly string[]\n}\n\n/**\n * The capability props a node with this child contract may carry, merged into\n * one map the prop sanitizer can read beside the element's own declarations.\n */\nexport function nodeCapabilityProps(\n capabilities: Readonly<Record<string, AiNodeCapability>>,\n acceptsChildren: boolean,\n): {\n properties: Record<string, AiPropSchema>\n propRoles: Record<string, AiPropRole>\n textLimits: Record<string, number>\n} {\n const properties: Record<string, AiPropSchema> = {}\n const propRoles: Record<string, AiPropRole> = {}\n const textLimits: Record<string, number> = {}\n for (const capability of Object.values(capabilities)) {\n for (const [name, schema] of Object.entries(\n capability.propsSchema.properties,\n )) {\n if (!acceptsChildren && capability.childrenOnlyProps.includes(name))\n continue\n properties[name] = schema\n const role = capability.propRoles[name]\n if (role) propRoles[name] = role\n const limit = capability.textLimits[name]\n if (limit !== undefined) textLimits[name] = limit\n }\n }\n return { properties, propRoles, textLimits }\n}\n\n/**\n * What a node capability declared in core and one carried by the palette\n * disagree about (AGL-3156).\n *\n * `check:ai-palette` pins the generated file to the generator's output for its\n * inputs, which cannot notice a capability leaving those inputs: when repeat\n * stopped being a component's attribute, the generator regenerated cleanly and\n * the palette simply stopped mentioning it. This reads the core declarations\n * instead and asks whether the palette still accounts for each one, so the\n * next generic capability cannot vanish the same way.\n *\n * `offeredFieldKinds` is every field kind the palette DOES carry somewhere,\n * derived from the generated entries rather than restated here: a capability\n * attribute drawn with one of those kinds has no excuse for being omitted.\n */\nexport function auditNodeCapabilities(\n declared: ReadonlyArray<{\n id: string\n label: string\n attributes: ReadonlyArray<{ name: string; component: string }>\n }>,\n carried: Readonly<Record<string, AiNodeCapability>>,\n offeredFieldKinds: ReadonlySet<string>,\n): string[] {\n const problems: string[] = []\n for (const capability of declared) {\n const entry = carried[capability.id]\n if (!entry) {\n problems.push(\n `the palette carries no node capability \"${capability.id}\": a model cannot use it at all`,\n )\n continue\n }\n if (!Object.keys(entry.propsSchema.properties).length) {\n problems.push(\n `node capability \"${capability.id}\" is carried with no prop a model can write`,\n )\n }\n for (const attribute of capability.attributes) {\n if (entry.propsSchema.properties[attribute.name]) continue\n if (!entry.omittedProps.includes(attribute.name)) {\n problems.push(\n `node capability \"${capability.id}\" declares \"${attribute.name}\", which the palette neither offers nor records as omitted`,\n )\n continue\n }\n if (offeredFieldKinds.has(attribute.component)) {\n problems.push(\n `node capability \"${capability.id}\" omits \"${attribute.name}\", whose \"${attribute.component}\" field the palette offers elsewhere`,\n )\n }\n }\n }\n for (const id of Object.keys(carried)) {\n if (!declared.some((capability) => capability.id === id)) {\n problems.push(\n `the palette carries node capability \"${id}\", which core no longer declares`,\n )\n }\n }\n return problems\n}\n\nexport interface AiSurfaceDefinition {\n /** The component id at the top of a tree for this surface. */\n root: string\n /** Every component id a node in this surface may carry. */\n allow: readonly string[]\n /**\n * Component ids admitted only in a tree the platform's own code composed\n * (AGL-3660), never listed in the surface's catalog: see\n * `AiNodeTreeContext.codeBuilt`.\n */\n codeOnly?: readonly string[]\n}\n\n/**\n * Theme vocabulary an `sx` value may name, read off the theme sources so the\n * validator's allowlist is derived rather than typed by hand.\n */\nexport interface AiSxTokens {\n /** Dotted palette paths such as `primary.main`. */\n palette: readonly string[]\n /** Responsive object keys. */\n breakpoints: readonly string[]\n /** Values `typography` may take. */\n typographyVariants: readonly string[]\n}\n\n/**\n * Text ceilings per role of copy (AGL-2905). A headline is one line of a\n * hero, body copy is a paragraph or two, and a button label has to fit a\n * button.\n */\nexport const AI_TEXT_LIMITS = {\n headline: 120,\n body: 2_000,\n button: 40,\n /** A short label, title or caption. */\n label: 200,\n} as const\n\n/**\n * Prompt budget per surface catalog, in characters at four per token.\n * Asserted by a spec so a component that grows its description cannot\n * silently push a system prompt past what the route pays for.\n */\nexport const AI_PALETTE_CATALOG_MAX_TOKENS = 2_500\nexport const AI_PALETTE_CATALOG_MAX_CHARS = AI_PALETTE_CATALOG_MAX_TOKENS * 4\n\n/** The model's own token estimate: characters over four. */\nexport function estimateCatalogTokens(catalog: string): number {\n return Math.ceil(catalog.length / 4)\n}\n\n/**\n * The documents a generator emits (AGL-2935). Each is composed on one\n * surface of the palette and held to its own budget below.\n */\nexport type AiOutputKind =\n | 'page'\n | 'template'\n | 'component'\n | 'layout'\n | 'form'\n | 'email'\n\nexport const AI_OUTPUT_KINDS: readonly AiOutputKind[] = [\n 'page',\n 'template',\n 'component',\n 'layout',\n 'form',\n 'email',\n]\n\n/** The surface each output kind is composed on. */\nexport const AI_OUTPUT_SURFACE: Record<AiOutputKind, AiSurface> = {\n page: 'screen',\n template: 'screen',\n component: 'component',\n layout: 'layout',\n form: 'form',\n email: 'email',\n}\n\n/**\n * What the rule-17 scorer measures on an emitted tree:\n *\n * - `nodes` — how many elements the tree carries;\n * - `bytes` — the stored node map, in the unit the save ceiling uses\n * (`nodeMapBytes`, msgpack);\n * - `imageBytes` — what the placed library images request at the slot's\n * variant, from each asset's recorded size;\n * - `embeds` — third-party players and frames the output loads;\n * - `fontFamilies` — families named beyond the theme's own;\n * - `emailHtmlBytes` — an email's rendered HTML, which is what a mail\n * client clips.\n */\nexport type AiBudgetMetric =\n | 'nodes'\n | 'bytes'\n | 'imageBytes'\n | 'embeds'\n | 'fontFamilies'\n | 'emailHtmlBytes'\n\nexport type AiOutputBudget = Record<Exclude<AiBudgetMetric, 'emailHtmlBytes'>, number> & {\n emailHtmlBytes?: number\n}\n\n/**\n * The size past which Gmail clips a message and hides the rest behind a\n * \"View entire message\" link — the unsubscribe footer included.\n */\nexport const AI_EMAIL_CLIP_BYTES = 102_000\n\n/**\n * The budget per output kind (AGL-2935, rule 17), beside the catalog budget\n * above for the same reason: a generator that grows its output past what\n * its kind is for is caught by a number, not noticed by a reader.\n *\n * A page is a few hundred elements and tens of kilobytes of stored map; a\n * reusable component is one repeated block; a layout is the chrome every\n * page loads, so it carries the least imagery; a form is fields and a\n * button; an email is held to the clipping threshold on its rendered HTML.\n * No kind names a font beyond the theme's.\n *\n * A third-party player is a page's or a component's to carry, and only where\n * a confirmed plan lists it (rule 16). A template is rendered for every\n * record it serves and a layout around every page, so neither carries one\n * (AGL-3433): an embed there loads a stranger's code on every page of a\n * collection or a site.\n */\nexport const AI_OUTPUT_BUDGETS: Record<AiOutputKind, AiOutputBudget> = {\n page: {\n nodes: 400,\n bytes: 60_000,\n imageBytes: 1_500_000,\n embeds: 1,\n fontFamilies: 0,\n },\n template: {\n nodes: 400,\n bytes: 60_000,\n imageBytes: 1_500_000,\n embeds: 0,\n fontFamilies: 0,\n },\n component: {\n nodes: 80,\n bytes: 12_000,\n imageBytes: 500_000,\n embeds: 1,\n fontFamilies: 0,\n },\n layout: {\n nodes: 160,\n bytes: 24_000,\n imageBytes: 200_000,\n embeds: 0,\n fontFamilies: 0,\n },\n form: {\n nodes: 60,\n bytes: 8_000,\n imageBytes: 0,\n embeds: 0,\n fontFamilies: 0,\n },\n email: {\n nodes: 150,\n bytes: 24_000,\n imageBytes: 1_000_000,\n embeds: 0,\n fontFamilies: 0,\n emailHtmlBytes: AI_EMAIL_CLIP_BYTES,\n },\n}\n\n/**\n * What a first visit to a generated output is estimated to transfer — the\n * weight the proposal shows before anything is applied (rule 17). A page\n * starts from the platform's measured page weight; a component or a layout\n * reports only what it adds to a page; an email is not a page load.\n */\nexport interface AiLoadEstimate {\n /** The measured weight of a published page before this output's own bytes; 0 for a fragment. */\n pageBytes: number\n /** The stored node map, in the save's own bytes. */\n documentBytes: number\n /** What the placed library images request at their slots. */\n imageBytes: number\n /** Images the estimate could not size: no recorded size, or not from the library. */\n imagesUnmeasured: number\n embeds: number\n totalBytes: number\n}\n\n/** Typography variants that read as headlines, for the `children` ceiling. */\nexport function isHeadlineVariant(variant: unknown): boolean {\n return typeof variant === 'string' && /^(display\\w*|h[1-6]|heading)$/.test(variant)\n}\n"],"names":["AI_SURFACE_NAMES","nodeCapabilityProps","capabilities","acceptsChildren","properties","propRoles","textLimits","capability","Object","values","name","schema","entries","propsSchema","childrenOnlyProps","includes","role","limit","undefined","auditNodeCapabilities","declared","carried","offeredFieldKinds","problems","entry","id","push","keys","length","attribute","attributes","omittedProps","has","component","some","AI_TEXT_LIMITS","headline","body","button","label","AI_PALETTE_CATALOG_MAX_TOKENS","AI_PALETTE_CATALOG_MAX_CHARS","estimateCatalogTokens","catalog","Math","ceil","AI_OUTPUT_KINDS","AI_OUTPUT_SURFACE","page","template","layout","form","email","AI_EMAIL_CLIP_BYTES","AI_OUTPUT_BUDGETS","nodes","bytes","imageBytes","embeds","fontFamilies","emailHtmlBytes","isHeadlineVariant","variant","test"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/runtime/ai-palette.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 type { JsonLinealOrder } from '@aglyn/aglyn/app-utils/lineal-order'\n\n/**\n * The element palette as a model sees it (AGL-2905).\n *\n * `ai-palette.generated.ts` is produced from the registered plugin bundles by\n * `libs/plugins/ai/scripts/generate-ai-palette.mts` and holds the data; this module\n * holds the shapes that data is typed against and the small readers over it,\n * so a regeneration never rewrites a type.\n */\n\n/** The document kinds a model is asked to compose. */\nexport type AiSurface = 'screen' | 'email' | 'form' | 'layout' | 'component'\n\nexport const AI_SURFACE_NAMES: readonly AiSurface[] = [\n 'screen',\n 'email',\n 'form',\n 'layout',\n 'component',\n]\n\n/**\n * The subset of JSON Schema a prop declaration is expressed in. Enough to\n * validate what an attribute field can persist; nothing the field editor\n * cannot itself model.\n */\nexport interface AiPropSchema {\n type?: 'string' | 'number' | 'integer' | 'boolean'\n enum?: readonly string[]\n minLength?: number\n maxLength?: number\n minimum?: number\n maximum?: number\n pattern?: string\n description?: string\n}\n\nexport interface AiPropsSchema {\n type: 'object'\n properties: Record<string, AiPropSchema>\n required: readonly string[]\n additionalProperties: false\n}\n\n/**\n * What a string prop MEANS, which decides how the validator checks it.\n *\n * - `text` — copy the visitor reads; held to a length ceiling.\n * - `url` — a navigable address: an `https:` URL or a root-relative path.\n * - `screen` — a screen id, checked against the screens the host has.\n * - `media` — an image or video source: a DAM reference or an `https:` URL.\n */\nexport type AiPropRole = 'text' | 'url' | 'screen' | 'media'\n\n/**\n * A `restrictChildren` / `restrictParent` directive as generated data: the\n * same tuple the schema declares, with the flag spelled as the string the\n * enum holds so a JSON emitter can write it.\n */\nexport type AiLinealOrder = JsonLinealOrder\n\nexport interface AiPaletteEntry {\n pluginId: string\n kind: 'element' | 'plaintext' | 'markdown'\n category: string\n displayName: string\n /** One sentence, from the schema's own description. */\n summary: string\n /** Whether the editor lets anything be dropped inside this element. */\n acceptsChildren: boolean\n restrictChildren?: AiLinealOrder\n restrictParent?: AiLinealOrder\n propsSchema: AiPropsSchema\n /** Roles of the string props the validator treats specially. */\n propRoles: Record<string, AiPropRole>\n /**\n * The attribute field kind each declared prop is edited with, as its\n * `FieldComponentType` value (`switch`, `select`, `screen-select`, …): what\n * the editor reads to decide which component properties a field can be\n * bound to (AGL-2908).\n */\n propFields: Record<string, string>\n /** Length ceilings for `text` props, in characters. */\n textLimits: Record<string, number>\n /** Names of the presets that place this component. */\n presets: readonly string[]\n}\n\n/**\n * What every NODE can do, as against what one component declares (AGL-3156).\n *\n * Generated from the node capabilities core declares, beside the per-component\n * schemas rather than out of them: repeating is not a Stack's feature, so no\n * component schema names it and a palette built from schemas alone advertises\n * it on nothing. Declared ONCE for the whole palette — a model may write these\n * props on any element the surface allows.\n */\nexport interface AiNodeCapability {\n /** Stable id, as core declares it. */\n id: string\n displayName: string\n /** One sentence, for the prompt catalog. */\n summary: string\n propsSchema: AiPropsSchema\n propRoles: Record<string, AiPropRole>\n propFields: Record<string, string>\n textLimits: Record<string, number>\n /** Props offered only on a node that holds a child list. */\n childrenOnlyProps: readonly string[]\n /**\n * Declared attributes the palette deliberately does not offer: a picker\n * that chooses a record the site holds, which a model cannot name from a\n * description. Recorded so a field left off on purpose reads differently\n * from one that went missing.\n */\n omittedProps: readonly string[]\n}\n\n/**\n * The capability props a node with this child contract may carry, merged into\n * one map the prop sanitizer can read beside the element's own declarations.\n */\nexport function nodeCapabilityProps(\n capabilities: Readonly<Record<string, AiNodeCapability>>,\n acceptsChildren: boolean,\n): {\n properties: Record<string, AiPropSchema>\n propRoles: Record<string, AiPropRole>\n textLimits: Record<string, number>\n} {\n const properties: Record<string, AiPropSchema> = {}\n const propRoles: Record<string, AiPropRole> = {}\n const textLimits: Record<string, number> = {}\n for (const capability of Object.values(capabilities)) {\n for (const [name, schema] of Object.entries(\n capability.propsSchema.properties,\n )) {\n if (!acceptsChildren && capability.childrenOnlyProps.includes(name))\n continue\n properties[name] = schema\n const role = capability.propRoles[name]\n if (role) propRoles[name] = role\n const limit = capability.textLimits[name]\n if (limit !== undefined) textLimits[name] = limit\n }\n }\n return { properties, propRoles, textLimits }\n}\n\n/**\n * What a node capability declared in core and one carried by the palette\n * disagree about (AGL-3156).\n *\n * `check:ai-palette` pins the generated file to the generator's output for its\n * inputs, which cannot notice a capability leaving those inputs: when repeat\n * stopped being a component's attribute, the generator regenerated cleanly and\n * the palette simply stopped mentioning it. This reads the core declarations\n * instead and asks whether the palette still accounts for each one, so the\n * next generic capability cannot vanish the same way.\n *\n * `offeredFieldKinds` is every field kind the palette DOES carry somewhere,\n * derived from the generated entries rather than restated here: a capability\n * attribute drawn with one of those kinds has no excuse for being omitted.\n */\nexport function auditNodeCapabilities(\n declared: ReadonlyArray<{\n id: string\n label: string\n attributes: ReadonlyArray<{ name: string; component: string }>\n }>,\n carried: Readonly<Record<string, AiNodeCapability>>,\n offeredFieldKinds: ReadonlySet<string>,\n): string[] {\n const problems: string[] = []\n for (const capability of declared) {\n const entry = carried[capability.id]\n if (!entry) {\n problems.push(\n `the palette carries no node capability \"${capability.id}\": a model cannot use it at all`,\n )\n continue\n }\n if (!Object.keys(entry.propsSchema.properties).length) {\n problems.push(\n `node capability \"${capability.id}\" is carried with no prop a model can write`,\n )\n }\n for (const attribute of capability.attributes) {\n if (entry.propsSchema.properties[attribute.name]) continue\n if (!entry.omittedProps.includes(attribute.name)) {\n problems.push(\n `node capability \"${capability.id}\" declares \"${attribute.name}\", which the palette neither offers nor records as omitted`,\n )\n continue\n }\n if (offeredFieldKinds.has(attribute.component)) {\n problems.push(\n `node capability \"${capability.id}\" omits \"${attribute.name}\", whose \"${attribute.component}\" field the palette offers elsewhere`,\n )\n }\n }\n }\n for (const id of Object.keys(carried)) {\n if (!declared.some((capability) => capability.id === id)) {\n problems.push(\n `the palette carries node capability \"${id}\", which core no longer declares`,\n )\n }\n }\n return problems\n}\n\nexport interface AiSurfaceDefinition {\n /** The component id at the top of a tree for this surface. */\n root: string\n /** Every component id a node in this surface may carry. */\n allow: readonly string[]\n /**\n * Component ids admitted only in a tree the platform's own code composed\n * (AGL-3660), never listed in the surface's catalog: see\n * `AiNodeTreeContext.codeBuilt`.\n */\n codeOnly?: readonly string[]\n}\n\n/**\n * Theme vocabulary an `sx` value may name, read off the theme sources so the\n * validator's allowlist is derived rather than typed by hand.\n */\nexport interface AiSxTokens {\n /** Dotted palette paths such as `primary.main`. */\n palette: readonly string[]\n /** Responsive object keys. */\n breakpoints: readonly string[]\n /** Values `typography` may take. */\n typographyVariants: readonly string[]\n}\n\n/**\n * Text ceilings per role of copy (AGL-2905). A headline is one line of a\n * hero, body copy is a paragraph or two, and a button label has to fit a\n * button.\n */\nexport const AI_TEXT_LIMITS = {\n headline: 120,\n body: 2_000,\n button: 40,\n /** A short label, title or caption. */\n label: 200,\n} as const\n\n/**\n * Prompt budget per surface catalog, in characters at four per token.\n * Asserted by a spec so a component that grows its description cannot\n * silently push a system prompt past what the route pays for.\n */\nexport const AI_PALETTE_CATALOG_MAX_TOKENS = 2_500\nexport const AI_PALETTE_CATALOG_MAX_CHARS = AI_PALETTE_CATALOG_MAX_TOKENS * 4\n\n/** The model's own token estimate: characters over four. */\nexport function estimateCatalogTokens(catalog: string): number {\n return Math.ceil(catalog.length / 4)\n}\n\n/**\n * The documents a generator emits (AGL-2935). Each is composed on one\n * surface of the palette and held to its own budget below.\n */\nexport type AiOutputKind =\n | 'page'\n | 'template'\n | 'component'\n | 'layout'\n | 'form'\n | 'email'\n\nexport const AI_OUTPUT_KINDS: readonly AiOutputKind[] = [\n 'page',\n 'template',\n 'component',\n 'layout',\n 'form',\n 'email',\n]\n\n/** The surface each output kind is composed on. */\nexport const AI_OUTPUT_SURFACE: Record<AiOutputKind, AiSurface> = {\n page: 'screen',\n template: 'screen',\n component: 'component',\n layout: 'layout',\n form: 'form',\n email: 'email',\n}\n\n/**\n * What the rule-17 scorer measures on an emitted tree:\n *\n * - `nodes` — how many elements the tree carries;\n * - `bytes` — the stored node map, in the unit the save ceiling uses\n * (`nodeMapBytes`, msgpack);\n * - `imageBytes` — what the placed library images request at the slot's\n * variant, from each asset's recorded size;\n * - `embeds` — third-party players and frames the output loads;\n * - `fontFamilies` — families named beyond the theme's own;\n * - `emailHtmlBytes` — an email's rendered HTML, which is what a mail\n * client clips.\n */\nexport type AiBudgetMetric =\n | 'nodes'\n | 'bytes'\n | 'imageBytes'\n | 'embeds'\n | 'fontFamilies'\n | 'emailHtmlBytes'\n\nexport type AiOutputBudget = Record<Exclude<AiBudgetMetric, 'emailHtmlBytes'>, number> & {\n emailHtmlBytes?: number\n}\n\n/**\n * The size past which Gmail clips a message and hides the rest behind a\n * \"View entire message\" link — the unsubscribe footer included.\n */\nexport const AI_EMAIL_CLIP_BYTES = 102_000\n\n/**\n * The budget per output kind (AGL-2935, rule 17), beside the catalog budget\n * above for the same reason: a generator that grows its output past what\n * its kind is for is caught by a number, not noticed by a reader.\n *\n * A page is a few hundred elements and tens of kilobytes of stored map; a\n * reusable component is one repeated block; a layout is the chrome every\n * page loads, so it carries the least imagery; a form is fields and a\n * button — each field a Form Control over a Form Label and its answer, a\n * choice a Choice Label over its Radio or Checkbox, so a form of a few dozen\n * questions is a hundred-odd elements; an email is held to the clipping threshold on its rendered HTML.\n * No kind names a font beyond the theme's.\n *\n * A third-party player is a page's or a component's to carry, and only where\n * a confirmed plan lists it (rule 16). A template is rendered for every\n * record it serves and a layout around every page, so neither carries one\n * (AGL-3433): an embed there loads a stranger's code on every page of a\n * collection or a site.\n */\nexport const AI_OUTPUT_BUDGETS: Record<AiOutputKind, AiOutputBudget> = {\n page: {\n nodes: 400,\n bytes: 60_000,\n imageBytes: 1_500_000,\n embeds: 1,\n fontFamilies: 0,\n },\n template: {\n nodes: 400,\n bytes: 60_000,\n imageBytes: 1_500_000,\n embeds: 0,\n fontFamilies: 0,\n },\n component: {\n nodes: 80,\n bytes: 12_000,\n imageBytes: 500_000,\n embeds: 1,\n fontFamilies: 0,\n },\n layout: {\n nodes: 160,\n bytes: 24_000,\n imageBytes: 200_000,\n embeds: 0,\n fontFamilies: 0,\n },\n form: {\n nodes: 150,\n bytes: 15_000,\n imageBytes: 0,\n embeds: 0,\n fontFamilies: 0,\n },\n email: {\n nodes: 150,\n bytes: 24_000,\n imageBytes: 1_000_000,\n embeds: 0,\n fontFamilies: 0,\n emailHtmlBytes: AI_EMAIL_CLIP_BYTES,\n },\n}\n\n/**\n * What a first visit to a generated output is estimated to transfer — the\n * weight the proposal shows before anything is applied (rule 17). A page\n * starts from the platform's measured page weight; a component or a layout\n * reports only what it adds to a page; an email is not a page load.\n */\nexport interface AiLoadEstimate {\n /** The measured weight of a published page before this output's own bytes; 0 for a fragment. */\n pageBytes: number\n /** The stored node map, in the save's own bytes. */\n documentBytes: number\n /** What the placed library images request at their slots. */\n imageBytes: number\n /** Images the estimate could not size: no recorded size, or not from the library. */\n imagesUnmeasured: number\n embeds: number\n totalBytes: number\n}\n\n/** Typography variants that read as headlines, for the `children` ceiling. */\nexport function isHeadlineVariant(variant: unknown): boolean {\n return typeof variant === 'string' && /^(display\\w*|h[1-6]|heading)$/.test(variant)\n}\n"],"names":["AI_SURFACE_NAMES","nodeCapabilityProps","capabilities","acceptsChildren","properties","propRoles","textLimits","capability","Object","values","name","schema","entries","propsSchema","childrenOnlyProps","includes","role","limit","undefined","auditNodeCapabilities","declared","carried","offeredFieldKinds","problems","entry","id","push","keys","length","attribute","attributes","omittedProps","has","component","some","AI_TEXT_LIMITS","headline","body","button","label","AI_PALETTE_CATALOG_MAX_TOKENS","AI_PALETTE_CATALOG_MAX_CHARS","estimateCatalogTokens","catalog","Math","ceil","AI_OUTPUT_KINDS","AI_OUTPUT_SURFACE","page","template","layout","form","email","AI_EMAIL_CLIP_BYTES","AI_OUTPUT_BUDGETS","nodes","bytes","imageBytes","embeds","fontFamilies","emailHtmlBytes","isHeadlineVariant","variant","test"],"mappings":"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"}
@@ -14,14 +14,20 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */ import { STARTER_TEMPLATES } from "@aglyn/aglyn/app-utils/starter-templates";
17
+ import { FORM_INPUT_ELEMENT_IDS } from "@aglyn/aglyn/app-utils/forms";
17
18
  import { CANVAS_ROOT_ELEMENT_ID } from "@aglyn/aglyn/foundation/constants/canvas";
18
19
  import { validateAiDoctrineTree } from "./ai-doctrine-validators.js";
19
20
  import { isHeadlineVariant } from "./ai-palette.js";
20
21
  import { AI_PALETTE, AI_SURFACES } from "./ai-palette.generated.js";
21
22
  /** The block's ceiling: 1,500 tokens at four characters a token. */ export const AI_TEMPLATE_EXAMPLES_MAX_CHARS = 6000;
22
- const FORM_COMPONENTS = new Set([
23
+ /**
24
+ * A form and every piece of one — the all-in-one Form Field and MUI's
25
+ * separate form elements — which a page never draws: it places a saved form.
26
+ */ const FORM_COMPONENTS = new Set([
23
27
  'form',
24
- 'formField'
28
+ 'formField',
29
+ ...Object.values(FORM_INPUT_ELEMENT_IDS),
30
+ 'muiFormHelperText'
25
31
  ]);
26
32
  /** The elements a page links with, which go where their `screenId` or `href` says. */ const LINK_COMPONENTS = new Set([
27
33
  'muiButton',
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/runtime/ai-template-examples.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 STARTER_TEMPLATES,\n type StarterTemplate,\n} from '@aglyn/aglyn/app-utils/starter-templates'\nimport { CANVAS_ROOT_ELEMENT_ID } from '@aglyn/aglyn/foundation/constants/canvas'\nimport {\n validateAiDoctrineTree,\n type AiDoctrineNode,\n type AiDoctrineTree,\n} from './ai-doctrine-validators'\nimport { isHeadlineVariant } from './ai-palette'\nimport { AI_PALETTE, AI_SURFACES } from './ai-palette.generated'\nimport type { AiSystemBlock } from './ai-runtime'\n\n/**\n * The page examples a template generation is shown (AGL-2909): pages from\n * the platform's own starter sites (`STARTER_TEMPLATES`), in the node-map\n * shape the tool takes, as one cached block that is the same bytes for every\n * org.\n *\n * Three things about a starter predate the building rules, and every example\n * is brought up to them the same way rather than shown as it stands:\n *\n * - HEADINGS. A starter picks a heading's look with its variant, and rule 11\n * reads its level from the element. The page's first heading renders as\n * its h1, each later one as an h2, and a sixth-level subtitle as a\n * paragraph.\n * - FORMS. A starter draws its contact form inline, and rule 3 places a form\n * built on the Forms page by id, so the form is left out.\n * - LINKS. A starter's call to action points nowhere until a member picks\n * where it goes, and rule 10 refuses a Button or a Screen Link with no\n * destination (AGL-3072), so such a link is left out.\n *\n * Only props the palette declares are kept, since the model is held to the\n * same palette, and none that holds a px, rem or em length, which rule 5\n * names as a length never to write. A page that still breaks a rule, that\n * the palette validator would change in any way, or that places an element\n * the page surface does not offer (a commerce block) is not shown, and two\n * pages of one shape are shown once: an example the model imitates is one\n * the doctrine accepts exactly as written.\n */\n\nexport interface AiTemplateExample {\n /** The starter site's name. */\n starter: string\n /** The page's name within it. */\n page: string\n tree: AiDoctrineTree\n}\n\n/** The block's ceiling: 1,500 tokens at four characters a token. */\nexport const AI_TEMPLATE_EXAMPLES_MAX_CHARS = 6_000\n\nconst FORM_COMPONENTS = new Set(['form', 'formField'])\n\n/** The elements a page links with, which go where their `screenId` or `href` says. */\nconst LINK_COMPONENTS = new Set(['muiButton', 'muiScreenLink'])\n\n/** Whether a starter node is a link that names nowhere to go. */\nfunction linksNowhere(componentId: string, props: Record<string, unknown> | undefined): boolean {\n if (!LINK_COMPONENTS.has(componentId)) return false\n const named = (value: unknown) => typeof value === 'string' && value.trim() !== ''\n return !named(props?.['screenId']) && !named(props?.['href'])\n}\n\n/** A CSS length in the units rule 5 forbids. */\nconst CSS_LENGTH = /\\d(?:\\.\\d+)?(?:px|rem|em)\\b/i\n\ntype StarterNode = {\n componentId?: string\n props?: Record<string, unknown>\n sx?: Record<string, unknown>\n nodes?: unknown\n}\n\n/** A starter page as the tool's node map, brought up to the rules above; `null` when it cannot be shown. */\nexport function aiStarterExampleTree(nodes: Record<string, StarterNode>): AiDoctrineTree | null {\n if (!nodes[CANVAS_ROOT_ELEMENT_ID]) return null\n const allowed = new Set(AI_SURFACES.screen.allow)\n const out: Record<string, AiDoctrineNode> = {}\n let headings = 0\n let unavailable = false\n const visit = (id: string): string | null => {\n const node = nodes[id]\n const componentId = String(node?.componentId ?? '')\n if (!node || FORM_COMPONENTS.has(componentId) || linksNowhere(componentId, node.props)) return null\n const root = id === CANVAS_ROOT_ELEMENT_ID\n if (!root && !allowed.has(componentId)) {\n unavailable = true\n return null\n }\n // Inserted before the children, so the map reads top-down.\n const shaped: AiDoctrineNode = { componentId: root ? 'div' : componentId }\n out[id] = shaped\n const declared = AI_PALETTE[componentId]?.propsSchema.properties ?? {}\n const props: Record<string, unknown> = {}\n for (const [name, value] of Object.entries(node.props ?? {})) {\n if (!declared[name] || value === undefined) continue\n if (typeof value === 'string' && CSS_LENGTH.test(value)) continue\n props[name] = value\n }\n if (componentId === 'muiTypography') {\n const variant = props['variant']\n if (variant === 'h6') props['component'] = 'p'\n else if (isHeadlineVariant(variant)) props['component'] = headings++ === 0 ? 'h1' : 'h2'\n }\n if (Object.keys(props).length) shaped.props = props\n if (node.sx && Object.keys(node.sx).length) shaped.sx = node.sx\n const children = (Array.isArray(node.nodes) ? (node.nodes as unknown[]) : [])\n .map((child) => (typeof child === 'string' ? visit(child) : null))\n .filter((child): child is string => child !== null)\n if (children.length) shaped.nodes = children\n return id\n }\n visit(CANVAS_ROOT_ELEMENT_ID)\n return unavailable ? null : { rootId: CANVAS_ROOT_ELEMENT_ID, nodes: out }\n}\n\n/** A tree's element structure without its copy or ids, to show one shape once. */\nfunction shapeOf(tree: AiDoctrineTree, id = tree.rootId): string {\n const node = tree.nodes[id]\n return `${node.componentId}(${(node.nodes ?? []).map((child) => shapeOf(tree, child)).join(',')})`\n}\n\n/** Every starter page the doctrine accepts as a template exactly as the example shows it. */\nexport function aiStarterTemplateExamples(\n starters: readonly StarterTemplate[] = STARTER_TEMPLATES,\n): AiTemplateExample[] {\n const examples: AiTemplateExample[] = []\n const shapes = new Set<string>()\n for (const starter of starters) {\n for (const screen of starter.screens) {\n const tree = aiStarterExampleTree(screen.nodes as Record<string, StarterNode>)\n if (!tree) continue\n const report = validateAiDoctrineTree(tree, 'template')\n if (!report.ok || !report.tree || report.tree.repairs.length) continue\n const shape = shapeOf(tree)\n if (shapes.has(shape)) continue\n shapes.add(shape)\n examples.push({ starter: starter.displayName, page: screen.displayName, tree })\n }\n }\n return examples\n}\n\nexport const AI_TEMPLATE_EXAMPLES_PREAMBLE = [\n 'Examples: pages from the platform’s starter sites, in the node-map shape the tool takes.',\n 'Build the way they are built: each band a Container at a stock width (xl for a marketing band, lg for an interactive one, md for prose) with vertical padding on the spacing scale, its content in a Stack, and one h1 for the page.',\n 'Their copy is the starter’s own; a template binds its subject’s tokens wherever a record’s own words, links or pictures go.',\n].join(' ')\n\n/** The examples as one cached system block, held to its ceiling an example at a time. */\nexport function aiTemplateExamplesBlock(\n examples: readonly AiTemplateExample[] = aiStarterTemplateExamples(),\n): AiSystemBlock {\n const parts = [AI_TEMPLATE_EXAMPLES_PREAMBLE]\n let used = AI_TEMPLATE_EXAMPLES_PREAMBLE.length\n for (const example of examples) {\n const text = `Starter \"${example.starter}\", page \"${example.page}\":\\n${JSON.stringify(example.tree)}`\n if (used + text.length + 2 > AI_TEMPLATE_EXAMPLES_MAX_CHARS) continue\n parts.push(text)\n used += text.length + 2\n }\n return { text: parts.join('\\n\\n'), cacheBreakpoint: true }\n}\n\nlet examplesBlock: AiSystemBlock | null = null\n\n/** The block a template generation sends, built once per process from the starters. */\nexport function aiTemplateExamplesSystemBlock(): AiSystemBlock {\n examplesBlock ??= aiTemplateExamplesBlock()\n return examplesBlock\n}\n"],"names":["STARTER_TEMPLATES","CANVAS_ROOT_ELEMENT_ID","validateAiDoctrineTree","isHeadlineVariant","AI_PALETTE","AI_SURFACES","AI_TEMPLATE_EXAMPLES_MAX_CHARS","FORM_COMPONENTS","Set","LINK_COMPONENTS","linksNowhere","componentId","props","has","named","value","trim","CSS_LENGTH","aiStarterExampleTree","nodes","allowed","screen","allow","out","headings","unavailable","visit","id","node","String","root","shaped","declared","propsSchema","properties","name","Object","entries","undefined","test","variant","keys","length","sx","children","Array","isArray","map","child","filter","rootId","shapeOf","tree","join","aiStarterTemplateExamples","starters","examples","shapes","starter","screens","report","ok","repairs","shape","add","push","displayName","page","AI_TEMPLATE_EXAMPLES_PREAMBLE","aiTemplateExamplesBlock","parts","used","example","text","JSON","stringify","cacheBreakpoint","examplesBlock","aiTemplateExamplesSystemBlock"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SACEA,iBAAiB,QAEZ,2CAA0C;AACjD,SAASC,sBAAsB,QAAQ,2CAA0C;AACjF,SACEC,sBAAsB,QAGjB,8BAA0B;AACjC,SAASC,iBAAiB,QAAQ,kBAAc;AAChD,SAASC,UAAU,EAAEC,WAAW,QAAQ,4BAAwB;AAuChE,kEAAkE,GAClE,OAAO,MAAMC,iCAAiC,KAAK;AAEnD,MAAMC,kBAAkB,IAAIC,IAAI;IAAC;IAAQ;CAAY;AAErD,oFAAoF,GACpF,MAAMC,kBAAkB,IAAID,IAAI;IAAC;IAAa;CAAgB;AAE9D,+DAA+D,GAC/D,SAASE,aAAaC,WAAmB,EAAEC,KAA0C;IACnF,IAAI,CAACH,gBAAgBI,GAAG,CAACF,cAAc,OAAO;IAC9C,MAAMG,QAAQ,CAACC,QAAmB,OAAOA,UAAU,YAAYA,MAAMC,IAAI,OAAO;IAChF,OAAO,CAACF,MAAMF,yBAAAA,KAAO,CAAC,WAAW,KAAK,CAACE,MAAMF,yBAAAA,KAAO,CAAC,OAAO;AAC9D;AAEA,8CAA8C,GAC9C,MAAMK,aAAa;AASnB,0GAA0G,GAC1G,OAAO,SAASC,qBAAqBC,KAAkC;IACrE,IAAI,CAACA,KAAK,CAAClB,uBAAuB,EAAE,OAAO;IAC3C,MAAMmB,UAAU,IAAIZ,IAAIH,YAAYgB,MAAM,CAACC,KAAK;IAChD,MAAMC,MAAsC,CAAC;IAC7C,IAAIC,WAAW;IACf,IAAIC,cAAc;IAClB,MAAMC,QAAQ,CAACC;yBAc8BC;YAF1BxB;QAXjB,MAAMwB,OAAOT,KAAK,CAACQ,GAAG;QACtB,MAAMhB,cAAckB,eAAOD,wBAAAA,KAAMjB,WAAW,mBAAI;QAChD,IAAI,CAACiB,QAAQrB,gBAAgBM,GAAG,CAACF,gBAAgBD,aAAaC,aAAaiB,KAAKhB,KAAK,GAAG,OAAO;QAC/F,MAAMkB,OAAOH,OAAO1B;QACpB,IAAI,CAAC6B,QAAQ,CAACV,QAAQP,GAAG,CAACF,cAAc;YACtCc,cAAc;YACd,OAAO;QACT;QACA,2DAA2D;QAC3D,MAAMM,SAAyB;YAAEpB,aAAamB,OAAO,QAAQnB;QAAY;QACzEY,GAAG,CAACI,GAAG,GAAGI;QACV,MAAMC,qBAAW5B,0BAAAA,UAAU,CAACO,YAAY,qBAAvBP,wBAAyB6B,WAAW,CAACC,UAAU,oBAAI,CAAC;QACrE,MAAMtB,QAAiC,CAAC;QACxC,KAAK,MAAM,CAACuB,MAAMpB,MAAM,IAAIqB,OAAOC,OAAO,EAACT,cAAAA,KAAKhB,KAAK,YAAVgB,cAAc,CAAC,GAAI;YAC5D,IAAI,CAACI,QAAQ,CAACG,KAAK,IAAIpB,UAAUuB,WAAW;YAC5C,IAAI,OAAOvB,UAAU,YAAYE,WAAWsB,IAAI,CAACxB,QAAQ;YACzDH,KAAK,CAACuB,KAAK,GAAGpB;QAChB;QACA,IAAIJ,gBAAgB,iBAAiB;YACnC,MAAM6B,UAAU5B,KAAK,CAAC,UAAU;YAChC,IAAI4B,YAAY,MAAM5B,KAAK,CAAC,YAAY,GAAG;iBACtC,IAAIT,kBAAkBqC,UAAU5B,KAAK,CAAC,YAAY,GAAGY,eAAe,IAAI,OAAO;QACtF;QACA,IAAIY,OAAOK,IAAI,CAAC7B,OAAO8B,MAAM,EAAEX,OAAOnB,KAAK,GAAGA;QAC9C,IAAIgB,KAAKe,EAAE,IAAIP,OAAOK,IAAI,CAACb,KAAKe,EAAE,EAAED,MAAM,EAAEX,OAAOY,EAAE,GAAGf,KAAKe,EAAE;QAC/D,MAAMC,WAAW,AAACC,CAAAA,MAAMC,OAAO,CAAClB,KAAKT,KAAK,IAAKS,KAAKT,KAAK,GAAiB,EAAE,AAAD,EACxE4B,GAAG,CAAC,CAACC,QAAW,OAAOA,UAAU,WAAWtB,MAAMsB,SAAS,MAC3DC,MAAM,CAAC,CAACD,QAA2BA,UAAU;QAChD,IAAIJ,SAASF,MAAM,EAAEX,OAAOZ,KAAK,GAAGyB;QACpC,OAAOjB;IACT;IACAD,MAAMzB;IACN,OAAOwB,cAAc,OAAO;QAAEyB,QAAQjD;QAAwBkB,OAAOI;IAAI;AAC3E;AAEA,gFAAgF,GAChF,SAAS4B,QAAQC,IAAoB,EAAEzB,KAAKyB,KAAKF,MAAM;QAEtBtB;IAD/B,MAAMA,OAAOwB,KAAKjC,KAAK,CAACQ,GAAG;IAC3B,OAAO,GAAGC,KAAKjB,WAAW,CAAC,CAAC,EAAE,EAACiB,cAAAA,KAAKT,KAAK,YAAVS,cAAc,EAAE,EAAEmB,GAAG,CAAC,CAACC,QAAUG,QAAQC,MAAMJ,QAAQK,IAAI,CAAC,KAAK,CAAC,CAAC;AACpG;AAEA,2FAA2F,GAC3F,OAAO,SAASC,0BACdC,WAAuCvD,iBAAiB;IAExD,MAAMwD,WAAgC,EAAE;IACxC,MAAMC,SAAS,IAAIjD;IACnB,KAAK,MAAMkD,WAAWH,SAAU;QAC9B,KAAK,MAAMlC,UAAUqC,QAAQC,OAAO,CAAE;YACpC,MAAMP,OAAOlC,qBAAqBG,OAAOF,KAAK;YAC9C,IAAI,CAACiC,MAAM;YACX,MAAMQ,SAAS1D,uBAAuBkD,MAAM;YAC5C,IAAI,CAACQ,OAAOC,EAAE,IAAI,CAACD,OAAOR,IAAI,IAAIQ,OAAOR,IAAI,CAACU,OAAO,CAACpB,MAAM,EAAE;YAC9D,MAAMqB,QAAQZ,QAAQC;YACtB,IAAIK,OAAO5C,GAAG,CAACkD,QAAQ;YACvBN,OAAOO,GAAG,CAACD;YACXP,SAASS,IAAI,CAAC;gBAAEP,SAASA,QAAQQ,WAAW;gBAAEC,MAAM9C,OAAO6C,WAAW;gBAAEd;YAAK;QAC/E;IACF;IACA,OAAOI;AACT;AAEA,OAAO,MAAMY,gCAAgC;IAC3C;IACA;IACA;CACD,CAACf,IAAI,CAAC,KAAI;AAEX,uFAAuF,GACvF,OAAO,SAASgB,wBACdb,WAAyCF,2BAA2B;IAEpE,MAAMgB,QAAQ;QAACF;KAA8B;IAC7C,IAAIG,OAAOH,8BAA8B1B,MAAM;IAC/C,KAAK,MAAM8B,WAAWhB,SAAU;QAC9B,MAAMiB,OAAO,CAAC,SAAS,EAAED,QAAQd,OAAO,CAAC,SAAS,EAAEc,QAAQL,IAAI,CAAC,IAAI,EAAEO,KAAKC,SAAS,CAACH,QAAQpB,IAAI,GAAG;QACrG,IAAImB,OAAOE,KAAK/B,MAAM,GAAG,IAAIpC,gCAAgC;QAC7DgE,MAAML,IAAI,CAACQ;QACXF,QAAQE,KAAK/B,MAAM,GAAG;IACxB;IACA,OAAO;QAAE+B,MAAMH,MAAMjB,IAAI,CAAC;QAASuB,iBAAiB;IAAK;AAC3D;AAEA,IAAIC,gBAAsC;AAE1C,qFAAqF,GACrF,OAAO,SAASC;IACdD,wBAAAA,gBAAAA,gBAAkBR;IAClB,OAAOQ;AACT"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/runtime/ai-template-examples.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 STARTER_TEMPLATES,\n type StarterTemplate,\n} from '@aglyn/aglyn/app-utils/starter-templates'\nimport { FORM_INPUT_ELEMENT_IDS } from '@aglyn/aglyn/app-utils/forms'\nimport { CANVAS_ROOT_ELEMENT_ID } from '@aglyn/aglyn/foundation/constants/canvas'\nimport {\n validateAiDoctrineTree,\n type AiDoctrineNode,\n type AiDoctrineTree,\n} from './ai-doctrine-validators'\nimport { isHeadlineVariant } from './ai-palette'\nimport { AI_PALETTE, AI_SURFACES } from './ai-palette.generated'\nimport type { AiSystemBlock } from './ai-runtime'\n\n/**\n * The page examples a template generation is shown (AGL-2909): pages from\n * the platform's own starter sites (`STARTER_TEMPLATES`), in the node-map\n * shape the tool takes, as one cached block that is the same bytes for every\n * org.\n *\n * Three things about a starter predate the building rules, and every example\n * is brought up to them the same way rather than shown as it stands:\n *\n * - HEADINGS. A starter picks a heading's look with its variant, and rule 11\n * reads its level from the element. The page's first heading renders as\n * its h1, each later one as an h2, and a sixth-level subtitle as a\n * paragraph.\n * - FORMS. A starter draws its contact form inline, and rule 3 places a form\n * built on the Forms page by id, so the form is left out.\n * - LINKS. A starter's call to action points nowhere until a member picks\n * where it goes, and rule 10 refuses a Button or a Screen Link with no\n * destination (AGL-3072), so such a link is left out.\n *\n * Only props the palette declares are kept, since the model is held to the\n * same palette, and none that holds a px, rem or em length, which rule 5\n * names as a length never to write. A page that still breaks a rule, that\n * the palette validator would change in any way, or that places an element\n * the page surface does not offer (a commerce block) is not shown, and two\n * pages of one shape are shown once: an example the model imitates is one\n * the doctrine accepts exactly as written.\n */\n\nexport interface AiTemplateExample {\n /** The starter site's name. */\n starter: string\n /** The page's name within it. */\n page: string\n tree: AiDoctrineTree\n}\n\n/** The block's ceiling: 1,500 tokens at four characters a token. */\nexport const AI_TEMPLATE_EXAMPLES_MAX_CHARS = 6_000\n\n/**\n * A form and every piece of one — the all-in-one Form Field and MUI's\n * separate form elements — which a page never draws: it places a saved form.\n */\nconst FORM_COMPONENTS = new Set<string>([\n 'form',\n 'formField',\n ...Object.values(FORM_INPUT_ELEMENT_IDS),\n 'muiFormHelperText',\n])\n\n/** The elements a page links with, which go where their `screenId` or `href` says. */\nconst LINK_COMPONENTS = new Set(['muiButton', 'muiScreenLink'])\n\n/** Whether a starter node is a link that names nowhere to go. */\nfunction linksNowhere(componentId: string, props: Record<string, unknown> | undefined): boolean {\n if (!LINK_COMPONENTS.has(componentId)) return false\n const named = (value: unknown) => typeof value === 'string' && value.trim() !== ''\n return !named(props?.['screenId']) && !named(props?.['href'])\n}\n\n/** A CSS length in the units rule 5 forbids. */\nconst CSS_LENGTH = /\\d(?:\\.\\d+)?(?:px|rem|em)\\b/i\n\ntype StarterNode = {\n componentId?: string\n props?: Record<string, unknown>\n sx?: Record<string, unknown>\n nodes?: unknown\n}\n\n/** A starter page as the tool's node map, brought up to the rules above; `null` when it cannot be shown. */\nexport function aiStarterExampleTree(nodes: Record<string, StarterNode>): AiDoctrineTree | null {\n if (!nodes[CANVAS_ROOT_ELEMENT_ID]) return null\n const allowed = new Set(AI_SURFACES.screen.allow)\n const out: Record<string, AiDoctrineNode> = {}\n let headings = 0\n let unavailable = false\n const visit = (id: string): string | null => {\n const node = nodes[id]\n const componentId = String(node?.componentId ?? '')\n if (!node || FORM_COMPONENTS.has(componentId) || linksNowhere(componentId, node.props)) return null\n const root = id === CANVAS_ROOT_ELEMENT_ID\n if (!root && !allowed.has(componentId)) {\n unavailable = true\n return null\n }\n // Inserted before the children, so the map reads top-down.\n const shaped: AiDoctrineNode = { componentId: root ? 'div' : componentId }\n out[id] = shaped\n const declared = AI_PALETTE[componentId]?.propsSchema.properties ?? {}\n const props: Record<string, unknown> = {}\n for (const [name, value] of Object.entries(node.props ?? {})) {\n if (!declared[name] || value === undefined) continue\n if (typeof value === 'string' && CSS_LENGTH.test(value)) continue\n props[name] = value\n }\n if (componentId === 'muiTypography') {\n const variant = props['variant']\n if (variant === 'h6') props['component'] = 'p'\n else if (isHeadlineVariant(variant)) props['component'] = headings++ === 0 ? 'h1' : 'h2'\n }\n if (Object.keys(props).length) shaped.props = props\n if (node.sx && Object.keys(node.sx).length) shaped.sx = node.sx\n const children = (Array.isArray(node.nodes) ? (node.nodes as unknown[]) : [])\n .map((child) => (typeof child === 'string' ? visit(child) : null))\n .filter((child): child is string => child !== null)\n if (children.length) shaped.nodes = children\n return id\n }\n visit(CANVAS_ROOT_ELEMENT_ID)\n return unavailable ? null : { rootId: CANVAS_ROOT_ELEMENT_ID, nodes: out }\n}\n\n/** A tree's element structure without its copy or ids, to show one shape once. */\nfunction shapeOf(tree: AiDoctrineTree, id = tree.rootId): string {\n const node = tree.nodes[id]\n return `${node.componentId}(${(node.nodes ?? []).map((child) => shapeOf(tree, child)).join(',')})`\n}\n\n/** Every starter page the doctrine accepts as a template exactly as the example shows it. */\nexport function aiStarterTemplateExamples(\n starters: readonly StarterTemplate[] = STARTER_TEMPLATES,\n): AiTemplateExample[] {\n const examples: AiTemplateExample[] = []\n const shapes = new Set<string>()\n for (const starter of starters) {\n for (const screen of starter.screens) {\n const tree = aiStarterExampleTree(screen.nodes as Record<string, StarterNode>)\n if (!tree) continue\n const report = validateAiDoctrineTree(tree, 'template')\n if (!report.ok || !report.tree || report.tree.repairs.length) continue\n const shape = shapeOf(tree)\n if (shapes.has(shape)) continue\n shapes.add(shape)\n examples.push({ starter: starter.displayName, page: screen.displayName, tree })\n }\n }\n return examples\n}\n\nexport const AI_TEMPLATE_EXAMPLES_PREAMBLE = [\n 'Examples: pages from the platform’s starter sites, in the node-map shape the tool takes.',\n 'Build the way they are built: each band a Container at a stock width (xl for a marketing band, lg for an interactive one, md for prose) with vertical padding on the spacing scale, its content in a Stack, and one h1 for the page.',\n 'Their copy is the starter’s own; a template binds its subject’s tokens wherever a record’s own words, links or pictures go.',\n].join(' ')\n\n/** The examples as one cached system block, held to its ceiling an example at a time. */\nexport function aiTemplateExamplesBlock(\n examples: readonly AiTemplateExample[] = aiStarterTemplateExamples(),\n): AiSystemBlock {\n const parts = [AI_TEMPLATE_EXAMPLES_PREAMBLE]\n let used = AI_TEMPLATE_EXAMPLES_PREAMBLE.length\n for (const example of examples) {\n const text = `Starter \"${example.starter}\", page \"${example.page}\":\\n${JSON.stringify(example.tree)}`\n if (used + text.length + 2 > AI_TEMPLATE_EXAMPLES_MAX_CHARS) continue\n parts.push(text)\n used += text.length + 2\n }\n return { text: parts.join('\\n\\n'), cacheBreakpoint: true }\n}\n\nlet examplesBlock: AiSystemBlock | null = null\n\n/** The block a template generation sends, built once per process from the starters. */\nexport function aiTemplateExamplesSystemBlock(): AiSystemBlock {\n examplesBlock ??= aiTemplateExamplesBlock()\n return examplesBlock\n}\n"],"names":["STARTER_TEMPLATES","FORM_INPUT_ELEMENT_IDS","CANVAS_ROOT_ELEMENT_ID","validateAiDoctrineTree","isHeadlineVariant","AI_PALETTE","AI_SURFACES","AI_TEMPLATE_EXAMPLES_MAX_CHARS","FORM_COMPONENTS","Set","Object","values","LINK_COMPONENTS","linksNowhere","componentId","props","has","named","value","trim","CSS_LENGTH","aiStarterExampleTree","nodes","allowed","screen","allow","out","headings","unavailable","visit","id","node","String","root","shaped","declared","propsSchema","properties","name","entries","undefined","test","variant","keys","length","sx","children","Array","isArray","map","child","filter","rootId","shapeOf","tree","join","aiStarterTemplateExamples","starters","examples","shapes","starter","screens","report","ok","repairs","shape","add","push","displayName","page","AI_TEMPLATE_EXAMPLES_PREAMBLE","aiTemplateExamplesBlock","parts","used","example","text","JSON","stringify","cacheBreakpoint","examplesBlock","aiTemplateExamplesSystemBlock"],"mappings":"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"}
package/src/lib/server.js CHANGED
@@ -38,6 +38,8 @@ import { registerAiEditJob } from "./jobs/ai-job-edit-step.js";
38
38
  import { registerAiNavigationJob } from "./jobs/ai-job-navigation-step.js";
39
39
  import { registerAiDuplicateJob } from "./jobs/ai-job-duplicate-step.js";
40
40
  import { registerAiSettingsJob } from "./jobs/ai-job-settings-step.js";
41
+ import { registerAiMediaJob } from "./jobs/ai-job-media-step.js";
42
+ import { registerAiTrackingJob } from "./jobs/ai-job-tracking-step.js";
41
43
  import { registerAiInsightJob } from "./jobs/ai-job-insight-step.js";
42
44
  import { registerAiFigureReaders } from "./insights/ai-figure-readers.js";
43
45
  import { registerAiTextGenerator } from "./server/plugin-text-generation.js";
@@ -136,6 +138,12 @@ export * from "./runtime/ai-runtime.js";
136
138
  // A change to a site's settings — its name, search defaults, icons,
137
139
  // language, time zone and contact details — saved live (AGL-3748).
138
140
  registerAiSettingsJob();
141
+ // Folders, tags, alt text and names of files already in a site's media
142
+ // library, as its Details drawer saves them, live (AGL-3752).
143
+ registerAiMediaJob();
144
+ // The site's analytics and advertising tags, as Setup → Tracking sets
145
+ // them, saved live (AGL-3751).
146
+ registerAiTrackingJob();
139
147
  // Product copy, and a store's products, categories and discounts from a
140
148
  // brief, as proposals the commerce plugin's surfaces apply (AGL-2916).
141
149
  registerAiProductsJob();
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../libs/plugins/ai/src/lib/server.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 { registerPluginApiRoute } from '@aglyn/aglyn/server'\nimport { registerAiDeclarations } from './declarations'\nimport { AI_JOBS_BEAT_PATH } from './jobs/ai-jobs-beat'\nimport { registerAiJobPlan } from './jobs/ai-job-plan-step'\nimport { registerAiJobsPause } from './jobs/ai-jobs-pause'\nimport { registerAiJobsNotify } from './jobs/ai-jobs-notify'\nimport { registerAiComponentJob } from './jobs/ai-job-component-step'\nimport { registerAiCrmJob } from './jobs/ai-job-crm-step'\nimport { registerAiLayoutJob } from './jobs/ai-job-layout-step'\nimport { registerAiTemplateJob } from './jobs/ai-job-template-step'\nimport { registerAiExperimentJob } from './jobs/ai-job-experiment-step'\nimport { registerAiFormJob } from './jobs/ai-job-form-step'\nimport { registerAiPageJob } from './jobs/ai-job-page-step'\nimport { registerAiProductsJob } from './jobs/ai-job-products-step'\nimport { registerAiEmailJob } from './jobs/ai-job-email-step'\nimport { registerAiCampaignJob } from './jobs/ai-job-campaign-step'\nimport { registerAiSiteJob } from './jobs/ai-job-site-step'\nimport { registerAiBuildJob } from './jobs/ai-job-build-step'\nimport { registerAiBuildCapabilities } from './jobs/ai-build-capabilities'\nimport { registerAiWorkflowJob } from './jobs/ai-job-workflow-step'\nimport { registerAiLogicJob } from './jobs/ai-job-logic-step'\nimport { registerAiEditJob } from './jobs/ai-job-edit-step'\nimport { registerAiNavigationJob } from './jobs/ai-job-navigation-step'\nimport { registerAiDuplicateJob } from './jobs/ai-job-duplicate-step'\nimport { registerAiSettingsJob } from './jobs/ai-job-settings-step'\nimport { registerAiInsightJob } from './jobs/ai-job-insight-step'\nimport { registerAiFigureReaders } from './insights/ai-figure-readers'\nimport { registerAiTextGenerator } from './server/plugin-text-generation'\nimport { firebaseAdmin } from '@aglyn/tenant-data-admin/server/firebase-admin'\nimport { ensureFirstPartyAiProviders } from './providers/registry'\nimport { aiAssistHandler } from './server/ai-assist'\nimport { GET as aiCrmAnswer } from './server/ai-crm-answer'\nimport { POST as runAiJobsBeat } from './server/ai-jobs-beat-route'\nimport { POST as cancelAiJob } from './server/ai-jobs-cancel'\nimport { GET as aiJobEvents } from './server/ai-jobs-events-route'\nimport { POST as createAiSiteBatch } from './server/ai-jobs-batch'\nimport { GET as listAiJobs, POST as createAiJob } from './server/ai-jobs-route'\nimport { POST as resumeAiJob } from './server/ai-jobs-resume'\nimport { POST as aiGenerateComponent } from './server/ai-generate-component'\nimport { POST as createAiMediaImages } from './server/ai-media-image'\nimport { POST as applyAiSeoAudit } from './server/ai-seo-apply'\nimport { POST as createAiExperimentVersions } from './server/ai-experiment-versions'\nimport { POST as assistChat } from './server/assist-chat'\nimport { POST as assistEditApplied } from './server/assist-edit-applied'\nimport { POST as assistFeedback } from './server/assist-feedback'\nimport { PATCH as aiHostPermissions } from './server/ai-host-permissions'\nimport { GET as aiAdminOrg } from './server/ai-admin-org'\nimport { GET as aiAdminConversations } from './server/ai-admin-conversations'\nimport { GET as aiAdminOrgsSpend } from './server/ai-admin-orgs-spend'\nimport { GET as aiAdminSignals } from './server/ai-admin-signals'\nimport { GET as aiAdminUser } from './server/ai-admin-user'\nimport { POST as aiAdminOverage } from './server/ai-admin-overage'\nimport { POST as aiAdminCredits } from './server/ai-admin-credits'\nimport { GET as aiUsage } from './server/ai-usage'\nimport { GET as aiAllotments } from './server/ai-allotments'\nimport { GET as aiInsightAnswer } from './server/ai-insight-answer'\nimport { POST as runAiInsightsDigest } from './server/ai-insight-digest-route'\nimport { AI_INSIGHTS_DIGEST_PATH } from './insights/ai-insight-digest'\nimport { GET as aiModels } from './server/ai-models'\nimport { GET as billingCredits } from './server/billing-credits'\nimport { POST as billingOverage } from './server/billing-overage'\n\nexport * from './providers/contract'\nexport * from './providers/catalog'\nexport * from './providers/registry'\nexport * from './providers/routing'\nexport * from './runtime/ai-runtime'\n\n/**\n * The two first-party adapters, registered once per process. A marketplace\n * plugin registers its own against `AI_PROVIDER_CONTRACT` from its own\n * entry and never touches this.\n */\nconst registerFirstPartyProviders = ensureFirstPartyAiProviders\n\n/**\n * Every job kind this plugin runs beyond the three the machine registers\n * itself (AGL-3025). Each is a CALL, never an import made for what the\n * module does as it loads: this package declares only this file\n * effect-ful, so a bundler honoring `sideEffects` deletes an import whose\n * exports go unused — and a step registered that way ran in every spec and\n * in neither app. `registrations-survive-a-bundler.spec.ts` holds this.\n *\n * The console surface is the only caller (AGL-3026): its doors run a job's\n * first step inline, and its beat runs every step after it. The tenant app\n * loads no server surface of this plugin at all, so nothing that serves a\n * published site can reach a provider.\n */\nfunction registerAiJobKinds(): void {\n // The workspace AI pause the machine asks before it claims any step, so a\n // paused workspace's queued jobs spend nothing (AGL-3037).\n registerAiJobsPause()\n // The person who started a job is told when it needs them, finishes or\n // stops (AGL-3593), from the write that changed it.\n registerAiJobsNotify()\n // The plan step every planned kind runs first (AGL-2935).\n registerAiJobPlan()\n // Components, layouts and templates (AGL-2908, AGL-2909).\n registerAiComponentJob()\n registerAiLayoutJob()\n registerAiTemplateJob()\n // Forms (AGL-2913).\n registerAiExperimentJob()\n registerAiFormJob()\n // Pages, with the least time one pass needs (AGL-2907).\n registerAiPageJob()\n // Email designs and campaigns, whose drafts the email and marketing\n // plugins write on the resource-draft seam (AGL-2912).\n registerAiEmailJob()\n registerAiCampaignJob()\n // Automations drafted from a description, and explained, whose drafts the\n // workflows plugin writes on the resource-draft seam (AGL-2919).\n registerAiWorkflowJob()\n // Site functions and variables proposed from a description, and functions\n // explained, as proposals the logic editor opens unsaved (AGL-3603).\n registerAiLogicJob()\n // A change to a page or a layout the site has, from a description, saved\n // where no visitor sees it — the Assist edit rung run as a job (AGL-3616).\n registerAiEditJob()\n // A change to a site's header or footer menu, made by code from a build's\n // item and saved beside the live layout (AGL-3728).\n registerAiNavigationJob()\n // A copy of something a site already has, through core's duplicate — the\n // console's own Duplicate… — as a build's item (AGL-3740).\n registerAiDuplicateJob()\n // A change to a site's settings — its name, search defaults, icons,\n // language, time zone and contact details — saved live (AGL-3748).\n registerAiSettingsJob()\n // Product copy, and a store's products, categories and discounts from a\n // brief, as proposals the commerce plugin's surfaces apply (AGL-2916).\n registerAiProductsJob()\n // The site scaffold, which builds a whole site through the steps above\n // (AGL-2911).\n registerAiSiteJob()\n // A build from one request (AGL-3616): pages, creations and other\n // plugins' drafts, through the steps above and the writers those plugins\n // register, settled item by item. What this plugin lets a build make is\n // registered on the core's capability contract like any plugin's.\n registerAiBuildCapabilities()\n registerAiBuildJob()\n // Insights (AGL-2915): a question about a site's figures, answered from\n // tables the figure readers return, and the weekly digest. The readers for\n // the platform's own records — page views, forms, datasets — are this\n // plugin's; every other plugin registers its own from its console surface.\n registerAiFigureReaders(() => firebaseAdmin.app().firestore())\n registerAiInsightJob()\n // CRM by AI (AGL-2917): a record's summary and next step, an email draft\n // and an import's column matches, read through the CRM's facts readers.\n registerAiCrmJob()\n}\n\n/**\n * The console-side API (AGL-2939): every AI door, under the `ai` and\n * `assist` prefixes the plugin owns. The URLs the panel and the besigner\n * call are unchanged — `/api/assist/chat`, `/api/ai/assist`,\n * `/api/ai/jobs` — because the dispatcher serves them from the registry\n * exactly where the named routes used to; the two billing doors and the\n * per-member usage read (`ai/usage`) live under the plugin's own prefix.\n * The jobs beat is the one path outside them, for the reason\n * `AI_JOBS_BEAT_PATH` gives.\n */\nexport function registerAiConsoleApi(): void {\n registerAiDeclarations()\n registerFirstPartyProviders()\n registerAiJobKinds()\n // This plugin as the workspace's text generator on the core's seam\n // (AGL-3324): another plugin drafts a paragraph through it under the\n // generation doors' own rules, metered as a text job is.\n registerAiTextGenerator()\n registerPluginApiRoute('assist/chat', { web: assistChat })\n registerPluginApiRoute('assist/feedback', { web: assistFeedback })\n // The applied-edit record (AGL-2906): counts of a proposal the author\n // applied in their editor, written to the site's activity log.\n registerPluginApiRoute('assist/edit-applied', { web: assistEditApplied })\n // A collaborator's AI toggles on one site (AGL-2927), set from the site's\n // collaborators card through this plugin's column there.\n registerPluginApiRoute('ai/host-permissions', { web: aiHostPermissions })\n registerPluginApiRoute('ai/assist', aiAssistHandler)\n registerPluginApiRoute('ai/jobs', {\n web: (request) => (request.method === 'GET' ? listAiJobs(request) : createAiJob(request)),\n })\n // The agency batch (AGL-2911): one brief, one `site` job per named site,\n // under one batch id. Before the `:jobId` routes, which it is not one of.\n registerPluginApiRoute('ai/jobs/batch', { web: createAiSiteBatch })\n registerPluginApiRoute('ai/jobs/:jobId/cancel', {\n web: (request, context) =>\n cancelAiJob(request, { params: Promise.resolve({ jobId: String(context.params['jobId']) }) }),\n })\n registerPluginApiRoute('ai/jobs/:jobId/resume', {\n web: (request, context) =>\n resumeAiJob(request, { params: Promise.resolve({ jobId: String(context.params['jobId']) }) }),\n })\n // An insight job's answer (AGL-2915), which is kept off the job document\n // and served only to the member who asked, or for a digest to the site's\n // members.\n registerPluginApiRoute('ai/insights/:jobId', {\n web: (request, context) =>\n aiInsightAnswer(request, { params: Promise.resolve({ jobId: String(context.params['jobId']) }) }),\n })\n // A CRM job's answer (AGL-2917), which is kept off the job document and\n // served only to a member the CRM still lets read the record.\n registerPluginApiRoute('ai/crm/:jobId', {\n web: (request, context) =>\n aiCrmAnswer(request, { params: Promise.resolve({ jobId: String(context.params['jobId']) }) }),\n })\n registerPluginApiRoute('ai/jobs/:jobId/events', {\n web: (request, context) =>\n aiJobEvents(request, { params: Promise.resolve({ jobId: String(context.params['jobId']) }) }),\n })\n // The jobs beat (AGL-3026): every step the doors leave queued, run every\n // minute by the console's scheduler on the cron secret, on the one surface\n // that holds the provider's key.\n registerPluginApiRoute(AI_JOBS_BEAT_PATH, { web: runAiJobsBeat })\n // The weekly insights (AGL-2915): Monday's digests made for the people who\n // asked for them, and delivered once the beat has written them. Beside the\n // beat under `/api/admin/`, where the console's scheduled sweeps live.\n // A machine's route: Cloud Scheduler calls it on the cron secret with no\n // site and no member, and the route checks that secret and the AI release\n // flag itself. Unmarked, it was counted in the dispatcher's per-address\n // write bucket and refused with a 429 at 14:00Z on 2026-10-09.\n registerPluginApiRoute(AI_INSIGHTS_DIGEST_PATH, { web: runAiInsightsDigest }, { machine: true })\n // A site SEO audit's \"Apply all\" (AGL-2910): content fixes as new\n // unpublished versions, listing values staged for their SEO cards.\n registerPluginApiRoute('ai/seo/apply', { web: applyAiSeoAudit })\n // A page's or a section's A/B variants as draft versions (AGL-3603): the\n // published version copied once per proposed variant, the variant's copy\n // put in, stored unpublished for the experiment editor to pin.\n registerPluginApiRoute('ai/experiments/versions', { web: createAiExperimentVersions })\n // Save the selection as a reusable component, with AI (AGL-2908): the\n // second entry point of the component job, from the besigner's Attributes\n // panel. It proposes, and the person's Apply writes.\n registerPluginApiRoute('ai/generate/component', { web: aiGenerateComponent })\n // Pictures from a description, in Media (AGL-3602): photos and SVG\n // illustrations, stored through the media upload route. POST only — the\n // button draws from the shell's gates and asks nothing until it is used.\n registerPluginApiRoute('ai/media/images', { web: createAiMediaImages })\n registerPluginApiRoute('ai/billing/credits', { web: billingCredits })\n registerPluginApiRoute('ai/billing/overage', { web: billingOverage })\n registerPluginApiRoute('ai/usage', { web: aiUsage })\n // The allotments a manager sets, and the options the model switch lists\n // (AGL-2942). One handler answers the allotments' GET and POST.\n registerPluginApiRoute('ai/allotments', { web: aiAllotments })\n registerPluginApiRoute('ai/models', { web: aiModels })\n // The staff doors (AGL-2928, AGL-2930, AGL-2252): one org's AI in full,\n // one account's usage across its workspaces, and the fleet's Assist\n // signals behind the Assist signal staff page.\n registerPluginApiRoute('ai/admin/org', { web: aiAdminOrg })\n registerPluginApiRoute('ai/admin/orgs-spend', { web: aiAdminOrgsSpend })\n registerPluginApiRoute('ai/admin/user', { web: aiAdminUser })\n registerPluginApiRoute('ai/admin/signals', { web: aiAdminSignals })\n // What an org asked Aglyn AI and what it answered (AGL-3675): one page at a\n // time, read on request, each page an audited access.\n registerPluginApiRoute('ai/admin/conversations', { web: aiAdminConversations })\n // One workspace's overage standing and the four acts staff have over it\n // (AGL-3011): the ceiling override, lifting a pause — the only way a\n // dispute pause comes off — and resetting the step. Every act audited.\n registerPluginApiRoute('ai/admin/overage', { web: aiAdminOverage })\n // Credits given back to a workspace's band or a Free owner's allowance\n // (AGL-3595): compensation for a fault of ours, bounded by what the month\n // used, keyed against a double-click, and audited.\n registerPluginApiRoute('ai/admin/credits', { web: aiAdminCredits })\n}\n\n// Type-only (AGL-3080): the plugin's entitlement keys, declared by module\n// augmentation, for every program that loads this entry point.\nexport type { aiPlanEntitlements } from './plan-entitlements'\n"],"names":["registerPluginApiRoute","registerAiDeclarations","AI_JOBS_BEAT_PATH","registerAiJobPlan","registerAiJobsPause","registerAiJobsNotify","registerAiComponentJob","registerAiCrmJob","registerAiLayoutJob","registerAiTemplateJob","registerAiExperimentJob","registerAiFormJob","registerAiPageJob","registerAiProductsJob","registerAiEmailJob","registerAiCampaignJob","registerAiSiteJob","registerAiBuildJob","registerAiBuildCapabilities","registerAiWorkflowJob","registerAiLogicJob","registerAiEditJob","registerAiNavigationJob","registerAiDuplicateJob","registerAiSettingsJob","registerAiInsightJob","registerAiFigureReaders","registerAiTextGenerator","firebaseAdmin","ensureFirstPartyAiProviders","aiAssistHandler","GET","aiCrmAnswer","POST","runAiJobsBeat","cancelAiJob","aiJobEvents","createAiSiteBatch","listAiJobs","createAiJob","resumeAiJob","aiGenerateComponent","createAiMediaImages","applyAiSeoAudit","createAiExperimentVersions","assistChat","assistEditApplied","assistFeedback","PATCH","aiHostPermissions","aiAdminOrg","aiAdminConversations","aiAdminOrgsSpend","aiAdminSignals","aiAdminUser","aiAdminOverage","aiAdminCredits","aiUsage","aiAllotments","aiInsightAnswer","runAiInsightsDigest","AI_INSIGHTS_DIGEST_PATH","aiModels","billingCredits","billingOverage","registerFirstPartyProviders","registerAiJobKinds","app","firestore","registerAiConsoleApi","web","request","method","context","params","Promise","resolve","jobId","String","machine"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../libs/plugins/ai/src/lib/server.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 { registerPluginApiRoute } from '@aglyn/aglyn/server'\nimport { registerAiDeclarations } from './declarations'\nimport { AI_JOBS_BEAT_PATH } from './jobs/ai-jobs-beat'\nimport { registerAiJobPlan } from './jobs/ai-job-plan-step'\nimport { registerAiJobsPause } from './jobs/ai-jobs-pause'\nimport { registerAiJobsNotify } from './jobs/ai-jobs-notify'\nimport { registerAiComponentJob } from './jobs/ai-job-component-step'\nimport { registerAiCrmJob } from './jobs/ai-job-crm-step'\nimport { registerAiLayoutJob } from './jobs/ai-job-layout-step'\nimport { registerAiTemplateJob } from './jobs/ai-job-template-step'\nimport { registerAiExperimentJob } from './jobs/ai-job-experiment-step'\nimport { registerAiFormJob } from './jobs/ai-job-form-step'\nimport { registerAiPageJob } from './jobs/ai-job-page-step'\nimport { registerAiProductsJob } from './jobs/ai-job-products-step'\nimport { registerAiEmailJob } from './jobs/ai-job-email-step'\nimport { registerAiCampaignJob } from './jobs/ai-job-campaign-step'\nimport { registerAiSiteJob } from './jobs/ai-job-site-step'\nimport { registerAiBuildJob } from './jobs/ai-job-build-step'\nimport { registerAiBuildCapabilities } from './jobs/ai-build-capabilities'\nimport { registerAiWorkflowJob } from './jobs/ai-job-workflow-step'\nimport { registerAiLogicJob } from './jobs/ai-job-logic-step'\nimport { registerAiEditJob } from './jobs/ai-job-edit-step'\nimport { registerAiNavigationJob } from './jobs/ai-job-navigation-step'\nimport { registerAiDuplicateJob } from './jobs/ai-job-duplicate-step'\nimport { registerAiSettingsJob } from './jobs/ai-job-settings-step'\nimport { registerAiMediaJob } from './jobs/ai-job-media-step'\nimport { registerAiTrackingJob } from './jobs/ai-job-tracking-step'\nimport { registerAiInsightJob } from './jobs/ai-job-insight-step'\nimport { registerAiFigureReaders } from './insights/ai-figure-readers'\nimport { registerAiTextGenerator } from './server/plugin-text-generation'\nimport { firebaseAdmin } from '@aglyn/tenant-data-admin/server/firebase-admin'\nimport { ensureFirstPartyAiProviders } from './providers/registry'\nimport { aiAssistHandler } from './server/ai-assist'\nimport { GET as aiCrmAnswer } from './server/ai-crm-answer'\nimport { POST as runAiJobsBeat } from './server/ai-jobs-beat-route'\nimport { POST as cancelAiJob } from './server/ai-jobs-cancel'\nimport { GET as aiJobEvents } from './server/ai-jobs-events-route'\nimport { POST as createAiSiteBatch } from './server/ai-jobs-batch'\nimport { GET as listAiJobs, POST as createAiJob } from './server/ai-jobs-route'\nimport { POST as resumeAiJob } from './server/ai-jobs-resume'\nimport { POST as aiGenerateComponent } from './server/ai-generate-component'\nimport { POST as createAiMediaImages } from './server/ai-media-image'\nimport { POST as applyAiSeoAudit } from './server/ai-seo-apply'\nimport { POST as createAiExperimentVersions } from './server/ai-experiment-versions'\nimport { POST as assistChat } from './server/assist-chat'\nimport { POST as assistEditApplied } from './server/assist-edit-applied'\nimport { POST as assistFeedback } from './server/assist-feedback'\nimport { PATCH as aiHostPermissions } from './server/ai-host-permissions'\nimport { GET as aiAdminOrg } from './server/ai-admin-org'\nimport { GET as aiAdminConversations } from './server/ai-admin-conversations'\nimport { GET as aiAdminOrgsSpend } from './server/ai-admin-orgs-spend'\nimport { GET as aiAdminSignals } from './server/ai-admin-signals'\nimport { GET as aiAdminUser } from './server/ai-admin-user'\nimport { POST as aiAdminOverage } from './server/ai-admin-overage'\nimport { POST as aiAdminCredits } from './server/ai-admin-credits'\nimport { GET as aiUsage } from './server/ai-usage'\nimport { GET as aiAllotments } from './server/ai-allotments'\nimport { GET as aiInsightAnswer } from './server/ai-insight-answer'\nimport { POST as runAiInsightsDigest } from './server/ai-insight-digest-route'\nimport { AI_INSIGHTS_DIGEST_PATH } from './insights/ai-insight-digest'\nimport { GET as aiModels } from './server/ai-models'\nimport { GET as billingCredits } from './server/billing-credits'\nimport { POST as billingOverage } from './server/billing-overage'\n\nexport * from './providers/contract'\nexport * from './providers/catalog'\nexport * from './providers/registry'\nexport * from './providers/routing'\nexport * from './runtime/ai-runtime'\n\n/**\n * The two first-party adapters, registered once per process. A marketplace\n * plugin registers its own against `AI_PROVIDER_CONTRACT` from its own\n * entry and never touches this.\n */\nconst registerFirstPartyProviders = ensureFirstPartyAiProviders\n\n/**\n * Every job kind this plugin runs beyond the three the machine registers\n * itself (AGL-3025). Each is a CALL, never an import made for what the\n * module does as it loads: this package declares only this file\n * effect-ful, so a bundler honoring `sideEffects` deletes an import whose\n * exports go unused — and a step registered that way ran in every spec and\n * in neither app. `registrations-survive-a-bundler.spec.ts` holds this.\n *\n * The console surface is the only caller (AGL-3026): its doors run a job's\n * first step inline, and its beat runs every step after it. The tenant app\n * loads no server surface of this plugin at all, so nothing that serves a\n * published site can reach a provider.\n */\nfunction registerAiJobKinds(): void {\n // The workspace AI pause the machine asks before it claims any step, so a\n // paused workspace's queued jobs spend nothing (AGL-3037).\n registerAiJobsPause()\n // The person who started a job is told when it needs them, finishes or\n // stops (AGL-3593), from the write that changed it.\n registerAiJobsNotify()\n // The plan step every planned kind runs first (AGL-2935).\n registerAiJobPlan()\n // Components, layouts and templates (AGL-2908, AGL-2909).\n registerAiComponentJob()\n registerAiLayoutJob()\n registerAiTemplateJob()\n // Forms (AGL-2913).\n registerAiExperimentJob()\n registerAiFormJob()\n // Pages, with the least time one pass needs (AGL-2907).\n registerAiPageJob()\n // Email designs and campaigns, whose drafts the email and marketing\n // plugins write on the resource-draft seam (AGL-2912).\n registerAiEmailJob()\n registerAiCampaignJob()\n // Automations drafted from a description, and explained, whose drafts the\n // workflows plugin writes on the resource-draft seam (AGL-2919).\n registerAiWorkflowJob()\n // Site functions and variables proposed from a description, and functions\n // explained, as proposals the logic editor opens unsaved (AGL-3603).\n registerAiLogicJob()\n // A change to a page or a layout the site has, from a description, saved\n // where no visitor sees it — the Assist edit rung run as a job (AGL-3616).\n registerAiEditJob()\n // A change to a site's header or footer menu, made by code from a build's\n // item and saved beside the live layout (AGL-3728).\n registerAiNavigationJob()\n // A copy of something a site already has, through core's duplicate — the\n // console's own Duplicate… — as a build's item (AGL-3740).\n registerAiDuplicateJob()\n // A change to a site's settings — its name, search defaults, icons,\n // language, time zone and contact details — saved live (AGL-3748).\n registerAiSettingsJob()\n // Folders, tags, alt text and names of files already in a site's media\n // library, as its Details drawer saves them, live (AGL-3752).\n registerAiMediaJob()\n // The site's analytics and advertising tags, as Setup → Tracking sets\n // them, saved live (AGL-3751).\n registerAiTrackingJob()\n // Product copy, and a store's products, categories and discounts from a\n // brief, as proposals the commerce plugin's surfaces apply (AGL-2916).\n registerAiProductsJob()\n // The site scaffold, which builds a whole site through the steps above\n // (AGL-2911).\n registerAiSiteJob()\n // A build from one request (AGL-3616): pages, creations and other\n // plugins' drafts, through the steps above and the writers those plugins\n // register, settled item by item. What this plugin lets a build make is\n // registered on the core's capability contract like any plugin's.\n registerAiBuildCapabilities()\n registerAiBuildJob()\n // Insights (AGL-2915): a question about a site's figures, answered from\n // tables the figure readers return, and the weekly digest. The readers for\n // the platform's own records — page views, forms, datasets — are this\n // plugin's; every other plugin registers its own from its console surface.\n registerAiFigureReaders(() => firebaseAdmin.app().firestore())\n registerAiInsightJob()\n // CRM by AI (AGL-2917): a record's summary and next step, an email draft\n // and an import's column matches, read through the CRM's facts readers.\n registerAiCrmJob()\n}\n\n/**\n * The console-side API (AGL-2939): every AI door, under the `ai` and\n * `assist` prefixes the plugin owns. The URLs the panel and the besigner\n * call are unchanged — `/api/assist/chat`, `/api/ai/assist`,\n * `/api/ai/jobs` — because the dispatcher serves them from the registry\n * exactly where the named routes used to; the two billing doors and the\n * per-member usage read (`ai/usage`) live under the plugin's own prefix.\n * The jobs beat is the one path outside them, for the reason\n * `AI_JOBS_BEAT_PATH` gives.\n */\nexport function registerAiConsoleApi(): void {\n registerAiDeclarations()\n registerFirstPartyProviders()\n registerAiJobKinds()\n // This plugin as the workspace's text generator on the core's seam\n // (AGL-3324): another plugin drafts a paragraph through it under the\n // generation doors' own rules, metered as a text job is.\n registerAiTextGenerator()\n registerPluginApiRoute('assist/chat', { web: assistChat })\n registerPluginApiRoute('assist/feedback', { web: assistFeedback })\n // The applied-edit record (AGL-2906): counts of a proposal the author\n // applied in their editor, written to the site's activity log.\n registerPluginApiRoute('assist/edit-applied', { web: assistEditApplied })\n // A collaborator's AI toggles on one site (AGL-2927), set from the site's\n // collaborators card through this plugin's column there.\n registerPluginApiRoute('ai/host-permissions', { web: aiHostPermissions })\n registerPluginApiRoute('ai/assist', aiAssistHandler)\n registerPluginApiRoute('ai/jobs', {\n web: (request) => (request.method === 'GET' ? listAiJobs(request) : createAiJob(request)),\n })\n // The agency batch (AGL-2911): one brief, one `site` job per named site,\n // under one batch id. Before the `:jobId` routes, which it is not one of.\n registerPluginApiRoute('ai/jobs/batch', { web: createAiSiteBatch })\n registerPluginApiRoute('ai/jobs/:jobId/cancel', {\n web: (request, context) =>\n cancelAiJob(request, { params: Promise.resolve({ jobId: String(context.params['jobId']) }) }),\n })\n registerPluginApiRoute('ai/jobs/:jobId/resume', {\n web: (request, context) =>\n resumeAiJob(request, { params: Promise.resolve({ jobId: String(context.params['jobId']) }) }),\n })\n // An insight job's answer (AGL-2915), which is kept off the job document\n // and served only to the member who asked, or for a digest to the site's\n // members.\n registerPluginApiRoute('ai/insights/:jobId', {\n web: (request, context) =>\n aiInsightAnswer(request, { params: Promise.resolve({ jobId: String(context.params['jobId']) }) }),\n })\n // A CRM job's answer (AGL-2917), which is kept off the job document and\n // served only to a member the CRM still lets read the record.\n registerPluginApiRoute('ai/crm/:jobId', {\n web: (request, context) =>\n aiCrmAnswer(request, { params: Promise.resolve({ jobId: String(context.params['jobId']) }) }),\n })\n registerPluginApiRoute('ai/jobs/:jobId/events', {\n web: (request, context) =>\n aiJobEvents(request, { params: Promise.resolve({ jobId: String(context.params['jobId']) }) }),\n })\n // The jobs beat (AGL-3026): every step the doors leave queued, run every\n // minute by the console's scheduler on the cron secret, on the one surface\n // that holds the provider's key.\n registerPluginApiRoute(AI_JOBS_BEAT_PATH, { web: runAiJobsBeat })\n // The weekly insights (AGL-2915): Monday's digests made for the people who\n // asked for them, and delivered once the beat has written them. Beside the\n // beat under `/api/admin/`, where the console's scheduled sweeps live.\n // A machine's route: Cloud Scheduler calls it on the cron secret with no\n // site and no member, and the route checks that secret and the AI release\n // flag itself. Unmarked, it was counted in the dispatcher's per-address\n // write bucket and refused with a 429 at 14:00Z on 2026-10-09.\n registerPluginApiRoute(AI_INSIGHTS_DIGEST_PATH, { web: runAiInsightsDigest }, { machine: true })\n // A site SEO audit's \"Apply all\" (AGL-2910): content fixes as new\n // unpublished versions, listing values staged for their SEO cards.\n registerPluginApiRoute('ai/seo/apply', { web: applyAiSeoAudit })\n // A page's or a section's A/B variants as draft versions (AGL-3603): the\n // published version copied once per proposed variant, the variant's copy\n // put in, stored unpublished for the experiment editor to pin.\n registerPluginApiRoute('ai/experiments/versions', { web: createAiExperimentVersions })\n // Save the selection as a reusable component, with AI (AGL-2908): the\n // second entry point of the component job, from the besigner's Attributes\n // panel. It proposes, and the person's Apply writes.\n registerPluginApiRoute('ai/generate/component', { web: aiGenerateComponent })\n // Pictures from a description, in Media (AGL-3602): photos and SVG\n // illustrations, stored through the media upload route. POST only — the\n // button draws from the shell's gates and asks nothing until it is used.\n registerPluginApiRoute('ai/media/images', { web: createAiMediaImages })\n registerPluginApiRoute('ai/billing/credits', { web: billingCredits })\n registerPluginApiRoute('ai/billing/overage', { web: billingOverage })\n registerPluginApiRoute('ai/usage', { web: aiUsage })\n // The allotments a manager sets, and the options the model switch lists\n // (AGL-2942). One handler answers the allotments' GET and POST.\n registerPluginApiRoute('ai/allotments', { web: aiAllotments })\n registerPluginApiRoute('ai/models', { web: aiModels })\n // The staff doors (AGL-2928, AGL-2930, AGL-2252): one org's AI in full,\n // one account's usage across its workspaces, and the fleet's Assist\n // signals behind the Assist signal staff page.\n registerPluginApiRoute('ai/admin/org', { web: aiAdminOrg })\n registerPluginApiRoute('ai/admin/orgs-spend', { web: aiAdminOrgsSpend })\n registerPluginApiRoute('ai/admin/user', { web: aiAdminUser })\n registerPluginApiRoute('ai/admin/signals', { web: aiAdminSignals })\n // What an org asked Aglyn AI and what it answered (AGL-3675): one page at a\n // time, read on request, each page an audited access.\n registerPluginApiRoute('ai/admin/conversations', { web: aiAdminConversations })\n // One workspace's overage standing and the four acts staff have over it\n // (AGL-3011): the ceiling override, lifting a pause — the only way a\n // dispute pause comes off — and resetting the step. Every act audited.\n registerPluginApiRoute('ai/admin/overage', { web: aiAdminOverage })\n // Credits given back to a workspace's band or a Free owner's allowance\n // (AGL-3595): compensation for a fault of ours, bounded by what the month\n // used, keyed against a double-click, and audited.\n registerPluginApiRoute('ai/admin/credits', { web: aiAdminCredits })\n}\n\n// Type-only (AGL-3080): the plugin's entitlement keys, declared by module\n// augmentation, for every program that loads this entry point.\nexport type { aiPlanEntitlements } from './plan-entitlements'\n"],"names":["registerPluginApiRoute","registerAiDeclarations","AI_JOBS_BEAT_PATH","registerAiJobPlan","registerAiJobsPause","registerAiJobsNotify","registerAiComponentJob","registerAiCrmJob","registerAiLayoutJob","registerAiTemplateJob","registerAiExperimentJob","registerAiFormJob","registerAiPageJob","registerAiProductsJob","registerAiEmailJob","registerAiCampaignJob","registerAiSiteJob","registerAiBuildJob","registerAiBuildCapabilities","registerAiWorkflowJob","registerAiLogicJob","registerAiEditJob","registerAiNavigationJob","registerAiDuplicateJob","registerAiSettingsJob","registerAiMediaJob","registerAiTrackingJob","registerAiInsightJob","registerAiFigureReaders","registerAiTextGenerator","firebaseAdmin","ensureFirstPartyAiProviders","aiAssistHandler","GET","aiCrmAnswer","POST","runAiJobsBeat","cancelAiJob","aiJobEvents","createAiSiteBatch","listAiJobs","createAiJob","resumeAiJob","aiGenerateComponent","createAiMediaImages","applyAiSeoAudit","createAiExperimentVersions","assistChat","assistEditApplied","assistFeedback","PATCH","aiHostPermissions","aiAdminOrg","aiAdminConversations","aiAdminOrgsSpend","aiAdminSignals","aiAdminUser","aiAdminOverage","aiAdminCredits","aiUsage","aiAllotments","aiInsightAnswer","runAiInsightsDigest","AI_INSIGHTS_DIGEST_PATH","aiModels","billingCredits","billingOverage","registerFirstPartyProviders","registerAiJobKinds","app","firestore","registerAiConsoleApi","web","request","method","context","params","Promise","resolve","jobId","String","machine"],"mappings":"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"}