@aglyn/plugins-ai 1.0.0-beta.147 → 1.0.0-beta.150
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-brief-dialog.component.js +7 -1
- package/src/lib/components/ai-brief-dialog.component.js.map +1 -1
- package/src/lib/jobs/ai-job-page-sections.d.ts +32 -3
- package/src/lib/jobs/ai-job-page-sections.js +33 -3
- package/src/lib/jobs/ai-job-page-sections.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-conformance.d.ts +33 -0
- package/src/lib/jobs/ai-job-plan-conformance.js +94 -1
- package/src/lib/jobs/ai-job-plan-conformance.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-step.d.ts +14 -0
- package/src/lib/jobs/ai-job-plan-step.js +24 -0
- package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
- package/src/lib/jobs/ai-job-template-step.js +16 -2
- package/src/lib/jobs/ai-job-template-step.js.map +1 -1
- package/src/lib/model/ai-build-plan.d.ts +6 -3
- package/src/lib/model/ai-build-plan.js.map +1 -1
- package/src/lib/runtime/ai-products-generation.js +1 -1
- package/src/lib/runtime/ai-products-generation.js.map +1 -1
- package/src/lib/usage/ai-usage-by-user.d.ts +25 -3
- package/src/lib/usage/ai-usage-by-user.js +31 -7
- package/src/lib/usage/ai-usage-by-user.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aglyn/plugins-ai",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.150",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"homepage": "https://aglyn.com",
|
|
6
6
|
"repository": {
|
|
@@ -25,22 +25,22 @@
|
|
|
25
25
|
"./package.json": "./package.json"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@aglyn/aglyn": "1.0.0-beta.
|
|
29
|
-
"@aglyn/shared-data-enums": "1.0.0-beta.
|
|
30
|
-
"@aglyn/shared-data-mdi": "1.0.0-beta.
|
|
31
|
-
"@aglyn/shared-data-types": "1.0.0-beta.
|
|
32
|
-
"@aglyn/shared-ui-email-campaigns": "1.0.0-beta.
|
|
33
|
-
"@aglyn/shared-ui-jsx": "1.0.0-beta.
|
|
34
|
-
"@aglyn/shared-ui-snackstack": "1.0.0-beta.
|
|
35
|
-
"@aglyn/shared-ui-theme": "1.0.0-beta.
|
|
36
|
-
"@aglyn/shared-util-email": "1.0.0-beta.
|
|
37
|
-
"@aglyn/shared-util-errors": "1.0.0-beta.
|
|
38
|
-
"@aglyn/shared-util-http": "1.0.0-beta.
|
|
39
|
-
"@aglyn/shared-util-timestamp": "1.0.0-beta.
|
|
40
|
-
"@aglyn/shared-util-tools": "1.0.0-beta.
|
|
41
|
-
"@aglyn/tenant-data-admin": "1.0.0-beta.
|
|
42
|
-
"@aglyn/tenant-feature-instance": "1.0.0-beta.
|
|
43
|
-
"@aglyn/tenant-runtime": "1.0.0-beta.
|
|
28
|
+
"@aglyn/aglyn": "1.0.0-beta.150",
|
|
29
|
+
"@aglyn/shared-data-enums": "1.0.0-beta.150",
|
|
30
|
+
"@aglyn/shared-data-mdi": "1.0.0-beta.150",
|
|
31
|
+
"@aglyn/shared-data-types": "1.0.0-beta.150",
|
|
32
|
+
"@aglyn/shared-ui-email-campaigns": "1.0.0-beta.150",
|
|
33
|
+
"@aglyn/shared-ui-jsx": "1.0.0-beta.150",
|
|
34
|
+
"@aglyn/shared-ui-snackstack": "1.0.0-beta.150",
|
|
35
|
+
"@aglyn/shared-ui-theme": "1.0.0-beta.150",
|
|
36
|
+
"@aglyn/shared-util-email": "1.0.0-beta.150",
|
|
37
|
+
"@aglyn/shared-util-errors": "1.0.0-beta.150",
|
|
38
|
+
"@aglyn/shared-util-http": "1.0.0-beta.150",
|
|
39
|
+
"@aglyn/shared-util-timestamp": "1.0.0-beta.150",
|
|
40
|
+
"@aglyn/shared-util-tools": "1.0.0-beta.150",
|
|
41
|
+
"@aglyn/tenant-data-admin": "1.0.0-beta.150",
|
|
42
|
+
"@aglyn/tenant-feature-instance": "1.0.0-beta.150",
|
|
43
|
+
"@aglyn/tenant-runtime": "1.0.0-beta.150",
|
|
44
44
|
"@swc/helpers": "0.5.23"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
@@ -230,7 +230,13 @@ export function AiBriefDialog({ kind, open, onClose, orgId, hostId, user }) {
|
|
|
230
230
|
children: [
|
|
231
231
|
/*#__PURE__*/ _jsx(ChipGroup, {
|
|
232
232
|
id: "ai-template-subject-label",
|
|
233
|
-
|
|
233
|
+
// Marked required because it IS (AGL-3143 §12): "Plan the
|
|
234
|
+
// template" stays disabled until one is picked, and the page
|
|
235
|
+
// dialog's chip group beside it reads "(optional)" — so an
|
|
236
|
+
// unmarked label here read as optional too, and a member who
|
|
237
|
+
// wrote a brief and stopped had nothing telling them why the
|
|
238
|
+
// button would not light.
|
|
239
|
+
label: "One page for each (required)",
|
|
234
240
|
options: AI_TEMPLATE_SUBJECTS.map((subject)=>({
|
|
235
241
|
id: subject,
|
|
236
242
|
label: AI_TEMPLATE_SUBJECT_LABELS[subject]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-brief-dialog.component.tsx"],"sourcesContent":["'use client'\n\n/**\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 { lockdownRefusalText, parseLockdownRefusal } from '@aglyn/aglyn'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport {\n Alert,\n Button,\n Chip,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport type { AiJobKind } from '../model/ai-jobs.types'\nimport { AI_PAGE_TYPES, type AiPageType } from '../model/ai-page-job'\nimport {\n AI_TEMPLATE_SUBJECTS,\n parseAiTemplateJobInputs,\n type AiTemplateSubject,\n} from '../model/ai-template-subjects'\nimport { AiTemplateCollectionField } from './ai-template-collection-field.component'\n\n/**\n * The brief dialog every \"Describe it\" opens (AGL-2907, AGL-3043, AGL-3051):\n * a page from the Screens page and from the Assist panel's AI jobs, a page\n * template from Templates, a layout from Layouts, a form from Forms and a\n * reusable component from Components.\n *\n * One text box, and whatever the kind's door reads beside it, start one job of\n * that kind: a page's optional page type; a template's subject and, for a\n * collection entry's page, its collection. A layout, a form and a component\n * read nothing but the brief and the site. The job plans first and waits in\n * AI jobs, where the member confirms the plan and opens the draft once it is\n * built; nothing here builds or writes anything.\n */\n\n/** The longest brief a job admits. */\nconst BRIEF_MAX_CHARS = 4_000\n\n/** The job kinds a member describes from a console page. */\nexport type AiBriefKind = Extract<\n AiJobKind,\n 'page' | 'template' | 'layout' | 'form' | 'component'\n>\n\n/** What the dialog says for one kind. */\nexport interface AiBriefCopy {\n title: string\n /** The brief box's label. */\n briefLabel: string\n placeholder: string\n /** What happens after the member starts the job, under the fields. */\n next: string\n /** The action that starts the job. */\n submit: string\n /** What the dialog says once the job exists. */\n started: string\n /** What it says when the door gave no reason, or could not be reached. */\n failed: string\n}\n\nexport const AI_BRIEF_COPY: Readonly<Record<AiBriefKind, AiBriefCopy>> = {\n page: {\n title: 'Describe a page',\n briefLabel: 'What is the page for?',\n placeholder:\n 'A landing page for our spring roof inspection offer, with the quote request form',\n next:\n 'A plan comes first, and nothing is built until you confirm it. The page uses your ' +\n 'theme and what the site already has, and the plan lists anything the job builds first.',\n submit: 'Plan the page',\n started:\n 'The page is being planned. Open AI jobs in the Assist panel to review the plan and ' +\n 'confirm it. The page is built as an unpublished draft.',\n failed: 'The page could not be started. Try again.',\n },\n template: {\n title: 'Describe a page template',\n briefLabel: 'What should each page show?',\n placeholder:\n 'A page for each of our team members: their name and photo, their role, a short ' +\n 'biography and a way to get in touch',\n next:\n 'A plan comes first, and nothing is built until you confirm it. What changes from one ' +\n 'page to the next, such as a name or a photo, is filled in from each record rather ' +\n 'than typed.',\n submit: 'Plan the template',\n started:\n 'The template is being planned. Open AI jobs in the Assist panel to review the plan ' +\n 'and confirm it. The template is built as a draft in your library, and nothing on ' +\n 'your site uses it until you do.',\n failed: 'The template could not be started. Try again.',\n },\n layout: {\n title: 'Describe a layout',\n briefLabel: 'What should the layout hold?',\n placeholder:\n 'A header with our name and links to Services, About and Contact, and a footer with ' +\n 'our address and opening hours',\n next:\n 'A plan comes first, and nothing is built until you confirm it. The layout links your ' +\n 'screens, and places your navigation or menu component when the site has one.',\n submit: 'Plan the layout',\n started:\n 'The layout is being planned. Open AI jobs in the Assist panel to review the plan and ' +\n 'confirm it. The layout is built as a draft, and no screen uses it until you assign it.',\n failed: 'The layout could not be started. Try again.',\n },\n form: {\n title: 'Describe a form',\n briefLabel: 'What is the form for?',\n placeholder:\n 'A quote request form for a roofing company, asking what needs fixing and the best ' +\n 'time to call',\n next:\n 'A plan comes first, and nothing is built until you confirm it. The form’s fields, its ' +\n 'marketing consent and where each submission goes are planned together.',\n submit: 'Plan the form',\n started:\n 'The form is being planned. Open AI jobs in the Assist panel to review the plan and ' +\n 'confirm it. The form is built as a draft, and it collects nothing until you place it ' +\n 'on a screen.',\n failed: 'The form could not be started. Try again.',\n },\n component: {\n title: 'Describe a reusable component',\n briefLabel: 'What should the component show?',\n placeholder:\n 'A testimonial card with the customer’s name, their role, their quote and a photo',\n next:\n 'A plan comes first, and nothing is built until you confirm it. What each page can ' +\n 'change, such as a name or a photo, becomes a property of the component.',\n submit: 'Plan the component',\n started:\n 'The component is being planned. Open AI jobs in the Assist panel to review the plan ' +\n 'and confirm it. The component is built as a draft, and no page shows it until you ' +\n 'insert it.',\n failed: 'The component could not be started. Try again.',\n },\n}\n\n/** Each template subject as its chip names it: what one page is drawn for. */\nexport const AI_TEMPLATE_SUBJECT_LABELS: Readonly<Record<AiTemplateSubject, string>> = {\n entry: 'Collection entry',\n product: 'Product',\n author: 'Author',\n}\n\n/** What a member picked beside the brief; each kind reads only its own. */\nexport interface AiBriefChoice {\n /** A page's type, or `null` for none. */\n pageType: AiPageType | null\n /** What a template's pages are drawn for, or `null` until one is picked. */\n subject: AiTemplateSubject | null\n /** A collection entry template's collection, or `null` until one is picked. */\n collectionId: string | null\n}\n\nexport const AI_BRIEF_NO_CHOICE: AiBriefChoice = {\n pageType: null,\n subject: null,\n collectionId: null,\n}\n\n/**\n * The job's `inputs` for what the member picked, or `null` while the kind's\n * door would refuse them.\n *\n * A template's are read by the parser its door reads, so the dialog cannot\n * send a subject the door does not know or an entry page with no collection.\n * A page's type is optional. A layout, a form and a component read no inputs\n * at all (AGL-2909, AGL-2913, AGL-2908), so theirs are empty.\n */\nexport function aiBriefJobInputs(\n kind: AiBriefKind,\n choice: AiBriefChoice,\n): Record<string, string> | null {\n if (kind === 'page') return choice.pageType ? { pageType: choice.pageType } : {}\n if (kind === 'template') {\n const inputs = parseAiTemplateJobInputs({\n subject: choice.subject,\n collectionId: choice.collectionId,\n })\n if (typeof inputs === 'string') return null\n return inputs.collectionId\n ? { subject: inputs.subject, collectionId: inputs.collectionId }\n : { subject: inputs.subject }\n }\n return {}\n}\n\nexport interface AiBriefDialogProps {\n /** The job the brief starts. */\n kind: AiBriefKind\n open: boolean\n onClose: () => void\n orgId: string | undefined\n hostId: string\n /** Who is signed in, whose token the request carries. */\n user: Parameters<typeof authorizedFetch>[0]\n}\n\nexport function AiBriefDialog({ kind, open, onClose, orgId, hostId, user }: AiBriefDialogProps) {\n const copy = AI_BRIEF_COPY[kind]\n // Held in a ref so the request reads WHO is signed in, and nothing keys on\n // the identity of the object that says so.\n const userRef = useRef(user)\n userRef.current = user\n const [brief, setBrief] = useState('')\n const [choice, setChoice] = useState<AiBriefChoice>(AI_BRIEF_NO_CHOICE)\n const [busy, setBusy] = useState(false)\n const [notice, setNotice] = useState<string | null>(null)\n const [started, setStarted] = useState(false)\n\n useEffect(() => {\n if (!open) return\n setNotice(null)\n setStarted(false)\n }, [open])\n\n const pick = useCallback(\n (patch: Partial<AiBriefChoice>) => setChoice((current) => ({ ...current, ...patch })),\n [],\n )\n const ready = aiBriefJobInputs(kind, choice) !== null\n\n const start = useCallback(async () => {\n const inputs = aiBriefJobInputs(kind, choice)\n if (!orgId || !brief.trim() || !inputs) return\n setBusy(true)\n setNotice(null)\n try {\n const response = await authorizedFetch(userRef.current, '/api/ai/jobs', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ orgId, hostId, kind, brief: brief.trim(), inputs }),\n })\n const payload = await response.json().catch(() => null)\n if (!response.ok) {\n const locked = parseLockdownRefusal(response.status, payload)\n setNotice(locked ? lockdownRefusalText(locked) : String(payload?.error ?? copy.failed))\n return\n }\n setStarted(true)\n setBrief('')\n setChoice(AI_BRIEF_NO_CHOICE)\n } catch {\n setNotice(copy.failed)\n } finally {\n setBusy(false)\n }\n }, [orgId, hostId, kind, brief, choice, copy])\n\n return (\n <Dialog open={open} onClose={busy ? undefined : onClose} fullWidth maxWidth=\"sm\">\n <DialogTitle>{copy.title}</DialogTitle>\n <DialogContent>\n {started ? (\n <Alert severity=\"success\">{copy.started}</Alert>\n ) : (\n <Stack spacing={2} sx={{ pt: 1 }}>\n <TextField\n label={copy.briefLabel}\n placeholder={copy.placeholder}\n multiline\n minRows={4}\n value={brief}\n onChange={(event) => setBrief(event.target.value.slice(0, BRIEF_MAX_CHARS))}\n helperText={`${brief.length.toLocaleString('en-US')} / ${BRIEF_MAX_CHARS.toLocaleString('en-US')}`}\n disabled={busy}\n autoFocus\n />\n {kind === 'page' ? (\n <ChipGroup\n id=\"ai-page-type-label\"\n label=\"Page type (optional)\"\n options={AI_PAGE_TYPES.map((type) => ({ id: type.id, label: type.label }))}\n value={choice.pageType}\n // Optional: pressing the pressed type again sends none.\n onPress={(id) =>\n pick({ pageType: choice.pageType === id ? null : (id as AiPageType) })\n }\n disabled={busy}\n />\n ) : null}\n {kind === 'template' ? (\n <>\n <ChipGroup\n id=\"ai-template-subject-label\"\n label=\"One page for each\"\n options={AI_TEMPLATE_SUBJECTS.map((subject) => ({\n id: subject,\n label: AI_TEMPLATE_SUBJECT_LABELS[subject],\n }))}\n value={choice.subject}\n // Required, so a press picks and never clears.\n onPress={(id) => pick({ subject: id as AiTemplateSubject })}\n disabled={busy}\n />\n {choice.subject === 'entry' ? (\n <AiTemplateCollectionField\n hostId={hostId}\n value={choice.collectionId}\n onChange={(collectionId) => pick({ collectionId })}\n disabled={busy}\n />\n ) : null}\n </>\n ) : null}\n <Typography variant=\"body2\" color=\"text.secondary\">\n {copy.next}\n </Typography>\n {notice ? <Alert severity=\"warning\">{notice}</Alert> : null}\n </Stack>\n )}\n </DialogContent>\n <DialogActions>\n <Button onClick={onClose} disabled={busy}>\n {started ? 'Close' : 'Cancel'}\n </Button>\n {started ? null : (\n <Button\n variant=\"contained\"\n onClick={() => void start()}\n disabled={busy || !orgId || !brief.trim() || !ready}\n >\n {copy.submit}\n </Button>\n )}\n </DialogActions>\n </Dialog>\n )\n}\n\n/** A labeled row of chips, each a toggle button that says whether it is pressed. */\nfunction ChipGroup(props: {\n id: string\n label: string\n options: ReadonlyArray<{ id: string; label: string }>\n value: string | null\n onPress: (id: string) => void\n disabled: boolean\n}) {\n const { id, label, options, value, onPress, disabled } = props\n return (\n <Stack spacing={1}>\n <Typography variant=\"body2\" color=\"text.secondary\" id={id}>\n {label}\n </Typography>\n <Stack\n direction=\"row\"\n useFlexGap\n role=\"group\"\n aria-labelledby={id}\n sx={{ flexWrap: 'wrap', gap: 1 }}\n >\n {options.map((option) => (\n <Chip\n key={option.id}\n label={option.label}\n color={value === option.id ? 'primary' : 'default'}\n variant={value === option.id ? 'filled' : 'outlined'}\n onClick={() => onPress(option.id)}\n aria-pressed={value === option.id}\n disabled={disabled}\n />\n ))}\n </Stack>\n </Stack>\n )\n}\n\nexport default AiBriefDialog\n"],"names":["lockdownRefusalText","parseLockdownRefusal","authorizedFetch","Alert","Button","Chip","Dialog","DialogActions","DialogContent","DialogTitle","Stack","TextField","Typography","useCallback","useEffect","useRef","useState","AI_PAGE_TYPES","AI_TEMPLATE_SUBJECTS","parseAiTemplateJobInputs","AiTemplateCollectionField","BRIEF_MAX_CHARS","AI_BRIEF_COPY","page","title","briefLabel","placeholder","next","submit","started","failed","template","layout","form","component","AI_TEMPLATE_SUBJECT_LABELS","entry","product","author","AI_BRIEF_NO_CHOICE","pageType","subject","collectionId","aiBriefJobInputs","kind","choice","inputs","AiBriefDialog","open","onClose","orgId","hostId","user","copy","userRef","current","brief","setBrief","setChoice","busy","setBusy","notice","setNotice","setStarted","pick","patch","ready","start","trim","response","method","headers","body","JSON","stringify","payload","json","catch","ok","locked","status","String","error","undefined","fullWidth","maxWidth","severity","spacing","sx","pt","label","multiline","minRows","value","onChange","event","target","slice","helperText","length","toLocaleString","disabled","autoFocus","ChipGroup","id","options","map","type","onPress","variant","color","onClick","props","direction","useFlexGap","role","aria-labelledby","flexWrap","gap","option","aria-pressed"],"mappings":"AAAA;;;AAEA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,mBAAmB,EAAEC,oBAAoB,QAAQ,eAAc;AACxE,SAASC,eAAe,QAAQ,2CAA0C;AAC1E,SACEC,KAAK,EACLC,MAAM,EACNC,IAAI,EACJC,MAAM,EACNC,aAAa,EACbC,aAAa,EACbC,WAAW,EACXC,KAAK,EACLC,SAAS,EACTC,UAAU,QACL,gBAAe;AACtB,SAASC,WAAW,EAAEC,SAAS,EAAEC,MAAM,EAAEC,QAAQ,QAAQ,QAAO;AAEhE,SAASC,aAAa,QAAyB,0BAAsB;AACrE,SACEC,oBAAoB,EACpBC,wBAAwB,QAEnB,mCAA+B;AACtC,SAASC,yBAAyB,QAAQ,8CAA0C;AAEpF;;;;;;;;;;;;CAYC,GAED,oCAAoC,GACpC,MAAMC,kBAAkB;AAwBxB,OAAO,MAAMC,gBAA4D;IACvEC,MAAM;QACJC,OAAO;QACPC,YAAY;QACZC,aACE;QACFC,MACE,uFACA;QACFC,QAAQ;QACRC,SACE,wFACA;QACFC,QAAQ;IACV;IACAC,UAAU;QACRP,OAAO;QACPC,YAAY;QACZC,aACE,oFACA;QACFC,MACE,0FACA,uFACA;QACFC,QAAQ;QACRC,SACE,wFACA,sFACA;QACFC,QAAQ;IACV;IACAE,QAAQ;QACNR,OAAO;QACPC,YAAY;QACZC,aACE,wFACA;QACFC,MACE,0FACA;QACFC,QAAQ;QACRC,SACE,0FACA;QACFC,QAAQ;IACV;IACAG,MAAM;QACJT,OAAO;QACPC,YAAY;QACZC,aACE,uFACA;QACFC,MACE,2FACA;QACFC,QAAQ;QACRC,SACE,wFACA,0FACA;QACFC,QAAQ;IACV;IACAI,WAAW;QACTV,OAAO;QACPC,YAAY;QACZC,aACE;QACFC,MACE,uFACA;QACFC,QAAQ;QACRC,SACE,yFACA,uFACA;QACFC,QAAQ;IACV;AACF,EAAC;AAED,4EAA4E,GAC5E,OAAO,MAAMK,6BAA0E;IACrFC,OAAO;IACPC,SAAS;IACTC,QAAQ;AACV,EAAC;AAYD,OAAO,MAAMC,qBAAoC;IAC/CC,UAAU;IACVC,SAAS;IACTC,cAAc;AAChB,EAAC;AAED;;;;;;;;CAQC,GACD,OAAO,SAASC,iBACdC,IAAiB,EACjBC,MAAqB;IAErB,IAAID,SAAS,QAAQ,OAAOC,OAAOL,QAAQ,GAAG;QAAEA,UAAUK,OAAOL,QAAQ;IAAC,IAAI,CAAC;IAC/E,IAAII,SAAS,YAAY;QACvB,MAAME,SAAS3B,yBAAyB;YACtCsB,SAASI,OAAOJ,OAAO;YACvBC,cAAcG,OAAOH,YAAY;QACnC;QACA,IAAI,OAAOI,WAAW,UAAU,OAAO;QACvC,OAAOA,OAAOJ,YAAY,GACtB;YAAED,SAASK,OAAOL,OAAO;YAAEC,cAAcI,OAAOJ,YAAY;QAAC,IAC7D;YAAED,SAASK,OAAOL,OAAO;QAAC;IAChC;IACA,OAAO,CAAC;AACV;AAaA,OAAO,SAASM,cAAc,EAAEH,IAAI,EAAEI,IAAI,EAAEC,OAAO,EAAEC,KAAK,EAAEC,MAAM,EAAEC,IAAI,EAAsB;IAC5F,MAAMC,OAAO/B,aAAa,CAACsB,KAAK;IAChC,2EAA2E;IAC3E,2CAA2C;IAC3C,MAAMU,UAAUvC,OAAOqC;IACvBE,QAAQC,OAAO,GAAGH;IAClB,MAAM,CAACI,OAAOC,SAAS,GAAGzC,SAAS;IACnC,MAAM,CAAC6B,QAAQa,UAAU,GAAG1C,SAAwBuB;IACpD,MAAM,CAACoB,MAAMC,QAAQ,GAAG5C,SAAS;IACjC,MAAM,CAAC6C,QAAQC,UAAU,GAAG9C,SAAwB;IACpD,MAAM,CAACa,SAASkC,WAAW,GAAG/C,SAAS;IAEvCF,UAAU;QACR,IAAI,CAACkC,MAAM;QACXc,UAAU;QACVC,WAAW;IACb,GAAG;QAACf;KAAK;IAET,MAAMgB,OAAOnD,YACX,CAACoD,QAAkCP,UAAU,CAACH,UAAa,aAAKA,SAAYU,SAC5E,EAAE;IAEJ,MAAMC,QAAQvB,iBAAiBC,MAAMC,YAAY;IAEjD,MAAMsB,QAAQtD,YAAY;QACxB,MAAMiC,SAASH,iBAAiBC,MAAMC;QACtC,IAAI,CAACK,SAAS,CAACM,MAAMY,IAAI,MAAM,CAACtB,QAAQ;QACxCc,QAAQ;QACRE,UAAU;QACV,IAAI;YACF,MAAMO,WAAW,MAAMnE,gBAAgBoD,QAAQC,OAAO,EAAE,gBAAgB;gBACtEe,QAAQ;gBACRC,SAAS;oBAAE,gBAAgB;gBAAmB;gBAC9CC,MAAMC,KAAKC,SAAS,CAAC;oBAAExB;oBAAOC;oBAAQP;oBAAMY,OAAOA,MAAMY,IAAI;oBAAItB;gBAAO;YAC1E;YACA,MAAM6B,UAAU,MAAMN,SAASO,IAAI,GAAGC,KAAK,CAAC,IAAM;YAClD,IAAI,CAACR,SAASS,EAAE,EAAE;;gBAChB,MAAMC,SAAS9E,qBAAqBoE,SAASW,MAAM,EAAEL;gBACrDb,UAAUiB,SAAS/E,oBAAoB+E,UAAUE,eAAON,2BAAAA,QAASO,KAAK,mBAAI7B,KAAKvB,MAAM;gBACrF;YACF;YACAiC,WAAW;YACXN,SAAS;YACTC,UAAUnB;QACZ,EAAE,eAAM;YACNuB,UAAUT,KAAKvB,MAAM;QACvB,SAAU;YACR8B,QAAQ;QACV;IACF,GAAG;QAACV;QAAOC;QAAQP;QAAMY;QAAOX;QAAQQ;KAAK;IAE7C,qBACE,MAAC/C;QAAO0C,MAAMA;QAAMC,SAASU,OAAOwB,YAAYlC;QAASmC,SAAS;QAACC,UAAS;;0BAC1E,KAAC5E;0BAAa4C,KAAK7B,KAAK;;0BACxB,KAAChB;0BACEqB,wBACC,KAAC1B;oBAAMmF,UAAS;8BAAWjC,KAAKxB,OAAO;mCAEvC,MAACnB;oBAAM6E,SAAS;oBAAGC,IAAI;wBAAEC,IAAI;oBAAE;;sCAC7B,KAAC9E;4BACC+E,OAAOrC,KAAK5B,UAAU;4BACtBC,aAAa2B,KAAK3B,WAAW;4BAC7BiE,SAAS;4BACTC,SAAS;4BACTC,OAAOrC;4BACPsC,UAAU,CAACC,QAAUtC,SAASsC,MAAMC,MAAM,CAACH,KAAK,CAACI,KAAK,CAAC,GAAG5E;4BAC1D6E,YAAY,GAAG1C,MAAM2C,MAAM,CAACC,cAAc,CAAC,SAAS,GAAG,EAAE/E,gBAAgB+E,cAAc,CAAC,UAAU;4BAClGC,UAAU1C;4BACV2C,SAAS;;wBAEV1D,SAAS,uBACR,KAAC2D;4BACCC,IAAG;4BACHd,OAAM;4BACNe,SAASxF,cAAcyF,GAAG,CAAC,CAACC,OAAU,CAAA;oCAAEH,IAAIG,KAAKH,EAAE;oCAAEd,OAAOiB,KAAKjB,KAAK;gCAAC,CAAA;4BACvEG,OAAOhD,OAAOL,QAAQ;4BACtB,wDAAwD;4BACxDoE,SAAS,CAACJ,KACRxC,KAAK;oCAAExB,UAAUK,OAAOL,QAAQ,KAAKgE,KAAK,OAAQA;gCAAkB;4BAEtEH,UAAU1C;6BAEV;wBACHf,SAAS,2BACR;;8CACE,KAAC2D;oCACCC,IAAG;oCACHd,OAAM;oCACNe,SAASvF,qBAAqBwF,GAAG,CAAC,CAACjE,UAAa,CAAA;4CAC9C+D,IAAI/D;4CACJiD,OAAOvD,0BAA0B,CAACM,QAAQ;wCAC5C,CAAA;oCACAoD,OAAOhD,OAAOJ,OAAO;oCACrB,+CAA+C;oCAC/CmE,SAAS,CAACJ,KAAOxC,KAAK;4CAAEvB,SAAS+D;wCAAwB;oCACzDH,UAAU1C;;gCAEXd,OAAOJ,OAAO,KAAK,wBAClB,KAACrB;oCACC+B,QAAQA;oCACR0C,OAAOhD,OAAOH,YAAY;oCAC1BoD,UAAU,CAACpD,eAAiBsB,KAAK;4CAAEtB;wCAAa;oCAChD2D,UAAU1C;qCAEV;;6BAEJ;sCACJ,KAAC/C;4BAAWiG,SAAQ;4BAAQC,OAAM;sCAC/BzD,KAAK1B,IAAI;;wBAEXkC,uBAAS,KAAC1D;4BAAMmF,UAAS;sCAAWzB;6BAAkB;;;;0BAI7D,MAACtD;;kCACC,KAACH;wBAAO2G,SAAS9D;wBAASoD,UAAU1C;kCACjC9B,UAAU,UAAU;;oBAEtBA,UAAU,qBACT,KAACzB;wBACCyG,SAAQ;wBACRE,SAAS,IAAM,KAAK5C;wBACpBkC,UAAU1C,QAAQ,CAACT,SAAS,CAACM,MAAMY,IAAI,MAAM,CAACF;kCAE7Cb,KAAKzB,MAAM;;;;;;AAMxB;AAEA,kFAAkF,GAClF,SAAS2E,UAAUS,KAOlB;IACC,MAAM,EAAER,EAAE,EAAEd,KAAK,EAAEe,OAAO,EAAEZ,KAAK,EAAEe,OAAO,EAAEP,QAAQ,EAAE,GAAGW;IACzD,qBACE,MAACtG;QAAM6E,SAAS;;0BACd,KAAC3E;gBAAWiG,SAAQ;gBAAQC,OAAM;gBAAiBN,IAAIA;0BACpDd;;0BAEH,KAAChF;gBACCuG,WAAU;gBACVC,UAAU;gBACVC,MAAK;gBACLC,mBAAiBZ;gBACjBhB,IAAI;oBAAE6B,UAAU;oBAAQC,KAAK;gBAAE;0BAE9Bb,QAAQC,GAAG,CAAC,CAACa,uBACZ,KAAClH;wBAECqF,OAAO6B,OAAO7B,KAAK;wBACnBoB,OAAOjB,UAAU0B,OAAOf,EAAE,GAAG,YAAY;wBACzCK,SAAShB,UAAU0B,OAAOf,EAAE,GAAG,WAAW;wBAC1CO,SAAS,IAAMH,QAAQW,OAAOf,EAAE;wBAChCgB,gBAAc3B,UAAU0B,OAAOf,EAAE;wBACjCH,UAAUA;uBANLkB,OAAOf,EAAE;;;;AAY1B;AAEA,eAAezD,cAAa"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-brief-dialog.component.tsx"],"sourcesContent":["'use client'\n\n/**\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 { lockdownRefusalText, parseLockdownRefusal } from '@aglyn/aglyn'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport {\n Alert,\n Button,\n Chip,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport type { AiJobKind } from '../model/ai-jobs.types'\nimport { AI_PAGE_TYPES, type AiPageType } from '../model/ai-page-job'\nimport {\n AI_TEMPLATE_SUBJECTS,\n parseAiTemplateJobInputs,\n type AiTemplateSubject,\n} from '../model/ai-template-subjects'\nimport { AiTemplateCollectionField } from './ai-template-collection-field.component'\n\n/**\n * The brief dialog every \"Describe it\" opens (AGL-2907, AGL-3043, AGL-3051):\n * a page from the Screens page and from the Assist panel's AI jobs, a page\n * template from Templates, a layout from Layouts, a form from Forms and a\n * reusable component from Components.\n *\n * One text box, and whatever the kind's door reads beside it, start one job of\n * that kind: a page's optional page type; a template's subject and, for a\n * collection entry's page, its collection. A layout, a form and a component\n * read nothing but the brief and the site. The job plans first and waits in\n * AI jobs, where the member confirms the plan and opens the draft once it is\n * built; nothing here builds or writes anything.\n */\n\n/** The longest brief a job admits. */\nconst BRIEF_MAX_CHARS = 4_000\n\n/** The job kinds a member describes from a console page. */\nexport type AiBriefKind = Extract<\n AiJobKind,\n 'page' | 'template' | 'layout' | 'form' | 'component'\n>\n\n/** What the dialog says for one kind. */\nexport interface AiBriefCopy {\n title: string\n /** The brief box's label. */\n briefLabel: string\n placeholder: string\n /** What happens after the member starts the job, under the fields. */\n next: string\n /** The action that starts the job. */\n submit: string\n /** What the dialog says once the job exists. */\n started: string\n /** What it says when the door gave no reason, or could not be reached. */\n failed: string\n}\n\nexport const AI_BRIEF_COPY: Readonly<Record<AiBriefKind, AiBriefCopy>> = {\n page: {\n title: 'Describe a page',\n briefLabel: 'What is the page for?',\n placeholder:\n 'A landing page for our spring roof inspection offer, with the quote request form',\n next:\n 'A plan comes first, and nothing is built until you confirm it. The page uses your ' +\n 'theme and what the site already has, and the plan lists anything the job builds first.',\n submit: 'Plan the page',\n started:\n 'The page is being planned. Open AI jobs in the Assist panel to review the plan and ' +\n 'confirm it. The page is built as an unpublished draft.',\n failed: 'The page could not be started. Try again.',\n },\n template: {\n title: 'Describe a page template',\n briefLabel: 'What should each page show?',\n placeholder:\n 'A page for each of our team members: their name and photo, their role, a short ' +\n 'biography and a way to get in touch',\n next:\n 'A plan comes first, and nothing is built until you confirm it. What changes from one ' +\n 'page to the next, such as a name or a photo, is filled in from each record rather ' +\n 'than typed.',\n submit: 'Plan the template',\n started:\n 'The template is being planned. Open AI jobs in the Assist panel to review the plan ' +\n 'and confirm it. The template is built as a draft in your library, and nothing on ' +\n 'your site uses it until you do.',\n failed: 'The template could not be started. Try again.',\n },\n layout: {\n title: 'Describe a layout',\n briefLabel: 'What should the layout hold?',\n placeholder:\n 'A header with our name and links to Services, About and Contact, and a footer with ' +\n 'our address and opening hours',\n next:\n 'A plan comes first, and nothing is built until you confirm it. The layout links your ' +\n 'screens, and places your navigation or menu component when the site has one.',\n submit: 'Plan the layout',\n started:\n 'The layout is being planned. Open AI jobs in the Assist panel to review the plan and ' +\n 'confirm it. The layout is built as a draft, and no screen uses it until you assign it.',\n failed: 'The layout could not be started. Try again.',\n },\n form: {\n title: 'Describe a form',\n briefLabel: 'What is the form for?',\n placeholder:\n 'A quote request form for a roofing company, asking what needs fixing and the best ' +\n 'time to call',\n next:\n 'A plan comes first, and nothing is built until you confirm it. The form’s fields, its ' +\n 'marketing consent and where each submission goes are planned together.',\n submit: 'Plan the form',\n started:\n 'The form is being planned. Open AI jobs in the Assist panel to review the plan and ' +\n 'confirm it. The form is built as a draft, and it collects nothing until you place it ' +\n 'on a screen.',\n failed: 'The form could not be started. Try again.',\n },\n component: {\n title: 'Describe a reusable component',\n briefLabel: 'What should the component show?',\n placeholder:\n 'A testimonial card with the customer’s name, their role, their quote and a photo',\n next:\n 'A plan comes first, and nothing is built until you confirm it. What each page can ' +\n 'change, such as a name or a photo, becomes a property of the component.',\n submit: 'Plan the component',\n started:\n 'The component is being planned. Open AI jobs in the Assist panel to review the plan ' +\n 'and confirm it. The component is built as a draft, and no page shows it until you ' +\n 'insert it.',\n failed: 'The component could not be started. Try again.',\n },\n}\n\n/** Each template subject as its chip names it: what one page is drawn for. */\nexport const AI_TEMPLATE_SUBJECT_LABELS: Readonly<Record<AiTemplateSubject, string>> = {\n entry: 'Collection entry',\n product: 'Product',\n author: 'Author',\n}\n\n/** What a member picked beside the brief; each kind reads only its own. */\nexport interface AiBriefChoice {\n /** A page's type, or `null` for none. */\n pageType: AiPageType | null\n /** What a template's pages are drawn for, or `null` until one is picked. */\n subject: AiTemplateSubject | null\n /** A collection entry template's collection, or `null` until one is picked. */\n collectionId: string | null\n}\n\nexport const AI_BRIEF_NO_CHOICE: AiBriefChoice = {\n pageType: null,\n subject: null,\n collectionId: null,\n}\n\n/**\n * The job's `inputs` for what the member picked, or `null` while the kind's\n * door would refuse them.\n *\n * A template's are read by the parser its door reads, so the dialog cannot\n * send a subject the door does not know or an entry page with no collection.\n * A page's type is optional. A layout, a form and a component read no inputs\n * at all (AGL-2909, AGL-2913, AGL-2908), so theirs are empty.\n */\nexport function aiBriefJobInputs(\n kind: AiBriefKind,\n choice: AiBriefChoice,\n): Record<string, string> | null {\n if (kind === 'page') return choice.pageType ? { pageType: choice.pageType } : {}\n if (kind === 'template') {\n const inputs = parseAiTemplateJobInputs({\n subject: choice.subject,\n collectionId: choice.collectionId,\n })\n if (typeof inputs === 'string') return null\n return inputs.collectionId\n ? { subject: inputs.subject, collectionId: inputs.collectionId }\n : { subject: inputs.subject }\n }\n return {}\n}\n\nexport interface AiBriefDialogProps {\n /** The job the brief starts. */\n kind: AiBriefKind\n open: boolean\n onClose: () => void\n orgId: string | undefined\n hostId: string\n /** Who is signed in, whose token the request carries. */\n user: Parameters<typeof authorizedFetch>[0]\n}\n\nexport function AiBriefDialog({ kind, open, onClose, orgId, hostId, user }: AiBriefDialogProps) {\n const copy = AI_BRIEF_COPY[kind]\n // Held in a ref so the request reads WHO is signed in, and nothing keys on\n // the identity of the object that says so.\n const userRef = useRef(user)\n userRef.current = user\n const [brief, setBrief] = useState('')\n const [choice, setChoice] = useState<AiBriefChoice>(AI_BRIEF_NO_CHOICE)\n const [busy, setBusy] = useState(false)\n const [notice, setNotice] = useState<string | null>(null)\n const [started, setStarted] = useState(false)\n\n useEffect(() => {\n if (!open) return\n setNotice(null)\n setStarted(false)\n }, [open])\n\n const pick = useCallback(\n (patch: Partial<AiBriefChoice>) => setChoice((current) => ({ ...current, ...patch })),\n [],\n )\n const ready = aiBriefJobInputs(kind, choice) !== null\n\n const start = useCallback(async () => {\n const inputs = aiBriefJobInputs(kind, choice)\n if (!orgId || !brief.trim() || !inputs) return\n setBusy(true)\n setNotice(null)\n try {\n const response = await authorizedFetch(userRef.current, '/api/ai/jobs', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ orgId, hostId, kind, brief: brief.trim(), inputs }),\n })\n const payload = await response.json().catch(() => null)\n if (!response.ok) {\n const locked = parseLockdownRefusal(response.status, payload)\n setNotice(locked ? lockdownRefusalText(locked) : String(payload?.error ?? copy.failed))\n return\n }\n setStarted(true)\n setBrief('')\n setChoice(AI_BRIEF_NO_CHOICE)\n } catch {\n setNotice(copy.failed)\n } finally {\n setBusy(false)\n }\n }, [orgId, hostId, kind, brief, choice, copy])\n\n return (\n <Dialog open={open} onClose={busy ? undefined : onClose} fullWidth maxWidth=\"sm\">\n <DialogTitle>{copy.title}</DialogTitle>\n <DialogContent>\n {started ? (\n <Alert severity=\"success\">{copy.started}</Alert>\n ) : (\n <Stack spacing={2} sx={{ pt: 1 }}>\n <TextField\n label={copy.briefLabel}\n placeholder={copy.placeholder}\n multiline\n minRows={4}\n value={brief}\n onChange={(event) => setBrief(event.target.value.slice(0, BRIEF_MAX_CHARS))}\n helperText={`${brief.length.toLocaleString('en-US')} / ${BRIEF_MAX_CHARS.toLocaleString('en-US')}`}\n disabled={busy}\n autoFocus\n />\n {kind === 'page' ? (\n <ChipGroup\n id=\"ai-page-type-label\"\n label=\"Page type (optional)\"\n options={AI_PAGE_TYPES.map((type) => ({ id: type.id, label: type.label }))}\n value={choice.pageType}\n // Optional: pressing the pressed type again sends none.\n onPress={(id) =>\n pick({ pageType: choice.pageType === id ? null : (id as AiPageType) })\n }\n disabled={busy}\n />\n ) : null}\n {kind === 'template' ? (\n <>\n <ChipGroup\n id=\"ai-template-subject-label\"\n // Marked required because it IS (AGL-3143 §12): \"Plan the\n // template\" stays disabled until one is picked, and the page\n // dialog's chip group beside it reads \"(optional)\" — so an\n // unmarked label here read as optional too, and a member who\n // wrote a brief and stopped had nothing telling them why the\n // button would not light.\n label=\"One page for each (required)\"\n options={AI_TEMPLATE_SUBJECTS.map((subject) => ({\n id: subject,\n label: AI_TEMPLATE_SUBJECT_LABELS[subject],\n }))}\n value={choice.subject}\n // Required, so a press picks and never clears.\n onPress={(id) => pick({ subject: id as AiTemplateSubject })}\n disabled={busy}\n />\n {choice.subject === 'entry' ? (\n <AiTemplateCollectionField\n hostId={hostId}\n value={choice.collectionId}\n onChange={(collectionId) => pick({ collectionId })}\n disabled={busy}\n />\n ) : null}\n </>\n ) : null}\n <Typography variant=\"body2\" color=\"text.secondary\">\n {copy.next}\n </Typography>\n {notice ? <Alert severity=\"warning\">{notice}</Alert> : null}\n </Stack>\n )}\n </DialogContent>\n <DialogActions>\n <Button onClick={onClose} disabled={busy}>\n {started ? 'Close' : 'Cancel'}\n </Button>\n {started ? null : (\n <Button\n variant=\"contained\"\n onClick={() => void start()}\n disabled={busy || !orgId || !brief.trim() || !ready}\n >\n {copy.submit}\n </Button>\n )}\n </DialogActions>\n </Dialog>\n )\n}\n\n/** A labeled row of chips, each a toggle button that says whether it is pressed. */\nfunction ChipGroup(props: {\n id: string\n label: string\n options: ReadonlyArray<{ id: string; label: string }>\n value: string | null\n onPress: (id: string) => void\n disabled: boolean\n}) {\n const { id, label, options, value, onPress, disabled } = props\n return (\n <Stack spacing={1}>\n <Typography variant=\"body2\" color=\"text.secondary\" id={id}>\n {label}\n </Typography>\n <Stack\n direction=\"row\"\n useFlexGap\n role=\"group\"\n aria-labelledby={id}\n sx={{ flexWrap: 'wrap', gap: 1 }}\n >\n {options.map((option) => (\n <Chip\n key={option.id}\n label={option.label}\n color={value === option.id ? 'primary' : 'default'}\n variant={value === option.id ? 'filled' : 'outlined'}\n onClick={() => onPress(option.id)}\n aria-pressed={value === option.id}\n disabled={disabled}\n />\n ))}\n </Stack>\n </Stack>\n )\n}\n\nexport default AiBriefDialog\n"],"names":["lockdownRefusalText","parseLockdownRefusal","authorizedFetch","Alert","Button","Chip","Dialog","DialogActions","DialogContent","DialogTitle","Stack","TextField","Typography","useCallback","useEffect","useRef","useState","AI_PAGE_TYPES","AI_TEMPLATE_SUBJECTS","parseAiTemplateJobInputs","AiTemplateCollectionField","BRIEF_MAX_CHARS","AI_BRIEF_COPY","page","title","briefLabel","placeholder","next","submit","started","failed","template","layout","form","component","AI_TEMPLATE_SUBJECT_LABELS","entry","product","author","AI_BRIEF_NO_CHOICE","pageType","subject","collectionId","aiBriefJobInputs","kind","choice","inputs","AiBriefDialog","open","onClose","orgId","hostId","user","copy","userRef","current","brief","setBrief","setChoice","busy","setBusy","notice","setNotice","setStarted","pick","patch","ready","start","trim","response","method","headers","body","JSON","stringify","payload","json","catch","ok","locked","status","String","error","undefined","fullWidth","maxWidth","severity","spacing","sx","pt","label","multiline","minRows","value","onChange","event","target","slice","helperText","length","toLocaleString","disabled","autoFocus","ChipGroup","id","options","map","type","onPress","variant","color","onClick","props","direction","useFlexGap","role","aria-labelledby","flexWrap","gap","option","aria-pressed"],"mappings":"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"}
|
|
@@ -79,11 +79,40 @@ export declare const AI_PAGE_SECTION_INLINE_LINE = "This site keeps no saved for
|
|
|
79
79
|
* the section check draws into its copies (`ai-repeated-items.ts`). It rides
|
|
80
80
|
* the request of a section whose plan line shows items on a workspace that
|
|
81
81
|
* keeps no reusable components, and no other: a section with nothing to repeat
|
|
82
|
-
* pays nothing for it, the cached prefix every workspace shares says only
|
|
83
|
-
* a repeated item is written once
|
|
84
|
-
* never shown how.
|
|
82
|
+
* pays nothing for it, and the cached prefix every workspace shares says only
|
|
83
|
+
* that a repeated item is written once.
|
|
85
84
|
*/
|
|
86
85
|
export declare const AI_PAGE_SECTION_REPEAT_LINE = "Write a repeated item once: put {{1}}, {{2}}\u2026 where its copies differ, and give its outermost node \"repeat\", one list of values a copy, in that order, as [[\"Title 1\", \"Text 1\"], [\"Title 2\", \"Text 2\"]].";
|
|
86
|
+
/**
|
|
87
|
+
* How a workspace that keeps reusable components writes a repeated item
|
|
88
|
+
* (AGL-3143 §10): as one instance of a component the section already places,
|
|
89
|
+
* repeated, rather than the item's subtree drawn once per copy.
|
|
90
|
+
*
|
|
91
|
+
* It rides the request of a section whose plan line shows items AND names a
|
|
92
|
+
* record to place, on the same terms the repeat line rides a Free one: a
|
|
93
|
+
* section with nothing to repeat, or nothing to repeat it FROM, pays nothing
|
|
94
|
+
* for it.
|
|
95
|
+
*
|
|
96
|
+
* ## Why a paid section could not be built without it (AGL-3024)
|
|
97
|
+
*
|
|
98
|
+
* The Free path is reconciled at both ends — its plan is held to the sections
|
|
99
|
+
* its wall pays for (AGL-3070) and its request is shown the repeat shape
|
|
100
|
+
* (AGL-3053). The paid path was reconciled at neither: `AI_BUILD_PLAN_LIMITS`
|
|
101
|
+
* lets a plan promise up to `items: 500` in one section, the pass that builds
|
|
102
|
+
* it fits `aiJobPageSectionMaxElements` — fifteen at the balanced tier — and
|
|
103
|
+
* the request said only how many elements to keep under, never how to get
|
|
104
|
+
* there. A six-card grid measured live on 2026-09-21 was drawn inline, ran
|
|
105
|
+
* past its ceiling, and was cut off; the re-ask then said to place an instance
|
|
106
|
+
* "of a component the site has" without naming one, so it was cut off too, and
|
|
107
|
+
* `AI_GENERATION_MAX_ATTEMPTS` ended the job with no page. A four-card grid on
|
|
108
|
+
* the same site had fitted, which is why this went unseen.
|
|
109
|
+
*
|
|
110
|
+
* ⛔ This does NOT make the plan and the pass agree about what fits — a plan
|
|
111
|
+
* may still promise more items than any ceiling can carry, and this line only
|
|
112
|
+
* gives the pass its cheapest way to try. That bound belongs on the plan step
|
|
113
|
+
* and is deliberately not taken here.
|
|
114
|
+
*/
|
|
115
|
+
export declare const AI_PAGE_SECTION_INSTANCE_LINE = "Write a repeated item once: place one instance of the component this section places and repeat that instance, rather than drawing the item again for each copy.";
|
|
87
116
|
/**
|
|
88
117
|
* What a pass whose answer ran past its ceiling is told makes a section
|
|
89
118
|
* smaller (AGL-3042): fewer elements, down to the budget the request gave,
|
|
@@ -106,10 +106,38 @@ import { aiPlanItemCountViolations } from "./ai-job-plan-conformance.js";
|
|
|
106
106
|
* the section check draws into its copies (`ai-repeated-items.ts`). It rides
|
|
107
107
|
* the request of a section whose plan line shows items on a workspace that
|
|
108
108
|
* keeps no reusable components, and no other: a section with nothing to repeat
|
|
109
|
-
* pays nothing for it, the cached prefix every workspace shares says only
|
|
110
|
-
* a repeated item is written once
|
|
111
|
-
* never shown how.
|
|
109
|
+
* pays nothing for it, and the cached prefix every workspace shares says only
|
|
110
|
+
* that a repeated item is written once.
|
|
112
111
|
*/ export const AI_PAGE_SECTION_REPEAT_LINE = `Write a repeated item once: ${AI_PAGE_SECTION_REPEAT_SHAPE}.`;
|
|
112
|
+
/**
|
|
113
|
+
* How a workspace that keeps reusable components writes a repeated item
|
|
114
|
+
* (AGL-3143 §10): as one instance of a component the section already places,
|
|
115
|
+
* repeated, rather than the item's subtree drawn once per copy.
|
|
116
|
+
*
|
|
117
|
+
* It rides the request of a section whose plan line shows items AND names a
|
|
118
|
+
* record to place, on the same terms the repeat line rides a Free one: a
|
|
119
|
+
* section with nothing to repeat, or nothing to repeat it FROM, pays nothing
|
|
120
|
+
* for it.
|
|
121
|
+
*
|
|
122
|
+
* ## Why a paid section could not be built without it (AGL-3024)
|
|
123
|
+
*
|
|
124
|
+
* The Free path is reconciled at both ends — its plan is held to the sections
|
|
125
|
+
* its wall pays for (AGL-3070) and its request is shown the repeat shape
|
|
126
|
+
* (AGL-3053). The paid path was reconciled at neither: `AI_BUILD_PLAN_LIMITS`
|
|
127
|
+
* lets a plan promise up to `items: 500` in one section, the pass that builds
|
|
128
|
+
* it fits `aiJobPageSectionMaxElements` — fifteen at the balanced tier — and
|
|
129
|
+
* the request said only how many elements to keep under, never how to get
|
|
130
|
+
* there. A six-card grid measured live on 2026-09-21 was drawn inline, ran
|
|
131
|
+
* past its ceiling, and was cut off; the re-ask then said to place an instance
|
|
132
|
+
* "of a component the site has" without naming one, so it was cut off too, and
|
|
133
|
+
* `AI_GENERATION_MAX_ATTEMPTS` ended the job with no page. A four-card grid on
|
|
134
|
+
* the same site had fitted, which is why this went unseen.
|
|
135
|
+
*
|
|
136
|
+
* ⛔ This does NOT make the plan and the pass agree about what fits — a plan
|
|
137
|
+
* may still promise more items than any ceiling can carry, and this line only
|
|
138
|
+
* gives the pass its cheapest way to try. That bound belongs on the plan step
|
|
139
|
+
* and is deliberately not taken here.
|
|
140
|
+
*/ export const AI_PAGE_SECTION_INSTANCE_LINE = 'Write a repeated item once: place one instance of the component this section places and repeat that instance, rather than drawing the item again for each copy.';
|
|
113
141
|
/**
|
|
114
142
|
* What a pass whose answer ran past its ceiling is told makes a section
|
|
115
143
|
* smaller (AGL-3042): fewer elements, down to the budget the request gave,
|
|
@@ -143,6 +171,8 @@ import { aiPlanItemCountViolations } from "./ai-job-plan-conformance.js";
|
|
|
143
171
|
...section.items ? [
|
|
144
172
|
AI_PAGE_SECTION_REPEAT_LINE
|
|
145
173
|
] : []
|
|
174
|
+
] : section.items && section.uses.length ? [
|
|
175
|
+
AI_PAGE_SECTION_INSTANCE_LINE
|
|
146
176
|
] : [],
|
|
147
177
|
`Keep this section to at most ${input.maxElements} elements.`
|
|
148
178
|
].join('\n');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-page-sections.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 { REUSABLE_INSTANCE_COMPONENT_ID } from '@aglyn/aglyn/app-utils/reusable-component-keys'\nimport { CANVAS_ROOT_ELEMENT_ID } from '@aglyn/aglyn/foundation/constants/canvas'\nimport type { NodesMap } from '@aglyn/aglyn/types/nodes'\nimport type { AiBuildPlanScreen, AiBuildPlanSection } from '../model/ai-build-plan'\nimport { aiPageTypeDefinition, parseAiPageJobInputs } from '../model/ai-page-job'\nimport type { AiJob, AiJobPlan } from '../model/ai-jobs.types'\nimport { aiHomeScreenIds, type AiSiteInventory } from '../model/ai-site-inventory'\nimport type { AiTool } from '../providers/contract'\nimport {\n AI_REASK_OFFENDING_MAX_CHARS,\n aiAnswerTree,\n aiNodeTreeContextFromInventory,\n type AiGenerationCheck,\n} from '../runtime/ai-doctrine'\nimport {\n detectCutLines,\n detectDisagreeingNodes,\n isAiLinkElement,\n validateAiDoctrineTree,\n walkTree,\n type AiDoctrineNode,\n type AiDoctrineTreeContext,\n type AiDoctrineViolation,\n} from '../runtime/ai-doctrine-validators'\nimport { AI_INSTANCE_REF_PROP, validateAiNodeTree } from '../runtime/ai-node-tree'\nimport { aiPageLinkTarget, aiPageScrollInteraction } from '../runtime/ai-page-links'\nimport type { AiLoadEstimate } from '../runtime/ai-palette'\nimport { AI_PALETTE_CATALOG } from '../runtime/ai-palette.generated'\nimport { AI_REPEAT_KEY, expandAiRepeatedItems } from '../runtime/ai-repeated-items'\nimport type { AiSystemBlock } from '../runtime/ai-runtime'\nimport { aiJobBriefLine, aiPlanReferenceLines } from './ai-job-generation'\nimport { aiPlanItemCountViolations } from './ai-job-plan-conformance'\n\n/**\n * A page built one section at a time (AGL-2907): what one section pass asks\n * for, how its answer is held to the building rules as part of the page, and\n * how it joins the page.\n *\n * Pure: no Firestore and no provider, so the page step, its specs and the\n * evals read one definition of a section.\n *\n * ── Held as part of the page ─────────────────────────────────────────────\n *\n * A section is checked twice over, by validators the doctrine already has:\n * the palette validator on the section as the model wrote it, then the\n * doctrine's whole page check on the page built so far with the section\n * added. So the page's one main, its one h1 and its heading outline, repeats\n * across sections, and its weight against the page budget are held on every\n * pass, not only on the last. The violations name the section's own nodes,\n * by the ids the model wrote, so a re-ask quotes this section and nothing\n * else — the page is never sent back.\n *\n * ── A repeated item written once (AGL-3053) ──────────────────────────────\n *\n * Where the workspace keeps no reusable components, an answer writes a\n * repeated item once and lists its copies' values, and the check draws the\n * copies before either validator reads the section (`ai-repeated-items.ts`).\n * Both then read the section as the page stores it, so the page's rules hold\n * on every copy exactly as on an item written out in full, and a finding on a\n * copy names the node the model wrote. Where the workspace keeps components,\n * an item written once is refused: it places instances.\n */\n\n/** The section root id a pass writes, from the job and the plan section's index. */\nexport function aiPageSectionNodeId(jobId: string, index: number): string {\n return `ai-${jobId.slice(0, 12)}-section-${index + 1}`\n}\n\n/** The strict tool a section arrives through. The node map rides as JSON text, as a page's does. */\nexport const AI_PAGE_SECTION_TOOL: AiTool = {\n name: 'submit_section',\n description: 'Submit one section of the page as one flat node map.',\n strict: true,\n inputSchema: {\n type: 'object',\n additionalProperties: false,\n required: ['tree'],\n properties: {\n tree: {\n type: 'string',\n description:\n 'The section as JSON: {\"rootId\": \"<id>\", \"nodes\": {\"<id>\": {\"componentId\": \"...\", \"props\": {...}, \"sx\": {...}, \"nodes\": [\"<child id>\", ...]}}}. The root is the document wrapper (componentId \"div\") holding one Section.',\n },\n },\n },\n}\n\n/** The page step's own instructions, cached after the doctrine, with the screen palette after them. */\nexport const AI_JOB_PAGE_INSTRUCTIONS: readonly AiSystemBlock[] = [\n {\n text: [\n 'You build a web page one section at a time, in the order a confirmed plan gives. Each answer is ONE section: call submit_section with a flat node map whose root is the document wrapper (componentId \"div\") holding exactly one Section (section).',\n 'The first section is the top of the page and holds the page’s one h1, its title. Every later section opens with an h2, and headings below it step down one level at a time.',\n 'Place what the section’s plan line names. A component the site has is placed as an instance, never drawn again: componentId \"reusableInstance\", props {\"refId\": \"<component id>\", \"propValues\": {…}} filling the props the inventory lists. A form the site has is a Form (form) whose formId is that form’s id. Another screen of the site is linked by its screen id, with a Screen Link (muiScreenLink) or a Button (muiButton) whose screenId is that id.',\n 'Where the request says the site keeps no saved forms or reusable components, draw them in the section instead: a repeated item is written once, and a form is a Form (form) with no formId, a formName saying what it collects and a submitLabel, holding one Form Field (formField) for each answer with its fieldName, label and fieldType.',\n 'Lay the section out with a Container, a Stack or a Grid (\"container\": true, \"spacing\": 3) of Grid items sized like \"xs:12 md:4\", and size nothing with a fixed width. Colors come from the theme’s palette tokens such as primary.main, spacing from the spacing scale, and type from the text variants.',\n 'The page renders inside the site’s layout, so a section is never a header, navigation or footer.',\n 'A picture is an Image (image) with alt text that says what it should show and no src, for the site owner to fill from the media library.',\n 'Write plain, specific copy in the site’s voice, with no filler. Where the brief leaves out a fact, such as a price, a phone number or an address, write the gap in square brackets instead of inventing it.',\n ].join('\\n'),\n },\n { text: AI_PALETTE_CATALOG.screen, cacheBreakpoint: true },\n]\n\n/** A plan reference as the request names it: the id with the name it was planned under. */\nfunction referenceOf(plan: AiJobPlan, ref: string): string {\n const label = plan.labels?.[ref]\n return label ? `${ref} (\"${label}\")` : ref\n}\n\nexport interface AiPageSectionPromptInput {\n job: Pick<AiJob, 'brief' | 'inputs'>\n plan: AiJobPlan\n screen: AiBuildPlanScreen\n index: number\n /** The most elements the section may carry at this pass's answer ceiling. */\n maxElements: number\n /**\n * Whether the workspace keeps reusable components and saved forms\n * (AGL-3030); `false` asks for the section built inline. Absent is `true`.\n */\n reusableComponents?: boolean\n}\n\n/** What a section request says where the workspace keeps no reusable components or saved forms. */\nexport const AI_PAGE_SECTION_INLINE_LINE =\n 'This site keeps no saved forms or reusable components: write a repeated item once, and draw a form as a Form holding its Form Fields.'\n\n/** How a repeated item is written once, in the words a request and a re-ask share. */\nconst AI_PAGE_SECTION_REPEAT_SHAPE = `put {{1}}, {{2}}… where its copies differ, and give its outermost node \"${AI_REPEAT_KEY}\", one list of values a copy, in that order, as [[\"Title 1\", \"Text 1\"], [\"Title 2\", \"Text 2\"]]`\n\n/**\n * How a repeated item is written once (AGL-3053): the item's subtree once,\n * with placeholders where its copies differ and their values listed, which\n * the section check draws into its copies (`ai-repeated-items.ts`). It rides\n * the request of a section whose plan line shows items on a workspace that\n * keeps no reusable components, and no other: a section with nothing to repeat\n * pays nothing for it, the cached prefix every workspace shares says only that\n * a repeated item is written once, and a workspace that places components is\n * never shown how.\n */\nexport const AI_PAGE_SECTION_REPEAT_LINE = `Write a repeated item once: ${AI_PAGE_SECTION_REPEAT_SHAPE}.`\n\n/**\n * What a pass whose answer ran past its ceiling is told makes a section\n * smaller (AGL-3042): fewer elements, down to the budget the request gave,\n * shorter copy, and a repeated item drawn no more than once — placed as an\n * instance where the workspace keeps reusable components, and written once\n * where it keeps none (AGL-3053), with the shape spelled out, since a section\n * whose plan line showed no items was never told it. It rides the re-ask,\n * never the first request, so a pass that fits sends no byte more.\n */\nexport function aiPageSectionSmaller(input: Pick<AiPageSectionPromptInput, 'maxElements' | 'reusableComponents'>): string {\n return input.reusableComponents === false\n ? `Make it smaller: use fewer elements, at most ${input.maxElements}; write shorter copy; and write a repeated item once instead of drawing it again: ${AI_PAGE_SECTION_REPEAT_SHAPE}.`\n : `Make it smaller: use fewer elements, at most ${input.maxElements}; write shorter copy; and place a repeated item as an instance of a component the site has instead of drawing it again.`\n}\n\n/** One pass's user turn: the page, the brief, the plan, this section and what is built above it. */\nexport function aiPageSectionPrompt(input: AiPageSectionPromptInput): string {\n const { job, plan, screen, index } = input\n const inputs = parseAiPageJobInputs(job.inputs)\n const type = typeof inputs === 'string' ? null : aiPageTypeDefinition(inputs.pageType)\n const section = screen.sections[index] as AiBuildPlanSection\n const places = section.uses.length\n ? ` It places ${section.uses.map((ref) => referenceOf(plan, ref)).join(', ')}.`\n : ''\n const items = section.items ? ` It shows ${section.items} items.` : ''\n const built = screen.sections.slice(0, index).map((entry, position) => `${position + 1}. ${entry.name}`)\n return [\n `Page: \"${screen.title}\" at ${screen.slug}`,\n ...(type ? [`Page type: ${type.purpose}`] : []),\n aiJobBriefLine(job),\n ...aiPlanReferenceLines(plan),\n `Build section ${index + 1} of ${screen.sections.length}: \"${section.name}\".${places}${items}`,\n built.length\n ? `Already built, above it: ${built.join('; ')}. The page’s h1 is in section 1.`\n : 'Nothing is built yet: this section holds the page’s h1.',\n ...(input.reusableComponents === false\n ? [AI_PAGE_SECTION_INLINE_LINE, ...(section.items ? [AI_PAGE_SECTION_REPEAT_LINE] : [])]\n : []),\n `Keep this section to at most ${input.maxElements} elements.`,\n ].join('\\n')\n}\n\n/** A validated section: its nodes keyed as the page stores them, its root under the page root. */\nexport interface AiPageSection {\n rootId: string\n nodes: NodesMap\n /** The page's weight with the section added, as the doctrine measured it. */\n load: AiLoadEstimate | null\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/** A page with nothing on it yet: the document wrapper. */\nexport function aiEmptyPage(): NodesMap {\n return {\n [CANVAS_ROOT_ELEMENT_ID]: {\n $id: CANVAS_ROOT_ELEMENT_ID,\n componentId: 'div',\n parentId: null,\n nodes: [],\n },\n } as unknown as NodesMap\n}\n\n/**\n * The page with a section in its plan position: after every section with a\n * lower index and before every one with a higher, the page's other children\n * — whatever a member placed — kept where they are. A section already on the\n * page is replaced, never doubled.\n */\nexport function aiPageWithSection(\n page: NodesMap,\n section: AiPageSection,\n sectionIds: readonly string[],\n): NodesMap {\n const nodes: Record<string, Record<string, unknown>> = {}\n const previous = (page as unknown as Record<string, Record<string, unknown>>)[section.rootId]\n const replaced = new Set<string>()\n if (previous) {\n for (const { id } of walkTree({\n rootId: section.rootId,\n nodes: page as unknown as Record<string, AiDoctrineNode>,\n })) {\n replaced.add(id)\n }\n }\n for (const [id, node] of Object.entries(page)) {\n if (!replaced.has(id)) nodes[id] = { ...(node as unknown as Record<string, unknown>) }\n }\n const root = nodes[CANVAS_ROOT_ELEMENT_ID] ?? (aiEmptyPage()[CANVAS_ROOT_ELEMENT_ID] as unknown as Record<string, unknown>)\n const children = (Array.isArray(root['nodes']) ? (root['nodes'] as string[]) : []).filter(\n (id) => id !== section.rootId,\n )\n const order = new Map(sectionIds.map((id, position) => [id, position]))\n const at = order.get(section.rootId) ?? sectionIds.length\n const before = children.findIndex((id) => (order.get(id) ?? -1) > at)\n children.splice(before === -1 ? children.length : before, 0, section.rootId)\n nodes[CANVAS_ROOT_ELEMENT_ID] = { ...root, nodes: children }\n for (const [id, node] of Object.entries(section.nodes)) {\n nodes[id] = { ...(node as unknown as Record<string, unknown>) }\n }\n return nodes as unknown as NodesMap\n}\n\n/**\n * What a tree generated against this inventory may reference, the brand it is\n * held to, where the workspace keeps no reusable components or saved forms\n * (AGL-3030) that the page is built inline, and the sections its plan names,\n * which a link may take a visitor to (AGL-3097).\n */\nexport function aiPageCheckContext(\n inventory: AiSiteInventory | null,\n options: { reusableComponents?: boolean; sections?: readonly string[] } = {},\n): AiDoctrineTreeContext {\n return {\n brand: inventory?.theme ? { colors: inventory.theme.colors, fonts: inventory.theme.fonts } : null,\n ...aiNodeTreeContextFromInventory(inventory),\n homeScreenIds: aiHomeScreenIds(inventory),\n ...(options.reusableComponents === false ? { reusableComponents: false } : {}),\n ...(options.sections?.length ? { pageSections: options.sections } : {}),\n }\n}\n\nexport interface AiPageSectionCheckInput {\n /** The page as stored so far; an empty page on the first pass. */\n page: NodesMap\n /** Every section root id of the plan, in order. */\n sectionIds: readonly string[]\n index: number\n context: AiDoctrineTreeContext\n /**\n * The plan line this pass answers for: what it places by inventory id, and\n * how many items it promised, which the answer is held to (AGL-3024).\n */\n section: Pick<AiBuildPlanSection, 'name' | 'uses' | 'items'>\n inventory: AiSiteInventory | null\n}\n\n/** The parts of an answer the violations name, as the model wrote them, capped as the doctrine caps a re-ask. */\nfunction offendingOf(raw: unknown, violations: readonly AiDoctrineViolation[]): { offending?: Record<string, unknown> } {\n const rawNodes = isRecord(raw) && isRecord(raw['nodes']) ? raw['nodes'] : {}\n const offending: Record<string, unknown> = {}\n let used = 2\n for (const id of new Set(violations.flatMap((violation) => violation.nodeIds ?? []))) {\n if (rawNodes[id] === undefined) continue\n const size = JSON.stringify(rawNodes[id]).length + id.length + 4\n if (used + size > AI_REASK_OFFENDING_MAX_CHARS) break\n offending[id] = rawNodes[id]\n used += size\n }\n return Object.keys(offending).length ? { offending } : {}\n}\n\n/**\n * The check a section's answer is held to: a repeated item written once drawn\n * into its copies (AGL-3053), then the palette validator on the section, with\n * any line it cut at its ceiling refused (AGL-3076), the doctrine's page check\n * on the page with the section added, and the plan line — every component it\n * names placed as an instance, every form bound by its id (rule 7), and as\n * many items as it promised (AGL-3024). Violations\n * name the section's own nodes by the ids the MODEL wrote, and a copy's nodes\n * by the item's; one that names only nodes an earlier pass stored belongs to\n * the page rather than to this answer, and is left to the last pass (AGL-3078).\n */\nexport function aiPageSectionCheck(input: AiPageSectionCheckInput): AiGenerationCheck<AiPageSection> {\n const sectionId = input.sectionIds[input.index]\n const components = new Set((input.inventory?.components ?? []).map((row) => row.id))\n const forms = new Set((input.inventory?.forms ?? []).map((row) => row.id))\n return (answer) => {\n const raw = aiAnswerTree(answer)\n // Before anything is drawn or dropped: a repeated item written and never\n // placed is content the section loses silently, and a child named and\n // never written ends the pass with nothing to answer (AGL-3143).\n const disagreeing = detectDisagreeingNodes(raw)\n if (disagreeing.length) {\n return { value: null, violations: disagreeing, ...offendingOf(raw, disagreeing) }\n }\n const drawn = expandAiRepeatedItems(raw, { inline: input.context.reusableComponents === false, noun: 'section' })\n if (drawn.ok === false) {\n return { value: null, violations: drawn.violations, ...offendingOf(raw, drawn.violations) }\n }\n // Every id below is the drawn tree's; a copy's leads back to the node the model wrote.\n const written = (id: string): string => drawn.sourceIds[id] ?? id\n const validated = validateAiNodeTree(drawn.tree, 'screen', input.context)\n if (validated.ok === false) {\n return {\n value: null,\n violations: [\n {\n rule: null,\n code: `tree-${validated.code}`,\n message: 'The answer could not be used as a section.',\n detail: validated.error,\n },\n ],\n }\n }\n const wrapper = validated.nodes[validated.rootId] as unknown as AiDoctrineNode | undefined\n const top = (wrapper?.nodes ?? []).filter((id) => validated.nodes[id])\n const sectionNode = top.length === 1 ? (validated.nodes[top[0]] as unknown as AiDoctrineNode) : null\n if (!sectionNode || sectionNode.componentId !== 'section') {\n return {\n value: null,\n violations: [\n {\n rule: null,\n code: 'section-shape',\n message: 'Answer with exactly one Section element inside the document wrapper.',\n },\n ],\n }\n }\n\n // The section's root takes its plan id, so a pass run again finds it, and\n // hangs under the page root; every other minted id stays as minted.\n const minted = top[0]\n const modelIds: Record<string, string> = Object.fromEntries(\n Object.entries(validated.sourceIds).map(([id, source]) => [id, written(source)]),\n )\n modelIds[sectionId] = written(validated.sourceIds[minted] ?? minted)\n const sectionNodes: Record<string, Record<string, unknown>> = {}\n for (const { id, node } of walkTree({\n rootId: minted,\n nodes: validated.nodes as unknown as Record<string, AiDoctrineNode>,\n })) {\n const stored = { ...(node as unknown as Record<string, unknown>) }\n if (id === minted) {\n stored['$id'] = sectionId\n stored['parentId'] = CANVAS_ROOT_ELEMENT_ID\n } else if (stored['parentId'] === minted) {\n stored['parentId'] = sectionId\n }\n sectionNodes[id === minted ? sectionId : id] = stored\n }\n const drawnNodes = isRecord(drawn.tree) && isRecord(drawn.tree['nodes']) ? drawn.tree['nodes'] : {}\n // What the model wrote of one of the section's nodes, by the id the page stores it under.\n const drawnIdOf = (stored: string): string =>\n validated.sourceIds[stored === sectionId ? minted : stored] ?? stored\n // A link that names a section of this page carries the platform's Scroll to\n // element interaction to that section's root, whose id is minted from the\n // plan before the section is built (AGL-3097).\n const sections = input.context.pageSections ?? []\n for (const [id, node] of Object.entries(sectionNodes)) {\n const props = isRecord(node['props']) ? node['props'] : {}\n if (!isAiLinkElement(node['componentId']) || props['screenId'] || props['href']) continue\n const target = aiPageLinkTarget(drawnNodes[drawnIdOf(id)], sections)\n if (target?.kind === 'section') {\n node['interactions'] = [aiPageScrollInteraction(input.sectionIds[target.section], sections[target.section])]\n }\n }\n const section: AiPageSection = { rootId: sectionId, nodes: sectionNodes as unknown as NodesMap, load: null }\n\n const page = aiPageWithSection(input.page, section, input.sectionIds)\n const own = new Set(Object.keys(sectionNodes))\n // The page check reads the section as the page stores it, where the palette\n // validator has already dropped what it could not read, such as a\n // `container` written as text or a link's `scrollTo`; what the model wrote\n // of each of the section's nodes is read from the section as it was drawn\n // (AGL-3078). A link the page already carries to one of its section roots\n // goes there, whichever pass wrote it (AGL-3097).\n const report = validateAiDoctrineTree({ rootId: CANVAS_ROOT_ELEMENT_ID, nodes: page }, 'page', {\n ...input.context,\n writtenNode: (id) => (own.has(id) ? drawnNodes[drawnIdOf(id)] : undefined),\n scrollTargetIds: input.sectionIds,\n })\n // The page check mints its ids afresh: its map leads back to the page's\n // ids, and the section's own lead back to the model's.\n const pageIds = report.tree?.sourceIds ?? {}\n const toModel = (id: string): string | null => {\n const stored = pageIds[id] ?? id\n return own.has(stored) ? (modelIds[stored] ?? stored) : null\n }\n // The page check reads the section as it is stored, where a line the\n // palette validator cut is already cut, so the cut is read from the\n // section's own validation, first (AGL-3076). Copies of one item name one\n // node the model wrote, once.\n //\n // A PASS ANSWERS ONLY FOR THE SECTION IT WROTE (AGL-3078). The page check\n // reads the whole page, so it also names nodes an earlier pass stored; the\n // model is not shown those and cannot mend them, so a finding that names\n // none of this section's nodes is not held against this answer. Dropping\n // it neither hides nor forgives it: the last pass runs the same check on\n // the finished page, where every node is the page's and the review names\n // each one and outlines it. Kept, such a finding would re-ask the model\n // for a node it never saw, refuse the same answer twice, and stop the page\n // with findings whose ids all filtered away — no node ids, and so no\n // outline either.\n const violations: AiDoctrineViolation[] = [\n ...detectCutLines(validated.repairs, drawnNodes, written),\n ...report.violations.flatMap((violation) => {\n if (!violation.nodeIds?.length) return [violation]\n const named = [...new Set(violation.nodeIds.map(toModel).filter((id): id is string => id !== null))]\n return named.length ? [{ ...violation, nodeIds: named }] : []\n }),\n ]\n\n const placed = new Set<string>()\n const bound = new Set<string>()\n for (const node of Object.values(sectionNodes)) {\n const props = isRecord(node['props']) ? node['props'] : {}\n if (node['componentId'] === REUSABLE_INSTANCE_COMPONENT_ID && typeof props[AI_INSTANCE_REF_PROP] === 'string') {\n placed.add(props[AI_INSTANCE_REF_PROP] as string)\n }\n if (node['componentId'] === 'form' && typeof props['formId'] === 'string') bound.add(props['formId'])\n }\n const missing = input.section.uses.filter(\n (ref) => (components.has(ref) && !placed.has(ref)) || (forms.has(ref) && !bound.has(ref)),\n )\n if (missing.length) {\n violations.push({\n rule: 7,\n code: 'plan-reuse-not-placed',\n message: `The confirmed plan places ${missing.join(', ')} in this section, and the section does not. Place each one by its id.`,\n })\n }\n // The count the plan promised, against the section as the page stores it\n // (AGL-3024): the prompt states it, and until now nothing read it back.\n violations.push(\n ...aiPlanItemCountViolations(input.section, {\n rootId: sectionId,\n nodes: sectionNodes as unknown as Record<string, AiDoctrineNode>,\n }),\n )\n\n return {\n value: { ...section, load: report.load },\n violations,\n ...offendingOf(raw, violations),\n }\n }\n}\n"],"names":["REUSABLE_INSTANCE_COMPONENT_ID","CANVAS_ROOT_ELEMENT_ID","aiPageTypeDefinition","parseAiPageJobInputs","aiHomeScreenIds","AI_REASK_OFFENDING_MAX_CHARS","aiAnswerTree","aiNodeTreeContextFromInventory","detectCutLines","detectDisagreeingNodes","isAiLinkElement","validateAiDoctrineTree","walkTree","AI_INSTANCE_REF_PROP","validateAiNodeTree","aiPageLinkTarget","aiPageScrollInteraction","AI_PALETTE_CATALOG","AI_REPEAT_KEY","expandAiRepeatedItems","aiJobBriefLine","aiPlanReferenceLines","aiPlanItemCountViolations","aiPageSectionNodeId","jobId","index","slice","AI_PAGE_SECTION_TOOL","name","description","strict","inputSchema","type","additionalProperties","required","properties","tree","AI_JOB_PAGE_INSTRUCTIONS","text","join","screen","cacheBreakpoint","referenceOf","plan","ref","label","labels","AI_PAGE_SECTION_INLINE_LINE","AI_PAGE_SECTION_REPEAT_SHAPE","AI_PAGE_SECTION_REPEAT_LINE","aiPageSectionSmaller","input","reusableComponents","maxElements","aiPageSectionPrompt","job","inputs","pageType","section","sections","places","uses","length","map","items","built","entry","position","title","slug","purpose","isRecord","value","Array","isArray","aiEmptyPage","$id","componentId","parentId","nodes","aiPageWithSection","page","sectionIds","order","previous","rootId","replaced","Set","id","add","node","Object","entries","has","root","children","filter","Map","at","get","before","findIndex","splice","aiPageCheckContext","inventory","options","brand","theme","colors","fonts","homeScreenIds","pageSections","offendingOf","raw","violations","rawNodes","offending","used","flatMap","violation","nodeIds","undefined","size","JSON","stringify","keys","aiPageSectionCheck","sectionId","components","row","forms","answer","validated","report","disagreeing","drawn","inline","context","noun","ok","written","sourceIds","rule","code","message","detail","error","wrapper","top","sectionNode","minted","modelIds","fromEntries","source","sectionNodes","stored","drawnNodes","drawnIdOf","props","target","kind","load","own","writtenNode","scrollTargetIds","pageIds","toModel","repairs","named","placed","bound","values","missing","push"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,8BAA8B,QAAQ,iDAAgD;AAC/F,SAASC,sBAAsB,QAAQ,2CAA0C;AAGjF,SAASC,oBAAoB,EAAEC,oBAAoB,QAAQ,0BAAsB;AAEjF,SAASC,eAAe,QAA8B,gCAA4B;AAElF,SACEC,4BAA4B,EAC5BC,YAAY,EACZC,8BAA8B,QAEzB,4BAAwB;AAC/B,SACEC,cAAc,EACdC,sBAAsB,EACtBC,eAAe,EACfC,sBAAsB,EACtBC,QAAQ,QAIH,uCAAmC;AAC1C,SAASC,oBAAoB,EAAEC,kBAAkB,QAAQ,6BAAyB;AAClF,SAASC,gBAAgB,EAAEC,uBAAuB,QAAQ,8BAA0B;AAEpF,SAASC,kBAAkB,QAAQ,qCAAiC;AACpE,SAASC,aAAa,EAAEC,qBAAqB,QAAQ,kCAA8B;AAEnF,SAASC,cAAc,EAAEC,oBAAoB,QAAQ,yBAAqB;AAC1E,SAASC,yBAAyB,QAAQ,+BAA2B;AAErE;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4BC,GAED,kFAAkF,GAClF,OAAO,SAASC,oBAAoBC,KAAa,EAAEC,KAAa;IAC9D,OAAO,CAAC,GAAG,EAAED,MAAME,KAAK,CAAC,GAAG,IAAI,SAAS,EAAED,QAAQ,GAAG;AACxD;AAEA,kGAAkG,GAClG,OAAO,MAAME,uBAA+B;IAC1CC,MAAM;IACNC,aAAa;IACbC,QAAQ;IACRC,aAAa;QACXC,MAAM;QACNC,sBAAsB;QACtBC,UAAU;YAAC;SAAO;QAClBC,YAAY;YACVC,MAAM;gBACJJ,MAAM;gBACNH,aACE;YACJ;QACF;IACF;AACF,EAAC;AAED,qGAAqG,GACrG,OAAO,MAAMQ,2BAAqD;IAChE;QACEC,MAAM;YACJ;YACA;YACA;YACA;YACA;YACA;YACA;YACA;SACD,CAACC,IAAI,CAAC;IACT;IACA;QAAED,MAAMrB,mBAAmBuB,MAAM;QAAEC,iBAAiB;IAAK;CAC1D,CAAA;AAED,yFAAyF,GACzF,SAASC,YAAYC,IAAe,EAAEC,GAAW;QACjCD;IAAd,MAAME,SAAQF,eAAAA,KAAKG,MAAM,qBAAXH,YAAa,CAACC,IAAI;IAChC,OAAOC,QAAQ,GAAGD,IAAI,GAAG,EAAEC,MAAM,EAAE,CAAC,GAAGD;AACzC;AAgBA,iGAAiG,GACjG,OAAO,MAAMG,8BACX,wIAAuI;AAEzI,oFAAoF,GACpF,MAAMC,+BAA+B,CAAC,wEAAwE,EAAE9B,cAAc,8FAA8F,CAAC;AAE7N;;;;;;;;;CASC,GACD,OAAO,MAAM+B,8BAA8B,CAAC,4BAA4B,EAAED,6BAA6B,CAAC,CAAC,CAAA;AAEzG;;;;;;;;CAQC,GACD,OAAO,SAASE,qBAAqBC,KAA2E;IAC9G,OAAOA,MAAMC,kBAAkB,KAAK,QAChC,CAAC,6CAA6C,EAAED,MAAME,WAAW,CAAC,kFAAkF,EAAEL,6BAA6B,CAAC,CAAC,GACrL,CAAC,6CAA6C,EAAEG,MAAME,WAAW,CAAC,uHAAuH,CAAC;AAChM;AAEA,kGAAkG,GAClG,OAAO,SAASC,oBAAoBH,KAA+B;IACjE,MAAM,EAAEI,GAAG,EAAEZ,IAAI,EAAEH,MAAM,EAAEf,KAAK,EAAE,GAAG0B;IACrC,MAAMK,SAASrD,qBAAqBoD,IAAIC,MAAM;IAC9C,MAAMxB,OAAO,OAAOwB,WAAW,WAAW,OAAOtD,qBAAqBsD,OAAOC,QAAQ;IACrF,MAAMC,UAAUlB,OAAOmB,QAAQ,CAAClC,MAAM;IACtC,MAAMmC,SAASF,QAAQG,IAAI,CAACC,MAAM,GAC9B,CAAC,WAAW,EAAEJ,QAAQG,IAAI,CAACE,GAAG,CAAC,CAACnB,MAAQF,YAAYC,MAAMC,MAAML,IAAI,CAAC,MAAM,CAAC,CAAC,GAC7E;IACJ,MAAMyB,QAAQN,QAAQM,KAAK,GAAG,CAAC,UAAU,EAAEN,QAAQM,KAAK,CAAC,OAAO,CAAC,GAAG;IACpE,MAAMC,QAAQzB,OAAOmB,QAAQ,CAACjC,KAAK,CAAC,GAAGD,OAAOsC,GAAG,CAAC,CAACG,OAAOC,WAAa,GAAGA,WAAW,EAAE,EAAE,EAAED,MAAMtC,IAAI,EAAE;IACvG,OAAO;QACL,CAAC,OAAO,EAAEY,OAAO4B,KAAK,CAAC,KAAK,EAAE5B,OAAO6B,IAAI,EAAE;WACvCrC,OAAO;YAAC,CAAC,WAAW,EAAEA,KAAKsC,OAAO,EAAE;SAAC,GAAG,EAAE;QAC9ClD,eAAemC;WACZlC,qBAAqBsB;QACxB,CAAC,cAAc,EAAElB,QAAQ,EAAE,IAAI,EAAEe,OAAOmB,QAAQ,CAACG,MAAM,CAAC,GAAG,EAAEJ,QAAQ9B,IAAI,CAAC,EAAE,EAAEgC,SAASI,OAAO;QAC9FC,MAAMH,MAAM,GACR,CAAC,yBAAyB,EAAEG,MAAM1B,IAAI,CAAC,MAAM,gCAAgC,CAAC,GAC9E;WACAY,MAAMC,kBAAkB,KAAK,QAC7B;YAACL;eAAiCW,QAAQM,KAAK,GAAG;gBAACf;aAA4B,GAAG,EAAE;SAAE,GACtF,EAAE;QACN,CAAC,6BAA6B,EAAEE,MAAME,WAAW,CAAC,UAAU,CAAC;KAC9D,CAACd,IAAI,CAAC;AACT;AAUA,SAASgC,SAASC,KAAc;IAC9B,OAAO,OAAOA,UAAU,YAAYA,UAAU,QAAQ,CAACC,MAAMC,OAAO,CAACF;AACvE;AAEA,yDAAyD,GACzD,OAAO,SAASG;IACd,OAAO;QACL,CAAC1E,uBAAuB,EAAE;YACxB2E,KAAK3E;YACL4E,aAAa;YACbC,UAAU;YACVC,OAAO,EAAE;QACX;IACF;AACF;AAEA;;;;;CAKC,GACD,OAAO,SAASC,kBACdC,IAAc,EACdvB,OAAsB,EACtBwB,UAA6B;QAgBhBH,+BAKFI;IAnBX,MAAMJ,QAAiD,CAAC;IACxD,MAAMK,WAAW,AAACH,IAA2D,CAACvB,QAAQ2B,MAAM,CAAC;IAC7F,MAAMC,WAAW,IAAIC;IACrB,IAAIH,UAAU;QACZ,KAAK,MAAM,EAAEI,EAAE,EAAE,IAAI5E,SAAS;YAC5ByE,QAAQ3B,QAAQ2B,MAAM;YACtBN,OAAOE;QACT,GAAI;YACFK,SAASG,GAAG,CAACD;QACf;IACF;IACA,KAAK,MAAM,CAACA,IAAIE,KAAK,IAAIC,OAAOC,OAAO,CAACX,MAAO;QAC7C,IAAI,CAACK,SAASO,GAAG,CAACL,KAAKT,KAAK,CAACS,GAAG,GAAG,aAAME;IAC3C;IACA,MAAMI,QAAOf,gCAAAA,KAAK,CAAC9E,uBAAuB,YAA7B8E,gCAAkCJ,aAAa,CAAC1E,uBAAuB;IACpF,MAAM8F,WAAW,AAACtB,CAAAA,MAAMC,OAAO,CAACoB,IAAI,CAAC,QAAQ,IAAKA,IAAI,CAAC,QAAQ,GAAgB,EAAE,AAAD,EAAGE,MAAM,CACvF,CAACR,KAAOA,OAAO9B,QAAQ2B,MAAM;IAE/B,MAAMF,QAAQ,IAAIc,IAAIf,WAAWnB,GAAG,CAAC,CAACyB,IAAIrB,WAAa;YAACqB;YAAIrB;SAAS;IACrE,MAAM+B,MAAKf,aAAAA,MAAMgB,GAAG,CAACzC,QAAQ2B,MAAM,aAAxBF,aAA6BD,WAAWpB,MAAM;IACzD,MAAMsC,SAASL,SAASM,SAAS,CAAC,CAACb;YAAQL;eAAD,EAACA,aAAAA,MAAMgB,GAAG,CAACX,eAAVL,aAAiB,CAAC,KAAKe;;IAClEH,SAASO,MAAM,CAACF,WAAW,CAAC,IAAIL,SAASjC,MAAM,GAAGsC,QAAQ,GAAG1C,QAAQ2B,MAAM;IAC3EN,KAAK,CAAC9E,uBAAuB,GAAG,aAAK6F;QAAMf,OAAOgB;;IAClD,KAAK,MAAM,CAACP,IAAIE,KAAK,IAAIC,OAAOC,OAAO,CAAClC,QAAQqB,KAAK,EAAG;QACtDA,KAAK,CAACS,GAAG,GAAG,aAAME;IACpB;IACA,OAAOX;AACT;AAEA;;;;;CAKC,GACD,OAAO,SAASwB,mBACdC,SAAiC,EACjCC,UAA0E,CAAC,CAAC;QAOtEA;IALN,OAAO;QACLC,OAAOF,CAAAA,6BAAAA,UAAWG,KAAK,IAAG;YAAEC,QAAQJ,UAAUG,KAAK,CAACC,MAAM;YAAEC,OAAOL,UAAUG,KAAK,CAACE,KAAK;QAAC,IAAI;OAC1FtG,+BAA+BiG;QAClCM,eAAe1G,gBAAgBoG;OAC3BC,QAAQrD,kBAAkB,KAAK,QAAQ;QAAEA,oBAAoB;IAAM,IAAI,CAAC,GACxEqD,EAAAA,oBAAAA,QAAQ9C,QAAQ,qBAAhB8C,kBAAkB3C,MAAM,IAAG;QAAEiD,cAAcN,QAAQ9C,QAAQ;IAAC,IAAI,CAAC;AAEzE;AAiBA,+GAA+G,GAC/G,SAASqD,YAAYC,GAAY,EAAEC,UAA0C;IAC3E,MAAMC,WAAW5C,SAAS0C,QAAQ1C,SAAS0C,GAAG,CAAC,QAAQ,IAAIA,GAAG,CAAC,QAAQ,GAAG,CAAC;IAC3E,MAAMG,YAAqC,CAAC;IAC5C,IAAIC,OAAO;IACX,KAAK,MAAM7B,MAAM,IAAID,IAAI2B,WAAWI,OAAO,CAAC,CAACC;YAAcA;gBAAAA,qBAAAA,UAAUC,OAAO,YAAjBD,qBAAqB,EAAE;QAAI;QACpF,IAAIJ,QAAQ,CAAC3B,GAAG,KAAKiC,WAAW;QAChC,MAAMC,OAAOC,KAAKC,SAAS,CAACT,QAAQ,CAAC3B,GAAG,EAAE1B,MAAM,GAAG0B,GAAG1B,MAAM,GAAG;QAC/D,IAAIuD,OAAOK,OAAOrH,8BAA8B;QAChD+G,SAAS,CAAC5B,GAAG,GAAG2B,QAAQ,CAAC3B,GAAG;QAC5B6B,QAAQK;IACV;IACA,OAAO/B,OAAOkC,IAAI,CAACT,WAAWtD,MAAM,GAAG;QAAEsD;IAAU,IAAI,CAAC;AAC1D;AAEA;;;;;;;;;;CAUC,GACD,OAAO,SAASU,mBAAmB3E,KAA8B;;QAEnCA,kBACLA;IAFvB,MAAM4E,YAAY5E,MAAM+B,UAAU,CAAC/B,MAAM1B,KAAK,CAAC;IAC/C,MAAMuG,aAAa,IAAIzC,IAAI,UAACpC,mBAAAA,MAAMqD,SAAS,qBAAfrD,iBAAiB6E,UAAU,mBAAI,EAAE,EAAEjE,GAAG,CAAC,CAACkE,MAAQA,IAAIzC,EAAE;IAClF,MAAM0C,QAAQ,IAAI3C,IAAI,WAACpC,oBAAAA,MAAMqD,SAAS,qBAAfrD,kBAAiB+E,KAAK,oBAAI,EAAE,EAAEnE,GAAG,CAAC,CAACkE,MAAQA,IAAIzC,EAAE;IACxE,OAAO,CAAC2C;kBAmDwBC,6BAsBbjF;YA0BDkF;QAlGhB,MAAMpB,MAAM3G,aAAa6H;QACzB,yEAAyE;QACzE,sEAAsE;QACtE,iEAAiE;QACjE,MAAMG,cAAc7H,uBAAuBwG;QAC3C,IAAIqB,YAAYxE,MAAM,EAAE;YACtB,OAAO;gBAAEU,OAAO;gBAAM0C,YAAYoB;eAAgBtB,YAAYC,KAAKqB;QACrE;QACA,MAAMC,QAAQpH,sBAAsB8F,KAAK;YAAEuB,QAAQrF,MAAMsF,OAAO,CAACrF,kBAAkB,KAAK;YAAOsF,MAAM;QAAU;QAC/G,IAAIH,MAAMI,EAAE,KAAK,OAAO;YACtB,OAAO;gBAAEnE,OAAO;gBAAM0C,YAAYqB,MAAMrB,UAAU;eAAKF,YAAYC,KAAKsB,MAAMrB,UAAU;QAC1F;QACA,uFAAuF;QACvF,MAAM0B,UAAU,CAACpD;gBAAuB+C;oBAAAA,sBAAAA,MAAMM,SAAS,CAACrD,GAAG,YAAnB+C,sBAAuB/C;;QAC/D,MAAM4C,YAAYtH,mBAAmByH,MAAMnG,IAAI,EAAE,UAAUe,MAAMsF,OAAO;QACxE,IAAIL,UAAUO,EAAE,KAAK,OAAO;YAC1B,OAAO;gBACLnE,OAAO;gBACP0C,YAAY;oBACV;wBACE4B,MAAM;wBACNC,MAAM,CAAC,KAAK,EAAEX,UAAUW,IAAI,EAAE;wBAC9BC,SAAS;wBACTC,QAAQb,UAAUc,KAAK;oBACzB;iBACD;YACH;QACF;QACA,MAAMC,UAAUf,UAAUrD,KAAK,CAACqD,UAAU/C,MAAM,CAAC;QACjD,MAAM+D,MAAM,SAACD,2BAAAA,QAASpE,KAAK,mBAAI,EAAE,EAAEiB,MAAM,CAAC,CAACR,KAAO4C,UAAUrD,KAAK,CAACS,GAAG;QACrE,MAAM6D,cAAcD,IAAItF,MAAM,KAAK,IAAKsE,UAAUrD,KAAK,CAACqE,GAAG,CAAC,EAAE,CAAC,GAAiC;QAChG,IAAI,CAACC,eAAeA,YAAYxE,WAAW,KAAK,WAAW;YACzD,OAAO;gBACLL,OAAO;gBACP0C,YAAY;oBACV;wBACE4B,MAAM;wBACNC,MAAM;wBACNC,SAAS;oBACX;iBACD;YACH;QACF;QAEA,0EAA0E;QAC1E,oEAAoE;QACpE,MAAMM,SAASF,GAAG,CAAC,EAAE;QACrB,MAAMG,WAAmC5D,OAAO6D,WAAW,CACzD7D,OAAOC,OAAO,CAACwC,UAAUS,SAAS,EAAE9E,GAAG,CAAC,CAAC,CAACyB,IAAIiE,OAAO,GAAK;gBAACjE;gBAAIoD,QAAQa;aAAQ;QAEjFF,QAAQ,CAACxB,UAAU,GAAGa,SAAQR,8BAAAA,UAAUS,SAAS,CAACS,OAAO,YAA3BlB,8BAA+BkB;QAC7D,MAAMI,eAAwD,CAAC;QAC/D,KAAK,MAAM,EAAElE,EAAE,EAAEE,IAAI,EAAE,IAAI9E,SAAS;YAClCyE,QAAQiE;YACRvE,OAAOqD,UAAUrD,KAAK;QACxB,GAAI;YACF,MAAM4E,SAAS,aAAMjE;YACrB,IAAIF,OAAO8D,QAAQ;gBACjBK,MAAM,CAAC,MAAM,GAAG5B;gBAChB4B,MAAM,CAAC,WAAW,GAAG1J;YACvB,OAAO,IAAI0J,MAAM,CAAC,WAAW,KAAKL,QAAQ;gBACxCK,MAAM,CAAC,WAAW,GAAG5B;YACvB;YACA2B,YAAY,CAAClE,OAAO8D,SAASvB,YAAYvC,GAAG,GAAGmE;QACjD;QACA,MAAMC,aAAarF,SAASgE,MAAMnG,IAAI,KAAKmC,SAASgE,MAAMnG,IAAI,CAAC,QAAQ,IAAImG,MAAMnG,IAAI,CAAC,QAAQ,GAAG,CAAC;QAClG,0FAA0F;QAC1F,MAAMyH,YAAY,CAACF;gBACjBvB;oBAAAA,wBAAAA,UAAUS,SAAS,CAACc,WAAW5B,YAAYuB,SAASK,OAAO,YAA3DvB,wBAA+DuB;;QACjE,4EAA4E;QAC5E,0EAA0E;QAC1E,+CAA+C;QAC/C,MAAMhG,YAAWR,8BAAAA,MAAMsF,OAAO,CAAC1B,YAAY,YAA1B5D,8BAA8B,EAAE;QACjD,KAAK,MAAM,CAACqC,IAAIE,KAAK,IAAIC,OAAOC,OAAO,CAAC8D,cAAe;YACrD,MAAMI,QAAQvF,SAASmB,IAAI,CAAC,QAAQ,IAAIA,IAAI,CAAC,QAAQ,GAAG,CAAC;YACzD,IAAI,CAAChF,gBAAgBgF,IAAI,CAAC,cAAc,KAAKoE,KAAK,CAAC,WAAW,IAAIA,KAAK,CAAC,OAAO,EAAE;YACjF,MAAMC,SAAShJ,iBAAiB6I,UAAU,CAACC,UAAUrE,IAAI,EAAE7B;YAC3D,IAAIoG,CAAAA,0BAAAA,OAAQC,IAAI,MAAK,WAAW;gBAC9BtE,IAAI,CAAC,eAAe,GAAG;oBAAC1E,wBAAwBmC,MAAM+B,UAAU,CAAC6E,OAAOrG,OAAO,CAAC,EAAEC,QAAQ,CAACoG,OAAOrG,OAAO,CAAC;iBAAE;YAC9G;QACF;QACA,MAAMA,UAAyB;YAAE2B,QAAQ0C;YAAWhD,OAAO2E;YAAqCO,MAAM;QAAK;QAE3G,MAAMhF,OAAOD,kBAAkB7B,MAAM8B,IAAI,EAAEvB,SAASP,MAAM+B,UAAU;QACpE,MAAMgF,MAAM,IAAI3E,IAAII,OAAOkC,IAAI,CAAC6B;QAChC,4EAA4E;QAC5E,kEAAkE;QAClE,2EAA2E;QAC3E,0EAA0E;QAC1E,0EAA0E;QAC1E,kDAAkD;QAClD,MAAMrB,SAAS1H,uBAAuB;YAAE0E,QAAQpF;YAAwB8E,OAAOE;QAAK,GAAG,QAAQ,aAC1F9B,MAAMsF,OAAO;YAChB0B,aAAa,CAAC3E,KAAQ0E,IAAIrE,GAAG,CAACL,MAAMoE,UAAU,CAACC,UAAUrE,IAAI,GAAGiC;YAChE2C,iBAAiBjH,MAAM+B,UAAU;;QAEnC,wEAAwE;QACxE,uDAAuD;QACvD,MAAMmF,oBAAUhC,eAAAA,OAAOjG,IAAI,qBAAXiG,aAAaQ,SAAS,oBAAI,CAAC;QAC3C,MAAMyB,UAAU,CAAC9E;gBACA6E,aACWd;YAD1B,MAAMI,UAASU,cAAAA,OAAO,CAAC7E,GAAG,YAAX6E,cAAe7E;YAC9B,OAAO0E,IAAIrE,GAAG,CAAC8D,WAAWJ,mBAAAA,QAAQ,CAACI,OAAO,YAAhBJ,mBAAoBI,SAAU;QAC1D;QACA,qEAAqE;QACrE,oEAAoE;QACpE,0EAA0E;QAC1E,8BAA8B;QAC9B,EAAE;QACF,0EAA0E;QAC1E,2EAA2E;QAC3E,yEAAyE;QACzE,yEAAyE;QACzE,yEAAyE;QACzE,yEAAyE;QACzE,wEAAwE;QACxE,2EAA2E;QAC3E,qEAAqE;QACrE,kBAAkB;QAClB,MAAMzC,aAAoC;eACrC1G,eAAe4H,UAAUmC,OAAO,EAAEX,YAAYhB;eAC9CP,OAAOnB,UAAU,CAACI,OAAO,CAAC,CAACC;oBACvBA;gBAAL,IAAI,GAACA,qBAAAA,UAAUC,OAAO,qBAAjBD,mBAAmBzD,MAAM,GAAE,OAAO;oBAACyD;iBAAU;gBAClD,MAAMiD,QAAQ;uBAAI,IAAIjF,IAAIgC,UAAUC,OAAO,CAACzD,GAAG,CAACuG,SAAStE,MAAM,CAAC,CAACR,KAAqBA,OAAO;iBAAO;gBACpG,OAAOgF,MAAM1G,MAAM,GAAG;oBAAC,aAAKyD;wBAAWC,SAASgD;;iBAAQ,GAAG,EAAE;YAC/D;SACD;QAED,MAAMC,SAAS,IAAIlF;QACnB,MAAMmF,QAAQ,IAAInF;QAClB,KAAK,MAAMG,QAAQC,OAAOgF,MAAM,CAACjB,cAAe;YAC9C,MAAMI,QAAQvF,SAASmB,IAAI,CAAC,QAAQ,IAAIA,IAAI,CAAC,QAAQ,GAAG,CAAC;YACzD,IAAIA,IAAI,CAAC,cAAc,KAAK1F,kCAAkC,OAAO8J,KAAK,CAACjJ,qBAAqB,KAAK,UAAU;gBAC7G4J,OAAOhF,GAAG,CAACqE,KAAK,CAACjJ,qBAAqB;YACxC;YACA,IAAI6E,IAAI,CAAC,cAAc,KAAK,UAAU,OAAOoE,KAAK,CAAC,SAAS,KAAK,UAAUY,MAAMjF,GAAG,CAACqE,KAAK,CAAC,SAAS;QACtG;QACA,MAAMc,UAAUzH,MAAMO,OAAO,CAACG,IAAI,CAACmC,MAAM,CACvC,CAACpD,MAAQ,AAACoF,WAAWnC,GAAG,CAACjD,QAAQ,CAAC6H,OAAO5E,GAAG,CAACjD,QAAUsF,MAAMrC,GAAG,CAACjD,QAAQ,CAAC8H,MAAM7E,GAAG,CAACjD;QAEtF,IAAIgI,QAAQ9G,MAAM,EAAE;YAClBoD,WAAW2D,IAAI,CAAC;gBACd/B,MAAM;gBACNC,MAAM;gBACNC,SAAS,CAAC,0BAA0B,EAAE4B,QAAQrI,IAAI,CAAC,MAAM,qEAAqE,CAAC;YACjI;QACF;QACA,yEAAyE;QACzE,wEAAwE;QACxE2E,WAAW2D,IAAI,IACVvJ,0BAA0B6B,MAAMO,OAAO,EAAE;YAC1C2B,QAAQ0C;YACRhD,OAAO2E;QACT;QAGF,OAAO;YACLlF,OAAO,aAAKd;gBAASuG,MAAM5B,OAAO4B,IAAI;;YACtC/C;WACGF,YAAYC,KAAKC;IAExB;AACF"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-page-sections.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 { REUSABLE_INSTANCE_COMPONENT_ID } from '@aglyn/aglyn/app-utils/reusable-component-keys'\nimport { CANVAS_ROOT_ELEMENT_ID } from '@aglyn/aglyn/foundation/constants/canvas'\nimport type { NodesMap } from '@aglyn/aglyn/types/nodes'\nimport type { AiBuildPlanScreen, AiBuildPlanSection } from '../model/ai-build-plan'\nimport { aiPageTypeDefinition, parseAiPageJobInputs } from '../model/ai-page-job'\nimport type { AiJob, AiJobPlan } from '../model/ai-jobs.types'\nimport { aiHomeScreenIds, type AiSiteInventory } from '../model/ai-site-inventory'\nimport type { AiTool } from '../providers/contract'\nimport {\n AI_REASK_OFFENDING_MAX_CHARS,\n aiAnswerTree,\n aiNodeTreeContextFromInventory,\n type AiGenerationCheck,\n} from '../runtime/ai-doctrine'\nimport {\n detectCutLines,\n detectDisagreeingNodes,\n isAiLinkElement,\n validateAiDoctrineTree,\n walkTree,\n type AiDoctrineNode,\n type AiDoctrineTreeContext,\n type AiDoctrineViolation,\n} from '../runtime/ai-doctrine-validators'\nimport { AI_INSTANCE_REF_PROP, validateAiNodeTree } from '../runtime/ai-node-tree'\nimport { aiPageLinkTarget, aiPageScrollInteraction } from '../runtime/ai-page-links'\nimport type { AiLoadEstimate } from '../runtime/ai-palette'\nimport { AI_PALETTE_CATALOG } from '../runtime/ai-palette.generated'\nimport { AI_REPEAT_KEY, expandAiRepeatedItems } from '../runtime/ai-repeated-items'\nimport type { AiSystemBlock } from '../runtime/ai-runtime'\nimport { aiJobBriefLine, aiPlanReferenceLines } from './ai-job-generation'\nimport { aiPlanItemCountViolations } from './ai-job-plan-conformance'\n\n/**\n * A page built one section at a time (AGL-2907): what one section pass asks\n * for, how its answer is held to the building rules as part of the page, and\n * how it joins the page.\n *\n * Pure: no Firestore and no provider, so the page step, its specs and the\n * evals read one definition of a section.\n *\n * ── Held as part of the page ─────────────────────────────────────────────\n *\n * A section is checked twice over, by validators the doctrine already has:\n * the palette validator on the section as the model wrote it, then the\n * doctrine's whole page check on the page built so far with the section\n * added. So the page's one main, its one h1 and its heading outline, repeats\n * across sections, and its weight against the page budget are held on every\n * pass, not only on the last. The violations name the section's own nodes,\n * by the ids the model wrote, so a re-ask quotes this section and nothing\n * else — the page is never sent back.\n *\n * ── A repeated item written once (AGL-3053) ──────────────────────────────\n *\n * Where the workspace keeps no reusable components, an answer writes a\n * repeated item once and lists its copies' values, and the check draws the\n * copies before either validator reads the section (`ai-repeated-items.ts`).\n * Both then read the section as the page stores it, so the page's rules hold\n * on every copy exactly as on an item written out in full, and a finding on a\n * copy names the node the model wrote. Where the workspace keeps components,\n * an item written once is refused: it places instances.\n */\n\n/** The section root id a pass writes, from the job and the plan section's index. */\nexport function aiPageSectionNodeId(jobId: string, index: number): string {\n return `ai-${jobId.slice(0, 12)}-section-${index + 1}`\n}\n\n/** The strict tool a section arrives through. The node map rides as JSON text, as a page's does. */\nexport const AI_PAGE_SECTION_TOOL: AiTool = {\n name: 'submit_section',\n description: 'Submit one section of the page as one flat node map.',\n strict: true,\n inputSchema: {\n type: 'object',\n additionalProperties: false,\n required: ['tree'],\n properties: {\n tree: {\n type: 'string',\n description:\n 'The section as JSON: {\"rootId\": \"<id>\", \"nodes\": {\"<id>\": {\"componentId\": \"...\", \"props\": {...}, \"sx\": {...}, \"nodes\": [\"<child id>\", ...]}}}. The root is the document wrapper (componentId \"div\") holding one Section.',\n },\n },\n },\n}\n\n/** The page step's own instructions, cached after the doctrine, with the screen palette after them. */\nexport const AI_JOB_PAGE_INSTRUCTIONS: readonly AiSystemBlock[] = [\n {\n text: [\n 'You build a web page one section at a time, in the order a confirmed plan gives. Each answer is ONE section: call submit_section with a flat node map whose root is the document wrapper (componentId \"div\") holding exactly one Section (section).',\n 'The first section is the top of the page and holds the page’s one h1, its title. Every later section opens with an h2, and headings below it step down one level at a time.',\n 'Place what the section’s plan line names. A component the site has is placed as an instance, never drawn again: componentId \"reusableInstance\", props {\"refId\": \"<component id>\", \"propValues\": {…}} filling the props the inventory lists. A form the site has is a Form (form) whose formId is that form’s id. Another screen of the site is linked by its screen id, with a Screen Link (muiScreenLink) or a Button (muiButton) whose screenId is that id.',\n 'Where the request says the site keeps no saved forms or reusable components, draw them in the section instead: a repeated item is written once, and a form is a Form (form) with no formId, a formName saying what it collects and a submitLabel, holding one Form Field (formField) for each answer with its fieldName, label and fieldType.',\n 'Lay the section out with a Container, a Stack or a Grid (\"container\": true, \"spacing\": 3) of Grid items sized like \"xs:12 md:4\", and size nothing with a fixed width. Colors come from the theme’s palette tokens such as primary.main, spacing from the spacing scale, and type from the text variants.',\n 'The page renders inside the site’s layout, so a section is never a header, navigation or footer.',\n 'A picture is an Image (image) with alt text that says what it should show and no src, for the site owner to fill from the media library.',\n 'Write plain, specific copy in the site’s voice, with no filler. Where the brief leaves out a fact, such as a price, a phone number or an address, write the gap in square brackets instead of inventing it.',\n ].join('\\n'),\n },\n { text: AI_PALETTE_CATALOG.screen, cacheBreakpoint: true },\n]\n\n/** A plan reference as the request names it: the id with the name it was planned under. */\nfunction referenceOf(plan: AiJobPlan, ref: string): string {\n const label = plan.labels?.[ref]\n return label ? `${ref} (\"${label}\")` : ref\n}\n\nexport interface AiPageSectionPromptInput {\n job: Pick<AiJob, 'brief' | 'inputs'>\n plan: AiJobPlan\n screen: AiBuildPlanScreen\n index: number\n /** The most elements the section may carry at this pass's answer ceiling. */\n maxElements: number\n /**\n * Whether the workspace keeps reusable components and saved forms\n * (AGL-3030); `false` asks for the section built inline. Absent is `true`.\n */\n reusableComponents?: boolean\n}\n\n/** What a section request says where the workspace keeps no reusable components or saved forms. */\nexport const AI_PAGE_SECTION_INLINE_LINE =\n 'This site keeps no saved forms or reusable components: write a repeated item once, and draw a form as a Form holding its Form Fields.'\n\n/** How a repeated item is written once, in the words a request and a re-ask share. */\nconst AI_PAGE_SECTION_REPEAT_SHAPE = `put {{1}}, {{2}}… where its copies differ, and give its outermost node \"${AI_REPEAT_KEY}\", one list of values a copy, in that order, as [[\"Title 1\", \"Text 1\"], [\"Title 2\", \"Text 2\"]]`\n\n/**\n * How a repeated item is written once (AGL-3053): the item's subtree once,\n * with placeholders where its copies differ and their values listed, which\n * the section check draws into its copies (`ai-repeated-items.ts`). It rides\n * the request of a section whose plan line shows items on a workspace that\n * keeps no reusable components, and no other: a section with nothing to repeat\n * pays nothing for it, and the cached prefix every workspace shares says only\n * that a repeated item is written once.\n */\nexport const AI_PAGE_SECTION_REPEAT_LINE = `Write a repeated item once: ${AI_PAGE_SECTION_REPEAT_SHAPE}.`\n\n/**\n * How a workspace that keeps reusable components writes a repeated item\n * (AGL-3143 §10): as one instance of a component the section already places,\n * repeated, rather than the item's subtree drawn once per copy.\n *\n * It rides the request of a section whose plan line shows items AND names a\n * record to place, on the same terms the repeat line rides a Free one: a\n * section with nothing to repeat, or nothing to repeat it FROM, pays nothing\n * for it.\n *\n * ## Why a paid section could not be built without it (AGL-3024)\n *\n * The Free path is reconciled at both ends — its plan is held to the sections\n * its wall pays for (AGL-3070) and its request is shown the repeat shape\n * (AGL-3053). The paid path was reconciled at neither: `AI_BUILD_PLAN_LIMITS`\n * lets a plan promise up to `items: 500` in one section, the pass that builds\n * it fits `aiJobPageSectionMaxElements` — fifteen at the balanced tier — and\n * the request said only how many elements to keep under, never how to get\n * there. A six-card grid measured live on 2026-09-21 was drawn inline, ran\n * past its ceiling, and was cut off; the re-ask then said to place an instance\n * \"of a component the site has\" without naming one, so it was cut off too, and\n * `AI_GENERATION_MAX_ATTEMPTS` ended the job with no page. A four-card grid on\n * the same site had fitted, which is why this went unseen.\n *\n * ⛔ This does NOT make the plan and the pass agree about what fits — a plan\n * may still promise more items than any ceiling can carry, and this line only\n * gives the pass its cheapest way to try. That bound belongs on the plan step\n * and is deliberately not taken here.\n */\nexport const AI_PAGE_SECTION_INSTANCE_LINE =\n 'Write a repeated item once: place one instance of the component this section places and repeat that instance, rather than drawing the item again for each copy.'\n\n/**\n * What a pass whose answer ran past its ceiling is told makes a section\n * smaller (AGL-3042): fewer elements, down to the budget the request gave,\n * shorter copy, and a repeated item drawn no more than once — placed as an\n * instance where the workspace keeps reusable components, and written once\n * where it keeps none (AGL-3053), with the shape spelled out, since a section\n * whose plan line showed no items was never told it. It rides the re-ask,\n * never the first request, so a pass that fits sends no byte more.\n */\nexport function aiPageSectionSmaller(input: Pick<AiPageSectionPromptInput, 'maxElements' | 'reusableComponents'>): string {\n return input.reusableComponents === false\n ? `Make it smaller: use fewer elements, at most ${input.maxElements}; write shorter copy; and write a repeated item once instead of drawing it again: ${AI_PAGE_SECTION_REPEAT_SHAPE}.`\n : `Make it smaller: use fewer elements, at most ${input.maxElements}; write shorter copy; and place a repeated item as an instance of a component the site has instead of drawing it again.`\n}\n\n/** One pass's user turn: the page, the brief, the plan, this section and what is built above it. */\nexport function aiPageSectionPrompt(input: AiPageSectionPromptInput): string {\n const { job, plan, screen, index } = input\n const inputs = parseAiPageJobInputs(job.inputs)\n const type = typeof inputs === 'string' ? null : aiPageTypeDefinition(inputs.pageType)\n const section = screen.sections[index] as AiBuildPlanSection\n const places = section.uses.length\n ? ` It places ${section.uses.map((ref) => referenceOf(plan, ref)).join(', ')}.`\n : ''\n const items = section.items ? ` It shows ${section.items} items.` : ''\n const built = screen.sections.slice(0, index).map((entry, position) => `${position + 1}. ${entry.name}`)\n return [\n `Page: \"${screen.title}\" at ${screen.slug}`,\n ...(type ? [`Page type: ${type.purpose}`] : []),\n aiJobBriefLine(job),\n ...aiPlanReferenceLines(plan),\n `Build section ${index + 1} of ${screen.sections.length}: \"${section.name}\".${places}${items}`,\n built.length\n ? `Already built, above it: ${built.join('; ')}. The page’s h1 is in section 1.`\n : 'Nothing is built yet: this section holds the page’s h1.',\n ...(input.reusableComponents === false\n ? [AI_PAGE_SECTION_INLINE_LINE, ...(section.items ? [AI_PAGE_SECTION_REPEAT_LINE] : [])]\n : section.items && section.uses.length\n ? [AI_PAGE_SECTION_INSTANCE_LINE]\n : []),\n `Keep this section to at most ${input.maxElements} elements.`,\n ].join('\\n')\n}\n\n/** A validated section: its nodes keyed as the page stores them, its root under the page root. */\nexport interface AiPageSection {\n rootId: string\n nodes: NodesMap\n /** The page's weight with the section added, as the doctrine measured it. */\n load: AiLoadEstimate | null\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/** A page with nothing on it yet: the document wrapper. */\nexport function aiEmptyPage(): NodesMap {\n return {\n [CANVAS_ROOT_ELEMENT_ID]: {\n $id: CANVAS_ROOT_ELEMENT_ID,\n componentId: 'div',\n parentId: null,\n nodes: [],\n },\n } as unknown as NodesMap\n}\n\n/**\n * The page with a section in its plan position: after every section with a\n * lower index and before every one with a higher, the page's other children\n * — whatever a member placed — kept where they are. A section already on the\n * page is replaced, never doubled.\n */\nexport function aiPageWithSection(\n page: NodesMap,\n section: AiPageSection,\n sectionIds: readonly string[],\n): NodesMap {\n const nodes: Record<string, Record<string, unknown>> = {}\n const previous = (page as unknown as Record<string, Record<string, unknown>>)[section.rootId]\n const replaced = new Set<string>()\n if (previous) {\n for (const { id } of walkTree({\n rootId: section.rootId,\n nodes: page as unknown as Record<string, AiDoctrineNode>,\n })) {\n replaced.add(id)\n }\n }\n for (const [id, node] of Object.entries(page)) {\n if (!replaced.has(id)) nodes[id] = { ...(node as unknown as Record<string, unknown>) }\n }\n const root = nodes[CANVAS_ROOT_ELEMENT_ID] ?? (aiEmptyPage()[CANVAS_ROOT_ELEMENT_ID] as unknown as Record<string, unknown>)\n const children = (Array.isArray(root['nodes']) ? (root['nodes'] as string[]) : []).filter(\n (id) => id !== section.rootId,\n )\n const order = new Map(sectionIds.map((id, position) => [id, position]))\n const at = order.get(section.rootId) ?? sectionIds.length\n const before = children.findIndex((id) => (order.get(id) ?? -1) > at)\n children.splice(before === -1 ? children.length : before, 0, section.rootId)\n nodes[CANVAS_ROOT_ELEMENT_ID] = { ...root, nodes: children }\n for (const [id, node] of Object.entries(section.nodes)) {\n nodes[id] = { ...(node as unknown as Record<string, unknown>) }\n }\n return nodes as unknown as NodesMap\n}\n\n/**\n * What a tree generated against this inventory may reference, the brand it is\n * held to, where the workspace keeps no reusable components or saved forms\n * (AGL-3030) that the page is built inline, and the sections its plan names,\n * which a link may take a visitor to (AGL-3097).\n */\nexport function aiPageCheckContext(\n inventory: AiSiteInventory | null,\n options: { reusableComponents?: boolean; sections?: readonly string[] } = {},\n): AiDoctrineTreeContext {\n return {\n brand: inventory?.theme ? { colors: inventory.theme.colors, fonts: inventory.theme.fonts } : null,\n ...aiNodeTreeContextFromInventory(inventory),\n homeScreenIds: aiHomeScreenIds(inventory),\n ...(options.reusableComponents === false ? { reusableComponents: false } : {}),\n ...(options.sections?.length ? { pageSections: options.sections } : {}),\n }\n}\n\nexport interface AiPageSectionCheckInput {\n /** The page as stored so far; an empty page on the first pass. */\n page: NodesMap\n /** Every section root id of the plan, in order. */\n sectionIds: readonly string[]\n index: number\n context: AiDoctrineTreeContext\n /**\n * The plan line this pass answers for: what it places by inventory id, and\n * how many items it promised, which the answer is held to (AGL-3024).\n */\n section: Pick<AiBuildPlanSection, 'name' | 'uses' | 'items'>\n inventory: AiSiteInventory | null\n}\n\n/** The parts of an answer the violations name, as the model wrote them, capped as the doctrine caps a re-ask. */\nfunction offendingOf(raw: unknown, violations: readonly AiDoctrineViolation[]): { offending?: Record<string, unknown> } {\n const rawNodes = isRecord(raw) && isRecord(raw['nodes']) ? raw['nodes'] : {}\n const offending: Record<string, unknown> = {}\n let used = 2\n for (const id of new Set(violations.flatMap((violation) => violation.nodeIds ?? []))) {\n if (rawNodes[id] === undefined) continue\n const size = JSON.stringify(rawNodes[id]).length + id.length + 4\n if (used + size > AI_REASK_OFFENDING_MAX_CHARS) break\n offending[id] = rawNodes[id]\n used += size\n }\n return Object.keys(offending).length ? { offending } : {}\n}\n\n/**\n * The check a section's answer is held to: a repeated item written once drawn\n * into its copies (AGL-3053), then the palette validator on the section, with\n * any line it cut at its ceiling refused (AGL-3076), the doctrine's page check\n * on the page with the section added, and the plan line — every component it\n * names placed as an instance, every form bound by its id (rule 7), and as\n * many items as it promised (AGL-3024). Violations\n * name the section's own nodes by the ids the MODEL wrote, and a copy's nodes\n * by the item's; one that names only nodes an earlier pass stored belongs to\n * the page rather than to this answer, and is left to the last pass (AGL-3078).\n */\nexport function aiPageSectionCheck(input: AiPageSectionCheckInput): AiGenerationCheck<AiPageSection> {\n const sectionId = input.sectionIds[input.index]\n const components = new Set((input.inventory?.components ?? []).map((row) => row.id))\n const forms = new Set((input.inventory?.forms ?? []).map((row) => row.id))\n return (answer) => {\n const raw = aiAnswerTree(answer)\n // Before anything is drawn or dropped: a repeated item written and never\n // placed is content the section loses silently, and a child named and\n // never written ends the pass with nothing to answer (AGL-3143).\n const disagreeing = detectDisagreeingNodes(raw)\n if (disagreeing.length) {\n return { value: null, violations: disagreeing, ...offendingOf(raw, disagreeing) }\n }\n const drawn = expandAiRepeatedItems(raw, { inline: input.context.reusableComponents === false, noun: 'section' })\n if (drawn.ok === false) {\n return { value: null, violations: drawn.violations, ...offendingOf(raw, drawn.violations) }\n }\n // Every id below is the drawn tree's; a copy's leads back to the node the model wrote.\n const written = (id: string): string => drawn.sourceIds[id] ?? id\n const validated = validateAiNodeTree(drawn.tree, 'screen', input.context)\n if (validated.ok === false) {\n return {\n value: null,\n violations: [\n {\n rule: null,\n code: `tree-${validated.code}`,\n message: 'The answer could not be used as a section.',\n detail: validated.error,\n },\n ],\n }\n }\n const wrapper = validated.nodes[validated.rootId] as unknown as AiDoctrineNode | undefined\n const top = (wrapper?.nodes ?? []).filter((id) => validated.nodes[id])\n const sectionNode = top.length === 1 ? (validated.nodes[top[0]] as unknown as AiDoctrineNode) : null\n if (!sectionNode || sectionNode.componentId !== 'section') {\n return {\n value: null,\n violations: [\n {\n rule: null,\n code: 'section-shape',\n message: 'Answer with exactly one Section element inside the document wrapper.',\n },\n ],\n }\n }\n\n // The section's root takes its plan id, so a pass run again finds it, and\n // hangs under the page root; every other minted id stays as minted.\n const minted = top[0]\n const modelIds: Record<string, string> = Object.fromEntries(\n Object.entries(validated.sourceIds).map(([id, source]) => [id, written(source)]),\n )\n modelIds[sectionId] = written(validated.sourceIds[minted] ?? minted)\n const sectionNodes: Record<string, Record<string, unknown>> = {}\n for (const { id, node } of walkTree({\n rootId: minted,\n nodes: validated.nodes as unknown as Record<string, AiDoctrineNode>,\n })) {\n const stored = { ...(node as unknown as Record<string, unknown>) }\n if (id === minted) {\n stored['$id'] = sectionId\n stored['parentId'] = CANVAS_ROOT_ELEMENT_ID\n } else if (stored['parentId'] === minted) {\n stored['parentId'] = sectionId\n }\n sectionNodes[id === minted ? sectionId : id] = stored\n }\n const drawnNodes = isRecord(drawn.tree) && isRecord(drawn.tree['nodes']) ? drawn.tree['nodes'] : {}\n // What the model wrote of one of the section's nodes, by the id the page stores it under.\n const drawnIdOf = (stored: string): string =>\n validated.sourceIds[stored === sectionId ? minted : stored] ?? stored\n // A link that names a section of this page carries the platform's Scroll to\n // element interaction to that section's root, whose id is minted from the\n // plan before the section is built (AGL-3097).\n const sections = input.context.pageSections ?? []\n for (const [id, node] of Object.entries(sectionNodes)) {\n const props = isRecord(node['props']) ? node['props'] : {}\n if (!isAiLinkElement(node['componentId']) || props['screenId'] || props['href']) continue\n const target = aiPageLinkTarget(drawnNodes[drawnIdOf(id)], sections)\n if (target?.kind === 'section') {\n node['interactions'] = [aiPageScrollInteraction(input.sectionIds[target.section], sections[target.section])]\n }\n }\n const section: AiPageSection = { rootId: sectionId, nodes: sectionNodes as unknown as NodesMap, load: null }\n\n const page = aiPageWithSection(input.page, section, input.sectionIds)\n const own = new Set(Object.keys(sectionNodes))\n // The page check reads the section as the page stores it, where the palette\n // validator has already dropped what it could not read, such as a\n // `container` written as text or a link's `scrollTo`; what the model wrote\n // of each of the section's nodes is read from the section as it was drawn\n // (AGL-3078). A link the page already carries to one of its section roots\n // goes there, whichever pass wrote it (AGL-3097).\n const report = validateAiDoctrineTree({ rootId: CANVAS_ROOT_ELEMENT_ID, nodes: page }, 'page', {\n ...input.context,\n writtenNode: (id) => (own.has(id) ? drawnNodes[drawnIdOf(id)] : undefined),\n scrollTargetIds: input.sectionIds,\n })\n // The page check mints its ids afresh: its map leads back to the page's\n // ids, and the section's own lead back to the model's.\n const pageIds = report.tree?.sourceIds ?? {}\n const toModel = (id: string): string | null => {\n const stored = pageIds[id] ?? id\n return own.has(stored) ? (modelIds[stored] ?? stored) : null\n }\n // The page check reads the section as it is stored, where a line the\n // palette validator cut is already cut, so the cut is read from the\n // section's own validation, first (AGL-3076). Copies of one item name one\n // node the model wrote, once.\n //\n // A PASS ANSWERS ONLY FOR THE SECTION IT WROTE (AGL-3078). The page check\n // reads the whole page, so it also names nodes an earlier pass stored; the\n // model is not shown those and cannot mend them, so a finding that names\n // none of this section's nodes is not held against this answer. Dropping\n // it neither hides nor forgives it: the last pass runs the same check on\n // the finished page, where every node is the page's and the review names\n // each one and outlines it. Kept, such a finding would re-ask the model\n // for a node it never saw, refuse the same answer twice, and stop the page\n // with findings whose ids all filtered away — no node ids, and so no\n // outline either.\n const violations: AiDoctrineViolation[] = [\n ...detectCutLines(validated.repairs, drawnNodes, written),\n ...report.violations.flatMap((violation) => {\n if (!violation.nodeIds?.length) return [violation]\n const named = [...new Set(violation.nodeIds.map(toModel).filter((id): id is string => id !== null))]\n return named.length ? [{ ...violation, nodeIds: named }] : []\n }),\n ]\n\n const placed = new Set<string>()\n const bound = new Set<string>()\n for (const node of Object.values(sectionNodes)) {\n const props = isRecord(node['props']) ? node['props'] : {}\n if (node['componentId'] === REUSABLE_INSTANCE_COMPONENT_ID && typeof props[AI_INSTANCE_REF_PROP] === 'string') {\n placed.add(props[AI_INSTANCE_REF_PROP] as string)\n }\n if (node['componentId'] === 'form' && typeof props['formId'] === 'string') bound.add(props['formId'])\n }\n const missing = input.section.uses.filter(\n (ref) => (components.has(ref) && !placed.has(ref)) || (forms.has(ref) && !bound.has(ref)),\n )\n if (missing.length) {\n violations.push({\n rule: 7,\n code: 'plan-reuse-not-placed',\n message: `The confirmed plan places ${missing.join(', ')} in this section, and the section does not. Place each one by its id.`,\n })\n }\n // The count the plan promised, against the section as the page stores it\n // (AGL-3024): the prompt states it, and until now nothing read it back.\n violations.push(\n ...aiPlanItemCountViolations(input.section, {\n rootId: sectionId,\n nodes: sectionNodes as unknown as Record<string, AiDoctrineNode>,\n }),\n )\n\n return {\n value: { ...section, load: report.load },\n violations,\n ...offendingOf(raw, violations),\n }\n }\n}\n"],"names":["REUSABLE_INSTANCE_COMPONENT_ID","CANVAS_ROOT_ELEMENT_ID","aiPageTypeDefinition","parseAiPageJobInputs","aiHomeScreenIds","AI_REASK_OFFENDING_MAX_CHARS","aiAnswerTree","aiNodeTreeContextFromInventory","detectCutLines","detectDisagreeingNodes","isAiLinkElement","validateAiDoctrineTree","walkTree","AI_INSTANCE_REF_PROP","validateAiNodeTree","aiPageLinkTarget","aiPageScrollInteraction","AI_PALETTE_CATALOG","AI_REPEAT_KEY","expandAiRepeatedItems","aiJobBriefLine","aiPlanReferenceLines","aiPlanItemCountViolations","aiPageSectionNodeId","jobId","index","slice","AI_PAGE_SECTION_TOOL","name","description","strict","inputSchema","type","additionalProperties","required","properties","tree","AI_JOB_PAGE_INSTRUCTIONS","text","join","screen","cacheBreakpoint","referenceOf","plan","ref","label","labels","AI_PAGE_SECTION_INLINE_LINE","AI_PAGE_SECTION_REPEAT_SHAPE","AI_PAGE_SECTION_REPEAT_LINE","AI_PAGE_SECTION_INSTANCE_LINE","aiPageSectionSmaller","input","reusableComponents","maxElements","aiPageSectionPrompt","job","inputs","pageType","section","sections","places","uses","length","map","items","built","entry","position","title","slug","purpose","isRecord","value","Array","isArray","aiEmptyPage","$id","componentId","parentId","nodes","aiPageWithSection","page","sectionIds","order","previous","rootId","replaced","Set","id","add","node","Object","entries","has","root","children","filter","Map","at","get","before","findIndex","splice","aiPageCheckContext","inventory","options","brand","theme","colors","fonts","homeScreenIds","pageSections","offendingOf","raw","violations","rawNodes","offending","used","flatMap","violation","nodeIds","undefined","size","JSON","stringify","keys","aiPageSectionCheck","sectionId","components","row","forms","answer","validated","report","disagreeing","drawn","inline","context","noun","ok","written","sourceIds","rule","code","message","detail","error","wrapper","top","sectionNode","minted","modelIds","fromEntries","source","sectionNodes","stored","drawnNodes","drawnIdOf","props","target","kind","load","own","writtenNode","scrollTargetIds","pageIds","toModel","repairs","named","placed","bound","values","missing","push"],"mappings":"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"}
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
import type { AiBuildPlanScreen, AiBuildPlanSection } from '../model/ai-build-plan';
|
|
18
18
|
import type { AiJobPlan } from '../model/ai-jobs.types';
|
|
19
19
|
import { type AiDoctrineTree, type AiDoctrineViolation, type AiLayoutRegion } from '../runtime/ai-doctrine-validators';
|
|
20
|
+
import { type AiTemplateSubjectDefinition } from '../model/ai-template-subjects';
|
|
20
21
|
/**
|
|
21
22
|
* The build held to the plan the member confirmed (AGL-3024).
|
|
22
23
|
*
|
|
@@ -43,6 +44,19 @@ import { type AiDoctrineTree, type AiDoctrineViolation, type AiLayoutRegion } fr
|
|
|
43
44
|
* properties, so the planner is asked there for the one thing about a
|
|
44
45
|
* layout worth promising and worth checking — a checkable assertion the
|
|
45
46
|
* planner emits beside its prose, rather than English parsed out of it.
|
|
47
|
+
* - **A token**: `create[].fields` on a template, from the subject's own
|
|
48
|
+
* closed catalog (`AI_TEMPLATE_SUBJECT_DEFINITIONS[subject].tokens`), which
|
|
49
|
+
* is the template's analogue of a layout's regions — the one thing about a
|
|
50
|
+
* template that differs per record, and therefore the one worth promising.
|
|
51
|
+
*
|
|
52
|
+
* The third was added after a measured miss (AGL-3024, 2026-09-21). A brief
|
|
53
|
+
* asked for an attorney's bar admissions; the plan's `why` promised "a
|
|
54
|
+
* bar-admissions block"; the built template had no such thing and reported
|
|
55
|
+
* `Done`. Nothing had gone wrong in the build: `author` has six tokens —
|
|
56
|
+
* name, bio, image, jobTitle, worksFor, url — and no catalog anywhere fills a
|
|
57
|
+
* bar admission, so the promise was unkeepable when it was written. Read as a
|
|
58
|
+
* token list the promise cannot even be MADE, which is a better place to stop
|
|
59
|
+
* than after the document is paid for.
|
|
46
60
|
*
|
|
47
61
|
* ── It fails closed ──────────────────────────────────────────────────────
|
|
48
62
|
*
|
|
@@ -105,3 +119,22 @@ export declare function aiPlanItemCountViolations(section: Pick<AiBuildPlanSecti
|
|
|
105
119
|
* — those are its layout's — so what is counted is the page's own content.
|
|
106
120
|
*/
|
|
107
121
|
export declare function aiPlanCopiedPageViolations(screen: Pick<AiBuildPlanScreen, 'sections'>, tree: AiDoctrineTree): AiDoctrineViolation[];
|
|
122
|
+
/** Every binding token a built tree holds, spelled as the catalog spells them. */
|
|
123
|
+
export declare function aiTreeBoundTokens(tree: AiDoctrineTree): Set<string>;
|
|
124
|
+
/**
|
|
125
|
+
* The subject tokens the confirmed plan's template creation names, and the
|
|
126
|
+
* words it named that the subject's catalog does not answer to. A plan with
|
|
127
|
+
* no template creation names none.
|
|
128
|
+
*/
|
|
129
|
+
export declare function aiPlannedTemplateTokens(plan: AiJobPlan | null, definition: AiTemplateSubjectDefinition): {
|
|
130
|
+
tokens: string[];
|
|
131
|
+
unreadable: string[];
|
|
132
|
+
};
|
|
133
|
+
/**
|
|
134
|
+
* The plan's promised tokens against a built template: one finding naming
|
|
135
|
+
* every token the plan lists that the template does not bind, and one naming
|
|
136
|
+
* a word the subject's catalog could not read — which is a promise this check
|
|
137
|
+
* cannot settle either way, and, more usefully, one the page could never have
|
|
138
|
+
* filled.
|
|
139
|
+
*/
|
|
140
|
+
export declare function aiPlanTemplateTokenViolations(plan: AiJobPlan | null, definition: AiTemplateSubjectDefinition, tree: AiDoctrineTree): AiDoctrineViolation[];
|