@aglyn/plugins-ai 1.0.0-beta.231 → 1.0.0-beta.232
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/activity/ai-activity-actions.js +8 -4
- package/src/lib/activity/ai-activity-actions.js.map +1 -1
- package/src/lib/components/ai-job-follow.component.js +1 -0
- package/src/lib/components/ai-job-follow.component.js.map +1 -1
- package/src/lib/components/ai-job-links.js +13 -0
- package/src/lib/components/ai-job-links.js.map +1 -1
- package/src/lib/components/ai-jobs-list-page.component.js +1 -0
- package/src/lib/components/ai-jobs-list-page.component.js.map +1 -1
- package/src/lib/components/ai-media-create.component.d.ts +20 -7
- package/src/lib/components/ai-media-create.component.js +107 -55
- package/src/lib/components/ai-media-create.component.js.map +1 -1
- package/src/lib/components/ai-seo-fields-card.component.js +1 -0
- package/src/lib/components/ai-seo-fields-card.component.js.map +1 -1
- package/src/lib/components/ai-site-memory-card.component.d.ts +32 -0
- package/src/lib/components/ai-site-memory-card.component.js +187 -0
- package/src/lib/components/ai-site-memory-card.component.js.map +1 -0
- package/src/lib/components/ai-site-start-card.component.js +80 -77
- package/src/lib/components/ai-site-start-card.component.js.map +1 -1
- package/src/lib/components/ai-theme-proposal-card.component.js +4 -0
- package/src/lib/components/ai-theme-proposal-card.component.js.map +1 -1
- package/src/lib/components/ai-usage-strip.component.js +1 -0
- package/src/lib/components/ai-usage-strip.component.js.map +1 -1
- package/src/lib/components/assist-panel.component.d.ts +10 -0
- package/src/lib/components/assist-panel.component.js +119 -9
- package/src/lib/components/assist-panel.component.js.map +1 -1
- package/src/lib/components/assist-signals-page.component.js +2 -0
- package/src/lib/components/assist-signals-page.component.js.map +1 -1
- package/src/lib/components/billing-ai-allotments.component.js +3 -0
- package/src/lib/components/billing-ai-allotments.component.js.map +1 -1
- package/src/lib/components/billing-ai-top-users.component.js +1 -0
- package/src/lib/components/billing-ai-top-users.component.js.map +1 -1
- package/src/lib/components/billing-assist-overage-card.component.js +2 -0
- package/src/lib/components/billing-assist-overage-card.component.js.map +1 -1
- package/src/lib/components/member-ai-usage-card.component.js +10 -0
- package/src/lib/components/member-ai-usage-card.component.js.map +1 -1
- package/src/lib/components/staff-org-ai-card.component.js +1 -0
- package/src/lib/components/staff-org-ai-card.component.js.map +1 -1
- package/src/lib/components/staff-org-ai-conversations.component.d.ts +23 -0
- package/src/lib/components/staff-org-ai-conversations.component.js +346 -0
- package/src/lib/components/staff-org-ai-conversations.component.js.map +1 -0
- package/src/lib/jobs/ai-build-capabilities.d.ts +13 -1
- package/src/lib/jobs/ai-build-capabilities.js +101 -7
- package/src/lib/jobs/ai-build-capabilities.js.map +1 -1
- package/src/lib/jobs/ai-business-profile-prefill.d.ts +29 -0
- package/src/lib/jobs/ai-business-profile-prefill.js +85 -0
- package/src/lib/jobs/ai-business-profile-prefill.js.map +1 -0
- package/src/lib/jobs/ai-job-build-step.js +3 -1
- package/src/lib/jobs/ai-job-build-step.js.map +1 -1
- package/src/lib/jobs/ai-job-edit-step.d.ts +225 -0
- package/src/lib/jobs/ai-job-edit-step.js +631 -0
- package/src/lib/jobs/ai-job-edit-step.js.map +1 -0
- package/src/lib/jobs/ai-job-insight-step.js +21 -4
- package/src/lib/jobs/ai-job-insight-step.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-language.d.ts +8 -3
- package/src/lib/jobs/ai-job-layout-language.js +8 -2
- package/src/lib/jobs/ai-job-layout-language.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-step.js +7 -6
- package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
- package/src/lib/jobs/ai-job-logic-step.d.ts +20 -0
- package/src/lib/jobs/ai-job-logic-step.js +87 -6
- package/src/lib/jobs/ai-job-logic-step.js.map +1 -1
- package/src/lib/jobs/ai-job-page-language.d.ts +52 -7
- package/src/lib/jobs/ai-job-page-language.js +72 -9
- package/src/lib/jobs/ai-job-page-language.js.map +1 -1
- package/src/lib/jobs/ai-job-page-step.js +12 -2
- package/src/lib/jobs/ai-job-page-step.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-step.d.ts +19 -5
- package/src/lib/jobs/ai-job-plan-step.js +54 -20
- package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-content.d.ts +163 -0
- package/src/lib/jobs/ai-job-site-content.js +443 -0
- package/src/lib/jobs/ai-job-site-content.js.map +1 -0
- package/src/lib/jobs/ai-job-site-look.d.ts +112 -0
- package/src/lib/jobs/ai-job-site-look.js +322 -0
- package/src/lib/jobs/ai-job-site-look.js.map +1 -0
- package/src/lib/jobs/ai-job-site-step.d.ts +26 -5
- package/src/lib/jobs/ai-job-site-step.js +163 -24
- package/src/lib/jobs/ai-job-site-step.js.map +1 -1
- package/src/lib/jobs/ai-jobs-notify.d.ts +6 -1
- package/src/lib/jobs/ai-jobs-notify.js +15 -3
- package/src/lib/jobs/ai-jobs-notify.js.map +1 -1
- package/src/lib/layout-language/ai-layout-compiler.d.ts +9 -0
- package/src/lib/layout-language/ai-layout-compiler.js +120 -33
- package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
- package/src/lib/layout-language/ai-layout-frame.js +5 -4
- package/src/lib/layout-language/ai-layout-frame.js.map +1 -1
- package/src/lib/layout-language/ai-layout-language.d.ts +12 -5
- package/src/lib/layout-language/ai-layout-language.js +92 -9
- package/src/lib/layout-language/ai-layout-language.js.map +1 -1
- package/src/lib/layout-language/ai-layout-pictures.d.ts +144 -0
- package/src/lib/layout-language/ai-layout-pictures.js +214 -0
- package/src/lib/layout-language/ai-layout-pictures.js.map +1 -0
- package/src/lib/layout-language/ai-layout-store.d.ts +2 -0
- package/src/lib/layout-language/ai-layout-store.js +7 -1
- package/src/lib/layout-language/ai-layout-store.js.map +1 -1
- package/src/lib/model/ai-build-progress.js +4 -1
- package/src/lib/model/ai-build-progress.js.map +1 -1
- package/src/lib/model/ai-edit-job.d.ts +109 -0
- package/src/lib/model/ai-edit-job.js +157 -0
- package/src/lib/model/ai-edit-job.js.map +1 -0
- package/src/lib/model/ai-job-activity.js +1 -0
- package/src/lib/model/ai-job-activity.js.map +1 -1
- package/src/lib/model/ai-jobs.types.d.ts +3 -1
- package/src/lib/model/ai-jobs.types.js.map +1 -1
- package/src/lib/model/ai-logic-job.d.ts +18 -1
- package/src/lib/model/ai-logic-job.js +24 -3
- package/src/lib/model/ai-logic-job.js.map +1 -1
- package/src/lib/model/ai-media-credits.d.ts +8 -4
- package/src/lib/model/ai-media-credits.js +11 -4
- package/src/lib/model/ai-media-credits.js.map +1 -1
- package/src/lib/model/ai-media-kinds.d.ts +68 -0
- package/src/lib/model/ai-media-kinds.js +98 -0
- package/src/lib/model/ai-media-kinds.js.map +1 -0
- package/src/lib/model/ai-site-build-progress.d.ts +4 -0
- package/src/lib/model/ai-site-build-progress.js +28 -11
- package/src/lib/model/ai-site-build-progress.js.map +1 -1
- package/src/lib/model/ai-site-context.d.ts +111 -0
- package/src/lib/model/ai-site-context.js +222 -0
- package/src/lib/model/ai-site-context.js.map +1 -0
- package/src/lib/model/ai-site-job.d.ts +10 -2
- package/src/lib/model/ai-site-job.js +12 -5
- package/src/lib/model/ai-site-job.js.map +1 -1
- package/src/lib/model/ai-site-kinds.d.ts +164 -0
- package/src/lib/model/ai-site-kinds.js +1942 -0
- package/src/lib/model/ai-site-kinds.js.map +1 -0
- package/src/lib/model/ai-site-look.d.ts +150 -0
- package/src/lib/model/ai-site-look.js +1236 -0
- package/src/lib/model/ai-site-look.js.map +1 -0
- package/src/lib/model/ai-site-memory.d.ts +80 -0
- package/src/lib/model/ai-site-memory.js +171 -0
- package/src/lib/model/ai-site-memory.js.map +1 -0
- package/src/lib/model/ai-site-start.d.ts +10 -1
- package/src/lib/model/ai-site-start.js +16 -2
- package/src/lib/model/ai-site-start.js.map +1 -1
- package/src/lib/model/assist-follow-up.d.ts +92 -0
- package/src/lib/model/assist-follow-up.js +128 -0
- package/src/lib/model/assist-follow-up.js.map +1 -0
- package/src/lib/plugin-config.js +2 -0
- package/src/lib/plugin-config.js.map +1 -1
- package/src/lib/plugin.js +20 -1
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/providers/anthropic.d.ts +9 -1
- package/src/lib/providers/anthropic.js +143 -9
- package/src/lib/providers/anthropic.js.map +1 -1
- package/src/lib/providers/catalog.d.ts +37 -7
- package/src/lib/providers/catalog.js +103 -11
- package/src/lib/providers/catalog.js.map +1 -1
- package/src/lib/providers/contract.d.ts +29 -0
- package/src/lib/providers/contract.js.map +1 -1
- package/src/lib/providers/image-contract.d.ts +22 -1
- package/src/lib/providers/image-contract.js +40 -1
- package/src/lib/providers/image-contract.js.map +1 -1
- package/src/lib/providers/image-endpoints.d.ts +24 -0
- package/src/lib/providers/image-endpoints.js +28 -0
- package/src/lib/providers/image-endpoints.js.map +1 -0
- package/src/lib/providers/model-choice.js +23 -2
- package/src/lib/providers/model-choice.js.map +1 -1
- package/src/lib/providers/routing.js +18 -0
- package/src/lib/providers/routing.js.map +1 -1
- package/src/lib/providers/vertex-image-endpoint.d.ts +48 -0
- package/src/lib/providers/vertex-image-endpoint.js +80 -0
- package/src/lib/providers/vertex-image-endpoint.js.map +1 -0
- package/src/lib/providers/vertex-image.d.ts +13 -27
- package/src/lib/providers/vertex-image.js +15 -42
- package/src/lib/providers/vertex-image.js.map +1 -1
- package/src/lib/runtime/ai-blog-post-generation.d.ts +62 -0
- package/src/lib/runtime/ai-blog-post-generation.js +89 -0
- package/src/lib/runtime/ai-blog-post-generation.js.map +1 -0
- package/src/lib/runtime/ai-dev-env.d.ts +53 -0
- package/src/lib/runtime/ai-dev-env.js +72 -0
- package/src/lib/runtime/ai-dev-env.js.map +1 -0
- package/src/lib/runtime/ai-dev-replay.d.ts +116 -0
- package/src/lib/runtime/ai-dev-replay.js +313 -0
- package/src/lib/runtime/ai-dev-replay.js.map +1 -0
- package/src/lib/runtime/ai-doctrine.js +5 -1
- package/src/lib/runtime/ai-doctrine.js.map +1 -1
- package/src/lib/runtime/ai-eval-live.js +4 -0
- package/src/lib/runtime/ai-eval-live.js.map +1 -1
- package/src/lib/runtime/ai-eval.d.ts +13 -1
- package/src/lib/runtime/ai-eval.js +84 -1
- package/src/lib/runtime/ai-eval.js.map +1 -1
- package/src/lib/runtime/ai-live-batch.d.ts +45 -0
- package/src/lib/runtime/ai-live-batch.js +89 -0
- package/src/lib/runtime/ai-live-batch.js.map +1 -0
- package/src/lib/runtime/ai-runtime.js +46 -2
- package/src/lib/runtime/ai-runtime.js.map +1 -1
- package/src/lib/runtime/site-context.d.ts +58 -0
- package/src/lib/runtime/site-context.js +127 -0
- package/src/lib/runtime/site-context.js.map +1 -0
- package/src/lib/server/ai-admin-conversations.d.ts +22 -0
- package/src/lib/server/ai-admin-conversations.js +197 -0
- package/src/lib/server/ai-admin-conversations.js.map +1 -0
- package/src/lib/server/ai-media-image.d.ts +5 -5
- package/src/lib/server/ai-media-image.js +25 -11
- package/src/lib/server/ai-media-image.js.map +1 -1
- package/src/lib/server/ai-media-raster-prompt.d.ts +34 -0
- package/src/lib/server/ai-media-raster-prompt.js +53 -0
- package/src/lib/server/ai-media-raster-prompt.js.map +1 -0
- package/src/lib/server/assist-chat.d.ts +11 -0
- package/src/lib/server/assist-chat.js +57 -18
- package/src/lib/server/assist-chat.js.map +1 -1
- package/src/lib/server/assist-edit-applied.js +16 -3
- package/src/lib/server/assist-edit-applied.js.map +1 -1
- package/src/lib/server/assist-view-context.d.ts +19 -1
- package/src/lib/server/assist-view-context.js +38 -1
- package/src/lib/server/assist-view-context.js.map +1 -1
- package/src/lib/server.js +10 -0
- package/src/lib/server.js.map +1 -1
- package/src/lib/subprocessors.js +17 -1
- package/src/lib/subprocessors.js.map +1 -1
- package/src/lib/tools/ai-blog-post-tool.d.ts +60 -0
- package/src/lib/tools/ai-blog-post-tool.js +175 -0
- package/src/lib/tools/ai-blog-post-tool.js.map +1 -0
- package/src/lib/usage/assist-usage.d.ts +3 -0
- package/src/lib/usage/assist-usage.js +4 -2
- package/src/lib/usage/assist-usage.js.map +1 -1
- package/src/lib/usage/staff-org-ai-conversations.d.ts +103 -0
- package/src/lib/usage/staff-org-ai-conversations.js +152 -0
- package/src/lib/usage/staff-org-ai-conversations.js.map +1 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-site-build-progress.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 { aiJobConfirmingOwnPlan, aiJobKindNoun } from './ai-job-activity'\nimport { aiJobRefundCopy } from './ai-job-failure-copy'\nimport { aiBuildItemRows, aiBuildOutcomeLine, aiSitePartialCopy } from './ai-build-progress'\nimport type { AiJobSummary } from './ai-jobs.types'\n\n/**\n * What the \"Building your site\" page shows of a site job (AGL-3594): one row\n * a stage — planning the pages, then each page — with its state, the state\n * the whole job is in, and the job's one price.\n *\n * Read off the job summary every surface already follows: the plan step's\n * status, the plan's pages, and the pages the build has reported as outputs.\n * A scaffold builds its units in plan order and reports each page when it is\n * written, so the pages written so far are the first pages of the plan.\n *\n * Pure: no React, no request. The page and its spec read the same rows.\n */\n\nexport type AiSiteBuildRowState = 'done' | 'active' | 'waiting' | 'failed' | 'skipped'\n\nexport interface AiSiteBuildRow {\n id: string\n label: string\n state: AiSiteBuildRowState\n /** What the person should know about this row (AGL-3616): a build item's failure, note or refund. */\n detail?: string | null\n /** What the row is doing while it is the active one, in a sentence. */\n hint?: string | null\n /** A page's planned sections, in order, so the person sees what is being written. */\n sections?: string[]\n /** The credits this row's work used, once it has finished; absent while it runs. */\n credits?: number\n /** When this row's work started, for the elapsed time beside the active row. */\n startedAt?: string | null\n}\n\n/** Where the whole job stands, as the page's header and actions read it. */\nexport type AiSiteBuildPhase = 'working' | 'done' | 'stopped' | 'failed' | 'canceled'\n\nexport function aiSiteBuildPhase(\n job: Pick<AiJobSummary, 'status'> & Partial<Pick<AiJobSummary, 'review' | 'autoConfirm' | 'updatedAt'>>,\n): AiSiteBuildPhase {\n // A guided start's plan parked for the instant its confirmation takes is\n // the build going on (AGL-3596), never a stop.\n if (aiJobConfirmingOwnPlan(job)) return 'working'\n switch (job.status) {\n case 'done':\n return 'done'\n case 'failed':\n return 'failed'\n case 'canceled':\n return 'canceled'\n case 'needs_review':\n case 'needs_input':\n return 'stopped'\n default:\n return 'working'\n }\n}\n\n/** The creations a scaffold builds before its pages, in its build order, and what each row says. */\nconst CREATION_ROWS: ReadonlyArray<{ kind: string; resource: string; label: (name: string) => string }> = [\n { kind: 'theme-change', resource: 'theme', label: () => 'Choosing your colors' },\n { kind: 'layout', resource: 'layout', label: (name) => `Building the header and footer: ${name}` },\n { kind: 'form', resource: 'form', label: (name) => `Building the form: ${name}` },\n]\n\n/**\n * One row a stage: planning, then each creation the plan builds before its\n * pages — the colors, the layout, the form — then each page (AGL-3596). A\n * scaffold builds them in that order and reports each as an output when it\n * is written, so the first row without its output is where the build is, and\n * where a failure stopped it: a layout that broke is the layout's row, never\n * the first page's.\n */\nexport function aiSiteBuildRows(\n job: Pick<AiJobSummary, 'status' | 'steps' | 'plan' | 'outputs' | 'review'> &\n Partial<Pick<AiJobSummary, 'items' | 'kind' | 'siteInputs' | 'sitePublish'>>,\n): AiSiteBuildRow[] {\n const phase = aiSiteBuildPhase(job)\n // A build is read off its item ledger (AGL-3616): one row an item, each with\n // its own state and what became of its credits.\n if (job.items?.length) {\n const site = job.kind !== 'build'\n const pages = (job.items ?? []).filter((row) => row.op === 'page')\n return [\n { id: 'plan', label: site ? 'Planning your pages' : 'Planning what to build', state: 'done' },\n ...aiBuildItemRows(job).map((row, index) => {\n const ledger = (job.items ?? [])[index]\n // A site keeps the scaffold's own words for each stage (AGL-3596).\n const label =\n !site || !ledger\n ? row.label\n : ledger.op === 'theme'\n ? 'Choosing your colors'\n : ledger.op === 'layout'\n ? `Building the header and footer: ${ledger.label}`\n : ledger.op === 'form'\n ? `Building the form: ${ledger.label}`\n : ledger.op === 'page'\n ? `Writing page ${pages.indexOf(ledger) + 1} of ${pages.length}: ${ledger.label}`\n : ledger.op === 'email'\n ? 'Writing your welcome email'\n : row.label\n const screen = ledger?.op === 'page' ? aiPlanScreenFor(job.plan, ledger.label, pages.indexOf(ledger)) : null\n const finished = row.state === 'done' || row.state === 'failed'\n const net = ledger ? Math.max(0, Math.floor((ledger.creditsSpent ?? 0) - (ledger.creditsRefunded ?? 0))) : 0\n return {\n id: row.slot,\n label,\n state: row.state,\n // A part that failed while the rest is still being built says the\n // build goes on (AGL-3596): read alone, it looked like the site had.\n detail:\n row.state === 'failed' && phase === 'working'\n ? [row.detail, site ? 'The rest of your site keeps building.' : 'The rest keeps building.'].filter(Boolean).join(' ')\n : row.detail,\n ...(row.state === 'active'\n ? { startedAt: row.startedAt ?? null, hint: ledger?.op === 'page' ? AI_SITE_PAGE_HINT : AI_SITE_ITEM_HINT }\n : {}),\n ...(screen?.sections.length ? { sections: screen.sections.map((section) => section.name) } : {}),\n ...(finished && ledger ? { credits: net } : {}),\n }\n }),\n ...aiSitePublishRow(job, phase),\n ]\n }\n const planStep = job.steps.find((step) => step.name === 'plan')\n const planDone = planStep?.status === 'done' || Boolean(job.plan)\n const stopped = phase === 'failed' || phase === 'stopped'\n const planState: AiSiteBuildRowState = planDone ? 'done' : stopped ? 'failed' : phase === 'working' ? 'active' : 'waiting'\n const rows: AiSiteBuildRow[] = [\n {\n id: 'plan',\n label: 'Planning your pages',\n state: planState,\n startedAt: planStep?.startedAt ?? null,\n hint: planState === 'active' ? AI_SITE_PLAN_HINT : null,\n ...(planDone && planStep ? { credits: Math.max(0, Math.floor(planStep.creditsSpent ?? 0)) } : {}),\n },\n ]\n // Before there is a plan, the stages a guided start always goes through\n // are named from its answers, so the page shows the whole way from the\n // start rather than one row.\n if (!job.plan && job.kind !== 'build') {\n const pagesAsked = typeof job.siteInputs?.['pages'] === 'number' ? Math.round(job.siteInputs['pages'] as number) : 0\n if (job.siteInputs) {\n rows.push({ id: 'layout', label: 'Building the header and footer', state: 'waiting' })\n // A guided start that says where its contact form's submissions go\n // plans that form, built after the layout and before the pages (AGL-3596).\n const submissions = job.siteInputs['submissions']\n if (submissions === 'inbox' || submissions === 'lead') {\n rows.push({ id: 'form', label: 'Building your contact form', state: 'waiting' })\n }\n rows.push({\n id: 'pages',\n label: pagesAsked > 1 ? `Writing your ${pagesAsked} pages` : 'Writing your pages',\n state: 'waiting',\n })\n rows.push(...aiSitePublishRow(job, phase))\n }\n return rows\n }\n const built = new Map<string, number>()\n for (const output of job.outputs) built.set(output.resource, (built.get(output.resource) ?? 0) + 1)\n const take = (resource: string): boolean => {\n const left = built.get(resource) ?? 0\n if (left <= 0) return false\n built.set(resource, left - 1)\n return true\n }\n const stages: Array<{ id: string; label: string; resource: string; sections?: string[] }> = []\n for (const row of CREATION_ROWS) {\n const creation = job.plan?.create.find((entry) => entry.kind === row.kind)\n if (creation) stages.push({ id: row.resource, label: row.label(creation.name), resource: row.resource })\n }\n const pages = job.plan?.screens ?? []\n pages.forEach((page, index) => {\n stages.push({\n id: `page-${index}`,\n label: `Writing page ${index + 1} of ${pages.length}: ${page.title}`,\n resource: 'screen',\n sections: page.sections.map((section) => section.name),\n })\n })\n let reached = false\n for (const stage of stages) {\n const done = phase === 'done' || take(stage.resource)\n const current = !done && !reached && planDone\n if (current) reached = true\n rows.push({\n id: stage.id,\n label: stage.label,\n state: done ? 'done' : current && stopped ? 'failed' : current && phase === 'working' ? 'active' : 'waiting',\n ...(stage.sections?.length ? { sections: stage.sections } : {}),\n })\n }\n rows.push(...aiSitePublishRow(job, phase))\n return rows\n}\n\n/** What the planning row says while it runs. */\nexport const AI_SITE_PLAN_HINT =\n 'Reading your answers and choosing your pages, what each one says, and the forms and layout they need. This usually takes under a minute.'\n\n/** What the page being written says while it is the active row (AGL-3596). */\nexport const AI_SITE_PAGE_HINT =\n 'Writing this page’s sections from your plan. It is done once every section is in.'\n\n/** What any other part being built says while it is the active row. */\nexport const AI_SITE_ITEM_HINT = 'Building this now. The next step starts when it is done.'\n\n/** The plan's page an item builds: by its title, else by its place among the pages. */\nfunction aiPlanScreenFor(\n plan: AiJobSummary['plan'],\n title: string,\n index: number,\n): NonNullable<AiJobSummary['plan']>['screens'][number] | null {\n const screens = plan?.screens ?? []\n return screens.find((screen) => screen.title === title) ?? (index >= 0 ? (screens[index] ?? null) : null)\n}\n\n/**\n * A guided start ends by putting its pages live (AGL-3596): the last row, so\n * the person sees that step coming and when it is done. Only a site job\n * publishes on its own; a build publishes when asked, and says so in its plan.\n */\nfunction aiSitePublishRow(\n job: Pick<AiJobSummary, 'status'> & Partial<Pick<AiJobSummary, 'kind' | 'sitePublish'>>,\n phase: AiSiteBuildPhase,\n): AiSiteBuildRow[] {\n if (job.kind !== 'site') return []\n const published = (job.sitePublish?.published.length ?? 0) > 0\n return [\n {\n id: 'publish',\n label: 'Publishing your site',\n // A job that stopped or failed publishes nothing, so the row is passed over.\n state: phase === 'done' ? (published ? 'done' : 'skipped') : phase === 'working' ? 'waiting' : 'skipped',\n },\n ]\n}\n\n/**\n * How far the job is, from 0 to 1, by its rows: a finished row counts whole\n * and the active one half. `null` before there is more than one row to count.\n */\nexport function aiSiteBuildFraction(rows: readonly AiSiteBuildRow[]): number | null {\n if (rows.length < 2) return null\n const counted = rows.reduce(\n (sum, row) => sum + (row.state === 'active' ? 0.5 : row.state === 'waiting' ? 0 : 1),\n 0,\n )\n return Math.min(1, counted / rows.length)\n}\n\n/**\n * The job's one price (AGL-3594), never a plan's on its own: what it has used\n * so far while it works, what it used once done, and, on a failure we gave\n * back, that nothing was charged.\n */\nexport function aiSiteBuildCreditsLine(\n job: Pick<AiJobSummary, 'status' | 'creditsSpent' | 'refundedCredits'> & Partial<Pick<AiJobSummary, 'kind'>>,\n): string | null {\n const spent = Math.max(0, job.creditsSpent ?? 0)\n const refunded = Math.max(0, job.refundedCredits ?? 0)\n const net = Math.max(0, spent - refunded)\n const phase = aiSiteBuildPhase(job)\n // What the job made, by its kind; a job that names none is a site's.\n const subject = !job.kind || job.kind === 'site' ? 'This site' : aiJobKindNoun(job.kind) === 'AI job' ? 'This AI job' : `This ${aiJobKindNoun(job.kind)}`\n // A failure on our side says so in the words every surface uses (AGL-3596),\n // from the job's recorded give-back.\n const refund = phase === 'working' || phase === 'done' ? null : aiJobRefundCopy(job)\n if (refund && refunded > 0) return refund\n if (phase === 'done') return `${subject} used ${net} credits.`\n if (phase === 'working') return `Credits used so far: ${net}`\n if (refund) return `${subject} used ${net} credits. ${refund}`\n return net > 0 ? `${subject} used ${net} credits.` : null\n}\n\n/** The heading and the sentence under it, for one job's page in its current phase. */\nexport interface AiJobPageCopy {\n heading: string\n lede: string\n}\n\n/**\n * What one job's page says at the top (AGL-3594, AGL-3596). A site job reads\n * as \"Building your site\"; any other kind names what it makes — \"Building\n * your page\" — or, for a kind with no noun, the job itself.\n *\n * A guided site start publishes its pages when it finishes, and says so\n * (`sitePublish`): with a page live the heading is \"Your site is live\". A\n * done site job without that record — an older job, or one that was not a\n * guided start — built drafts, and says that instead.\n */\nexport function aiJobPageCopy(\n job: Pick<AiJobSummary, 'kind' | 'status' | 'review' | 'error'> & Partial<Pick<AiJobSummary, 'sitePublish' | 'items'>>,\n brand: string,\n): AiJobPageCopy {\n const phase = aiSiteBuildPhase(job)\n // A build is what the person asked for, item by item (AGL-3616).\n if (job.kind === 'build') return aiBuildPageCopy(job, brand)\n const noun = job.kind === 'site' ? 'site' : aiJobKindNoun(job.kind)\n const generic = noun === 'AI job'\n // `products` is the one plural noun.\n const isAre = noun.endsWith('s') ? 'are' : 'is'\n const why = job.review?.message ?? job.error ?? null\n if (phase === 'working') {\n return {\n heading: generic ? 'Your AI job is running' : `Building your ${noun}`,\n lede:\n job.kind === 'site'\n ? `${brand} AI is planning your pages and writing each one. You can leave this page; it keeps going.`\n : `${brand} AI is working on it. You can leave this page; it keeps going.`,\n }\n }\n if (phase === 'done') {\n const published = job.sitePublish?.published.length ?? 0\n // Part of the site was built (AGL-3616): what was, what was not, and its credits.\n const partial = job.kind === 'site' ? aiSitePartialCopy({ items: job.items, status: job.status }) : null\n if (job.kind === 'site' && published > 0) {\n return {\n heading: 'Your site is live',\n lede: [\n partial,\n job.sitePublish?.drafts.length\n ? 'Your pages are published, except the ones listed below, which stayed drafts.'\n : 'Your pages are published, and anyone can visit your site now.',\n ]\n .filter(Boolean)\n .join(' '),\n }\n }\n if (partial) return { heading: 'Your site is ready', lede: `${partial} Your new pages are drafts. Publish them when you’re happy.` }\n if (job.kind === 'site' && job.sitePublish) {\n return {\n heading: 'Your site is ready',\n lede: 'None of your new pages could be published, so they are drafts. The reasons are below.',\n }\n }\n return {\n heading: generic ? 'Your AI job finished' : `Your ${noun} ${isAre} ready`,\n lede:\n job.kind === 'site'\n ? 'Your new pages are drafts. Publish them when you’re happy.'\n : 'Everything it built is an unpublished draft until you publish it.',\n }\n }\n if (phase === 'canceled') {\n return { heading: generic ? 'Your AI job was canceled' : `Your ${noun} was not built`, lede: 'The job was canceled.' }\n }\n return {\n heading: generic ? 'Your AI job stopped' : `Your ${noun} ${noun.endsWith('s') ? 'were' : 'was'} not built`,\n lede: why ?? (generic ? 'Something went wrong running this job.' : `Something went wrong building your ${noun}.`),\n }\n}\n\n/** A build's page heading and sentence (AGL-3616). */\nfunction aiBuildPageCopy(\n job: Pick<AiJobSummary, 'status' | 'review' | 'error'> & Partial<Pick<AiJobSummary, 'items' | 'sitePublish'>>,\n brand: string,\n): AiJobPageCopy {\n const phase = aiSiteBuildPhase(job)\n const outcome = aiBuildOutcomeLine({ items: job.items ?? [], status: job.status })\n if (phase === 'working') {\n return {\n heading: 'Building what you asked for',\n lede: `${brand} AI is building each part in turn. You can leave this page; it keeps going.`,\n }\n }\n if (phase === 'stopped') {\n return {\n heading: job.review?.reason === 'plan' ? 'Your plan is ready' : 'Your build needs you',\n lede: job.review?.message ?? job.error ?? 'It waits for your decision.',\n }\n }\n if (phase === 'done') {\n const failed = (job.items ?? []).some((row) => row.status === 'failed' || row.status === 'skipped')\n const live = job.sitePublish?.published.length ?? 0\n return {\n heading: failed ? 'Most of it is ready' : 'Everything you asked for is ready',\n lede: [\n outcome,\n live ? 'Your new pages are published.' : 'Everything it built is an unpublished draft until you publish it.',\n failed ? 'Try again builds only what failed.' : '',\n ]\n .filter(Boolean)\n .join(' '),\n }\n }\n if (phase === 'canceled') return { heading: 'Your build was canceled', lede: 'The job was canceled.' }\n return {\n heading: 'Nothing could be built',\n lede: [job.error ?? 'Something went wrong building what you asked for.', outcome].filter(Boolean).join(' '),\n }\n}\n"],"names":["aiJobConfirmingOwnPlan","aiJobKindNoun","aiJobRefundCopy","aiBuildItemRows","aiBuildOutcomeLine","aiSitePartialCopy","aiSiteBuildPhase","job","status","CREATION_ROWS","kind","resource","label","name","aiSiteBuildRows","planStep","built","phase","items","length","site","pages","filter","row","op","id","state","map","index","ledger","indexOf","screen","aiPlanScreenFor","plan","finished","net","Math","max","floor","creditsSpent","creditsRefunded","slot","detail","Boolean","join","startedAt","hint","AI_SITE_PAGE_HINT","AI_SITE_ITEM_HINT","sections","section","credits","aiSitePublishRow","steps","find","step","planDone","stopped","planState","rows","AI_SITE_PLAN_HINT","pagesAsked","siteInputs","round","push","submissions","Map","output","outputs","set","get","take","left","stages","creation","create","entry","screens","forEach","page","title","reached","stage","done","current","published","sitePublish","aiSiteBuildFraction","counted","reduce","sum","min","aiSiteBuildCreditsLine","spent","refunded","refundedCredits","subject","refund","aiJobPageCopy","brand","aiBuildPageCopy","noun","generic","isAre","endsWith","why","review","message","error","heading","lede","partial","drafts","outcome","reason","failed","some","live"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-site-build-progress.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 { aiJobConfirmingOwnPlan, aiJobKindNoun } from './ai-job-activity'\nimport { aiJobRefundCopy } from './ai-job-failure-copy'\nimport { aiBuildItemRows, aiBuildOutcomeLine, aiSitePartialCopy } from './ai-build-progress'\nimport type { AiJobSummary } from './ai-jobs.types'\n\n/**\n * What the \"Building your site\" page shows of a site job (AGL-3594): one row\n * a stage — planning the pages, then each page — with its state, the state\n * the whole job is in, and the job's one price.\n *\n * Read off the job summary every surface already follows: the plan step's\n * status, the plan's pages, and the pages the build has reported as outputs.\n * A scaffold builds its units in plan order and reports each page when it is\n * written, so the pages written so far are the first pages of the plan.\n *\n * Pure: no React, no request. The page and its spec read the same rows.\n */\n\nexport type AiSiteBuildRowState = 'done' | 'active' | 'waiting' | 'failed' | 'skipped'\n\nexport interface AiSiteBuildRow {\n id: string\n label: string\n state: AiSiteBuildRowState\n /** What the person should know about this row (AGL-3616): a build item's failure, note or refund. */\n detail?: string | null\n /** What the row is doing while it is the active one, in a sentence. */\n hint?: string | null\n /** A page's planned sections, in order, so the person sees what is being written. */\n sections?: string[]\n /** The credits this row's work used, once it has finished; absent while it runs. */\n credits?: number\n /** When this row's work started, for the elapsed time beside the active row. */\n startedAt?: string | null\n}\n\n/** Where the whole job stands, as the page's header and actions read it. */\nexport type AiSiteBuildPhase = 'working' | 'done' | 'stopped' | 'failed' | 'canceled'\n\nexport function aiSiteBuildPhase(\n job: Pick<AiJobSummary, 'status'> & Partial<Pick<AiJobSummary, 'review' | 'autoConfirm' | 'updatedAt'>>,\n): AiSiteBuildPhase {\n // A guided start's plan parked for the instant its confirmation takes is\n // the build going on (AGL-3596), never a stop.\n if (aiJobConfirmingOwnPlan(job)) return 'working'\n switch (job.status) {\n case 'done':\n return 'done'\n case 'failed':\n return 'failed'\n case 'canceled':\n return 'canceled'\n case 'needs_review':\n case 'needs_input':\n return 'stopped'\n default:\n return 'working'\n }\n}\n\n/** The creations a scaffold builds before its pages, in its build order, and what each row says. */\nconst CREATION_ROWS: ReadonlyArray<{ kind: string; resource: string; label: (name: string) => string }> = [\n { kind: 'theme-change', resource: 'theme', label: () => AI_SITE_LOOK_ROW_LABEL },\n { kind: 'layout', resource: 'layout', label: (name) => `Building the header and footer: ${name}` },\n { kind: 'form', resource: 'form', label: (name) => `Building the form: ${name}` },\n]\n\n/**\n * One row a stage: planning, then each creation the plan builds before its\n * pages — the colors, the layout, the form — then each page (AGL-3596). A\n * scaffold builds them in that order and reports each as an output when it\n * is written, so the first row without its output is where the build is, and\n * where a failure stopped it: a layout that broke is the layout's row, never\n * the first page's.\n */\nexport function aiSiteBuildRows(\n job: Pick<AiJobSummary, 'status' | 'steps' | 'plan' | 'outputs' | 'review'> &\n Partial<Pick<AiJobSummary, 'items' | 'kind' | 'siteInputs' | 'sitePublish'>>,\n): AiSiteBuildRow[] {\n const phase = aiSiteBuildPhase(job)\n // A build is read off its item ledger (AGL-3616): one row an item, each with\n // its own state and what became of its credits.\n if (job.items?.length) {\n const site = job.kind !== 'build'\n const pages = (job.items ?? []).filter((row) => row.op === 'page')\n // The plan keeps the credits it used once the items take over the page.\n const planStep = job.steps.find((step) => step.name === 'plan')\n return [\n {\n id: 'plan',\n label: site ? 'Planning your pages' : 'Planning what to build',\n state: 'done',\n ...(planStep ? { credits: Math.max(0, Math.floor(planStep.creditsSpent ?? 0)) } : {}),\n },\n ...aiBuildItemRows(job).map((row, index) => {\n const ledger = (job.items ?? [])[index]\n // A site keeps the scaffold's own words for each stage (AGL-3596).\n const label =\n !site || !ledger\n ? row.label\n : ledger.op === 'theme'\n ? AI_SITE_LOOK_ROW_LABEL\n : ledger.op === 'layout'\n ? `Building the header and footer: ${ledger.label}`\n : ledger.op === 'form'\n ? `Building the form: ${ledger.label}`\n : ledger.op === 'page'\n ? `Writing page ${pages.indexOf(ledger) + 1} of ${pages.length}: ${ledger.label}`\n : ledger.op === 'email'\n ? 'Writing your welcome email'\n : row.label\n const screen = ledger?.op === 'page' ? aiPlanScreenFor(job.plan, ledger.label, pages.indexOf(ledger)) : null\n const finished = row.state === 'done' || row.state === 'failed'\n const net = ledger ? Math.max(0, Math.floor((ledger.creditsSpent ?? 0) - (ledger.creditsRefunded ?? 0))) : 0\n return {\n id: row.slot,\n label,\n state: row.state,\n // A part that failed while the rest is still being built says the\n // build goes on (AGL-3596): read alone, it looked like the site had.\n detail:\n row.state === 'failed' && phase === 'working'\n ? [row.detail, site ? 'The rest of your site keeps building.' : 'The rest keeps building.'].filter(Boolean).join(' ')\n : row.detail,\n ...(row.state === 'active'\n ? {\n startedAt: row.startedAt ?? null,\n hint:\n ledger?.op === 'page'\n ? AI_SITE_PAGE_HINT\n : site && ledger?.op === 'theme'\n ? AI_SITE_LOOK_HINT\n : AI_SITE_ITEM_HINT,\n }\n : {}),\n ...(screen?.sections.length ? { sections: screen.sections.map((section) => section.name) } : {}),\n ...(finished && ledger ? { credits: net } : {}),\n }\n }),\n ...aiSitePublishRow(job, phase),\n ]\n }\n const planStep = job.steps.find((step) => step.name === 'plan')\n const planDone = planStep?.status === 'done' || Boolean(job.plan)\n const stopped = phase === 'failed' || phase === 'stopped'\n const planState: AiSiteBuildRowState = planDone ? 'done' : stopped ? 'failed' : phase === 'working' ? 'active' : 'waiting'\n const rows: AiSiteBuildRow[] = [\n {\n id: 'plan',\n label: 'Planning your pages',\n state: planState,\n startedAt: planStep?.startedAt ?? null,\n hint: planState === 'active' ? AI_SITE_PLAN_HINT : null,\n ...(planDone && planStep ? { credits: Math.max(0, Math.floor(planStep.creditsSpent ?? 0)) } : {}),\n },\n ]\n // Before there is a plan, the stages a guided start always goes through\n // are named from its answers, so the page shows the whole way from the\n // start rather than one row.\n if (!job.plan && job.kind !== 'build') {\n const pagesAsked = typeof job.siteInputs?.['pages'] === 'number' ? Math.round(job.siteInputs['pages'] as number) : 0\n if (job.siteInputs) {\n // The look is designed first, before the header and footer (AGL-3660).\n rows.push({ id: 'look', label: AI_SITE_LOOK_ROW_LABEL, state: 'waiting' })\n rows.push({ id: 'layout', label: 'Building the header and footer', state: 'waiting' })\n // A guided start that says where its contact form's submissions go\n // plans that form, built after the layout and before the pages (AGL-3596).\n const submissions = job.siteInputs['submissions']\n if (submissions === 'inbox' || submissions === 'lead') {\n rows.push({ id: 'form', label: 'Building your contact form', state: 'waiting' })\n }\n rows.push({\n id: 'pages',\n label: pagesAsked > 1 ? `Writing your ${pagesAsked} pages` : 'Writing your pages',\n state: 'waiting',\n })\n rows.push(...aiSitePublishRow(job, phase))\n }\n return rows\n }\n const built = new Map<string, number>()\n for (const output of job.outputs) built.set(output.resource, (built.get(output.resource) ?? 0) + 1)\n const take = (resource: string): boolean => {\n const left = built.get(resource) ?? 0\n if (left <= 0) return false\n built.set(resource, left - 1)\n return true\n }\n const stages: Array<{ id: string; label: string; resource: string; sections?: string[] }> = []\n for (const row of CREATION_ROWS) {\n const creation = job.plan?.create.find((entry) => entry.kind === row.kind)\n // A job read without its item ledger is one from before the look had a\n // unit of its own (AGL-3660): its look is a stage only where it built one.\n if (creation || (row.resource === 'theme' && job.outputs.some((output) => output.resource === 'theme'))) {\n stages.push({ id: row.resource, label: row.label(creation?.name ?? ''), resource: row.resource })\n }\n }\n const pages = job.plan?.screens ?? []\n pages.forEach((page, index) => {\n stages.push({\n id: `page-${index}`,\n label: `Writing page ${index + 1} of ${pages.length}: ${page.title}`,\n resource: 'screen',\n sections: page.sections.map((section) => section.name),\n })\n })\n let reached = false\n for (const stage of stages) {\n const done = phase === 'done' || take(stage.resource)\n const current = !done && !reached && planDone\n if (current) reached = true\n rows.push({\n id: stage.id,\n label: stage.label,\n state: done ? 'done' : current && stopped ? 'failed' : current && phase === 'working' ? 'active' : 'waiting',\n ...(stage.sections?.length ? { sections: stage.sections } : {}),\n })\n }\n rows.push(...aiSitePublishRow(job, phase))\n return rows\n}\n\n/** The row the site's look is designed on (AGL-3660). */\nexport const AI_SITE_LOOK_ROW_LABEL = 'Designing your look'\n\n/** What the look's row says while it runs. */\nexport const AI_SITE_LOOK_HINT =\n 'Choosing a base theme, colors, fonts and the style of your buttons, cards and forms for your kind of site. This takes a few seconds.'\n\n/** What the planning row says while it runs. */\nexport const AI_SITE_PLAN_HINT =\n 'Reading your answers and choosing your pages, what each one says, and the forms and layout they need. This usually takes under a minute.'\n\n/** What the page being written says while it is the active row (AGL-3596). */\nexport const AI_SITE_PAGE_HINT =\n 'Writing this page’s sections from your plan. It is done once every section is in.'\n\n/** What any other part being built says while it is the active row. */\nexport const AI_SITE_ITEM_HINT = 'Building this now. The next step starts when it is done.'\n\n/** The plan's page an item builds: by its title, else by its place among the pages. */\nfunction aiPlanScreenFor(\n plan: AiJobSummary['plan'],\n title: string,\n index: number,\n): NonNullable<AiJobSummary['plan']>['screens'][number] | null {\n const screens = plan?.screens ?? []\n return screens.find((screen) => screen.title === title) ?? (index >= 0 ? (screens[index] ?? null) : null)\n}\n\n/**\n * A guided start ends by putting its pages live (AGL-3596): the last row, so\n * the person sees that step coming and when it is done. Only a site job\n * publishes on its own; a build publishes when asked, and says so in its plan.\n */\nfunction aiSitePublishRow(\n job: Pick<AiJobSummary, 'status'> & Partial<Pick<AiJobSummary, 'kind' | 'sitePublish'>>,\n phase: AiSiteBuildPhase,\n): AiSiteBuildRow[] {\n if (job.kind !== 'site') return []\n const published = (job.sitePublish?.published.length ?? 0) > 0\n return [\n {\n id: 'publish',\n label: 'Publishing your site',\n // A job that stopped or failed publishes nothing, so the row is passed over.\n state: phase === 'done' ? (published ? 'done' : 'skipped') : phase === 'working' ? 'waiting' : 'skipped',\n },\n ]\n}\n\n/**\n * How far the job is, from 0 to 1, by its rows: a finished row counts whole\n * and the active one half. `null` before there is more than one row to count.\n */\nexport function aiSiteBuildFraction(rows: readonly AiSiteBuildRow[]): number | null {\n if (rows.length < 2) return null\n const counted = rows.reduce(\n (sum, row) => sum + (row.state === 'active' ? 0.5 : row.state === 'waiting' ? 0 : 1),\n 0,\n )\n return Math.min(1, counted / rows.length)\n}\n\n/**\n * The job's one price (AGL-3594), never a plan's on its own: what it has used\n * so far while it works, what it used once done, and, on a failure we gave\n * back, that nothing was charged.\n */\nexport function aiSiteBuildCreditsLine(\n job: Pick<AiJobSummary, 'status' | 'creditsSpent' | 'refundedCredits'> & Partial<Pick<AiJobSummary, 'kind'>>,\n): string | null {\n const spent = Math.max(0, job.creditsSpent ?? 0)\n const refunded = Math.max(0, job.refundedCredits ?? 0)\n const net = Math.max(0, spent - refunded)\n const phase = aiSiteBuildPhase(job)\n // What the job made, by its kind; a job that names none is a site's.\n const subject = !job.kind || job.kind === 'site' ? 'This site' : aiJobKindNoun(job.kind) === 'AI job' ? 'This AI job' : `This ${aiJobKindNoun(job.kind)}`\n // A failure on our side says so in the words every surface uses (AGL-3596),\n // from the job's recorded give-back.\n const refund = phase === 'working' || phase === 'done' ? null : aiJobRefundCopy(job)\n if (refund && refunded > 0) return refund\n if (phase === 'done') return `${subject} used ${net} credits.`\n if (phase === 'working') return `Credits used so far: ${net}`\n if (refund) return `${subject} used ${net} credits. ${refund}`\n return net > 0 ? `${subject} used ${net} credits.` : null\n}\n\n/** The heading and the sentence under it, for one job's page in its current phase. */\nexport interface AiJobPageCopy {\n heading: string\n lede: string\n}\n\n/**\n * What one job's page says at the top (AGL-3594, AGL-3596). A site job reads\n * as \"Building your site\"; any other kind names what it makes — \"Building\n * your page\" — or, for a kind with no noun, the job itself.\n *\n * A guided site start publishes its pages when it finishes, and says so\n * (`sitePublish`): with a page live the heading is \"Your site is live\". A\n * done site job without that record — an older job, or one that was not a\n * guided start — built drafts, and says that instead.\n */\nexport function aiJobPageCopy(\n job: Pick<AiJobSummary, 'kind' | 'status' | 'review' | 'error'> & Partial<Pick<AiJobSummary, 'sitePublish' | 'items'>>,\n brand: string,\n): AiJobPageCopy {\n const phase = aiSiteBuildPhase(job)\n // A build is what the person asked for, item by item (AGL-3616).\n if (job.kind === 'build') return aiBuildPageCopy(job, brand)\n const noun = job.kind === 'site' ? 'site' : aiJobKindNoun(job.kind)\n const generic = noun === 'AI job'\n // `products` is the one plural noun.\n const isAre = noun.endsWith('s') ? 'are' : 'is'\n const why = job.review?.message ?? job.error ?? null\n if (phase === 'working') {\n return {\n heading: generic ? 'Your AI job is running' : `Building your ${noun}`,\n lede:\n job.kind === 'site'\n ? `${brand} AI is planning your pages and writing each one. You can leave this page; it keeps going.`\n : `${brand} AI is working on it. You can leave this page; it keeps going.`,\n }\n }\n if (phase === 'done') {\n const published = job.sitePublish?.published.length ?? 0\n // Part of the site was built (AGL-3616): what was, what was not, and its credits.\n const partial = job.kind === 'site' ? aiSitePartialCopy({ items: job.items, status: job.status }) : null\n if (job.kind === 'site' && published > 0) {\n return {\n heading: 'Your site is live',\n lede: [\n partial,\n job.sitePublish?.drafts.length\n ? 'Your pages are published, except the ones listed below, which stayed drafts.'\n : 'Your pages are published, and anyone can visit your site now.',\n ]\n .filter(Boolean)\n .join(' '),\n }\n }\n if (partial) return { heading: 'Your site is ready', lede: `${partial} Your new pages are drafts. Publish them when you’re happy.` }\n if (job.kind === 'site' && job.sitePublish) {\n return {\n heading: 'Your site is ready',\n lede: 'None of your new pages could be published, so they are drafts. The reasons are below.',\n }\n }\n return {\n heading: generic ? 'Your AI job finished' : `Your ${noun} ${isAre} ready`,\n lede:\n job.kind === 'site'\n ? 'Your new pages are drafts. Publish them when you’re happy.'\n : 'Everything it built is an unpublished draft until you publish it.',\n }\n }\n if (phase === 'canceled') {\n return { heading: generic ? 'Your AI job was canceled' : `Your ${noun} was not built`, lede: 'The job was canceled.' }\n }\n return {\n heading: generic ? 'Your AI job stopped' : `Your ${noun} ${noun.endsWith('s') ? 'were' : 'was'} not built`,\n lede: why ?? (generic ? 'Something went wrong running this job.' : `Something went wrong building your ${noun}.`),\n }\n}\n\n/** A build's page heading and sentence (AGL-3616). */\nfunction aiBuildPageCopy(\n job: Pick<AiJobSummary, 'status' | 'review' | 'error'> & Partial<Pick<AiJobSummary, 'items' | 'sitePublish'>>,\n brand: string,\n): AiJobPageCopy {\n const phase = aiSiteBuildPhase(job)\n const outcome = aiBuildOutcomeLine({ items: job.items ?? [], status: job.status })\n if (phase === 'working') {\n return {\n heading: 'Building what you asked for',\n lede: `${brand} AI is building each part in turn. You can leave this page; it keeps going.`,\n }\n }\n if (phase === 'stopped') {\n return {\n heading: job.review?.reason === 'plan' ? 'Your plan is ready' : 'Your build needs you',\n lede: job.review?.message ?? job.error ?? 'It waits for your decision.',\n }\n }\n if (phase === 'done') {\n const failed = (job.items ?? []).some((row) => row.status === 'failed' || row.status === 'skipped')\n const live = job.sitePublish?.published.length ?? 0\n return {\n heading: failed ? 'Most of it is ready' : 'Everything you asked for is ready',\n lede: [\n outcome,\n live ? 'Your new pages are published.' : 'Everything it built is an unpublished draft until you publish it.',\n failed ? 'Try again builds only what failed.' : '',\n ]\n .filter(Boolean)\n .join(' '),\n }\n }\n if (phase === 'canceled') return { heading: 'Your build was canceled', lede: 'The job was canceled.' }\n return {\n heading: 'Nothing could be built',\n lede: [job.error ?? 'Something went wrong building what you asked for.', outcome].filter(Boolean).join(' '),\n }\n}\n"],"names":["aiJobConfirmingOwnPlan","aiJobKindNoun","aiJobRefundCopy","aiBuildItemRows","aiBuildOutcomeLine","aiSitePartialCopy","aiSiteBuildPhase","job","status","CREATION_ROWS","kind","resource","label","AI_SITE_LOOK_ROW_LABEL","name","aiSiteBuildRows","planStep","built","phase","items","length","site","pages","filter","row","op","steps","find","step","id","state","credits","Math","max","floor","creditsSpent","map","index","ledger","indexOf","screen","aiPlanScreenFor","plan","finished","net","creditsRefunded","slot","detail","Boolean","join","startedAt","hint","AI_SITE_PAGE_HINT","AI_SITE_LOOK_HINT","AI_SITE_ITEM_HINT","sections","section","aiSitePublishRow","planDone","stopped","planState","rows","AI_SITE_PLAN_HINT","pagesAsked","siteInputs","round","push","submissions","Map","output","outputs","set","get","take","left","stages","creation","create","entry","some","screens","forEach","page","title","reached","stage","done","current","published","sitePublish","aiSiteBuildFraction","counted","reduce","sum","min","aiSiteBuildCreditsLine","spent","refunded","refundedCredits","subject","refund","aiJobPageCopy","brand","aiBuildPageCopy","noun","generic","isAre","endsWith","why","review","message","error","heading","lede","partial","drafts","outcome","reason","failed","live"],"mappings":"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"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
import { type ResolvedBusinessProfile } from '@aglyn/aglyn/app-utils/business-profile';
|
|
18
|
+
import type { AiSystemBlock } from '../providers/contract';
|
|
19
|
+
import type { AiHostPublishContext } from './ai-host-publish-context';
|
|
20
|
+
import type { AiSiteInventory } from './ai-site-inventory';
|
|
21
|
+
/**
|
|
22
|
+
* What every AI job knows about the site it works on (AGL-3661), as ONE
|
|
23
|
+
* compact block: the business profile, what the site already has and whether
|
|
24
|
+
* it is live, and what the owner's edits taught it.
|
|
25
|
+
*
|
|
26
|
+
* ── Why it exists ─────────────────────────────────────────────────────────
|
|
27
|
+
*
|
|
28
|
+
* Before it, a job saw its brief, the guided start's answers and the site's
|
|
29
|
+
* name. It named a Free site "Austin Paws Grooming" that was called nothing
|
|
30
|
+
* of the kind, and nothing stopped it inventing a phone number. The block
|
|
31
|
+
* gives it the facts the owner entered and a standing rule for the ones they
|
|
32
|
+
* did not: never invent a name, a contact detail, a price or a review.
|
|
33
|
+
*
|
|
34
|
+
* ── Where it sits, and what that costs ────────────────────────────────────
|
|
35
|
+
*
|
|
36
|
+
* {@link aiSiteContextSystemBlock} marks it a `cacheBreakpoint`, placed AFTER
|
|
37
|
+
* every platform-wide cached block and before the volatile ones. The
|
|
38
|
+
* platform's prefix is still shared by every workspace; this block extends it
|
|
39
|
+
* with one cache entry per site, which every request of a job — each re-ask,
|
|
40
|
+
* each section of a page — reads at the cache rate. It changes only when the
|
|
41
|
+
* owner edits the profile, a page is published, or an edit is remembered.
|
|
42
|
+
*
|
|
43
|
+
* ── Held to a ceiling ─────────────────────────────────────────────────────
|
|
44
|
+
*
|
|
45
|
+
* {@link AI_SITE_CONTEXT_MAX_CHARS}: 400 tokens at the runtime's four
|
|
46
|
+
* characters a token. Lines are dropped from the least useful up — the site
|
|
47
|
+
* index, the profiles, then the preferences — and the closing rule is never
|
|
48
|
+
* dropped, because it is the line that stops an invented phone number.
|
|
49
|
+
*/
|
|
50
|
+
/** The block's ceiling: 400 tokens at four characters a token. */
|
|
51
|
+
export declare const AI_SITE_CONTEXT_MAX_CHARS = 1600;
|
|
52
|
+
/** Pages, forms, components and layouts listed by name in the site index, each. */
|
|
53
|
+
export declare const AI_SITE_CONTEXT_INDEX_PER_KIND = 8;
|
|
54
|
+
/** The rule every block closes with, whatever else it says. */
|
|
55
|
+
export declare const AI_SITE_CONTEXT_RULE = "Never invent a business name, a person\u2019s name, an email address, a phone number, a street address, opening hours, prices, reviews or testimonials. Use only the facts above; where a page needs one that is missing, write around it or leave a bracketed placeholder such as [phone number] for the owner to fill.";
|
|
56
|
+
export interface AiSiteContextInput {
|
|
57
|
+
/** The resolved business profile; `null` when the site has none to read. */
|
|
58
|
+
profile: ResolvedBusinessProfile | null;
|
|
59
|
+
/**
|
|
60
|
+
* The site's inventory, for a caller that does NOT already send it. A job
|
|
61
|
+
* whose prompt carries the inventory block (the plan step and the writers
|
|
62
|
+
* built on the doctrine) leaves this out: the block would only repeat it.
|
|
63
|
+
*/
|
|
64
|
+
inventory?: AiSiteInventory | null;
|
|
65
|
+
/** Whether the site is live and where. */
|
|
66
|
+
publish?: AiHostPublishContext | null;
|
|
67
|
+
/** What the owner's edits taught, strongest first. */
|
|
68
|
+
preferences?: readonly string[];
|
|
69
|
+
/**
|
|
70
|
+
* Whether the block may carry contact details. An answer that must name
|
|
71
|
+
* nobody and print no address (the insight rules) leaves them out.
|
|
72
|
+
*/
|
|
73
|
+
contact?: boolean;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* The site's index (AGL-3661): what it already has, by name and id, so a job
|
|
77
|
+
* reuses before it builds. Pages say whether they are live; a form says how
|
|
78
|
+
* many fields it asks for. Pure, so a caller holding an inventory pays one
|
|
79
|
+
* render and no read.
|
|
80
|
+
*/
|
|
81
|
+
export declare function aiSiteInventoryIndex(inventory: AiSiteInventory | null | undefined, publish?: AiHostPublishContext | null): string[];
|
|
82
|
+
/**
|
|
83
|
+
* The block as text (AGL-3661), never over {@link AI_SITE_CONTEXT_MAX_CHARS}.
|
|
84
|
+
* `''` when there is nothing to say, so a caller sends no block at all.
|
|
85
|
+
*/
|
|
86
|
+
export declare function aiSiteContextBlock(input: AiSiteContextInput): string;
|
|
87
|
+
/**
|
|
88
|
+
* The block as the system block a door appends after its own cached blocks
|
|
89
|
+
* (AGL-3661): a `cacheBreakpoint` of its own, so the platform's prefix ahead
|
|
90
|
+
* of it is still shared and this one is cached per site. `[]` when there is
|
|
91
|
+
* nothing to say.
|
|
92
|
+
*
|
|
93
|
+
* A door already carrying the runtime's limit of breakpoints passes
|
|
94
|
+
* `cache: false`, and the block rides as a volatile block after them instead.
|
|
95
|
+
*/
|
|
96
|
+
export declare function aiSiteContextSystemBlock(input: AiSiteContextInput | null | undefined, options?: {
|
|
97
|
+
cache?: boolean;
|
|
98
|
+
}): AiSystemBlock[];
|
|
99
|
+
/**
|
|
100
|
+
* A door's own instructions with the site context after them (AGL-3661) —
|
|
101
|
+
* THE call a generator makes to give its model the site:
|
|
102
|
+
*
|
|
103
|
+
* instructions: aiInstructionsWithSiteContext(MY_INSTRUCTIONS, context)
|
|
104
|
+
*
|
|
105
|
+
* The door's last instruction block is marked a breakpoint, so the
|
|
106
|
+
* platform-wide prefix (the doctrine and the door's rules) stays one cache
|
|
107
|
+
* entry for every workspace, and the context block after it is the per-site
|
|
108
|
+
* entry. Without a context it hands the instructions back unchanged, so a
|
|
109
|
+
* door's prompt is byte-for-byte what it was for a job that has no site.
|
|
110
|
+
*/
|
|
111
|
+
export declare function aiInstructionsWithSiteContext(instructions: readonly AiSystemBlock[], context: AiSiteContextInput | null | undefined): readonly AiSystemBlock[];
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright 2026 Aglyn LLC
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/ import { BUSINESS_TONE_LABELS } from "@aglyn/aglyn/app-utils/business-profile";
|
|
18
|
+
/**
|
|
19
|
+
* What every AI job knows about the site it works on (AGL-3661), as ONE
|
|
20
|
+
* compact block: the business profile, what the site already has and whether
|
|
21
|
+
* it is live, and what the owner's edits taught it.
|
|
22
|
+
*
|
|
23
|
+
* ── Why it exists ─────────────────────────────────────────────────────────
|
|
24
|
+
*
|
|
25
|
+
* Before it, a job saw its brief, the guided start's answers and the site's
|
|
26
|
+
* name. It named a Free site "Austin Paws Grooming" that was called nothing
|
|
27
|
+
* of the kind, and nothing stopped it inventing a phone number. The block
|
|
28
|
+
* gives it the facts the owner entered and a standing rule for the ones they
|
|
29
|
+
* did not: never invent a name, a contact detail, a price or a review.
|
|
30
|
+
*
|
|
31
|
+
* ── Where it sits, and what that costs ────────────────────────────────────
|
|
32
|
+
*
|
|
33
|
+
* {@link aiSiteContextSystemBlock} marks it a `cacheBreakpoint`, placed AFTER
|
|
34
|
+
* every platform-wide cached block and before the volatile ones. The
|
|
35
|
+
* platform's prefix is still shared by every workspace; this block extends it
|
|
36
|
+
* with one cache entry per site, which every request of a job — each re-ask,
|
|
37
|
+
* each section of a page — reads at the cache rate. It changes only when the
|
|
38
|
+
* owner edits the profile, a page is published, or an edit is remembered.
|
|
39
|
+
*
|
|
40
|
+
* ── Held to a ceiling ─────────────────────────────────────────────────────
|
|
41
|
+
*
|
|
42
|
+
* {@link AI_SITE_CONTEXT_MAX_CHARS}: 400 tokens at the runtime's four
|
|
43
|
+
* characters a token. Lines are dropped from the least useful up — the site
|
|
44
|
+
* index, the profiles, then the preferences — and the closing rule is never
|
|
45
|
+
* dropped, because it is the line that stops an invented phone number.
|
|
46
|
+
*/ /** The block's ceiling: 400 tokens at four characters a token. */ export const AI_SITE_CONTEXT_MAX_CHARS = 1600;
|
|
47
|
+
/** Pages, forms, components and layouts listed by name in the site index, each. */ export const AI_SITE_CONTEXT_INDEX_PER_KIND = 8;
|
|
48
|
+
/** The rule every block closes with, whatever else it says. */ export const AI_SITE_CONTEXT_RULE = 'Never invent a business name, a person’s name, an email address, a phone number, a street address, opening hours, prices, reviews or testimonials. Use only the facts above; where a page needs one that is missing, write around it or leave a bracketed placeholder such as [phone number] for the owner to fill.';
|
|
49
|
+
const clip = (value, max)=>value.length > max ? `${value.slice(0, max - 1)}…` : value;
|
|
50
|
+
/** One labelled line, or nothing for an empty value. */ const line = (label, value, max = 240)=>value && value.trim() ? `${label}: ${clip(value.trim(), max)}` : null;
|
|
51
|
+
const pathOf = (slug)=>`/${slug.trim().replace(/^\/+|\/+$/g, '')}`;
|
|
52
|
+
/**
|
|
53
|
+
* The site's index (AGL-3661): what it already has, by name and id, so a job
|
|
54
|
+
* reuses before it builds. Pages say whether they are live; a form says how
|
|
55
|
+
* many fields it asks for. Pure, so a caller holding an inventory pays one
|
|
56
|
+
* render and no read.
|
|
57
|
+
*/ export function aiSiteInventoryIndex(inventory, publish) {
|
|
58
|
+
var _ref;
|
|
59
|
+
if (!inventory) return [];
|
|
60
|
+
const live = new Set(((_ref = publish == null ? void 0 : publish.publishedPages) != null ? _ref : []).map((path)=>pathOf(path)));
|
|
61
|
+
const cap = AI_SITE_CONTEXT_INDEX_PER_KIND;
|
|
62
|
+
const more = (total)=>total > cap ? `, and ${total - cap} more` : '';
|
|
63
|
+
const lines = [];
|
|
64
|
+
const pages = inventory.screens.filter((screen)=>!screen.template);
|
|
65
|
+
if (pages.length) {
|
|
66
|
+
const rows = pages.slice(0, cap).map((page)=>{
|
|
67
|
+
const path = pathOf(page.slug);
|
|
68
|
+
return `${page.name} ${path} (${live.has(path) ? 'live' : 'draft'})`;
|
|
69
|
+
});
|
|
70
|
+
lines.push(`Pages: ${rows.join('; ')}${more(pages.length)}`);
|
|
71
|
+
}
|
|
72
|
+
if (inventory.forms.length) {
|
|
73
|
+
const rows = inventory.forms.slice(0, cap).map((form)=>`${form.name} [${form.id}, ${form.fields.length} fields]`);
|
|
74
|
+
lines.push(`Forms: ${rows.join('; ')}${more(inventory.forms.length)}`);
|
|
75
|
+
}
|
|
76
|
+
if (inventory.components.length) {
|
|
77
|
+
const rows = inventory.components.slice(0, cap).map((component)=>`${component.name} [${component.id}]`);
|
|
78
|
+
lines.push(`Components: ${rows.join('; ')}${more(inventory.components.length)}`);
|
|
79
|
+
}
|
|
80
|
+
if (inventory.layouts.length) {
|
|
81
|
+
const rows = inventory.layouts.slice(0, cap).map((layout)=>`${layout.name} [${layout.id}]`);
|
|
82
|
+
lines.push(`Layouts: ${rows.join('; ')}${more(inventory.layouts.length)}`);
|
|
83
|
+
}
|
|
84
|
+
return lines;
|
|
85
|
+
}
|
|
86
|
+
/** The block's lines in priority order; each group is dropped whole when it does not fit. */ function contextGroups(input) {
|
|
87
|
+
const { profile, publish, preferences } = input;
|
|
88
|
+
const business = [];
|
|
89
|
+
if (profile) {
|
|
90
|
+
var _ref;
|
|
91
|
+
var _profile_toneNotes, _profile_name, _profile_whatYouDo, _profile_services, _profile_serviceArea, _profile_audience;
|
|
92
|
+
const toneLabel = profile.tone ? BUSINESS_TONE_LABELS[profile.tone.value] : null;
|
|
93
|
+
const toneNotes = (_ref = (_profile_toneNotes = profile.toneNotes) == null ? void 0 : _profile_toneNotes.value) != null ? _ref : '';
|
|
94
|
+
business.push(...[
|
|
95
|
+
line('Business name', (_profile_name = profile.name) == null ? void 0 : _profile_name.value, 120),
|
|
96
|
+
line('What it does', (_profile_whatYouDo = profile.whatYouDo) == null ? void 0 : _profile_whatYouDo.value),
|
|
97
|
+
line('Business type', profile.businessType, 80),
|
|
98
|
+
line('Services', (_profile_services = profile.services) == null ? void 0 : _profile_services.value.join('; ')),
|
|
99
|
+
line('Area served', (_profile_serviceArea = profile.serviceArea) == null ? void 0 : _profile_serviceArea.value, 160),
|
|
100
|
+
line('Audience', (_profile_audience = profile.audience) == null ? void 0 : _profile_audience.value, 160),
|
|
101
|
+
line('Tone of voice', [
|
|
102
|
+
toneLabel,
|
|
103
|
+
toneNotes
|
|
104
|
+
].filter(Boolean).join(' — ') || null)
|
|
105
|
+
].filter((entry)=>Boolean(entry)));
|
|
106
|
+
}
|
|
107
|
+
const contact = [];
|
|
108
|
+
if (profile && input.contact !== false) {
|
|
109
|
+
const parts = [
|
|
110
|
+
profile.contact.email ? `email ${profile.contact.email}` : null,
|
|
111
|
+
profile.contact.phone ? `phone ${profile.contact.phone}` : null,
|
|
112
|
+
profile.contact.address ? `address ${profile.contact.address}` : null,
|
|
113
|
+
profile.contact.hours ? `hours ${profile.contact.hours}` : null
|
|
114
|
+
].filter(Boolean);
|
|
115
|
+
contact.push(parts.length ? `Contact details the owner entered (use exactly as written): ${clip(parts.join('; '), 400)}` : 'Contact details: none entered yet.');
|
|
116
|
+
}
|
|
117
|
+
const status = [];
|
|
118
|
+
if (publish) {
|
|
119
|
+
var _publish_liveUrl;
|
|
120
|
+
status.push(publish.published ? `Site: live at ${(_publish_liveUrl = publish.liveUrl) != null ? _publish_liveUrl : 'its address'}; live pages ${clip(publish.publishedPages.join(', '), 200)}` : `Site: not published yet${publish.liveUrl ? `; it will be at ${publish.liveUrl}` : ''}`);
|
|
121
|
+
}
|
|
122
|
+
const remembered = (preferences == null ? void 0 : preferences.length) ? [
|
|
123
|
+
`What the owner's past edits showed: ${clip(preferences.join('; '), 360)}.`
|
|
124
|
+
] : [];
|
|
125
|
+
const profiles = profile && input.contact !== false && profile.profiles.length ? [
|
|
126
|
+
`Social profiles: ${clip(profile.profiles.join(', '), 300)}`
|
|
127
|
+
] : [];
|
|
128
|
+
const index = aiSiteInventoryIndex(input.inventory, publish);
|
|
129
|
+
// Priority order: what the business is, how to reach it, whether it is
|
|
130
|
+
// live, what the owner taught, its profiles, then what the site holds.
|
|
131
|
+
return [
|
|
132
|
+
business,
|
|
133
|
+
contact,
|
|
134
|
+
status,
|
|
135
|
+
remembered,
|
|
136
|
+
profiles,
|
|
137
|
+
index
|
|
138
|
+
];
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* The block as text (AGL-3661), never over {@link AI_SITE_CONTEXT_MAX_CHARS}.
|
|
142
|
+
* `''` when there is nothing to say, so a caller sends no block at all.
|
|
143
|
+
*/ export function aiSiteContextBlock(input) {
|
|
144
|
+
const groups = contextGroups(input).filter((group)=>group.length);
|
|
145
|
+
if (!groups.length) return '';
|
|
146
|
+
const head = 'About this site, from its owner’s settings. Write for this business and do not contradict these facts:';
|
|
147
|
+
const render = (kept)=>[
|
|
148
|
+
head,
|
|
149
|
+
...kept.flat(),
|
|
150
|
+
AI_SITE_CONTEXT_RULE
|
|
151
|
+
].join('\n');
|
|
152
|
+
const kept = [
|
|
153
|
+
...groups
|
|
154
|
+
];
|
|
155
|
+
let text = render(kept);
|
|
156
|
+
while(text.length > AI_SITE_CONTEXT_MAX_CHARS && kept.length > 1){
|
|
157
|
+
kept.pop();
|
|
158
|
+
text = render(kept);
|
|
159
|
+
}
|
|
160
|
+
if (text.length <= AI_SITE_CONTEXT_MAX_CHARS) return text;
|
|
161
|
+
// One group still too long (a very long profile): its lines are cut, the
|
|
162
|
+
// closing rule is kept whole.
|
|
163
|
+
const room = AI_SITE_CONTEXT_MAX_CHARS - head.length - AI_SITE_CONTEXT_RULE.length - 2;
|
|
164
|
+
return [
|
|
165
|
+
head,
|
|
166
|
+
clip(kept.flat().join('\n'), Math.max(0, room)),
|
|
167
|
+
AI_SITE_CONTEXT_RULE
|
|
168
|
+
].join('\n');
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* The block as the system block a door appends after its own cached blocks
|
|
172
|
+
* (AGL-3661): a `cacheBreakpoint` of its own, so the platform's prefix ahead
|
|
173
|
+
* of it is still shared and this one is cached per site. `[]` when there is
|
|
174
|
+
* nothing to say.
|
|
175
|
+
*
|
|
176
|
+
* A door already carrying the runtime's limit of breakpoints passes
|
|
177
|
+
* `cache: false`, and the block rides as a volatile block after them instead.
|
|
178
|
+
*/ export function aiSiteContextSystemBlock(input, options = {}) {
|
|
179
|
+
const text = input ? aiSiteContextBlock(input) : '';
|
|
180
|
+
if (!text) return [];
|
|
181
|
+
return [
|
|
182
|
+
options.cache === false ? {
|
|
183
|
+
text,
|
|
184
|
+
volatile: true,
|
|
185
|
+
site: true
|
|
186
|
+
} : {
|
|
187
|
+
text,
|
|
188
|
+
cacheBreakpoint: true,
|
|
189
|
+
site: true
|
|
190
|
+
}
|
|
191
|
+
];
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* A door's own instructions with the site context after them (AGL-3661) —
|
|
195
|
+
* THE call a generator makes to give its model the site:
|
|
196
|
+
*
|
|
197
|
+
* instructions: aiInstructionsWithSiteContext(MY_INSTRUCTIONS, context)
|
|
198
|
+
*
|
|
199
|
+
* The door's last instruction block is marked a breakpoint, so the
|
|
200
|
+
* platform-wide prefix (the doctrine and the door's rules) stays one cache
|
|
201
|
+
* entry for every workspace, and the context block after it is the per-site
|
|
202
|
+
* entry. Without a context it hands the instructions back unchanged, so a
|
|
203
|
+
* door's prompt is byte-for-byte what it was for a job that has no site.
|
|
204
|
+
*/ export function aiInstructionsWithSiteContext(instructions, context) {
|
|
205
|
+
const site = aiSiteContextSystemBlock(context);
|
|
206
|
+
if (!site.length) return instructions;
|
|
207
|
+
const blocks = [
|
|
208
|
+
...instructions
|
|
209
|
+
];
|
|
210
|
+
const last = blocks.length - 1;
|
|
211
|
+
if (last >= 0 && !blocks[last].volatile && !blocks.some((block)=>block.cacheBreakpoint)) {
|
|
212
|
+
blocks[last] = _extends({}, blocks[last], {
|
|
213
|
+
cacheBreakpoint: true
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
return [
|
|
217
|
+
...blocks,
|
|
218
|
+
...site
|
|
219
|
+
];
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
//# sourceMappingURL=ai-site-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-site-context.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n BUSINESS_TONE_LABELS,\n type ResolvedBusinessProfile,\n} from '@aglyn/aglyn/app-utils/business-profile'\nimport type { AiSystemBlock } from '../providers/contract'\nimport type { AiHostPublishContext } from './ai-host-publish-context'\nimport type { AiSiteInventory } from './ai-site-inventory'\n\n/**\n * What every AI job knows about the site it works on (AGL-3661), as ONE\n * compact block: the business profile, what the site already has and whether\n * it is live, and what the owner's edits taught it.\n *\n * ── Why it exists ─────────────────────────────────────────────────────────\n *\n * Before it, a job saw its brief, the guided start's answers and the site's\n * name. It named a Free site \"Austin Paws Grooming\" that was called nothing\n * of the kind, and nothing stopped it inventing a phone number. The block\n * gives it the facts the owner entered and a standing rule for the ones they\n * did not: never invent a name, a contact detail, a price or a review.\n *\n * ── Where it sits, and what that costs ────────────────────────────────────\n *\n * {@link aiSiteContextSystemBlock} marks it a `cacheBreakpoint`, placed AFTER\n * every platform-wide cached block and before the volatile ones. The\n * platform's prefix is still shared by every workspace; this block extends it\n * with one cache entry per site, which every request of a job — each re-ask,\n * each section of a page — reads at the cache rate. It changes only when the\n * owner edits the profile, a page is published, or an edit is remembered.\n *\n * ── Held to a ceiling ─────────────────────────────────────────────────────\n *\n * {@link AI_SITE_CONTEXT_MAX_CHARS}: 400 tokens at the runtime's four\n * characters a token. Lines are dropped from the least useful up — the site\n * index, the profiles, then the preferences — and the closing rule is never\n * dropped, because it is the line that stops an invented phone number.\n */\n\n/** The block's ceiling: 400 tokens at four characters a token. */\nexport const AI_SITE_CONTEXT_MAX_CHARS = 1_600\n\n/** Pages, forms, components and layouts listed by name in the site index, each. */\nexport const AI_SITE_CONTEXT_INDEX_PER_KIND = 8\n\n/** The rule every block closes with, whatever else it says. */\nexport const AI_SITE_CONTEXT_RULE =\n 'Never invent a business name, a person’s name, an email address, a phone number, a street address, opening hours, prices, reviews or testimonials. Use only the facts above; where a page needs one that is missing, write around it or leave a bracketed placeholder such as [phone number] for the owner to fill.'\n\nexport interface AiSiteContextInput {\n /** The resolved business profile; `null` when the site has none to read. */\n profile: ResolvedBusinessProfile | null\n /**\n * The site's inventory, for a caller that does NOT already send it. A job\n * whose prompt carries the inventory block (the plan step and the writers\n * built on the doctrine) leaves this out: the block would only repeat it.\n */\n inventory?: AiSiteInventory | null\n /** Whether the site is live and where. */\n publish?: AiHostPublishContext | null\n /** What the owner's edits taught, strongest first. */\n preferences?: readonly string[]\n /**\n * Whether the block may carry contact details. An answer that must name\n * nobody and print no address (the insight rules) leaves them out.\n */\n contact?: boolean\n}\n\nconst clip = (value: string, max: number) => (value.length > max ? `${value.slice(0, max - 1)}…` : value)\n\n/** One labelled line, or nothing for an empty value. */\nconst line = (label: string, value: string | null | undefined, max = 240): string | null =>\n value && value.trim() ? `${label}: ${clip(value.trim(), max)}` : null\n\nconst pathOf = (slug: string) => `/${slug.trim().replace(/^\\/+|\\/+$/g, '')}`\n\n/**\n * The site's index (AGL-3661): what it already has, by name and id, so a job\n * reuses before it builds. Pages say whether they are live; a form says how\n * many fields it asks for. Pure, so a caller holding an inventory pays one\n * render and no read.\n */\nexport function aiSiteInventoryIndex(\n inventory: AiSiteInventory | null | undefined,\n publish?: AiHostPublishContext | null,\n): string[] {\n if (!inventory) return []\n const live = new Set((publish?.publishedPages ?? []).map((path) => pathOf(path)))\n const cap = AI_SITE_CONTEXT_INDEX_PER_KIND\n const more = (total: number) => (total > cap ? `, and ${total - cap} more` : '')\n const lines: string[] = []\n const pages = inventory.screens.filter((screen) => !screen.template)\n if (pages.length) {\n const rows = pages.slice(0, cap).map((page) => {\n const path = pathOf(page.slug)\n return `${page.name} ${path} (${live.has(path) ? 'live' : 'draft'})`\n })\n lines.push(`Pages: ${rows.join('; ')}${more(pages.length)}`)\n }\n if (inventory.forms.length) {\n const rows = inventory.forms.slice(0, cap).map((form) => `${form.name} [${form.id}, ${form.fields.length} fields]`)\n lines.push(`Forms: ${rows.join('; ')}${more(inventory.forms.length)}`)\n }\n if (inventory.components.length) {\n const rows = inventory.components.slice(0, cap).map((component) => `${component.name} [${component.id}]`)\n lines.push(`Components: ${rows.join('; ')}${more(inventory.components.length)}`)\n }\n if (inventory.layouts.length) {\n const rows = inventory.layouts.slice(0, cap).map((layout) => `${layout.name} [${layout.id}]`)\n lines.push(`Layouts: ${rows.join('; ')}${more(inventory.layouts.length)}`)\n }\n return lines\n}\n\n/** The block's lines in priority order; each group is dropped whole when it does not fit. */\nfunction contextGroups(input: AiSiteContextInput): string[][] {\n const { profile, publish, preferences } = input\n const business: string[] = []\n if (profile) {\n const toneLabel = profile.tone ? BUSINESS_TONE_LABELS[profile.tone.value] : null\n const toneNotes = profile.toneNotes?.value ?? ''\n business.push(\n ...[\n line('Business name', profile.name?.value, 120),\n line('What it does', profile.whatYouDo?.value),\n line('Business type', profile.businessType, 80),\n line('Services', profile.services?.value.join('; ')),\n line('Area served', profile.serviceArea?.value, 160),\n line('Audience', profile.audience?.value, 160),\n line('Tone of voice', [toneLabel, toneNotes].filter(Boolean).join(' — ') || null),\n ].filter((entry): entry is string => Boolean(entry)),\n )\n }\n const contact: string[] = []\n if (profile && input.contact !== false) {\n const parts = [\n profile.contact.email ? `email ${profile.contact.email}` : null,\n profile.contact.phone ? `phone ${profile.contact.phone}` : null,\n profile.contact.address ? `address ${profile.contact.address}` : null,\n profile.contact.hours ? `hours ${profile.contact.hours}` : null,\n ].filter(Boolean)\n contact.push(\n parts.length\n ? `Contact details the owner entered (use exactly as written): ${clip(parts.join('; '), 400)}`\n : 'Contact details: none entered yet.',\n )\n }\n const status: string[] = []\n if (publish) {\n status.push(\n publish.published\n ? `Site: live at ${publish.liveUrl ?? 'its address'}; live pages ${clip(publish.publishedPages.join(', '), 200)}`\n : `Site: not published yet${publish.liveUrl ? `; it will be at ${publish.liveUrl}` : ''}`,\n )\n }\n const remembered = preferences?.length\n ? [`What the owner's past edits showed: ${clip(preferences.join('; '), 360)}.`]\n : []\n const profiles =\n profile && input.contact !== false && profile.profiles.length\n ? [`Social profiles: ${clip(profile.profiles.join(', '), 300)}`]\n : []\n const index = aiSiteInventoryIndex(input.inventory, publish)\n // Priority order: what the business is, how to reach it, whether it is\n // live, what the owner taught, its profiles, then what the site holds.\n return [business, contact, status, remembered, profiles, index]\n}\n\n/**\n * The block as text (AGL-3661), never over {@link AI_SITE_CONTEXT_MAX_CHARS}.\n * `''` when there is nothing to say, so a caller sends no block at all.\n */\nexport function aiSiteContextBlock(input: AiSiteContextInput): string {\n const groups = contextGroups(input).filter((group) => group.length)\n if (!groups.length) return ''\n const head = 'About this site, from its owner’s settings. Write for this business and do not contradict these facts:'\n const render = (kept: string[][]) => [head, ...kept.flat(), AI_SITE_CONTEXT_RULE].join('\\n')\n const kept = [...groups]\n let text = render(kept)\n while (text.length > AI_SITE_CONTEXT_MAX_CHARS && kept.length > 1) {\n kept.pop()\n text = render(kept)\n }\n if (text.length <= AI_SITE_CONTEXT_MAX_CHARS) return text\n // One group still too long (a very long profile): its lines are cut, the\n // closing rule is kept whole.\n const room = AI_SITE_CONTEXT_MAX_CHARS - head.length - AI_SITE_CONTEXT_RULE.length - 2\n return [head, clip(kept.flat().join('\\n'), Math.max(0, room)), AI_SITE_CONTEXT_RULE].join('\\n')\n}\n\n/**\n * The block as the system block a door appends after its own cached blocks\n * (AGL-3661): a `cacheBreakpoint` of its own, so the platform's prefix ahead\n * of it is still shared and this one is cached per site. `[]` when there is\n * nothing to say.\n *\n * A door already carrying the runtime's limit of breakpoints passes\n * `cache: false`, and the block rides as a volatile block after them instead.\n */\nexport function aiSiteContextSystemBlock(\n input: AiSiteContextInput | null | undefined,\n options: { cache?: boolean } = {},\n): AiSystemBlock[] {\n const text = input ? aiSiteContextBlock(input) : ''\n if (!text) return []\n return [options.cache === false ? { text, volatile: true, site: true } : { text, cacheBreakpoint: true, site: true }]\n}\n\n/**\n * A door's own instructions with the site context after them (AGL-3661) —\n * THE call a generator makes to give its model the site:\n *\n * instructions: aiInstructionsWithSiteContext(MY_INSTRUCTIONS, context)\n *\n * The door's last instruction block is marked a breakpoint, so the\n * platform-wide prefix (the doctrine and the door's rules) stays one cache\n * entry for every workspace, and the context block after it is the per-site\n * entry. Without a context it hands the instructions back unchanged, so a\n * door's prompt is byte-for-byte what it was for a job that has no site.\n */\nexport function aiInstructionsWithSiteContext(\n instructions: readonly AiSystemBlock[],\n context: AiSiteContextInput | null | undefined,\n): readonly AiSystemBlock[] {\n const site = aiSiteContextSystemBlock(context)\n if (!site.length) return instructions\n const blocks = [...instructions]\n const last = blocks.length - 1\n if (last >= 0 && !blocks[last].volatile && !blocks.some((block) => block.cacheBreakpoint)) {\n blocks[last] = { ...blocks[last], cacheBreakpoint: true }\n }\n return [...blocks, ...site]\n}\n"],"names":["BUSINESS_TONE_LABELS","AI_SITE_CONTEXT_MAX_CHARS","AI_SITE_CONTEXT_INDEX_PER_KIND","AI_SITE_CONTEXT_RULE","clip","value","max","length","slice","line","label","trim","pathOf","slug","replace","aiSiteInventoryIndex","inventory","publish","live","Set","publishedPages","map","path","cap","more","total","lines","pages","screens","filter","screen","template","rows","page","name","has","push","join","forms","form","id","fields","components","component","layouts","layout","contextGroups","input","profile","preferences","business","toneLabel","tone","toneNotes","whatYouDo","businessType","services","serviceArea","audience","Boolean","entry","contact","parts","email","phone","address","hours","status","published","liveUrl","remembered","profiles","index","aiSiteContextBlock","groups","group","head","render","kept","flat","text","pop","room","Math","aiSiteContextSystemBlock","options","cache","volatile","site","cacheBreakpoint","aiInstructionsWithSiteContext","instructions","context","blocks","last","some","block"],"mappings":"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"}
|
|
@@ -84,6 +84,8 @@ export declare const AI_SITE_PLAN_MAX_TOKENS: {
|
|
|
84
84
|
export interface AiFreeSiteWorstCase {
|
|
85
85
|
/** The site plan: its answer and its one re-ask, each at its ceiling. */
|
|
86
86
|
plan: number;
|
|
87
|
+
/** The site's look (AGL-3660): its one answer at its ceiling, on the fast tier. */
|
|
88
|
+
look: number;
|
|
87
89
|
/** The one layout a Free plan includes, built first on a site with none. */
|
|
88
90
|
layout: number;
|
|
89
91
|
/** A page's first section pass, which writes the cached prefix. */
|
|
@@ -104,7 +106,7 @@ export interface AiFreeSiteWorstCase {
|
|
|
104
106
|
export declare const AI_FREE_SITE_WORST_CASE_CREDITS: Readonly<AiFreeSiteWorstCase>;
|
|
105
107
|
/**
|
|
106
108
|
* The most sections a Free site's plan fits in the Free taste at its worst,
|
|
107
|
-
* across all its pages: the plan, the layouts it builds first, each page's
|
|
109
|
+
* across all its pages: the plan, the look, the layouts it builds first, each page's
|
|
108
110
|
* listing and first pass, and ROOM FOR ONE RETRIED SECTION, then as many later
|
|
109
111
|
* passes as the rest pays for; none when that is already past the wall. Every
|
|
110
112
|
* page's first pass is counted as a cache write, which errs dear.
|
|
@@ -117,7 +119,7 @@ export declare function aiFreeSiteSectionsWithin(creations: {
|
|
|
117
119
|
export declare const AI_FREE_SITE_NOMINAL_SECTIONS = 3;
|
|
118
120
|
/**
|
|
119
121
|
* About what a Free site start of this many pages costs at its worst, in
|
|
120
|
-
* credits, for the dialog that asks for one: the site plan, each page's
|
|
122
|
+
* credits, for the dialog that asks for one: the site plan, its look, each page's
|
|
121
123
|
* listing and its sections at the nominal count, from the figures the wall is
|
|
122
124
|
* proven with rather than the nominal credits a paid estimate counts.
|
|
123
125
|
*/
|
|
@@ -173,6 +175,12 @@ export interface AiSiteJobInputs {
|
|
|
173
175
|
* few words in a brief, not a lookup.
|
|
174
176
|
*/
|
|
175
177
|
starter: string;
|
|
178
|
+
/**
|
|
179
|
+
* The kind of site the person picked (AGL-3660), by its `AI_SITE_KINDS`
|
|
180
|
+
* id; empty when nobody picked one, and the business type suggests it.
|
|
181
|
+
* It sets the look the site is designed in and how its pages are arranged.
|
|
182
|
+
*/
|
|
183
|
+
siteKind: string;
|
|
176
184
|
/** How many pages the member asked for. */
|
|
177
185
|
pages: number;
|
|
178
186
|
/** The per-site variables an agency batch varies; empty when none was given. */
|