@aglyn/plugins-ai 1.0.0-beta.238 → 1.0.0-beta.240
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +17 -17
- package/src/lib/components/ai-job-links.js +8 -0
- package/src/lib/components/ai-job-links.js.map +1 -1
- package/src/lib/components/ai-job-requests.d.ts +5 -0
- package/src/lib/components/ai-job-requests.js +4 -2
- package/src/lib/components/ai-job-requests.js.map +1 -1
- package/src/lib/components/ai-jobs-store.d.ts +12 -0
- package/src/lib/components/ai-jobs-store.js +15 -0
- package/src/lib/components/ai-jobs-store.js.map +1 -1
- package/src/lib/components/ai-usage-meter-refresh.d.ts +35 -0
- package/src/lib/components/ai-usage-meter-refresh.js +63 -0
- package/src/lib/components/ai-usage-meter-refresh.js.map +1 -0
- package/src/lib/components/assist-panel.component.js +17 -1
- package/src/lib/components/assist-panel.component.js.map +1 -1
- package/src/lib/components/use-ai-usage-meter.d.ts +2 -0
- package/src/lib/components/use-ai-usage-meter.js +8 -3
- package/src/lib/components/use-ai-usage-meter.js.map +1 -1
- package/src/lib/jobs/ai-build-unit-outcome.d.ts +3 -1
- package/src/lib/jobs/ai-build-unit-outcome.js +19 -3
- package/src/lib/jobs/ai-build-unit-outcome.js.map +1 -1
- package/src/lib/jobs/ai-job-build-step.js +29 -2
- package/src/lib/jobs/ai-job-build-step.js.map +1 -1
- package/src/lib/jobs/ai-job-draft-ids.js +7 -2
- package/src/lib/jobs/ai-job-draft-ids.js.map +1 -1
- package/src/lib/jobs/ai-job-form-dataset.d.ts +100 -0
- package/src/lib/jobs/ai-job-form-dataset.js +161 -0
- package/src/lib/jobs/ai-job-form-dataset.js.map +1 -0
- package/src/lib/jobs/ai-job-form-step.d.ts +15 -3
- package/src/lib/jobs/ai-job-form-step.js +37 -9
- package/src/lib/jobs/ai-job-form-step.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-language.js +7 -2
- package/src/lib/jobs/ai-job-layout-language.js.map +1 -1
- package/src/lib/jobs/ai-job-page-language.d.ts +9 -0
- package/src/lib/jobs/ai-job-page-language.js +44 -4
- package/src/lib/jobs/ai-job-page-language.js.map +1 -1
- package/src/lib/jobs/ai-job-page-sections.js +13 -2
- package/src/lib/jobs/ai-job-page-sections.js.map +1 -1
- package/src/lib/jobs/ai-job-page-step.js +32 -2
- package/src/lib/jobs/ai-job-page-step.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-step.d.ts +18 -0
- package/src/lib/jobs/ai-job-plan-step.js +41 -4
- package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-datasets.d.ts +183 -0
- package/src/lib/jobs/ai-job-site-datasets.js +530 -0
- package/src/lib/jobs/ai-job-site-datasets.js.map +1 -0
- package/src/lib/jobs/ai-job-site-step.d.ts +15 -8
- package/src/lib/jobs/ai-job-site-step.js +148 -29
- package/src/lib/jobs/ai-job-site-step.js.map +1 -1
- package/src/lib/jobs/fixtures/ai-dataset-site-plan-recordings.d.ts +30 -0
- package/src/lib/jobs/fixtures/ai-dataset-site-plan-recordings.js +637 -0
- package/src/lib/jobs/fixtures/ai-dataset-site-plan-recordings.js.map +1 -0
- package/src/lib/layout-language/ai-layout-compiler.d.ts +10 -0
- package/src/lib/layout-language/ai-layout-compiler.js +93 -1
- package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
- package/src/lib/layout-language/ai-layout-gaps.js +17 -1
- package/src/lib/layout-language/ai-layout-gaps.js.map +1 -1
- package/src/lib/layout-language/ai-layout-listings.d.ts +32 -3
- package/src/lib/layout-language/ai-layout-listings.js +62 -9
- package/src/lib/layout-language/ai-layout-listings.js.map +1 -1
- package/src/lib/layout-language/ai-layout-pictures.js +12 -4
- package/src/lib/layout-language/ai-layout-pictures.js.map +1 -1
- package/src/lib/model/ai-build-job.d.ts +2 -0
- package/src/lib/model/ai-build-job.js +16 -1
- package/src/lib/model/ai-build-job.js.map +1 -1
- package/src/lib/model/ai-build-plan.d.ts +23 -3
- package/src/lib/model/ai-build-plan.js +47 -6
- package/src/lib/model/ai-build-plan.js.map +1 -1
- package/src/lib/model/ai-plan-capabilities.d.ts +7 -0
- package/src/lib/model/ai-plan-capabilities.js.map +1 -1
- package/src/lib/model/ai-site-job.d.ts +19 -2
- package/src/lib/model/ai-site-job.js +21 -6
- package/src/lib/model/ai-site-job.js.map +1 -1
- package/src/lib/runtime/ai-dataset-generation.d.ts +77 -0
- package/src/lib/runtime/ai-dataset-generation.js +127 -0
- package/src/lib/runtime/ai-dataset-generation.js.map +1 -0
- package/src/lib/runtime/ai-doctrine-validators.d.ts +84 -0
- package/src/lib/runtime/ai-doctrine-validators.js +307 -1
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-doctrine.js +10 -4
- package/src/lib/runtime/ai-doctrine.js.map +1 -1
- package/src/lib/runtime/ai-node-tree.d.ts +2 -0
- package/src/lib/runtime/ai-node-tree.js +16 -0
- package/src/lib/runtime/ai-node-tree.js.map +1 -1
- package/src/lib/tools/ai-dataset-tool.d.ts +68 -0
- package/src/lib/tools/ai-dataset-tool.js +231 -0
- package/src/lib/tools/ai-dataset-tool.js.map +1 -0
- package/src/lib/usage/free-ai-credits-left.js +26 -2
- package/src/lib/usage/free-ai-credits-left.js.map +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-build-plan.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 { AiTool } from '../providers/contract'\n\n/**\n * The plan a generation job builds from (AGL-2935): what the job's plan step\n * (`jobs/ai-job-plan-step.ts`) returns, before a single node is generated.\n *\n * This module is the plan's MODEL — its shape, its tool schema and its\n * reader — and imports nothing at runtime, so the doctrine validators, the\n * plan step, the job document and the proposal can all read it without\n * importing each other.\n *\n * A plan is REFERENCES, never documents (AGL-2937). It names what the site\n * already has by inventory id, what the job will create by a short name, and\n * which screens it builds from which of those — so the doctrine can be held\n * against the plan while fixing it costs a sentence, not a regenerated page,\n * and the customer reads what the job will do before it spends a credit on\n * doing it.\n *\n * `new:<name>` is how an entry refers to something the plan itself creates;\n * any other reference is an id from the site inventory.\n */\n\nexport const AI_PLAN_NEW_REF_PREFIX = 'new:'\n\n/** Records a plan may reuse, by the inventory list they come from. */\nexport type AiBuildPlanReuseKind =\n | 'component'\n | 'layout'\n | 'template'\n | 'form'\n | 'dataset'\n | 'collection'\n | 'screen'\n | 'theme'\n\nexport const AI_BUILD_PLAN_REUSE_KINDS: readonly AiBuildPlanReuseKind[] = [\n 'component',\n 'layout',\n 'template',\n 'form',\n 'dataset',\n 'collection',\n 'screen',\n 'theme',\n]\n\n/** What a plan may create. A theme change is a draft through the theme door, never a literal. */\nexport type AiBuildPlanCreateKind =\n | 'component'\n | 'layout'\n | 'template'\n | 'form'\n | 'theme-change'\n | 'dataset'\n | 'email'\n\n/**\n * What each creation is called in a sentence, and where a member makes one.\n *\n * Total over the union deliberately: a refusal that cannot name a creation is\n * a refusal a member cannot act on, so widening `AiBuildPlanCreateKind`\n * without saying where the new thing is made does not compile.\n */\nexport const AI_BUILD_PLAN_CREATION_NOUNS: Record<\n AiBuildPlanCreateKind,\n { noun: string; where: string }\n> = {\n component: { noun: 'component', where: 'on the Components page' },\n form: { noun: 'form', where: 'on the Forms page' },\n layout: { noun: 'layout', where: 'on the Layouts page' },\n template: { noun: 'template', where: 'in the Templates library' },\n 'theme-change': { noun: 'theme change', where: 'in the Theme section' },\n dataset: { noun: 'dataset', where: 'on the Datasets page' },\n email: { noun: 'email design', where: 'in Emails → Templates' },\n}\n\nexport const AI_BUILD_PLAN_CREATE_KINDS: readonly AiBuildPlanCreateKind[] = [\n 'component',\n 'layout',\n 'template',\n 'form',\n 'theme-change',\n 'dataset',\n 'email',\n]\n\nexport interface AiBuildPlanReuse {\n kind: AiBuildPlanReuseKind\n /** The inventory id. */\n id: string\n /** What the plan uses it for, in a few words. */\n purpose: string\n}\n\nexport interface AiBuildPlanCreate {\n kind: AiBuildPlanCreateKind\n /** Unique within the plan; referenced as `new:<name>`. */\n name: string\n /** Why nothing the site has will do — shown in the proposal. */\n why: string\n /** The inventory id this starts from as a duplicate, or null. */\n duplicateOf: string | null\n /**\n * A component: its props as `name:type`, an icon as `name:icon`\n * (AGL-3054). A layout: the regions it has, from the closed vocabulary\n * `AI_LAYOUT_REGIONS` holds (AGL-3024) — a layout has no props, and a\n * region named here is what the built layout is held to. A template: the\n * binding tokens its page shows, from the subject's own closed catalog\n * (`AI_TEMPLATE_SUBJECT_DEFINITIONS[subject].tokens`, AGL-3143) — the\n * template's analogue of a layout's regions, and what the built template is\n * held to. A form or a dataset: its field names. A theme change: the\n * palette paths it adds or changes.\n */\n fields: string[]\n /**\n * The id the draft this creation becomes is written under, where a unit of\n * the job builds it (AGL-3079). Minted when the job keeps the plan, never\n * part of the model's answer.\n */\n id?: string\n}\n\nexport interface AiBuildPlanSection {\n /** A few words: \"hero\", \"services grid\", \"contact form\". */\n name: string\n /** What the section places: inventory ids or `new:<name>` references. */\n uses: string[]\n /** How many repeated items it shows (cards, rows, tiles); 0 when none. */\n items: number\n}\n\n/**\n * A screen that is one dataset's record template (AGL-3475): the data plugin\n * serves it once per record at `/{base}/{slug}`, its copy bound with\n * `{{item.<field>}}`. The plan names the binding and the build drafts the\n * page; the member saves the binding in Page Properties → Record pages, since\n * a binding is a route and a job writes drafts only (rule 13).\n */\nexport interface AiBuildPlanRecord {\n /** The dataset: an inventory id or `new:<name>`. */\n dataset: string\n /** The path its record pages live under, one or more segments: `services`, `services/residential`. */\n base: string\n}\n\nexport interface AiBuildPlanScreen {\n title: string\n slug: string\n /** The layout it renders inside. */\n layout: string | null\n /** The template it applies, or null for a one-off page. */\n template: string | null\n /** The inventory screen it starts from as a duplicate, or null. */\n duplicateOf: string | null\n /** Whether the site navigation gains an entry for it. */\n nav: boolean\n seoTitle: string\n seoDescription: string\n sections: AiBuildPlanSection[]\n /**\n * The dataset this page is the record template of, or null for a page that\n * is one page. A plan kept before record templates carries none, which\n * reads as null.\n */\n record: AiBuildPlanRecord | null\n /**\n * The id the draft screen is written under, where a unit of the job builds\n * it (AGL-3079). Minted when the job keeps the plan, never part of the\n * model's answer.\n */\n id?: string\n}\n\n/** The third-party players a plan may embed: the hosts the Video embed element plays. */\nexport type AiBuildPlanEmbedHost = 'youtube' | 'vimeo'\n\nexport const AI_BUILD_PLAN_EMBED_HOSTS: readonly AiBuildPlanEmbedHost[] = ['youtube', 'vimeo']\n\n/** What each host is called in a sentence. */\nexport const AI_BUILD_PLAN_EMBED_HOST_NAMES: Record<AiBuildPlanEmbedHost, string> = {\n youtube: 'YouTube',\n vimeo: 'Vimeo',\n}\n\n/**\n * A third-party player the brief asks for (rule 16, AGL-3433). It loads its\n * host's code on the page that carries it, so it is planned, where the member\n * reads that cost before confirming, and a build admits only what the\n * confirmed plan lists.\n */\nexport interface AiBuildPlanEmbed {\n host: AiBuildPlanEmbedHost\n /** Where it plays: a screen of the plan by its slug, or `new:<name>` for a component the plan creates. */\n where: string\n /** The brief's own words asking for it, which the plan step finds in the brief. */\n asked: string\n /** The video's link as the brief gives it, or null for the site owner to paste. */\n url: string | null\n}\n\n/** How the items that depend on a failed item behave (AGL-3616). */\nexport type AiBuildItemDegrade = 'omit' | 'fallback'\n\nexport const AI_BUILD_ITEM_DEGRADES: readonly AiBuildItemDegrade[] = ['omit', 'fallback']\n\n/** One argument value of an item: scalars and lists of text only. */\nexport type AiBuildItemArg = string | number | boolean | string[]\n\n/**\n * One thing a `build` plan makes that its creations and screens do not\n * (AGL-3616): an operation another plugin — or this one — registered as an\n * AI capability, with the arguments its schema declares. `reuse`, `create`\n * and `screens` keep their meaning; an item is everything else.\n */\nexport interface AiBuildItem {\n /** Stable within the plan: `i0`, `i1`… in plan order. */\n slot: string\n /** The capability's operation. */\n op: string\n /** Unique within the plan, creations included; referenced as `new:<name>`. */\n name: string\n /** Why the brief needs it, in one sentence. */\n why: string\n /** What it is built after: `new:<name>` of a creation or another item. */\n dependsOn: string[]\n /** How the items that depend on it behave when it fails. */\n degrade: AiBuildItemDegrade\n /** The operation's arguments, in the shape its capability declares. */\n args: Record<string, AiBuildItemArg>\n /** The id its draft is written under, minted when the job keeps the plan; never the model's. */\n id?: string\n /** The most it can cost in credits, from its capability, recorded when the plan is kept. */\n credits?: number\n}\n\nexport interface AiBuildPlan {\n reuse: AiBuildPlanReuse[]\n create: AiBuildPlanCreate[]\n screens: AiBuildPlanScreen[]\n /**\n * The third-party players the brief asks for. Absent is none: a plan kept\n * before the list existed, and every plan whose brief asks for no player.\n */\n embeds?: AiBuildPlanEmbed[]\n /**\n * What a `build` plan makes beyond its creations and pages (AGL-3616).\n * Absent is none: every other kind's plan.\n */\n items?: AiBuildItem[]\n}\n\n/**\n * How much one plan may hold. A job builds what one proposal can describe\n * and one person can read before confirming; a brief asking for more is\n * several jobs.\n */\nexport const AI_BUILD_PLAN_LIMITS = {\n reuse: 40,\n create: 20,\n screens: 12,\n sections: 16,\n uses: 8,\n fields: 30,\n embeds: 4,\n text: 200,\n seoDescription: 320,\n items: 500,\n /** A build plan's items (AGL-3616). */\n buildItems: 16,\n /** What one item depends on. */\n dependsOn: 8,\n /** The arguments one item carries, as JSON. */\n argsChars: 2_000,\n} as const\n\nconst string = (description: string) => ({ type: 'string', description })\nconst nullableString = (description: string) => ({\n anyOf: [{ type: 'string' }, { type: 'null' }],\n description,\n})\nconst strings = (description: string) => ({\n type: 'array',\n items: { type: 'string' },\n description,\n})\n\n/**\n * The ceiling `parseAiBuildPlan` cuts a field's text at, in words (AGL-3022).\n * The reader repairs an overlong value by cutting it, and a model never told\n * where the cut falls writes a rationale that stops mid-sentence there.\n */\nconst atMost = (limit: number = AI_BUILD_PLAN_LIMITS.text) => `at most ${limit} characters`\n\n/**\n * The plan as a strict tool's input schema. Only the keywords strict\n * structured output accepts: every object closes its properties and lists\n * every one as required, an optional value is a `null` branch, and no\n * length or count bound appears as a keyword — those are enforced by\n * `parseAiBuildPlan`, which every answer passes through anyway.\n *\n * A text ceiling is stated in words instead, on each field the model writes.\n * A reference is copied from the inventory or from a creation's name, so its\n * length is not the model's to choose. A search title and description are\n * held by rule 10 to ceilings far inside the reader's, which the doctrine\n * states, so naming the reader's here would contradict the rule.\n */\nexport const AI_BUILD_PLAN_TOOL: AiTool = {\n name: 'submit_build_plan',\n description:\n 'Submit the build plan: what the site already has that this job reuses, what it creates and why, and the pages it builds from them.',\n strict: true,\n inputSchema: {\n type: 'object',\n additionalProperties: false,\n required: ['reuse', 'create', 'screens'],\n properties: {\n reuse: {\n type: 'array',\n description: 'Records the site already has that this plan uses, by inventory id.',\n items: {\n type: 'object',\n additionalProperties: false,\n required: ['kind', 'id', 'purpose'],\n properties: {\n kind: { type: 'string', enum: [...AI_BUILD_PLAN_REUSE_KINDS] },\n id: string('The id from the site inventory.'),\n purpose: string(`What the plan uses it for, in a few words, ${atMost()}.`),\n },\n },\n },\n create: {\n type: 'array',\n description:\n 'What the plan creates. Creating is the exception: each entry says why nothing listed will do.',\n items: {\n type: 'object',\n additionalProperties: false,\n required: ['kind', 'name', 'why', 'duplicateOf', 'fields'],\n properties: {\n kind: { type: 'string', enum: [...AI_BUILD_PLAN_CREATE_KINDS] },\n name: string(\n `Unique within the plan, ${atMost()}; other entries refer to it as new:<name>.`,\n ),\n why: string(`Why nothing the site already has will do, in one sentence of ${atMost()}.`),\n duplicateOf: nullableString(\n 'The inventory id this starts from as a duplicate, or null.',\n ),\n fields: strings(\n `A component: its props as name:type, an icon as name:icon. A layout: the regions it has, each one of header, nav, sidebar, main, footer — list every region the finished layout has, not only the ones it gains. A form or dataset: its field names. A theme change: the palette paths it adds or changes. Each ${atMost()}.`,\n ),\n },\n },\n },\n screens: {\n type: 'array',\n description: 'The pages the job builds, each a new draft.',\n items: {\n type: 'object',\n additionalProperties: false,\n required: [\n 'title',\n 'slug',\n 'layout',\n 'template',\n 'duplicateOf',\n 'nav',\n 'seoTitle',\n 'seoDescription',\n 'sections',\n ],\n properties: {\n title: string(`The page name, ${atMost()}.`),\n slug: string(\n `The path, ${atMost()}: lowercase words joined by hyphens, e.g. /services/roof-repair.`,\n ),\n layout: nullableString(\n 'The layout it renders inside: an inventory id or new:<name>.',\n ),\n template: nullableString(\n 'The template it applies (inventory id or new:<name>), or null for a one-off page.',\n ),\n duplicateOf: nullableString(\n 'The inventory page it starts from as a duplicate, or null.',\n ),\n nav: {\n type: 'boolean',\n description: 'Whether the site navigation gains an entry for it.',\n },\n seoTitle: string('The search-result title.'),\n seoDescription: string('The search-result description.'),\n sections: {\n type: 'array',\n description: 'The sections, top to bottom.',\n items: {\n type: 'object',\n additionalProperties: false,\n required: ['name', 'uses', 'items'],\n properties: {\n name: string(`A few words, ${atMost()}: hero, services grid, contact form.`),\n uses: strings('What the section places: inventory ids or new:<name>.'),\n items: {\n type: 'integer',\n description: 'How many repeated items it shows; 0 when none.',\n },\n },\n },\n },\n },\n },\n },\n },\n },\n}\n\n/** Words that ask for a video, or a link to one. */\nexport const AI_PLAN_ASKS_FOR_VIDEO = /\\b(?:videos?|films?|clips?|youtube|vimeo|player)\\b|youtu\\.be/i\n\nconst EMBEDS_SCHEMA = {\n type: 'array',\n description:\n 'A YouTube or Vimeo player the brief asks for, on a screen or a created component. Empty unless the brief asks; a library film is a Video.',\n items: {\n type: 'object',\n additionalProperties: false,\n required: ['host', 'where', 'asked', 'url'],\n properties: {\n host: { type: 'string', enum: [...AI_BUILD_PLAN_EMBED_HOSTS] },\n where: string('A screen slug, or new:<name> of a component.'),\n asked: string('The words of the brief that ask for it, copied exactly.'),\n url: nullableString('The video link the brief gives, or null.'),\n },\n },\n}\n\n/**\n * The plan tool with its list of third-party players, offered only to a brief\n * that asks for a video (AGL-3433). Every other plan's request is the plain\n * tool, byte for byte, so the list costs nothing on the plans that cannot use\n * it, and a model answering a brief that names no video has nowhere to plan a\n * player it was never asked for.\n */\nexport const AI_BUILD_PLAN_EMBEDS_TOOL: AiTool = {\n ...AI_BUILD_PLAN_TOOL,\n inputSchema: {\n ...AI_BUILD_PLAN_TOOL.inputSchema,\n required: [...(AI_BUILD_PLAN_TOOL.inputSchema['required'] as string[]), 'embeds'],\n properties: {\n ...(AI_BUILD_PLAN_TOOL.inputSchema['properties'] as Record<string, unknown>),\n embeds: EMBEDS_SCHEMA,\n },\n },\n}\n\nconst RECORD_SCHEMA = {\n anyOf: [\n {\n type: 'object',\n additionalProperties: false,\n required: ['dataset', 'base'],\n properties: {\n dataset: string('The dataset: an inventory id or new:<name>.'),\n base: string('The path its record pages live under, e.g. services for /services/roofing.'),\n },\n },\n { type: 'null' },\n ],\n description:\n \"When this page is one dataset's record template, serving a page per record at /<base>/<record address> with its copy bound as {{item.<field>}}: the dataset and the base. Otherwise null.\",\n}\n\n/** A plan tool whose screens each name the dataset they are the record template of, or null. */\nfunction withRecords(tool: AiTool): AiTool {\n const properties = tool.inputSchema['properties'] as Record<string, unknown>\n const screens = properties['screens'] as { items: Record<string, unknown> }\n return {\n ...tool,\n inputSchema: {\n ...tool.inputSchema,\n properties: {\n ...properties,\n screens: {\n ...screens,\n items: {\n ...screens.items,\n required: [...(screens.items['required'] as string[]), 'record'],\n properties: {\n ...(screens.items['properties'] as Record<string, unknown>),\n record: RECORD_SCHEMA,\n },\n },\n },\n },\n },\n }\n}\n\n/**\n * The plan tools with a record template on every screen (AGL-3475), offered\n * only where the job may bind a dataset: a site with a dataset, or a job that\n * may create one. Record pages are a Starter feature, and every other plan's\n * request is the plain tool, byte for byte, so the field costs a Free taste\n * nothing and a model planning a site with no dataset has none to bind.\n */\nexport const AI_BUILD_PLAN_RECORDS_TOOL: AiTool = withRecords(AI_BUILD_PLAN_TOOL)\nexport const AI_BUILD_PLAN_EMBEDS_RECORDS_TOOL: AiTool = withRecords(AI_BUILD_PLAN_EMBEDS_TOOL)\n\nconst ITEMS_SCHEMA = {\n type: 'array',\n description:\n 'What the plan makes beyond its creations and pages, each one operation from the list in the request. Empty when the brief asks for nothing else.',\n items: {\n type: 'object',\n additionalProperties: false,\n required: ['op', 'name', 'why', 'dependsOn', 'degrade', 'args'],\n properties: {\n op: string('The operation, exactly as the request lists it.'),\n name: string(`Unique within the plan, creations included, ${atMost()}; other entries refer to it as new:<name>.`),\n why: string(`Why the brief needs it, in one sentence of ${atMost()}.`),\n dependsOn: strings('What it is built after: new:<name> of a creation or another item. Empty when nothing.'),\n degrade: {\n type: 'string',\n enum: [...AI_BUILD_ITEM_DEGRADES],\n description:\n 'omit: what depends on it is built without it if it fails. fallback: what depends on it uses something the site already has instead.',\n },\n args: string('Its arguments as one JSON object, with exactly the fields the request lists for the operation.'),\n },\n },\n}\n\n/**\n * Any plan tool with a build's items (AGL-3616), offered only to a `build`\n * job. The items' arguments travel as one JSON string whose fields the\n * request lists per operation, so the tool stays byte for byte the same\n * whichever plugins registered operations, and the prompt cache keeps it.\n */\nexport function withBuildItems(tool: AiTool): AiTool {\n return {\n ...tool,\n inputSchema: {\n ...tool.inputSchema,\n required: [...(tool.inputSchema['required'] as string[]), 'items'],\n properties: {\n ...(tool.inputSchema['properties'] as Record<string, unknown>),\n items: ITEMS_SCHEMA,\n },\n },\n }\n}\n\n/** The plan tool a brief is answered with, on a job that may or may not bind a dataset. */\nexport function aiBuildPlanToolFor(\n brief: string,\n options: { records?: boolean; items?: boolean } = {},\n): AiTool {\n const video = AI_PLAN_ASKS_FOR_VIDEO.test(brief)\n const tool = options.records\n ? video\n ? AI_BUILD_PLAN_EMBEDS_RECORDS_TOOL\n : AI_BUILD_PLAN_RECORDS_TOOL\n : video\n ? AI_BUILD_PLAN_EMBEDS_TOOL\n : AI_BUILD_PLAN_TOOL\n return options.items ? withBuildItems(tool) : tool\n}\n\nexport type AiBuildPlanParse =\n | { ok: true; plan: AiBuildPlan; repairs: string[] }\n | { ok: false; error: string }\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nclass PlanShapeError extends Error {}\n\n/**\n * An item's arguments as the plan keeps them: the model writes one JSON\n * string, a kept plan stores the object. Values are scalars and lists of\n * text; anything else is a shape the item cannot be built from.\n */\nfunction buildItemArgs(value: unknown, path: string): Record<string, AiBuildItemArg> {\n let parsed: unknown = value\n if (value === undefined || value === null || value === '') return {}\n if (typeof value === 'string') {\n if (value.length > AI_BUILD_PLAN_LIMITS.argsChars) {\n throw new PlanShapeError(`${path} is over ${AI_BUILD_PLAN_LIMITS.argsChars} characters`)\n }\n try {\n parsed = JSON.parse(value)\n } catch {\n throw new PlanShapeError(`${path} is not a JSON object`)\n }\n }\n if (!isRecord(parsed)) throw new PlanShapeError(`${path} is not a JSON object`)\n const args: Record<string, AiBuildItemArg> = {}\n for (const [key, raw] of Object.entries(parsed)) {\n if (raw === null || raw === undefined) continue\n if (typeof raw === 'string' || typeof raw === 'boolean' || (typeof raw === 'number' && Number.isFinite(raw))) {\n args[key] = raw\n } else if (Array.isArray(raw) && raw.every((one) => typeof one === 'string')) {\n args[key] = raw as string[]\n } else {\n throw new PlanShapeError(`${path}.${key} is not text, a number, true or false, or a list of text`)\n }\n }\n return args\n}\n\n/**\n * Read a plan out of a tool call, or name what makes it unreadable.\n *\n * Structure is refused — a missing list, an unknown kind, more screens than\n * a job holds, two creations under one name — because a plan that reads\n * differently from what the model meant is worse than a re-ask. Length is\n * repaired: copy past a ceiling is cut and the cut is listed.\n */\n/**\n * A layout's regions, one word each. A plan sometimes writes the whole list as\n * one entry — \"regions: header, nav, main, footer\" — which names regions the\n * platform builds but reads as one unknown word, and the plan was refused for\n * it (job AvVxbFbzQx). A leading label is dropped and a list is split on its\n * commas, slashes, plus signs and \"and\", so each region is checked on its own;\n * a duplicate keeps its first place.\n */\nexport function aiPlanLayoutRegionWords(fields: readonly string[]): string[] {\n const words: string[] = []\n for (const field of fields) {\n const listed = /^\\s*(?:the\\s+)?(?:layout\\s+)?(?:regions?|areas?)\\s*[:=-]\\s*(.+)$/i.exec(field)?.[1] ?? field\n const parts = /[,/+]|\\band\\b/i.test(listed)\n ? listed.split(/\\s*(?:,|\\/|\\+|\\band\\b)\\s*/i)\n : [listed]\n for (const part of parts) {\n const word = part.trim()\n if (word && !words.some((seen) => seen.toLowerCase() === word.toLowerCase())) words.push(word)\n }\n }\n return words\n}\n\nexport function parseAiBuildPlan(input: unknown): AiBuildPlanParse {\n const repairs: string[] = []\n const text = (\n value: unknown,\n path: string,\n limit: number = AI_BUILD_PLAN_LIMITS.text,\n ): string => {\n if (typeof value !== 'string') throw new PlanShapeError(`${path} is not text`)\n const trimmed = value.trim()\n if (trimmed.length > limit) {\n repairs.push(`${path} was over ${limit} characters; truncated`)\n // On the last word break inside the ceiling, not through a word: a\n // rationale is read by the member confirming the plan, and half a word\n // reads as a broken answer rather than a long one (AGL-3022). A run of\n // `limit` characters with no break in it still has to be cut where the\n // ceiling falls.\n const cut = trimmed.slice(0, limit)\n const brk = cut.search(/\\s\\S*$/)\n return (brk > 0 ? cut.slice(0, brk) : cut).trimEnd()\n }\n return trimmed\n }\n const nullable = (value: unknown, path: string): string | null =>\n value === null || value === undefined || value === '' ? null : text(value, path) || null\n const list = (value: unknown, path: string, limit: number): unknown[] => {\n if (!Array.isArray(value)) throw new PlanShapeError(`${path} is not a list`)\n if (value.length > limit) {\n throw new PlanShapeError(`${path} lists ${value.length}; one plan holds at most ${limit}`)\n }\n return value\n }\n const record = (value: unknown, path: string): Record<string, unknown> => {\n if (!isRecord(value)) throw new PlanShapeError(`${path} is not an object`)\n return value\n }\n const oneOf = <T extends string>(value: unknown, allowed: readonly T[], path: string): T => {\n if (typeof value !== 'string' || !(allowed as readonly string[]).includes(value)) {\n throw new PlanShapeError(`${path} is not one of ${allowed.join(', ')}`)\n }\n return value as T\n }\n // Absent or null is a page that is one page. A base is held to the shape a\n // record page's address takes, so the binding the member saves from it is\n // one the save route admits.\n const planRecord = (value: unknown, path: string): AiBuildPlanRecord | null => {\n if (value === null || value === undefined) return null\n const entry = record(value, path)\n const dataset = text(entry['dataset'], `${path}.dataset`)\n if (!dataset) throw new PlanShapeError(`${path}.dataset is empty`)\n const base = aiPlanRecordBase(text(entry['base'], `${path}.base`))\n if (!base) throw new PlanShapeError(`${path}.base is not a path of lowercase words joined by hyphens`)\n return { dataset, base }\n }\n\n try {\n const root = record(input, 'plan')\n const reuse = list(root['reuse'], 'reuse', AI_BUILD_PLAN_LIMITS.reuse).map((raw, index) => {\n const entry = record(raw, `reuse[${index}]`)\n const id = text(entry['id'], `reuse[${index}].id`)\n if (!id) throw new PlanShapeError(`reuse[${index}].id is empty`)\n return {\n kind: oneOf(entry['kind'], AI_BUILD_PLAN_REUSE_KINDS, `reuse[${index}].kind`),\n id,\n purpose: text(entry['purpose'], `reuse[${index}].purpose`),\n }\n })\n const names = new Set<string>()\n const create = list(root['create'], 'create', AI_BUILD_PLAN_LIMITS.create).map(\n (raw, index) => {\n const entry = record(raw, `create[${index}]`)\n const name = text(entry['name'], `create[${index}].name`)\n if (!name) throw new PlanShapeError(`create[${index}].name is empty`)\n const key = name.toLowerCase()\n if (names.has(key)) {\n throw new PlanShapeError(`create[${index}].name \"${name}\" is used twice`)\n }\n names.add(key)\n const kind = oneOf(entry['kind'], AI_BUILD_PLAN_CREATE_KINDS, `create[${index}].kind`)\n const fields = list(entry['fields'] ?? [], `create[${index}].fields`, AI_BUILD_PLAN_LIMITS.fields)\n .map((field, fieldIndex) => text(field, `create[${index}].fields[${fieldIndex}]`))\n .filter(Boolean)\n return {\n kind,\n name,\n why: text(entry['why'], `create[${index}].why`),\n duplicateOf: nullable(entry['duplicateOf'], `create[${index}].duplicateOf`),\n fields: kind === 'layout' ? aiPlanLayoutRegionWords(fields) : fields,\n }\n },\n )\n const screens = list(root['screens'], 'screens', AI_BUILD_PLAN_LIMITS.screens).map(\n (raw, index) => {\n const entry = record(raw, `screens[${index}]`)\n if (typeof entry['nav'] !== 'boolean') {\n throw new PlanShapeError(`screens[${index}].nav is not true or false`)\n }\n return {\n title: text(entry['title'], `screens[${index}].title`),\n slug: text(entry['slug'], `screens[${index}].slug`),\n layout: nullable(entry['layout'], `screens[${index}].layout`),\n template: nullable(entry['template'], `screens[${index}].template`),\n duplicateOf: nullable(entry['duplicateOf'], `screens[${index}].duplicateOf`),\n nav: entry['nav'],\n seoTitle: text(entry['seoTitle'], `screens[${index}].seoTitle`),\n seoDescription: text(\n entry['seoDescription'],\n `screens[${index}].seoDescription`,\n AI_BUILD_PLAN_LIMITS.seoDescription,\n ),\n sections: list(\n entry['sections'],\n `screens[${index}].sections`,\n AI_BUILD_PLAN_LIMITS.sections,\n ).map((rawSection, sectionIndex) => {\n const path = `screens[${index}].sections[${sectionIndex}]`\n const section = record(rawSection, path)\n const items = Number(section['items'] ?? 0)\n if (!Number.isInteger(items) || items < 0 || items > AI_BUILD_PLAN_LIMITS.items) {\n throw new PlanShapeError(`${path}.items is not a count`)\n }\n return {\n name: text(section['name'], `${path}.name`),\n uses: list(section['uses'] ?? [], `${path}.uses`, AI_BUILD_PLAN_LIMITS.uses)\n .map((ref, refIndex) => text(ref, `${path}.uses[${refIndex}]`))\n .filter(Boolean),\n items,\n }\n }),\n record: planRecord(entry['record'], `screens[${index}].record`),\n }\n },\n )\n const embeds = list(root['embeds'] ?? [], 'embeds', AI_BUILD_PLAN_LIMITS.embeds).map(\n (raw, index) => {\n const entry = record(raw, `embeds[${index}]`)\n const where = text(entry['where'], `embeds[${index}].where`)\n if (!where) throw new PlanShapeError(`embeds[${index}].where is empty`)\n return {\n host: oneOf(entry['host'], AI_BUILD_PLAN_EMBED_HOSTS, `embeds[${index}].host`),\n where,\n asked: text(entry['asked'], `embeds[${index}].asked`),\n url: nullable(entry['url'], `embeds[${index}].url`),\n }\n },\n )\n const items = list(root['items'] ?? [], 'items', AI_BUILD_PLAN_LIMITS.buildItems).map(\n (raw, index): AiBuildItem => {\n const path = `items[${index}]`\n const entry = record(raw, path)\n const op = text(entry['op'], `${path}.op`)\n if (!op) throw new PlanShapeError(`${path}.op is empty`)\n const name = text(entry['name'], `${path}.name`)\n if (!name) throw new PlanShapeError(`${path}.name is empty`)\n const key = name.toLowerCase()\n if (names.has(key)) throw new PlanShapeError(`${path}.name \"${name}\" is used twice`)\n names.add(key)\n return {\n slot: `i${index}`,\n op,\n name,\n why: text(entry['why'] ?? '', `${path}.why`),\n dependsOn: list(entry['dependsOn'] ?? [], `${path}.dependsOn`, AI_BUILD_PLAN_LIMITS.dependsOn)\n .map((ref, refIndex) => text(ref, `${path}.dependsOn[${refIndex}]`))\n .filter(Boolean),\n degrade:\n entry['degrade'] === undefined || entry['degrade'] === null\n ? 'omit'\n : oneOf(entry['degrade'], AI_BUILD_ITEM_DEGRADES, `${path}.degrade`),\n args: buildItemArgs(entry['args'], `${path}.args`),\n ...(typeof entry['id'] === 'string' && entry['id'] ? { id: entry['id'] } : {}),\n ...(typeof entry['credits'] === 'number' && Number.isFinite(entry['credits'])\n ? { credits: Math.max(0, Math.floor(entry['credits'])) }\n : {}),\n }\n },\n )\n // A plan whose brief asks for no player carries no list, as it did before the list existed;\n // so does a plan with no items.\n return {\n ok: true,\n plan: {\n reuse,\n create,\n screens,\n ...(embeds.length ? { embeds } : {}),\n ...(items.length ? { items } : {}),\n },\n repairs,\n }\n } catch (error) {\n if (error instanceof PlanShapeError) return { ok: false, error: error.message }\n throw error\n }\n}\n\n/**\n * A record template's base as the data plugin's save route stores it\n * (`normalizeRecordPageBase`, which this plugin may not import): one to four\n * lowercase segments of letters, digits and hyphens, with no leading or\n * trailing slash; null for a base that is no such path.\n */\nexport function aiPlanRecordBase(base: string): string | null {\n const segments = base.trim().toLowerCase().split('/').filter(Boolean)\n if (!segments.length || segments.length > 4) return null\n return segments.every(\n (segment) => segment.length <= 60 && /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(segment),\n )\n ? segments.join('/')\n : null\n}\n\n/** Whether a reference names something the plan creates. */\nexport function isAiPlanNewRef(ref: string | null | undefined): ref is string {\n return typeof ref === 'string' && ref.startsWith(AI_PLAN_NEW_REF_PREFIX)\n}\n\n/** The creation a `new:<name>` reference names, case-insensitively. */\nexport function aiPlanCreateFor(\n plan: AiBuildPlan,\n ref: string,\n): AiBuildPlanCreate | undefined {\n if (!isAiPlanNewRef(ref)) return undefined\n const name = ref.slice(AI_PLAN_NEW_REF_PREFIX.length).trim().toLowerCase()\n return plan.create.find((entry) => entry.name.toLowerCase() === name)\n}\n\n/** The item a `new:<name>` reference names, case-insensitively (AGL-3616). */\nexport function aiPlanItemFor(\n plan: Pick<AiBuildPlan, 'items'>,\n ref: string,\n): AiBuildItem | undefined {\n if (!isAiPlanNewRef(ref)) return undefined\n const name = ref.slice(AI_PLAN_NEW_REF_PREFIX.length).trim().toLowerCase()\n return (plan.items ?? []).find((entry) => entry.name.toLowerCase() === name)\n}\n\n/** A slug as two spellings of one path compare: lowercase, one leading slash, no trailing one. */\nexport function aiPlanSlugKey(slug: string): string {\n const path = slug.trim().toLowerCase().replace(/^\\/*/, '/').replace(/\\/+$/, '')\n return path || '/'\n}\n\n/**\n * The players the plan lists for one output (AGL-3433): a screen by its slug,\n * or a creation by its name. A build admits these and no other.\n */\nexport function aiPlanEmbedsFor(\n plan: Pick<AiBuildPlan, 'embeds'> | null | undefined,\n target: { slug: string } | { create: string },\n): AiBuildPlanEmbed[] {\n const key =\n 'slug' in target\n ? aiPlanSlugKey(target.slug)\n : `${AI_PLAN_NEW_REF_PREFIX}${target.create.trim().toLowerCase()}`\n return (plan?.embeds ?? []).filter((embed) =>\n 'slug' in target\n ? !isAiPlanNewRef(embed.where) && aiPlanSlugKey(embed.where) === key\n : embed.where.trim().toLowerCase() === key,\n )\n}\n\n/** The host a video link plays from, or null for a link neither host serves. */\nexport function aiEmbedHostOf(url: string): AiBuildPlanEmbedHost | null {\n try {\n const parsed = new URL(url.trim())\n if (parsed.protocol !== 'https:') return null\n const host = parsed.hostname.replace(/^(?:www|m)\\./, '')\n if (host === 'youtu.be' || host === 'youtube.com' || host === 'youtube-nocookie.com') {\n return 'youtube'\n }\n if (host === 'vimeo.com' || host === 'player.vimeo.com') return 'vimeo'\n } catch {\n /* not a link */\n }\n return null\n}\n\n/**\n * One video however its link is spelled — `youtu.be/<id>`, a watch link, an\n * embed or a Shorts link — as `<host>:<id>`, the way the Video embed element\n * reads a link; null for a link it cannot play.\n */\nexport function aiEmbedVideoKey(url: string): string | null {\n const host = aiEmbedHostOf(url)\n if (!host) return null\n const parsed = new URL(url.trim())\n const parts = parsed.pathname.split('/').filter(Boolean)\n const id =\n host === 'vimeo'\n ? parts.find((part) => /^\\d{6,}$/.test(part))\n : parsed.hostname.endsWith('youtu.be')\n ? parts[0]\n : (parsed.searchParams.get('v') ??\n (parts[0] === 'embed' || parts[0] === 'shorts' ? parts[1] : undefined))\n return id && /^[\\w-]{6,20}$/.test(id) ? `${host}:${id}` : null\n}\n\n/**\n * A `new:<name>` reference that names no entry of the plan's create list: a\n * screen's own layout or template, or what one of its sections places, with\n * that section's index.\n */\nexport type AiPlanUndeclaredRef = {\n /** The name the reference gives the creation, as written after `new:`. */\n name: string\n /** Where the plan writes it: `screens[0].template`, `screens[0].sections[4].uses[0]`. */\n path: string\n /** The screen that writes it, by index. */\n screenIndex: number\n} & ({ field: 'layout' | 'template'; sectionIndex: null } | { field: 'uses'; sectionIndex: number })\n\n/**\n * Every `new:<name>` reference the plan's create list does not carry, in plan\n * order: each screen's layout, then its template, then what its sections\n * place. A plan that places a creation it never declares has nothing to build\n * it from, so the plan rules refuse each (AGL-3040), and the page and\n * scaffold doors still name one in a plan confirmed before they did.\n */\nexport function aiPlanUndeclaredRefs(plan: AiBuildPlan): AiPlanUndeclaredRef[] {\n const found: AiPlanUndeclaredRef[] = []\n const undeclared = (ref: string | null): ref is string =>\n isAiPlanNewRef(ref) && !aiPlanCreateFor(plan, ref) && !aiPlanItemFor(plan, ref)\n const nameOf = (ref: string) => ref.slice(AI_PLAN_NEW_REF_PREFIX.length).trim()\n plan.screens.forEach((screen, screenIndex) => {\n for (const field of ['layout', 'template'] as const) {\n const ref = screen[field]\n if (!undeclared(ref)) continue\n found.push({\n name: nameOf(ref),\n path: `screens[${screenIndex}].${field}`,\n screenIndex,\n field,\n sectionIndex: null,\n })\n }\n screen.sections.forEach((section, sectionIndex) => {\n section.uses.forEach((ref, useIndex) => {\n if (!undeclared(ref)) return\n found.push({\n name: nameOf(ref),\n path: `screens[${screenIndex}].sections[${sectionIndex}].uses[${useIndex}]`,\n screenIndex,\n field: 'uses',\n sectionIndex,\n })\n })\n })\n })\n return found\n}\n"],"names":["AI_PLAN_NEW_REF_PREFIX","AI_BUILD_PLAN_REUSE_KINDS","AI_BUILD_PLAN_CREATION_NOUNS","component","noun","where","form","layout","template","dataset","email","AI_BUILD_PLAN_CREATE_KINDS","AI_BUILD_PLAN_EMBED_HOSTS","AI_BUILD_PLAN_EMBED_HOST_NAMES","youtube","vimeo","AI_BUILD_ITEM_DEGRADES","AI_BUILD_PLAN_LIMITS","reuse","create","screens","sections","uses","fields","embeds","text","seoDescription","items","buildItems","dependsOn","argsChars","string","description","type","nullableString","anyOf","strings","atMost","limit","AI_BUILD_PLAN_TOOL","name","strict","inputSchema","additionalProperties","required","properties","kind","enum","id","purpose","why","duplicateOf","title","slug","nav","seoTitle","AI_PLAN_ASKS_FOR_VIDEO","EMBEDS_SCHEMA","host","asked","url","AI_BUILD_PLAN_EMBEDS_TOOL","RECORD_SCHEMA","base","withRecords","tool","record","AI_BUILD_PLAN_RECORDS_TOOL","AI_BUILD_PLAN_EMBEDS_RECORDS_TOOL","ITEMS_SCHEMA","op","degrade","args","withBuildItems","aiBuildPlanToolFor","brief","options","video","test","records","isRecord","value","Array","isArray","PlanShapeError","Error","buildItemArgs","path","parsed","undefined","length","JSON","parse","key","raw","Object","entries","Number","isFinite","every","one","aiPlanLayoutRegionWords","words","field","listed","exec","parts","split","part","word","trim","some","seen","toLowerCase","push","parseAiBuildPlan","input","repairs","trimmed","cut","slice","brk","search","trimEnd","nullable","list","oneOf","allowed","includes","join","planRecord","entry","aiPlanRecordBase","root","map","index","names","Set","has","add","fieldIndex","filter","Boolean","rawSection","sectionIndex","section","isInteger","ref","refIndex","slot","credits","Math","max","floor","ok","plan","error","message","segments","segment","isAiPlanNewRef","startsWith","aiPlanCreateFor","find","aiPlanItemFor","aiPlanSlugKey","replace","aiPlanEmbedsFor","target","embed","aiEmbedHostOf","URL","protocol","hostname","aiEmbedVideoKey","pathname","endsWith","searchParams","get","aiPlanUndeclaredRefs","found","undeclared","nameOf","forEach","screen","screenIndex","useIndex"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-build-plan.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 { AiTool } from '../providers/contract'\n\n/**\n * The plan a generation job builds from (AGL-2935): what the job's plan step\n * (`jobs/ai-job-plan-step.ts`) returns, before a single node is generated.\n *\n * This module is the plan's MODEL — its shape, its tool schema and its\n * reader — and imports nothing at runtime, so the doctrine validators, the\n * plan step, the job document and the proposal can all read it without\n * importing each other.\n *\n * A plan is REFERENCES, never documents (AGL-2937). It names what the site\n * already has by inventory id, what the job will create by a short name, and\n * which screens it builds from which of those — so the doctrine can be held\n * against the plan while fixing it costs a sentence, not a regenerated page,\n * and the customer reads what the job will do before it spends a credit on\n * doing it.\n *\n * `new:<name>` is how an entry refers to something the plan itself creates;\n * any other reference is an id from the site inventory.\n */\n\nexport const AI_PLAN_NEW_REF_PREFIX = 'new:'\n\n/** Records a plan may reuse, by the inventory list they come from. */\nexport type AiBuildPlanReuseKind =\n | 'component'\n | 'layout'\n | 'template'\n | 'form'\n | 'dataset'\n | 'collection'\n | 'screen'\n | 'theme'\n\nexport const AI_BUILD_PLAN_REUSE_KINDS: readonly AiBuildPlanReuseKind[] = [\n 'component',\n 'layout',\n 'template',\n 'form',\n 'dataset',\n 'collection',\n 'screen',\n 'theme',\n]\n\n/** What a plan may create. A theme change is a draft through the theme door, never a literal. */\nexport type AiBuildPlanCreateKind =\n | 'component'\n | 'layout'\n | 'template'\n | 'form'\n | 'theme-change'\n | 'dataset'\n | 'email'\n\n/**\n * What each creation is called in a sentence, and where a member makes one.\n *\n * Total over the union deliberately: a refusal that cannot name a creation is\n * a refusal a member cannot act on, so widening `AiBuildPlanCreateKind`\n * without saying where the new thing is made does not compile.\n */\nexport const AI_BUILD_PLAN_CREATION_NOUNS: Record<\n AiBuildPlanCreateKind,\n { noun: string; where: string }\n> = {\n component: { noun: 'component', where: 'on the Components page' },\n form: { noun: 'form', where: 'on the Forms page' },\n layout: { noun: 'layout', where: 'on the Layouts page' },\n template: { noun: 'template', where: 'in the Templates library' },\n 'theme-change': { noun: 'theme change', where: 'in the Theme section' },\n dataset: { noun: 'dataset', where: 'on the Datasets page' },\n email: { noun: 'email design', where: 'in Emails → Templates' },\n}\n\nexport const AI_BUILD_PLAN_CREATE_KINDS: readonly AiBuildPlanCreateKind[] = [\n 'component',\n 'layout',\n 'template',\n 'form',\n 'theme-change',\n 'dataset',\n 'email',\n]\n\nexport interface AiBuildPlanReuse {\n kind: AiBuildPlanReuseKind\n /** The inventory id. */\n id: string\n /** What the plan uses it for, in a few words. */\n purpose: string\n}\n\nexport interface AiBuildPlanCreate {\n kind: AiBuildPlanCreateKind\n /** Unique within the plan; referenced as `new:<name>`. */\n name: string\n /** Why nothing the site has will do — shown in the proposal. */\n why: string\n /** The inventory id this starts from as a duplicate, or null. */\n duplicateOf: string | null\n /**\n * A component: its props as `name:type`, an icon as `name:icon`\n * (AGL-3054). A layout: the regions it has, from the closed vocabulary\n * `AI_LAYOUT_REGIONS` holds (AGL-3024) — a layout has no props, and a\n * region named here is what the built layout is held to. A template: the\n * binding tokens its page shows, from the subject's own closed catalog\n * (`AI_TEMPLATE_SUBJECT_DEFINITIONS[subject].tokens`, AGL-3143) — the\n * template's analogue of a layout's regions, and what the built template is\n * held to. A form or a dataset: its field names. A theme change: the\n * palette paths it adds or changes.\n */\n fields: string[]\n /**\n * A form only: the dataset each of its submissions is also written to as a\n * record (AGL-3616, Zach 2026-10-10: a volunteer sign-up writing to a\n * Volunteers dataset) — `new:<name>` of a dataset this plan creates, or a\n * dataset id from the inventory. Said by the plan, never inferred from a\n * section's `uses`. Absent or null on every other creation, and on a form\n * whose submissions only reach the Inbox.\n */\n writesTo?: string | null\n /**\n * The id the draft this creation becomes is written under, where a unit of\n * the job builds it (AGL-3079). Minted when the job keeps the plan, never\n * part of the model's answer.\n */\n id?: string\n}\n\nexport interface AiBuildPlanSection {\n /** A few words: \"hero\", \"services grid\", \"contact form\". */\n name: string\n /** What the section places: inventory ids or `new:<name>` references. */\n uses: string[]\n /** How many repeated items it shows (cards, rows, tiles); 0 when none. */\n items: number\n}\n\n/**\n * A screen that is one dataset's record template (AGL-3475): the data plugin\n * serves it once per record at `/{base}/{slug}`, its copy bound with\n * `{{item.<field>}}`. The plan names the binding and the build drafts the\n * page; the member saves the binding in Page Properties → Record pages, since\n * a binding is a route and a job writes drafts only (rule 13).\n */\nexport interface AiBuildPlanRecord {\n /** The dataset: an inventory id or `new:<name>`. */\n dataset: string\n /** The path its record pages live under, one or more segments: `services`, `services/residential`. */\n base: string\n}\n\nexport interface AiBuildPlanScreen {\n title: string\n slug: string\n /** The layout it renders inside. */\n layout: string | null\n /** The template it applies, or null for a one-off page. */\n template: string | null\n /** The inventory screen it starts from as a duplicate, or null. */\n duplicateOf: string | null\n /** Whether the site navigation gains an entry for it. */\n nav: boolean\n seoTitle: string\n seoDescription: string\n sections: AiBuildPlanSection[]\n /**\n * The dataset this page is the record template of, or null for a page that\n * is one page. A plan kept before record templates carries none, which\n * reads as null.\n */\n record: AiBuildPlanRecord | null\n /**\n * The id the draft screen is written under, where a unit of the job builds\n * it (AGL-3079). Minted when the job keeps the plan, never part of the\n * model's answer.\n */\n id?: string\n}\n\n/** The third-party players a plan may embed: the hosts the Video embed element plays. */\nexport type AiBuildPlanEmbedHost = 'youtube' | 'vimeo'\n\nexport const AI_BUILD_PLAN_EMBED_HOSTS: readonly AiBuildPlanEmbedHost[] = ['youtube', 'vimeo']\n\n/** What each host is called in a sentence. */\nexport const AI_BUILD_PLAN_EMBED_HOST_NAMES: Record<AiBuildPlanEmbedHost, string> = {\n youtube: 'YouTube',\n vimeo: 'Vimeo',\n}\n\n/**\n * A third-party player the brief asks for (rule 16, AGL-3433). It loads its\n * host's code on the page that carries it, so it is planned, where the member\n * reads that cost before confirming, and a build admits only what the\n * confirmed plan lists.\n */\nexport interface AiBuildPlanEmbed {\n host: AiBuildPlanEmbedHost\n /** Where it plays: a screen of the plan by its slug, or `new:<name>` for a component the plan creates. */\n where: string\n /** The brief's own words asking for it, which the plan step finds in the brief. */\n asked: string\n /** The video's link as the brief gives it, or null for the site owner to paste. */\n url: string | null\n}\n\n/** How the items that depend on a failed item behave (AGL-3616). */\nexport type AiBuildItemDegrade = 'omit' | 'fallback'\n\nexport const AI_BUILD_ITEM_DEGRADES: readonly AiBuildItemDegrade[] = ['omit', 'fallback']\n\n/** One argument value of an item: scalars and lists of text only. */\nexport type AiBuildItemArg = string | number | boolean | string[]\n\n/**\n * One thing a `build` plan makes that its creations and screens do not\n * (AGL-3616): an operation another plugin — or this one — registered as an\n * AI capability, with the arguments its schema declares. `reuse`, `create`\n * and `screens` keep their meaning; an item is everything else.\n */\nexport interface AiBuildItem {\n /** Stable within the plan: `i0`, `i1`… in plan order. */\n slot: string\n /** The capability's operation. */\n op: string\n /** Unique within the plan, creations included; referenced as `new:<name>`. */\n name: string\n /** Why the brief needs it, in one sentence. */\n why: string\n /** What it is built after: `new:<name>` of a creation or another item. */\n dependsOn: string[]\n /** How the items that depend on it behave when it fails. */\n degrade: AiBuildItemDegrade\n /** The operation's arguments, in the shape its capability declares. */\n args: Record<string, AiBuildItemArg>\n /** The id its draft is written under, minted when the job keeps the plan; never the model's. */\n id?: string\n /** The most it can cost in credits, from its capability, recorded when the plan is kept. */\n credits?: number\n}\n\nexport interface AiBuildPlan {\n reuse: AiBuildPlanReuse[]\n create: AiBuildPlanCreate[]\n screens: AiBuildPlanScreen[]\n /**\n * The third-party players the brief asks for. Absent is none: a plan kept\n * before the list existed, and every plan whose brief asks for no player.\n */\n embeds?: AiBuildPlanEmbed[]\n /**\n * What a `build` plan makes beyond its creations and pages (AGL-3616).\n * Absent is none: every other kind's plan.\n */\n items?: AiBuildItem[]\n}\n\n/**\n * How much one plan may hold. A job builds what one proposal can describe\n * and one person can read before confirming; a brief asking for more is\n * several jobs.\n */\nexport const AI_BUILD_PLAN_LIMITS = {\n reuse: 40,\n create: 20,\n screens: 12,\n sections: 16,\n uses: 8,\n fields: 30,\n embeds: 4,\n text: 200,\n seoDescription: 320,\n items: 500,\n /** A build plan's items (AGL-3616). */\n buildItems: 16,\n /** What one item depends on. */\n dependsOn: 8,\n /** The arguments one item carries, as JSON. */\n argsChars: 2_000,\n} as const\n\nconst string = (description: string) => ({ type: 'string', description })\nconst nullableString = (description: string) => ({\n anyOf: [{ type: 'string' }, { type: 'null' }],\n description,\n})\nconst strings = (description: string) => ({\n type: 'array',\n items: { type: 'string' },\n description,\n})\n\n/**\n * The ceiling `parseAiBuildPlan` cuts a field's text at, in words (AGL-3022).\n * The reader repairs an overlong value by cutting it, and a model never told\n * where the cut falls writes a rationale that stops mid-sentence there.\n */\nconst atMost = (limit: number = AI_BUILD_PLAN_LIMITS.text) => `at most ${limit} characters`\n\n/**\n * The plan as a strict tool's input schema. Only the keywords strict\n * structured output accepts: every object closes its properties and lists\n * every one as required, an optional value is a `null` branch, and no\n * length or count bound appears as a keyword — those are enforced by\n * `parseAiBuildPlan`, which every answer passes through anyway.\n *\n * A text ceiling is stated in words instead, on each field the model writes.\n * A reference is copied from the inventory or from a creation's name, so its\n * length is not the model's to choose. A search title and description are\n * held by rule 10 to ceilings far inside the reader's, which the doctrine\n * states, so naming the reader's here would contradict the rule.\n */\nexport const AI_BUILD_PLAN_TOOL: AiTool = {\n name: 'submit_build_plan',\n description:\n 'Submit the build plan: what the site already has that this job reuses, what it creates and why, and the pages it builds from them.',\n strict: true,\n inputSchema: {\n type: 'object',\n additionalProperties: false,\n required: ['reuse', 'create', 'screens'],\n properties: {\n reuse: {\n type: 'array',\n description: 'Records the site already has that this plan uses, by inventory id.',\n items: {\n type: 'object',\n additionalProperties: false,\n required: ['kind', 'id', 'purpose'],\n properties: {\n kind: { type: 'string', enum: [...AI_BUILD_PLAN_REUSE_KINDS] },\n id: string('The id from the site inventory.'),\n purpose: string(`What the plan uses it for, in a few words, ${atMost()}.`),\n },\n },\n },\n create: {\n type: 'array',\n description:\n 'What the plan creates. Creating is the exception: each entry says why nothing listed will do.',\n items: {\n type: 'object',\n additionalProperties: false,\n required: ['kind', 'name', 'why', 'duplicateOf', 'fields'],\n properties: {\n kind: { type: 'string', enum: [...AI_BUILD_PLAN_CREATE_KINDS] },\n name: string(\n `Unique within the plan, ${atMost()}; other entries refer to it as new:<name>.`,\n ),\n why: string(`Why nothing the site already has will do, in one sentence of ${atMost()}.`),\n duplicateOf: nullableString(\n 'The inventory id this starts from as a duplicate, or null.',\n ),\n fields: strings(\n `A component: its props as name:type, an icon as name:icon. A layout: the regions it has, each one of header, nav, sidebar, main, footer — list every region the finished layout has, not only the ones it gains. A form or dataset: its field names. A theme change: the palette paths it adds or changes. Each ${atMost()}.`,\n ),\n },\n },\n },\n screens: {\n type: 'array',\n description: 'The pages the job builds, each a new draft.',\n items: {\n type: 'object',\n additionalProperties: false,\n required: [\n 'title',\n 'slug',\n 'layout',\n 'template',\n 'duplicateOf',\n 'nav',\n 'seoTitle',\n 'seoDescription',\n 'sections',\n ],\n properties: {\n title: string(`The page name, ${atMost()}.`),\n slug: string(\n `The path, ${atMost()}: lowercase words joined by hyphens, e.g. /services/roof-repair.`,\n ),\n layout: nullableString(\n 'The layout it renders inside: an inventory id or new:<name>.',\n ),\n template: nullableString(\n 'The template it applies (inventory id or new:<name>), or null for a one-off page.',\n ),\n duplicateOf: nullableString(\n 'The inventory page it starts from as a duplicate, or null.',\n ),\n nav: {\n type: 'boolean',\n description: 'Whether the site navigation gains an entry for it.',\n },\n seoTitle: string('The search-result title.'),\n seoDescription: string('The search-result description.'),\n sections: {\n type: 'array',\n description: 'The sections, top to bottom.',\n items: {\n type: 'object',\n additionalProperties: false,\n required: ['name', 'uses', 'items'],\n properties: {\n name: string(`A few words, ${atMost()}: hero, services grid, contact form.`),\n uses: strings('What the section places: inventory ids or new:<name>.'),\n items: {\n type: 'integer',\n description: 'How many repeated items it shows; 0 when none.',\n },\n },\n },\n },\n },\n },\n },\n },\n },\n}\n\n/** Words that ask for a video, or a link to one. */\nexport const AI_PLAN_ASKS_FOR_VIDEO = /\\b(?:videos?|films?|clips?|youtube|vimeo|player)\\b|youtu\\.be/i\n\nconst EMBEDS_SCHEMA = {\n type: 'array',\n description:\n 'A YouTube or Vimeo player the brief asks for, on a screen or a created component. Empty unless the brief asks; a library film is a Video.',\n items: {\n type: 'object',\n additionalProperties: false,\n required: ['host', 'where', 'asked', 'url'],\n properties: {\n host: { type: 'string', enum: [...AI_BUILD_PLAN_EMBED_HOSTS] },\n where: string('A screen slug, or new:<name> of a component.'),\n asked: string('The words of the brief that ask for it, copied exactly.'),\n url: nullableString('The video link the brief gives, or null.'),\n },\n },\n}\n\n/**\n * The plan tool with its list of third-party players, offered only to a brief\n * that asks for a video (AGL-3433). Every other plan's request is the plain\n * tool, byte for byte, so the list costs nothing on the plans that cannot use\n * it, and a model answering a brief that names no video has nowhere to plan a\n * player it was never asked for.\n */\nexport const AI_BUILD_PLAN_EMBEDS_TOOL: AiTool = {\n ...AI_BUILD_PLAN_TOOL,\n inputSchema: {\n ...AI_BUILD_PLAN_TOOL.inputSchema,\n required: [...(AI_BUILD_PLAN_TOOL.inputSchema['required'] as string[]), 'embeds'],\n properties: {\n ...(AI_BUILD_PLAN_TOOL.inputSchema['properties'] as Record<string, unknown>),\n embeds: EMBEDS_SCHEMA,\n },\n },\n}\n\nconst RECORD_SCHEMA = {\n anyOf: [\n {\n type: 'object',\n additionalProperties: false,\n required: ['dataset', 'base'],\n properties: {\n dataset: string('The dataset: an inventory id or new:<name>.'),\n base: string('The path its record pages live under, e.g. services for /services/roofing.'),\n },\n },\n { type: 'null' },\n ],\n description:\n \"When this page is one dataset's record template, serving a page per record at /<base>/<record address> with its copy bound as {{item.<field>}}: the dataset and the base. Otherwise null.\",\n}\n\nconst WRITES_TO_SCHEMA = nullableString(\n 'A form only, and only where each submission should also be kept as a record (a volunteer sign-up, an RSVP, an inquiry): the dataset it writes to, as new:<name> of a dataset this plan creates or a dataset id from the inventory. Otherwise null.',\n)\n\n/**\n * A plan tool whose screens each name the dataset they are the record\n * template of, or null, and whose creations each name the dataset a form\n * writes its submissions to, or null (AGL-3616).\n */\nfunction withRecords(tool: AiTool): AiTool {\n const properties = tool.inputSchema['properties'] as Record<string, unknown>\n const screens = properties['screens'] as { items: Record<string, unknown> }\n const create = properties['create'] as { items: Record<string, unknown> }\n return {\n ...tool,\n inputSchema: {\n ...tool.inputSchema,\n properties: {\n ...properties,\n create: {\n ...create,\n items: {\n ...create.items,\n required: [...(create.items['required'] as string[]), 'writesTo'],\n properties: {\n ...(create.items['properties'] as Record<string, unknown>),\n writesTo: WRITES_TO_SCHEMA,\n },\n },\n },\n screens: {\n ...screens,\n items: {\n ...screens.items,\n required: [...(screens.items['required'] as string[]), 'record'],\n properties: {\n ...(screens.items['properties'] as Record<string, unknown>),\n record: RECORD_SCHEMA,\n },\n },\n },\n },\n },\n }\n}\n\n/**\n * The plan tools with a record template on every screen (AGL-3475) and the\n * dataset a form writes to on every creation (AGL-3616), offered only where\n * the job may bind a dataset: a site with a dataset, or a job that may create\n * one. Record pages are a Starter feature, and every other plan's\n * request is the plain tool, byte for byte, so the field costs a Free taste\n * nothing and a model planning a site with no dataset has none to bind.\n */\nexport const AI_BUILD_PLAN_RECORDS_TOOL: AiTool = withRecords(AI_BUILD_PLAN_TOOL)\nexport const AI_BUILD_PLAN_EMBEDS_RECORDS_TOOL: AiTool = withRecords(AI_BUILD_PLAN_EMBEDS_TOOL)\n\nconst ITEMS_SCHEMA = {\n type: 'array',\n description:\n 'What the plan makes beyond its creations and pages, each one operation from the list in the request. Empty when the brief asks for nothing else.',\n items: {\n type: 'object',\n additionalProperties: false,\n required: ['op', 'name', 'why', 'dependsOn', 'degrade', 'args'],\n properties: {\n op: string('The operation, exactly as the request lists it.'),\n name: string(`Unique within the plan, creations included, ${atMost()}; other entries refer to it as new:<name>.`),\n why: string(`Why the brief needs it, in one sentence of ${atMost()}.`),\n dependsOn: strings('What it is built after: new:<name> of a creation or another item. Empty when nothing.'),\n degrade: {\n type: 'string',\n enum: [...AI_BUILD_ITEM_DEGRADES],\n description:\n 'omit: what depends on it is built without it if it fails. fallback: what depends on it uses something the site already has instead.',\n },\n args: string('Its arguments as one JSON object, with exactly the fields the request lists for the operation.'),\n },\n },\n}\n\n/**\n * Any plan tool with a build's items (AGL-3616), offered only to a `build`\n * job. The items' arguments travel as one JSON string whose fields the\n * request lists per operation, so the tool stays byte for byte the same\n * whichever plugins registered operations, and the prompt cache keeps it.\n */\nexport function withBuildItems(tool: AiTool): AiTool {\n return {\n ...tool,\n inputSchema: {\n ...tool.inputSchema,\n required: [...(tool.inputSchema['required'] as string[]), 'items'],\n properties: {\n ...(tool.inputSchema['properties'] as Record<string, unknown>),\n items: ITEMS_SCHEMA,\n },\n },\n }\n}\n\n/** The plan tool a brief is answered with, on a job that may or may not bind a dataset. */\nexport function aiBuildPlanToolFor(\n brief: string,\n options: { records?: boolean; items?: boolean } = {},\n): AiTool {\n const video = AI_PLAN_ASKS_FOR_VIDEO.test(brief)\n const tool = options.records\n ? video\n ? AI_BUILD_PLAN_EMBEDS_RECORDS_TOOL\n : AI_BUILD_PLAN_RECORDS_TOOL\n : video\n ? AI_BUILD_PLAN_EMBEDS_TOOL\n : AI_BUILD_PLAN_TOOL\n return options.items ? withBuildItems(tool) : tool\n}\n\nexport type AiBuildPlanParse =\n | { ok: true; plan: AiBuildPlan; repairs: string[] }\n | { ok: false; error: string }\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nclass PlanShapeError extends Error {}\n\n/**\n * An item's arguments as the plan keeps them: the model writes one JSON\n * string, a kept plan stores the object. Values are scalars and lists of\n * text; anything else is a shape the item cannot be built from.\n */\nfunction buildItemArgs(value: unknown, path: string): Record<string, AiBuildItemArg> {\n let parsed: unknown = value\n if (value === undefined || value === null || value === '') return {}\n if (typeof value === 'string') {\n if (value.length > AI_BUILD_PLAN_LIMITS.argsChars) {\n throw new PlanShapeError(`${path} is over ${AI_BUILD_PLAN_LIMITS.argsChars} characters`)\n }\n try {\n parsed = JSON.parse(value)\n } catch {\n throw new PlanShapeError(`${path} is not a JSON object`)\n }\n }\n if (!isRecord(parsed)) throw new PlanShapeError(`${path} is not a JSON object`)\n const args: Record<string, AiBuildItemArg> = {}\n for (const [key, raw] of Object.entries(parsed)) {\n if (raw === null || raw === undefined) continue\n if (typeof raw === 'string' || typeof raw === 'boolean' || (typeof raw === 'number' && Number.isFinite(raw))) {\n args[key] = raw\n } else if (Array.isArray(raw) && raw.every((one) => typeof one === 'string')) {\n args[key] = raw as string[]\n } else {\n throw new PlanShapeError(`${path}.${key} is not text, a number, true or false, or a list of text`)\n }\n }\n return args\n}\n\n/**\n * Read a plan out of a tool call, or name what makes it unreadable.\n *\n * Structure is refused — a missing list, an unknown kind, more screens than\n * a job holds, two creations under one name — because a plan that reads\n * differently from what the model meant is worse than a re-ask. Length is\n * repaired: copy past a ceiling is cut and the cut is listed.\n */\n/**\n * A layout's regions, one word each. A plan sometimes writes the whole list as\n * one entry — \"regions: header, nav, main, footer\" — which names regions the\n * platform builds but reads as one unknown word, and the plan was refused for\n * it (job AvVxbFbzQx). A leading label is dropped and a list is split on its\n * commas, slashes, plus signs and \"and\", so each region is checked on its own;\n * a duplicate keeps its first place.\n */\nexport function aiPlanLayoutRegionWords(fields: readonly string[]): string[] {\n const words: string[] = []\n for (const field of fields) {\n const listed = /^\\s*(?:the\\s+)?(?:layout\\s+)?(?:regions?|areas?)\\s*[:=-]\\s*(.+)$/i.exec(field)?.[1] ?? field\n const parts = /[,/+]|\\band\\b/i.test(listed)\n ? listed.split(/\\s*(?:,|\\/|\\+|\\band\\b)\\s*/i)\n : [listed]\n for (const part of parts) {\n const word = part.trim()\n if (word && !words.some((seen) => seen.toLowerCase() === word.toLowerCase())) words.push(word)\n }\n }\n return words\n}\n\nexport function parseAiBuildPlan(input: unknown): AiBuildPlanParse {\n const repairs: string[] = []\n const text = (\n value: unknown,\n path: string,\n limit: number = AI_BUILD_PLAN_LIMITS.text,\n ): string => {\n if (typeof value !== 'string') throw new PlanShapeError(`${path} is not text`)\n const trimmed = value.trim()\n if (trimmed.length > limit) {\n repairs.push(`${path} was over ${limit} characters; truncated`)\n // On the last word break inside the ceiling, not through a word: a\n // rationale is read by the member confirming the plan, and half a word\n // reads as a broken answer rather than a long one (AGL-3022). A run of\n // `limit` characters with no break in it still has to be cut where the\n // ceiling falls.\n const cut = trimmed.slice(0, limit)\n const brk = cut.search(/\\s\\S*$/)\n return (brk > 0 ? cut.slice(0, brk) : cut).trimEnd()\n }\n return trimmed\n }\n const nullable = (value: unknown, path: string): string | null =>\n value === null || value === undefined || value === '' ? null : text(value, path) || null\n const list = (value: unknown, path: string, limit: number): unknown[] => {\n if (!Array.isArray(value)) throw new PlanShapeError(`${path} is not a list`)\n if (value.length > limit) {\n throw new PlanShapeError(`${path} lists ${value.length}; one plan holds at most ${limit}`)\n }\n return value\n }\n const record = (value: unknown, path: string): Record<string, unknown> => {\n if (!isRecord(value)) throw new PlanShapeError(`${path} is not an object`)\n return value\n }\n const oneOf = <T extends string>(value: unknown, allowed: readonly T[], path: string): T => {\n if (typeof value !== 'string' || !(allowed as readonly string[]).includes(value)) {\n throw new PlanShapeError(`${path} is not one of ${allowed.join(', ')}`)\n }\n return value as T\n }\n // Absent or null is a page that is one page. A base is held to the shape a\n // record page's address takes, so the binding the member saves from it is\n // one the save route admits.\n const planRecord = (value: unknown, path: string): AiBuildPlanRecord | null => {\n if (value === null || value === undefined) return null\n const entry = record(value, path)\n const dataset = text(entry['dataset'], `${path}.dataset`)\n if (!dataset) throw new PlanShapeError(`${path}.dataset is empty`)\n const base = aiPlanRecordBase(text(entry['base'], `${path}.base`))\n if (!base) throw new PlanShapeError(`${path}.base is not a path of lowercase words joined by hyphens`)\n return { dataset, base }\n }\n\n try {\n const root = record(input, 'plan')\n const reuse = list(root['reuse'], 'reuse', AI_BUILD_PLAN_LIMITS.reuse).map((raw, index) => {\n const entry = record(raw, `reuse[${index}]`)\n const id = text(entry['id'], `reuse[${index}].id`)\n if (!id) throw new PlanShapeError(`reuse[${index}].id is empty`)\n return {\n kind: oneOf(entry['kind'], AI_BUILD_PLAN_REUSE_KINDS, `reuse[${index}].kind`),\n id,\n purpose: text(entry['purpose'], `reuse[${index}].purpose`),\n }\n })\n const names = new Set<string>()\n const create = list(root['create'], 'create', AI_BUILD_PLAN_LIMITS.create).map(\n (raw, index) => {\n const entry = record(raw, `create[${index}]`)\n const name = text(entry['name'], `create[${index}].name`)\n if (!name) throw new PlanShapeError(`create[${index}].name is empty`)\n const key = name.toLowerCase()\n if (names.has(key)) {\n throw new PlanShapeError(`create[${index}].name \"${name}\" is used twice`)\n }\n names.add(key)\n const kind = oneOf(entry['kind'], AI_BUILD_PLAN_CREATE_KINDS, `create[${index}].kind`)\n const fields = list(entry['fields'] ?? [], `create[${index}].fields`, AI_BUILD_PLAN_LIMITS.fields)\n .map((field, fieldIndex) => text(field, `create[${index}].fields[${fieldIndex}]`))\n .filter(Boolean)\n // Only a form writes its submissions to a dataset (AGL-3616); kept\n // only where it says one, so a plan without one reads as it always did.\n const writesTo = kind === 'form' ? nullable(entry['writesTo'], `create[${index}].writesTo`) : null\n return {\n kind,\n name,\n why: text(entry['why'], `create[${index}].why`),\n duplicateOf: nullable(entry['duplicateOf'], `create[${index}].duplicateOf`),\n fields: kind === 'layout' ? aiPlanLayoutRegionWords(fields) : fields,\n ...(writesTo ? { writesTo } : {}),\n }\n },\n )\n const screens = list(root['screens'], 'screens', AI_BUILD_PLAN_LIMITS.screens).map(\n (raw, index) => {\n const entry = record(raw, `screens[${index}]`)\n if (typeof entry['nav'] !== 'boolean') {\n throw new PlanShapeError(`screens[${index}].nav is not true or false`)\n }\n return {\n title: text(entry['title'], `screens[${index}].title`),\n slug: text(entry['slug'], `screens[${index}].slug`),\n layout: nullable(entry['layout'], `screens[${index}].layout`),\n template: nullable(entry['template'], `screens[${index}].template`),\n duplicateOf: nullable(entry['duplicateOf'], `screens[${index}].duplicateOf`),\n nav: entry['nav'],\n seoTitle: text(entry['seoTitle'], `screens[${index}].seoTitle`),\n seoDescription: text(\n entry['seoDescription'],\n `screens[${index}].seoDescription`,\n AI_BUILD_PLAN_LIMITS.seoDescription,\n ),\n sections: list(\n entry['sections'],\n `screens[${index}].sections`,\n AI_BUILD_PLAN_LIMITS.sections,\n ).map((rawSection, sectionIndex) => {\n const path = `screens[${index}].sections[${sectionIndex}]`\n const section = record(rawSection, path)\n const items = Number(section['items'] ?? 0)\n if (!Number.isInteger(items) || items < 0 || items > AI_BUILD_PLAN_LIMITS.items) {\n throw new PlanShapeError(`${path}.items is not a count`)\n }\n return {\n name: text(section['name'], `${path}.name`),\n uses: list(section['uses'] ?? [], `${path}.uses`, AI_BUILD_PLAN_LIMITS.uses)\n .map((ref, refIndex) => text(ref, `${path}.uses[${refIndex}]`))\n .filter(Boolean),\n items,\n }\n }),\n record: planRecord(entry['record'], `screens[${index}].record`),\n }\n },\n )\n const embeds = list(root['embeds'] ?? [], 'embeds', AI_BUILD_PLAN_LIMITS.embeds).map(\n (raw, index) => {\n const entry = record(raw, `embeds[${index}]`)\n const where = text(entry['where'], `embeds[${index}].where`)\n if (!where) throw new PlanShapeError(`embeds[${index}].where is empty`)\n return {\n host: oneOf(entry['host'], AI_BUILD_PLAN_EMBED_HOSTS, `embeds[${index}].host`),\n where,\n asked: text(entry['asked'], `embeds[${index}].asked`),\n url: nullable(entry['url'], `embeds[${index}].url`),\n }\n },\n )\n const items = list(root['items'] ?? [], 'items', AI_BUILD_PLAN_LIMITS.buildItems).map(\n (raw, index): AiBuildItem => {\n const path = `items[${index}]`\n const entry = record(raw, path)\n const op = text(entry['op'], `${path}.op`)\n if (!op) throw new PlanShapeError(`${path}.op is empty`)\n const name = text(entry['name'], `${path}.name`)\n if (!name) throw new PlanShapeError(`${path}.name is empty`)\n const key = name.toLowerCase()\n if (names.has(key)) throw new PlanShapeError(`${path}.name \"${name}\" is used twice`)\n names.add(key)\n return {\n slot: `i${index}`,\n op,\n name,\n why: text(entry['why'] ?? '', `${path}.why`),\n dependsOn: list(entry['dependsOn'] ?? [], `${path}.dependsOn`, AI_BUILD_PLAN_LIMITS.dependsOn)\n .map((ref, refIndex) => text(ref, `${path}.dependsOn[${refIndex}]`))\n .filter(Boolean),\n degrade:\n entry['degrade'] === undefined || entry['degrade'] === null\n ? 'omit'\n : oneOf(entry['degrade'], AI_BUILD_ITEM_DEGRADES, `${path}.degrade`),\n args: buildItemArgs(entry['args'], `${path}.args`),\n ...(typeof entry['id'] === 'string' && entry['id'] ? { id: entry['id'] } : {}),\n ...(typeof entry['credits'] === 'number' && Number.isFinite(entry['credits'])\n ? { credits: Math.max(0, Math.floor(entry['credits'])) }\n : {}),\n }\n },\n )\n // A plan whose brief asks for no player carries no list, as it did before the list existed;\n // so does a plan with no items.\n return {\n ok: true,\n plan: {\n reuse,\n create,\n screens,\n ...(embeds.length ? { embeds } : {}),\n ...(items.length ? { items } : {}),\n },\n repairs,\n }\n } catch (error) {\n if (error instanceof PlanShapeError) return { ok: false, error: error.message }\n throw error\n }\n}\n\n/**\n * A record template's base as the data plugin's save route stores it\n * (`normalizeRecordPageBase`, which this plugin may not import): one to four\n * lowercase segments of letters, digits and hyphens, with no leading or\n * trailing slash; null for a base that is no such path.\n */\nexport function aiPlanRecordBase(base: string): string | null {\n const segments = base.trim().toLowerCase().split('/').filter(Boolean)\n if (!segments.length || segments.length > 4) return null\n return segments.every(\n (segment) => segment.length <= 60 && /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(segment),\n )\n ? segments.join('/')\n : null\n}\n\n/** Whether a reference names something the plan creates. */\nexport function isAiPlanNewRef(ref: string | null | undefined): ref is string {\n return typeof ref === 'string' && ref.startsWith(AI_PLAN_NEW_REF_PREFIX)\n}\n\n/** The creation a `new:<name>` reference names, case-insensitively. */\nexport function aiPlanCreateFor(\n plan: AiBuildPlan,\n ref: string,\n): AiBuildPlanCreate | undefined {\n if (!isAiPlanNewRef(ref)) return undefined\n const name = ref.slice(AI_PLAN_NEW_REF_PREFIX.length).trim().toLowerCase()\n return plan.create.find((entry) => entry.name.toLowerCase() === name)\n}\n\n/** The operation a build's dataset item is planned as (AGL-3616), owned by the data plugin. */\nexport const AI_PLAN_DATASET_ITEM_OP = 'dataset'\n\n/**\n * The datasets this plan makes that one of its forms writes its submissions\n * to (AGL-3616), by name lowercased: a dataset creation of a site start, or a\n * build's dataset item. Such a dataset starts with no records — its records\n * are the submissions — so a site start designs it from its planned fields\n * with no model, and no page lists it.\n */\nexport function aiPlanFormDatasetNames(plan: Pick<AiBuildPlan, 'create'> & Partial<Pick<AiBuildPlan, 'items'>>): Set<string> {\n const names = new Set<string>()\n for (const entry of plan.create) {\n if (entry.kind !== 'form' || !isAiPlanNewRef(entry.writesTo)) continue\n const name = entry.writesTo.slice(AI_PLAN_NEW_REF_PREFIX.length).trim().toLowerCase()\n const made =\n plan.create.some((one) => one.kind === 'dataset' && one.name.trim().toLowerCase() === name) ||\n (plan.items ?? []).some((one) => one.op === AI_PLAN_DATASET_ITEM_OP && one.name.trim().toLowerCase() === name)\n if (made) names.add(name)\n }\n return names\n}\n\n/** The item a `new:<name>` reference names, case-insensitively (AGL-3616). */\nexport function aiPlanItemFor(\n plan: Pick<AiBuildPlan, 'items'>,\n ref: string,\n): AiBuildItem | undefined {\n if (!isAiPlanNewRef(ref)) return undefined\n const name = ref.slice(AI_PLAN_NEW_REF_PREFIX.length).trim().toLowerCase()\n return (plan.items ?? []).find((entry) => entry.name.toLowerCase() === name)\n}\n\n/** A slug as two spellings of one path compare: lowercase, one leading slash, no trailing one. */\nexport function aiPlanSlugKey(slug: string): string {\n const path = slug.trim().toLowerCase().replace(/^\\/*/, '/').replace(/\\/+$/, '')\n return path || '/'\n}\n\n/**\n * The players the plan lists for one output (AGL-3433): a screen by its slug,\n * or a creation by its name. A build admits these and no other.\n */\nexport function aiPlanEmbedsFor(\n plan: Pick<AiBuildPlan, 'embeds'> | null | undefined,\n target: { slug: string } | { create: string },\n): AiBuildPlanEmbed[] {\n const key =\n 'slug' in target\n ? aiPlanSlugKey(target.slug)\n : `${AI_PLAN_NEW_REF_PREFIX}${target.create.trim().toLowerCase()}`\n return (plan?.embeds ?? []).filter((embed) =>\n 'slug' in target\n ? !isAiPlanNewRef(embed.where) && aiPlanSlugKey(embed.where) === key\n : embed.where.trim().toLowerCase() === key,\n )\n}\n\n/** The host a video link plays from, or null for a link neither host serves. */\nexport function aiEmbedHostOf(url: string): AiBuildPlanEmbedHost | null {\n try {\n const parsed = new URL(url.trim())\n if (parsed.protocol !== 'https:') return null\n const host = parsed.hostname.replace(/^(?:www|m)\\./, '')\n if (host === 'youtu.be' || host === 'youtube.com' || host === 'youtube-nocookie.com') {\n return 'youtube'\n }\n if (host === 'vimeo.com' || host === 'player.vimeo.com') return 'vimeo'\n } catch {\n /* not a link */\n }\n return null\n}\n\n/**\n * One video however its link is spelled — `youtu.be/<id>`, a watch link, an\n * embed or a Shorts link — as `<host>:<id>`, the way the Video embed element\n * reads a link; null for a link it cannot play.\n */\nexport function aiEmbedVideoKey(url: string): string | null {\n const host = aiEmbedHostOf(url)\n if (!host) return null\n const parsed = new URL(url.trim())\n const parts = parsed.pathname.split('/').filter(Boolean)\n const id =\n host === 'vimeo'\n ? parts.find((part) => /^\\d{6,}$/.test(part))\n : parsed.hostname.endsWith('youtu.be')\n ? parts[0]\n : (parsed.searchParams.get('v') ??\n (parts[0] === 'embed' || parts[0] === 'shorts' ? parts[1] : undefined))\n return id && /^[\\w-]{6,20}$/.test(id) ? `${host}:${id}` : null\n}\n\n/**\n * A `new:<name>` reference that names no entry of the plan's create list: a\n * screen's own layout or template, or what one of its sections places, with\n * that section's index.\n */\nexport type AiPlanUndeclaredRef = {\n /** The name the reference gives the creation, as written after `new:`. */\n name: string\n /** Where the plan writes it: `screens[0].template`, `screens[0].sections[4].uses[0]`. */\n path: string\n /** The screen that writes it, by index. */\n screenIndex: number\n} & ({ field: 'layout' | 'template'; sectionIndex: null } | { field: 'uses'; sectionIndex: number })\n\n/**\n * Every `new:<name>` reference the plan's create list does not carry, in plan\n * order: each screen's layout, then its template, then what its sections\n * place. A plan that places a creation it never declares has nothing to build\n * it from, so the plan rules refuse each (AGL-3040), and the page and\n * scaffold doors still name one in a plan confirmed before they did.\n */\nexport function aiPlanUndeclaredRefs(plan: AiBuildPlan): AiPlanUndeclaredRef[] {\n const found: AiPlanUndeclaredRef[] = []\n const undeclared = (ref: string | null): ref is string =>\n isAiPlanNewRef(ref) && !aiPlanCreateFor(plan, ref) && !aiPlanItemFor(plan, ref)\n const nameOf = (ref: string) => ref.slice(AI_PLAN_NEW_REF_PREFIX.length).trim()\n plan.screens.forEach((screen, screenIndex) => {\n for (const field of ['layout', 'template'] as const) {\n const ref = screen[field]\n if (!undeclared(ref)) continue\n found.push({\n name: nameOf(ref),\n path: `screens[${screenIndex}].${field}`,\n screenIndex,\n field,\n sectionIndex: null,\n })\n }\n screen.sections.forEach((section, sectionIndex) => {\n section.uses.forEach((ref, useIndex) => {\n if (!undeclared(ref)) return\n found.push({\n name: nameOf(ref),\n path: `screens[${screenIndex}].sections[${sectionIndex}].uses[${useIndex}]`,\n screenIndex,\n field: 'uses',\n sectionIndex,\n })\n })\n })\n })\n return found\n}\n"],"names":["AI_PLAN_NEW_REF_PREFIX","AI_BUILD_PLAN_REUSE_KINDS","AI_BUILD_PLAN_CREATION_NOUNS","component","noun","where","form","layout","template","dataset","email","AI_BUILD_PLAN_CREATE_KINDS","AI_BUILD_PLAN_EMBED_HOSTS","AI_BUILD_PLAN_EMBED_HOST_NAMES","youtube","vimeo","AI_BUILD_ITEM_DEGRADES","AI_BUILD_PLAN_LIMITS","reuse","create","screens","sections","uses","fields","embeds","text","seoDescription","items","buildItems","dependsOn","argsChars","string","description","type","nullableString","anyOf","strings","atMost","limit","AI_BUILD_PLAN_TOOL","name","strict","inputSchema","additionalProperties","required","properties","kind","enum","id","purpose","why","duplicateOf","title","slug","nav","seoTitle","AI_PLAN_ASKS_FOR_VIDEO","EMBEDS_SCHEMA","host","asked","url","AI_BUILD_PLAN_EMBEDS_TOOL","RECORD_SCHEMA","base","WRITES_TO_SCHEMA","withRecords","tool","writesTo","record","AI_BUILD_PLAN_RECORDS_TOOL","AI_BUILD_PLAN_EMBEDS_RECORDS_TOOL","ITEMS_SCHEMA","op","degrade","args","withBuildItems","aiBuildPlanToolFor","brief","options","video","test","records","isRecord","value","Array","isArray","PlanShapeError","Error","buildItemArgs","path","parsed","undefined","length","JSON","parse","key","raw","Object","entries","Number","isFinite","every","one","aiPlanLayoutRegionWords","words","field","listed","exec","parts","split","part","word","trim","some","seen","toLowerCase","push","parseAiBuildPlan","input","repairs","trimmed","cut","slice","brk","search","trimEnd","nullable","list","oneOf","allowed","includes","join","planRecord","entry","aiPlanRecordBase","root","map","index","names","Set","has","add","fieldIndex","filter","Boolean","rawSection","sectionIndex","section","isInteger","ref","refIndex","slot","credits","Math","max","floor","ok","plan","error","message","segments","segment","isAiPlanNewRef","startsWith","aiPlanCreateFor","find","AI_PLAN_DATASET_ITEM_OP","aiPlanFormDatasetNames","made","aiPlanItemFor","aiPlanSlugKey","replace","aiPlanEmbedsFor","target","embed","aiEmbedHostOf","URL","protocol","hostname","aiEmbedVideoKey","pathname","endsWith","searchParams","get","aiPlanUndeclaredRefs","found","undeclared","nameOf","forEach","screen","screenIndex","useIndex"],"mappings":"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"}
|
|
@@ -102,6 +102,13 @@ export interface AiPlanCapabilities {
|
|
|
102
102
|
* typed out twice, and rule 1 asks no component of them (AGL-3660).
|
|
103
103
|
*/
|
|
104
104
|
repeatsCompiled?: boolean;
|
|
105
|
+
/**
|
|
106
|
+
* The most datasets this job's plan keeps (AGL-3616): a site start builds
|
|
107
|
+
* at most a few. A plan naming more is settled in code before the rules
|
|
108
|
+
* read it (`aiSettlePlanDatasets`), never re-asked. Absent is no cap beyond
|
|
109
|
+
* `create.dataset.left`.
|
|
110
|
+
*/
|
|
111
|
+
datasetsMax?: number;
|
|
105
112
|
}
|
|
106
113
|
/** What a job of one kind builds from its own plan, where it builds only some creations. */
|
|
107
114
|
export interface AiPlanJobScope {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-plan-capabilities.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 AI_BUILD_PLAN_CREATE_KINDS,\n AI_BUILD_PLAN_CREATION_NOUNS,\n type AiBuildPlan,\n type AiBuildPlanCreateKind,\n} from './ai-build-plan'\n\n/**\n * What a plan may create on one site (AGL-3030): the workspace's plan and the\n * site's counts against its limits, narrowed to what the job itself builds.\n *\n * A plan that names a creation nothing can make is a dead end the member pays\n * for twice: once for the plan, and again for the plan they describe after\n * being told to make the creation by hand — which, on a workspace whose plan\n * does not include it, they cannot. So the plan step is TOLD what may be\n * created before it answers, and the plan rules refuse a creation outside it\n * with the one re-ask every other rule gets.\n *\n * ── Where the doctrine builds inline ─────────────────────────────────────\n *\n * The building doctrine makes a repeat a reusable component and a form a\n * saved form placed by id. A workspace whose plan keeps no reusable\n * components draws a list's repeated items in one section instead\n * (AGL-3071); `reusableComponents` is the one flag every validator reads for\n * that.\n *\n * A form is never drawn inline (AGL-3596): it is a saved form, counted\n * against the site's form allowance like one a member makes. Where the site\n * has none to place and may create none, the page places no form at all.\n *\n * ── Kept out of the cached prefix ────────────────────────────────────────\n *\n * The capabilities are per workspace and per site, so they ride the plan's\n * USER turn, never a system block: the doctrine's cached prefix stays one\n * entry for the platform. The doctrine states the rule once — create only\n * what the request says may be created, and build inline where it says the\n * workspace keeps no reusable components — and the request states the facts.\n *\n * This module imports nothing at runtime beyond the plan's own model, so the\n * validators, the plan step, the doors and their specs read one vocabulary.\n * What a workspace's plan and counts come to is read by\n * `readAiPlanCapabilities` in `jobs/ai-job-drafts.ts`, beside the band\n * arithmetic the create route and the draft writer share.\n */\n\n/** One creation kind, as a job on this site may make it or not. */\nexport interface AiPlanCreation {\n allowed: boolean\n /** How many more the site may hold; `null` when the plan counts none. */\n left: number | null\n /**\n * Why not, as a clause a sentence completes: \"this workspace's plan does\n * not include reusable components\". `null` when allowed.\n */\n reason: string | null\n}\n\nexport interface AiPlanCapabilities {\n /**\n * Whether the workspace keeps reusable components. Where it does not, a\n * list's repeated items are drawn in one section. Forms are not read off\n * it: whether one may be created is `create.form` (AGL-3596).\n */\n reusableComponents: boolean\n /** Every creation kind a plan may name, and whether this job may make one here. */\n create: Readonly<Record<AiBuildPlanCreateKind, AiPlanCreation>>\n /**\n * Whether the workspace spends the Free taste, whose monthly credits are a\n * wall (AGL-2925): a plan is then held to the sections the wall's worst\n * case pays for (AGL-3070). Absent is `false`.\n */\n freeTaste?: boolean\n /**\n * A Free workspace's site scaffold (AGL-3594): the most pages its plan may\n * build, which the plan step sets for a `site` job on the Free taste. The\n * Free wall then holds the plan to that many pages and to the sections a\n * SITE plan's worst case leaves room for. Absent for every other job.\n */\n freeSitePages?: number\n /**\n * Whether this job's pages are written in the compact layout language\n * (AGL-3660), whose compiler draws a section's repeated items itself: as\n * instances of a component the plan places there, or, with none, in their\n * compact form, a title over its text, which is smaller than a block rule 1\n * calls a repeat. A plan is then not asked for a component for a section's\n * repeated items (AGL-3616); a section two pages share still is one. The\n * plan step sets it for a `build` job. Absent is `false`.\n *\n * It is the plan half of one rule. The page half is the tree check's\n * `AiDoctrineTreeContext.repeatsCompiled`, which every page the layout\n * language compiles is checked with: where the site's design draws a\n * section's items as picture cards — larger than the compact form, and so\n * a block rule 1 would count — those cards are the compiler's, not a block\n * typed out twice, and rule 1 asks no component of them (AGL-3660).\n */\n repeatsCompiled?: boolean\n}\n\n/** What a job of one kind builds from its own plan, where it builds only some creations. */\nexport interface AiPlanJobScope {\n /** The job as a refusal names it: \"a page job\". */\n noun: string\n /** The creation kinds the job builds itself; any other is refused before the plan is kept. */\n creates: readonly AiBuildPlanCreateKind[]\n}\n\nconst ALLOWED: AiPlanCreation = { allowed: true, left: null, reason: null }\n\n/**\n * Capabilities that restrict nothing: what a plan is held to when no\n * workspace was read — an org-level job, an eval, a spec about something\n * else. The doctrine applies whole.\n */\nexport function aiUnrestrictedPlanCapabilities(): AiPlanCapabilities {\n return {\n reusableComponents: true,\n create: Object.fromEntries(\n AI_BUILD_PLAN_CREATE_KINDS.map((kind) => [kind, ALLOWED]),\n ) as Record<AiBuildPlanCreateKind, AiPlanCreation>,\n }\n}\n\n/**\n * The workspace's capabilities narrowed to what a job of one kind builds.\n * A creation the workspace itself cannot make keeps the workspace's reason,\n * which is the one a member can act on; one the job does not build says so.\n */\nexport function aiPlanCapabilitiesForJob(\n capabilities: AiPlanCapabilities,\n scope: AiPlanJobScope | null | undefined,\n): AiPlanCapabilities {\n if (!scope) return capabilities\n const create = { ...capabilities.create }\n for (const kind of AI_BUILD_PLAN_CREATE_KINDS) {\n if (!create[kind].allowed || scope.creates.includes(kind)) continue\n create[kind] = { allowed: false, left: null, reason: `${scope.noun} does not build one` }\n }\n return { ...capabilities, create }\n}\n\n/** \"a layout\", \"an email design\": a creation kind with its article. */\nexport function aiCreationNoun(kind: AiBuildPlanCreateKind): string {\n const noun = AI_BUILD_PLAN_CREATION_NOUNS[kind].noun\n return `${/^[aeiou]/.test(noun) ? 'an' : 'a'} ${noun}`\n}\n\n/**\n * The sentence a request states when the workspace keeps no reusable\n * components. It says a list's repeated items go in one section (AGL-3071):\n * \"a repeated item in its own section\" reads just as well as a section for\n * each item, which is how a live About page's four practice areas came to be\n * planned as four sections of one item.\n */\nexport const AI_PLAN_INLINE_SENTENCE =\n \"This workspace keeps no reusable components: draw a list's repeated items in one section.\"\n\n/**\n * The sentence a build's request states (AGL-3616): its pages are written in\n * the layout language, which draws a section's repeated items itself, so the\n * plan counts them in the section's `items` and needs no component for them.\n */\nexport const AI_PLAN_COMPILED_REPEATS_SENTENCE =\n \"A section's repeated items need no component here: count them in its items and the page draws them. A section two pages share is still one component.\"\n\n/**\n * The sentence a request states when the site has no saved form and this job\n * may create none (AGL-3596): the plan places no form, rather than one drawn\n * on the page with no saved form behind it.\n */\nexport const AI_PLAN_NO_FORM_SENTENCE =\n 'This site has no saved form and may create none: plan no form, and no section that collects answers.'\n\n/**\n * What the plan step's user turn says the job may create here: one line a\n * kind, and the inline sentence where the workspace keeps no reusable\n * components. Per workspace and per site, so it is never a system block.\n */\nexport function aiPlanCapabilityLines(capabilities: AiPlanCapabilities): string[] {\n const lines = ['What this job may create on this site:']\n for (const kind of AI_BUILD_PLAN_CREATE_KINDS) {\n const entry = capabilities.create[kind]\n const noun = AI_BUILD_PLAN_CREATION_NOUNS[kind].noun\n if (entry.allowed) {\n lines.push(`- ${noun}: yes${entry.left === null ? '' : `, ${entry.left} more`}`)\n } else {\n lines.push(`- ${noun}: no, because ${entry.reason ?? 'it cannot be made here'}`)\n }\n }\n if (!capabilities.reusableComponents) lines.push(AI_PLAN_INLINE_SENTENCE)\n return lines\n}\n\n/**\n * Whether a plan on this site can place a saved form at all (AGL-3596): the\n * site has one, or the job may create one. Absent capabilities restrict\n * nothing, as everywhere else.\n */\nexport function aiPlanCanPlaceForm(\n inventory: { forms: readonly unknown[] } | null,\n capabilities: Pick<AiPlanCapabilities, 'create'> | null,\n): boolean {\n if (inventory?.forms.length) return true\n return capabilities?.create.form.allowed !== false\n}\n\n/** What to do instead of a creation the job may not make, for the re-ask and the review. */\nfunction insteadOf(kind: AiBuildPlanCreateKind, capabilities: AiPlanCapabilities): string {\n switch (kind) {\n case 'component':\n return capabilities.reusableComponents\n ? 'Place a component the site already has.'\n : \"Draw a list's repeated items in one section instead.\"\n case 'form':\n return 'Place a form the site already has, or leave the form off the page.'\n case 'layout':\n return 'Put the page in a layout the site already has.'\n case 'template':\n return 'Build the page without a template.'\n case 'theme-change':\n return \"Use the colors the theme already has.\"\n case 'dataset':\n return 'Bind a dataset or collection the site already has, or keep the list short enough to write out.'\n case 'email':\n return 'Leave the email out of this job.'\n }\n}\n\n/** One creation a plan names that this job may not make here. */\nexport interface AiPlanUncreatable {\n kind: AiBuildPlanCreateKind\n name: string\n /** The plan path of the creation: `create[2]`. */\n path: string\n /** Why not, as a clause: \"this site has room for 1 more\". */\n reason: string\n /** One customer-safe sentence: what was refused, why, and what to do instead. */\n message: string\n}\n\n/**\n * Why the nth creation of a kind may not be made here, as a clause; `null`\n * when it may. A kind the site may make only so many more of refuses the ones\n * past that count, not the first of them.\n */\nfunction refusedReason(capability: AiPlanCreation, nth: number): string | null {\n const overCount = capability.allowed && capability.left !== null && nth > capability.left\n if (capability.allowed && !overCount) return null\n return overCount\n ? `this site has room for ${capability.left} more`\n : (capability.reason ?? 'it cannot be made here')\n}\n\n/** The creations a plan names that the capabilities refuse, in plan order. */\nexport function aiPlanUncreatable(\n plan: Pick<AiBuildPlan, 'create'>,\n capabilities: AiPlanCapabilities,\n): AiPlanUncreatable[] {\n const refused: AiPlanUncreatable[] = []\n const counted = new Map<AiBuildPlanCreateKind, number>()\n plan.create.forEach((entry, index) => {\n const nth = (counted.get(entry.kind) ?? 0) + 1\n counted.set(entry.kind, nth)\n const reason = refusedReason(capabilities.create[entry.kind], nth)\n if (reason === null) return\n refused.push({\n kind: entry.kind,\n name: entry.name,\n path: `create[${index}]`,\n reason,\n message: `The plan creates ${aiCreationNoun(entry.kind)} named \"${entry.name}\", and ${reason}. ${insteadOf(entry.kind, capabilities)}`,\n })\n })\n return refused\n}\n\n/**\n * Why the plan could not add one more creation of a kind here, and what to\n * do instead; `null` when it could (AGL-3040). Counted past the creations of\n * that kind the plan already names, as `aiPlanUncreatable` counts them, so a\n * plan told to declare a creation is never told to declare one the next\n * answer is refused for.\n */\nexport function aiPlanUncreatableKind(\n plan: Pick<AiBuildPlan, 'create'>,\n kind: AiBuildPlanCreateKind,\n capabilities: AiPlanCapabilities,\n): { reason: string; instead: string } | null {\n const planned = plan.create.filter((entry) => entry.kind === kind).length\n const reason = refusedReason(capabilities.create[kind], planned + 1)\n return reason === null ? null : { reason, instead: insteadOf(kind, capabilities) }\n}\n"],"names":["AI_BUILD_PLAN_CREATE_KINDS","AI_BUILD_PLAN_CREATION_NOUNS","ALLOWED","allowed","left","reason","aiUnrestrictedPlanCapabilities","reusableComponents","create","Object","fromEntries","map","kind","aiPlanCapabilitiesForJob","capabilities","scope","creates","includes","noun","aiCreationNoun","test","AI_PLAN_INLINE_SENTENCE","AI_PLAN_COMPILED_REPEATS_SENTENCE","AI_PLAN_NO_FORM_SENTENCE","aiPlanCapabilityLines","lines","entry","push","aiPlanCanPlaceForm","inventory","forms","length","form","insteadOf","refusedReason","capability","nth","overCount","aiPlanUncreatable","plan","refused","counted","Map","forEach","index","get","set","name","path","message","aiPlanUncreatableKind","planned","filter","instead"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-plan-capabilities.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 AI_BUILD_PLAN_CREATE_KINDS,\n AI_BUILD_PLAN_CREATION_NOUNS,\n type AiBuildPlan,\n type AiBuildPlanCreateKind,\n} from './ai-build-plan'\n\n/**\n * What a plan may create on one site (AGL-3030): the workspace's plan and the\n * site's counts against its limits, narrowed to what the job itself builds.\n *\n * A plan that names a creation nothing can make is a dead end the member pays\n * for twice: once for the plan, and again for the plan they describe after\n * being told to make the creation by hand — which, on a workspace whose plan\n * does not include it, they cannot. So the plan step is TOLD what may be\n * created before it answers, and the plan rules refuse a creation outside it\n * with the one re-ask every other rule gets.\n *\n * ── Where the doctrine builds inline ─────────────────────────────────────\n *\n * The building doctrine makes a repeat a reusable component and a form a\n * saved form placed by id. A workspace whose plan keeps no reusable\n * components draws a list's repeated items in one section instead\n * (AGL-3071); `reusableComponents` is the one flag every validator reads for\n * that.\n *\n * A form is never drawn inline (AGL-3596): it is a saved form, counted\n * against the site's form allowance like one a member makes. Where the site\n * has none to place and may create none, the page places no form at all.\n *\n * ── Kept out of the cached prefix ────────────────────────────────────────\n *\n * The capabilities are per workspace and per site, so they ride the plan's\n * USER turn, never a system block: the doctrine's cached prefix stays one\n * entry for the platform. The doctrine states the rule once — create only\n * what the request says may be created, and build inline where it says the\n * workspace keeps no reusable components — and the request states the facts.\n *\n * This module imports nothing at runtime beyond the plan's own model, so the\n * validators, the plan step, the doors and their specs read one vocabulary.\n * What a workspace's plan and counts come to is read by\n * `readAiPlanCapabilities` in `jobs/ai-job-drafts.ts`, beside the band\n * arithmetic the create route and the draft writer share.\n */\n\n/** One creation kind, as a job on this site may make it or not. */\nexport interface AiPlanCreation {\n allowed: boolean\n /** How many more the site may hold; `null` when the plan counts none. */\n left: number | null\n /**\n * Why not, as a clause a sentence completes: \"this workspace's plan does\n * not include reusable components\". `null` when allowed.\n */\n reason: string | null\n}\n\nexport interface AiPlanCapabilities {\n /**\n * Whether the workspace keeps reusable components. Where it does not, a\n * list's repeated items are drawn in one section. Forms are not read off\n * it: whether one may be created is `create.form` (AGL-3596).\n */\n reusableComponents: boolean\n /** Every creation kind a plan may name, and whether this job may make one here. */\n create: Readonly<Record<AiBuildPlanCreateKind, AiPlanCreation>>\n /**\n * Whether the workspace spends the Free taste, whose monthly credits are a\n * wall (AGL-2925): a plan is then held to the sections the wall's worst\n * case pays for (AGL-3070). Absent is `false`.\n */\n freeTaste?: boolean\n /**\n * A Free workspace's site scaffold (AGL-3594): the most pages its plan may\n * build, which the plan step sets for a `site` job on the Free taste. The\n * Free wall then holds the plan to that many pages and to the sections a\n * SITE plan's worst case leaves room for. Absent for every other job.\n */\n freeSitePages?: number\n /**\n * Whether this job's pages are written in the compact layout language\n * (AGL-3660), whose compiler draws a section's repeated items itself: as\n * instances of a component the plan places there, or, with none, in their\n * compact form, a title over its text, which is smaller than a block rule 1\n * calls a repeat. A plan is then not asked for a component for a section's\n * repeated items (AGL-3616); a section two pages share still is one. The\n * plan step sets it for a `build` job. Absent is `false`.\n *\n * It is the plan half of one rule. The page half is the tree check's\n * `AiDoctrineTreeContext.repeatsCompiled`, which every page the layout\n * language compiles is checked with: where the site's design draws a\n * section's items as picture cards — larger than the compact form, and so\n * a block rule 1 would count — those cards are the compiler's, not a block\n * typed out twice, and rule 1 asks no component of them (AGL-3660).\n */\n repeatsCompiled?: boolean\n /**\n * The most datasets this job's plan keeps (AGL-3616): a site start builds\n * at most a few. A plan naming more is settled in code before the rules\n * read it (`aiSettlePlanDatasets`), never re-asked. Absent is no cap beyond\n * `create.dataset.left`.\n */\n datasetsMax?: number\n}\n\n/** What a job of one kind builds from its own plan, where it builds only some creations. */\nexport interface AiPlanJobScope {\n /** The job as a refusal names it: \"a page job\". */\n noun: string\n /** The creation kinds the job builds itself; any other is refused before the plan is kept. */\n creates: readonly AiBuildPlanCreateKind[]\n}\n\nconst ALLOWED: AiPlanCreation = { allowed: true, left: null, reason: null }\n\n/**\n * Capabilities that restrict nothing: what a plan is held to when no\n * workspace was read — an org-level job, an eval, a spec about something\n * else. The doctrine applies whole.\n */\nexport function aiUnrestrictedPlanCapabilities(): AiPlanCapabilities {\n return {\n reusableComponents: true,\n create: Object.fromEntries(\n AI_BUILD_PLAN_CREATE_KINDS.map((kind) => [kind, ALLOWED]),\n ) as Record<AiBuildPlanCreateKind, AiPlanCreation>,\n }\n}\n\n/**\n * The workspace's capabilities narrowed to what a job of one kind builds.\n * A creation the workspace itself cannot make keeps the workspace's reason,\n * which is the one a member can act on; one the job does not build says so.\n */\nexport function aiPlanCapabilitiesForJob(\n capabilities: AiPlanCapabilities,\n scope: AiPlanJobScope | null | undefined,\n): AiPlanCapabilities {\n if (!scope) return capabilities\n const create = { ...capabilities.create }\n for (const kind of AI_BUILD_PLAN_CREATE_KINDS) {\n if (!create[kind].allowed || scope.creates.includes(kind)) continue\n create[kind] = { allowed: false, left: null, reason: `${scope.noun} does not build one` }\n }\n return { ...capabilities, create }\n}\n\n/** \"a layout\", \"an email design\": a creation kind with its article. */\nexport function aiCreationNoun(kind: AiBuildPlanCreateKind): string {\n const noun = AI_BUILD_PLAN_CREATION_NOUNS[kind].noun\n return `${/^[aeiou]/.test(noun) ? 'an' : 'a'} ${noun}`\n}\n\n/**\n * The sentence a request states when the workspace keeps no reusable\n * components. It says a list's repeated items go in one section (AGL-3071):\n * \"a repeated item in its own section\" reads just as well as a section for\n * each item, which is how a live About page's four practice areas came to be\n * planned as four sections of one item.\n */\nexport const AI_PLAN_INLINE_SENTENCE =\n \"This workspace keeps no reusable components: draw a list's repeated items in one section.\"\n\n/**\n * The sentence a build's request states (AGL-3616): its pages are written in\n * the layout language, which draws a section's repeated items itself, so the\n * plan counts them in the section's `items` and needs no component for them.\n */\nexport const AI_PLAN_COMPILED_REPEATS_SENTENCE =\n \"A section's repeated items need no component here: count them in its items and the page draws them. A section two pages share is still one component.\"\n\n/**\n * The sentence a request states when the site has no saved form and this job\n * may create none (AGL-3596): the plan places no form, rather than one drawn\n * on the page with no saved form behind it.\n */\nexport const AI_PLAN_NO_FORM_SENTENCE =\n 'This site has no saved form and may create none: plan no form, and no section that collects answers.'\n\n/**\n * What the plan step's user turn says the job may create here: one line a\n * kind, and the inline sentence where the workspace keeps no reusable\n * components. Per workspace and per site, so it is never a system block.\n */\nexport function aiPlanCapabilityLines(capabilities: AiPlanCapabilities): string[] {\n const lines = ['What this job may create on this site:']\n for (const kind of AI_BUILD_PLAN_CREATE_KINDS) {\n const entry = capabilities.create[kind]\n const noun = AI_BUILD_PLAN_CREATION_NOUNS[kind].noun\n if (entry.allowed) {\n lines.push(`- ${noun}: yes${entry.left === null ? '' : `, ${entry.left} more`}`)\n } else {\n lines.push(`- ${noun}: no, because ${entry.reason ?? 'it cannot be made here'}`)\n }\n }\n if (!capabilities.reusableComponents) lines.push(AI_PLAN_INLINE_SENTENCE)\n return lines\n}\n\n/**\n * Whether a plan on this site can place a saved form at all (AGL-3596): the\n * site has one, or the job may create one. Absent capabilities restrict\n * nothing, as everywhere else.\n */\nexport function aiPlanCanPlaceForm(\n inventory: { forms: readonly unknown[] } | null,\n capabilities: Pick<AiPlanCapabilities, 'create'> | null,\n): boolean {\n if (inventory?.forms.length) return true\n return capabilities?.create.form.allowed !== false\n}\n\n/** What to do instead of a creation the job may not make, for the re-ask and the review. */\nfunction insteadOf(kind: AiBuildPlanCreateKind, capabilities: AiPlanCapabilities): string {\n switch (kind) {\n case 'component':\n return capabilities.reusableComponents\n ? 'Place a component the site already has.'\n : \"Draw a list's repeated items in one section instead.\"\n case 'form':\n return 'Place a form the site already has, or leave the form off the page.'\n case 'layout':\n return 'Put the page in a layout the site already has.'\n case 'template':\n return 'Build the page without a template.'\n case 'theme-change':\n return \"Use the colors the theme already has.\"\n case 'dataset':\n return 'Bind a dataset or collection the site already has, or keep the list short enough to write out.'\n case 'email':\n return 'Leave the email out of this job.'\n }\n}\n\n/** One creation a plan names that this job may not make here. */\nexport interface AiPlanUncreatable {\n kind: AiBuildPlanCreateKind\n name: string\n /** The plan path of the creation: `create[2]`. */\n path: string\n /** Why not, as a clause: \"this site has room for 1 more\". */\n reason: string\n /** One customer-safe sentence: what was refused, why, and what to do instead. */\n message: string\n}\n\n/**\n * Why the nth creation of a kind may not be made here, as a clause; `null`\n * when it may. A kind the site may make only so many more of refuses the ones\n * past that count, not the first of them.\n */\nfunction refusedReason(capability: AiPlanCreation, nth: number): string | null {\n const overCount = capability.allowed && capability.left !== null && nth > capability.left\n if (capability.allowed && !overCount) return null\n return overCount\n ? `this site has room for ${capability.left} more`\n : (capability.reason ?? 'it cannot be made here')\n}\n\n/** The creations a plan names that the capabilities refuse, in plan order. */\nexport function aiPlanUncreatable(\n plan: Pick<AiBuildPlan, 'create'>,\n capabilities: AiPlanCapabilities,\n): AiPlanUncreatable[] {\n const refused: AiPlanUncreatable[] = []\n const counted = new Map<AiBuildPlanCreateKind, number>()\n plan.create.forEach((entry, index) => {\n const nth = (counted.get(entry.kind) ?? 0) + 1\n counted.set(entry.kind, nth)\n const reason = refusedReason(capabilities.create[entry.kind], nth)\n if (reason === null) return\n refused.push({\n kind: entry.kind,\n name: entry.name,\n path: `create[${index}]`,\n reason,\n message: `The plan creates ${aiCreationNoun(entry.kind)} named \"${entry.name}\", and ${reason}. ${insteadOf(entry.kind, capabilities)}`,\n })\n })\n return refused\n}\n\n/**\n * Why the plan could not add one more creation of a kind here, and what to\n * do instead; `null` when it could (AGL-3040). Counted past the creations of\n * that kind the plan already names, as `aiPlanUncreatable` counts them, so a\n * plan told to declare a creation is never told to declare one the next\n * answer is refused for.\n */\nexport function aiPlanUncreatableKind(\n plan: Pick<AiBuildPlan, 'create'>,\n kind: AiBuildPlanCreateKind,\n capabilities: AiPlanCapabilities,\n): { reason: string; instead: string } | null {\n const planned = plan.create.filter((entry) => entry.kind === kind).length\n const reason = refusedReason(capabilities.create[kind], planned + 1)\n return reason === null ? null : { reason, instead: insteadOf(kind, capabilities) }\n}\n"],"names":["AI_BUILD_PLAN_CREATE_KINDS","AI_BUILD_PLAN_CREATION_NOUNS","ALLOWED","allowed","left","reason","aiUnrestrictedPlanCapabilities","reusableComponents","create","Object","fromEntries","map","kind","aiPlanCapabilitiesForJob","capabilities","scope","creates","includes","noun","aiCreationNoun","test","AI_PLAN_INLINE_SENTENCE","AI_PLAN_COMPILED_REPEATS_SENTENCE","AI_PLAN_NO_FORM_SENTENCE","aiPlanCapabilityLines","lines","entry","push","aiPlanCanPlaceForm","inventory","forms","length","form","insteadOf","refusedReason","capability","nth","overCount","aiPlanUncreatable","plan","refused","counted","Map","forEach","index","get","set","name","path","message","aiPlanUncreatableKind","planned","filter","instead"],"mappings":"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"}
|
|
@@ -172,6 +172,19 @@ export interface AiFreeCreditsLeft {
|
|
|
172
172
|
left: number;
|
|
173
173
|
total: number;
|
|
174
174
|
resetsOn: string;
|
|
175
|
+
/**
|
|
176
|
+
* Both meters `left` is the lesser of, in credits net of give-backs, for the
|
|
177
|
+
* usage strip's live read (AGL-3722): the owner's Free allowance across
|
|
178
|
+
* their Free workspaces (`account`, `null` with no owner) and this
|
|
179
|
+
* workspace's band.
|
|
180
|
+
*/
|
|
181
|
+
used?: {
|
|
182
|
+
account: number | null;
|
|
183
|
+
org: number;
|
|
184
|
+
orgBand: number | null;
|
|
185
|
+
/** The strip's warn state for these figures, worked out where the strip's own rule is not shipped to the browser. */
|
|
186
|
+
state: 'ok' | 'warn' | 'capped';
|
|
187
|
+
};
|
|
175
188
|
}
|
|
176
189
|
/** The first UTC day of the month after `now`, `YYYY-MM-DD`: when the Free credits come back. */
|
|
177
190
|
export declare function aiFreeCreditsResetOn(now: Date): string;
|
|
@@ -517,10 +530,14 @@ export declare function aiSiteWordsSaidAnything(words: AiSiteWords): boolean;
|
|
|
517
530
|
export declare function aiSiteWords(inputs: Readonly<Record<string, unknown>> | null | undefined): AiSiteWords;
|
|
518
531
|
/**
|
|
519
532
|
* What a scaffold builds for itself: the layout its pages render inside, the
|
|
520
|
-
* form they place,
|
|
521
|
-
* section
|
|
533
|
+
* form they place, the palette suggestion a member applies in the Theme
|
|
534
|
+
* section, and the datasets its structured content is kept in (AGL-3616),
|
|
535
|
+
* which the data plugin writes. Anything else a plan asks to create is a job
|
|
536
|
+
* of its own.
|
|
522
537
|
*/
|
|
523
538
|
export declare const AI_SITE_CREATE_KINDS: readonly AiBuildPlanCreateKind[];
|
|
539
|
+
/** The most datasets one site start creates (AGL-3616). */
|
|
540
|
+
export declare const AI_SITE_DATASETS_MAX = 3;
|
|
524
541
|
/** The creations a scaffold cannot build, in the order the plan lists them, each named once. */
|
|
525
542
|
export declare function aiSitePlanPrerequisites(plan: AiBuildPlan): Array<{
|
|
526
543
|
kind: AiBuildPlanCreateKind;
|
|
@@ -15,7 +15,7 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
|
15
15
|
* See the License for the specific language governing permissions and
|
|
16
16
|
* limitations under the License.
|
|
17
17
|
*/ import { aiSiteKind } from "./ai-site-kinds.js";
|
|
18
|
-
import { AI_BUILD_PLAN_CREATION_NOUNS, aiPlanUndeclaredRefs } from "./ai-build-plan.js";
|
|
18
|
+
import { AI_BUILD_PLAN_CREATION_NOUNS, aiPlanFormDatasetNames, aiPlanUndeclaredRefs } from "./ai-build-plan.js";
|
|
19
19
|
import { AI_JOB_CREATE_KINDS } from "./ai-job-creations.js";
|
|
20
20
|
import { AI_PAGE_CREATE_KINDS } from "./ai-page-job.js";
|
|
21
21
|
import { FREE_AI_TASTE_CREDITS_PER_MONTH } from "../plan-entitlements.js";
|
|
@@ -573,13 +573,17 @@ const ID_CHARS = /^[A-Za-z0-9_-]{1,64}$/;
|
|
|
573
573
|
}
|
|
574
574
|
/**
|
|
575
575
|
* What a scaffold builds for itself: the layout its pages render inside, the
|
|
576
|
-
* form they place,
|
|
577
|
-
* section
|
|
576
|
+
* form they place, the palette suggestion a member applies in the Theme
|
|
577
|
+
* section, and the datasets its structured content is kept in (AGL-3616),
|
|
578
|
+
* which the data plugin writes. Anything else a plan asks to create is a job
|
|
579
|
+
* of its own.
|
|
578
580
|
*/ export const AI_SITE_CREATE_KINDS = [
|
|
579
581
|
'layout',
|
|
580
582
|
'form',
|
|
581
|
-
'theme-change'
|
|
583
|
+
'theme-change',
|
|
584
|
+
'dataset'
|
|
582
585
|
];
|
|
586
|
+
/** The most datasets one site start creates (AGL-3616). */ export const AI_SITE_DATASETS_MAX = 3;
|
|
583
587
|
/** The creations a scaffold cannot build, in the order the plan lists them, each named once. */ export function aiSitePlanPrerequisites(plan) {
|
|
584
588
|
const seen = new Set();
|
|
585
589
|
const prerequisites = [];
|
|
@@ -632,6 +636,10 @@ const ID_CHARS = /^[A-Za-z0-9_-]{1,64}$/;
|
|
|
632
636
|
if (!screens.some((screen)=>screen.nav)) {
|
|
633
637
|
return 'No page in this plan is in the site navigation. Describe the site again.';
|
|
634
638
|
}
|
|
639
|
+
const datasets = plan.create.filter((entry)=>entry.kind === 'dataset').length;
|
|
640
|
+
if (datasets > AI_SITE_DATASETS_MAX) {
|
|
641
|
+
return `This plan creates ${datasets} datasets, and a site scaffold creates at most ${AI_SITE_DATASETS_MAX}. Keep the others' items on their pages.`;
|
|
642
|
+
}
|
|
635
643
|
return null;
|
|
636
644
|
}
|
|
637
645
|
/**
|
|
@@ -646,6 +654,13 @@ const ID_CHARS = /^[A-Za-z0-9_-]{1,64}$/;
|
|
|
646
654
|
const listed = parts.length === 1 ? parts[0] : `${parts.slice(0, -1).join(', ')} and ${parts[parts.length - 1]}`;
|
|
647
655
|
return `This site needs what the workspace does not have yet. Create ${listed}, then describe the site again.`;
|
|
648
656
|
}
|
|
657
|
+
/**
|
|
658
|
+
* Whether a creation is built with no model: a dataset a form of the plan
|
|
659
|
+
* writes to (AGL-3616) starts empty, designed from its planned fields, so it
|
|
660
|
+
* is no pass of the estimate.
|
|
661
|
+
*/ function aiPlanCreationIsModelFree(plan, entry) {
|
|
662
|
+
return entry.kind === 'dataset' && aiPlanFormDatasetNames(plan).has(entry.name.trim().toLowerCase());
|
|
663
|
+
}
|
|
649
664
|
/**
|
|
650
665
|
* The passes a plan implies: one per section of every screen, one more per
|
|
651
666
|
* screen for its search listing and its draft, and one for each thing the
|
|
@@ -654,7 +669,7 @@ const ID_CHARS = /^[A-Za-z0-9_-]{1,64}$/;
|
|
|
654
669
|
var _options_creates;
|
|
655
670
|
const screens = plan.screens.reduce((total, screen)=>total + screen.sections.length + 1, 0);
|
|
656
671
|
const creates = (_options_creates = options.creates) != null ? _options_creates : AI_SITE_CREATE_KINDS;
|
|
657
|
-
const creations = plan.create.filter((entry)=>creates.includes(entry.kind)).length;
|
|
672
|
+
const creations = plan.create.filter((entry)=>creates.includes(entry.kind) && !aiPlanCreationIsModelFree(plan, entry)).length;
|
|
658
673
|
return screens + creations + (options.welcomeEmail ? 1 : 0);
|
|
659
674
|
}
|
|
660
675
|
/**
|
|
@@ -723,7 +738,7 @@ const ID_CHARS = /^[A-Za-z0-9_-]{1,64}$/;
|
|
|
723
738
|
const screenRange = options.standalonePages ? aiStandaloneScreenCreditRange : aiScreenCreditRange;
|
|
724
739
|
for (const screen of plan.screens)range = aiCreditRangeAdd(range, screenRange(screen.sections.length));
|
|
725
740
|
for (const entry of plan.create){
|
|
726
|
-
if (!creates.includes(entry.kind)) continue;
|
|
741
|
+
if (!creates.includes(entry.kind) || aiPlanCreationIsModelFree(plan, entry)) continue;
|
|
727
742
|
range = aiCreditRangeAdd(range, aiCreditRangeOf(aiCreationMeasuredCredits(entry.kind, {
|
|
728
743
|
standalone: options.standalonePages
|
|
729
744
|
}), AI_SITE_PASS_CREDITS));
|