@aglyn/plugins-ai 1.0.0-beta.293 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/package.json +17 -17
  2. package/src/lib/components/ai-job-links.d.ts +17 -3
  3. package/src/lib/components/ai-job-links.js +32 -5
  4. package/src/lib/components/ai-job-links.js.map +1 -1
  5. package/src/lib/components/ai-job-requests.d.ts +9 -0
  6. package/src/lib/components/ai-job-requests.js +34 -0
  7. package/src/lib/components/ai-job-requests.js.map +1 -1
  8. package/src/lib/components/ai-site-build-page.component.js +28 -2
  9. package/src/lib/components/ai-site-build-page.component.js.map +1 -1
  10. package/src/lib/components/assist-edit-canvas.d.ts +12 -6
  11. package/src/lib/components/assist-edit-canvas.js +86 -36
  12. package/src/lib/components/assist-edit-canvas.js.map +1 -1
  13. package/src/lib/components/assist-edit-card.component.js +9 -8
  14. package/src/lib/components/assist-edit-card.component.js.map +1 -1
  15. package/src/lib/components/assist-jobs-drawer.component.d.ts +7 -1
  16. package/src/lib/components/assist-jobs-drawer.component.js +20 -6
  17. package/src/lib/components/assist-jobs-drawer.component.js.map +1 -1
  18. package/src/lib/components/assist-page-change-card.component.d.ts +44 -0
  19. package/src/lib/components/assist-page-change-card.component.js +166 -0
  20. package/src/lib/components/assist-page-change-card.component.js.map +1 -0
  21. package/src/lib/components/assist-panel.component.d.ts +105 -1
  22. package/src/lib/components/assist-panel.component.js +242 -36
  23. package/src/lib/components/assist-panel.component.js.map +1 -1
  24. package/src/lib/jobs/ai-job-edit-step.d.ts +82 -16
  25. package/src/lib/jobs/ai-job-edit-step.js +209 -62
  26. package/src/lib/jobs/ai-job-edit-step.js.map +1 -1
  27. package/src/lib/jobs/ai-job-navigation-step.js +4 -2
  28. package/src/lib/jobs/ai-job-navigation-step.js.map +1 -1
  29. package/src/lib/model/ai-edit-job.d.ts +33 -16
  30. package/src/lib/model/ai-edit-job.js +50 -34
  31. package/src/lib/model/ai-edit-job.js.map +1 -1
  32. package/src/lib/model/ai-job-activity.d.ts +6 -11
  33. package/src/lib/model/ai-job-activity.js +8 -15
  34. package/src/lib/model/ai-job-activity.js.map +1 -1
  35. package/src/lib/model/ai-job-notice.js +3 -1
  36. package/src/lib/model/ai-job-notice.js.map +1 -1
  37. package/src/lib/model/ai-jobs.types.d.ts +6 -0
  38. package/src/lib/model/ai-jobs.types.js.map +1 -1
  39. package/src/lib/model/ai-site-inventory.d.ts +6 -0
  40. package/src/lib/model/ai-site-inventory.js.map +1 -1
  41. package/src/lib/model/assist-edit.d.ts +19 -1
  42. package/src/lib/model/assist-edit.js +7 -0
  43. package/src/lib/model/assist-edit.js.map +1 -1
  44. package/src/lib/model/assist-page-change.d.ts +100 -0
  45. package/src/lib/model/assist-page-change.js +107 -0
  46. package/src/lib/model/assist-page-change.js.map +1 -0
  47. package/src/lib/runtime/ai-eval.js +3 -1
  48. package/src/lib/runtime/ai-eval.js.map +1 -1
  49. package/src/lib/runtime/ai-node-tree.d.ts +5 -0
  50. package/src/lib/runtime/ai-node-tree.js +18 -3
  51. package/src/lib/runtime/ai-node-tree.js.map +1 -1
  52. package/src/lib/runtime/site-inventory.d.ts +8 -1
  53. package/src/lib/runtime/site-inventory.js +18 -0
  54. package/src/lib/runtime/site-inventory.js.map +1 -1
  55. package/src/lib/server/assist-build.js +2 -0
  56. package/src/lib/server/assist-build.js.map +1 -1
  57. package/src/lib/server/assist-chat.js +56 -3
  58. package/src/lib/server/assist-chat.js.map +1 -1
  59. package/src/lib/server/assist-edit.d.ts +61 -4
  60. package/src/lib/server/assist-edit.js +248 -38
  61. package/src/lib/server/assist-edit.js.map +1 -1
  62. package/src/lib/server/assist-page-change.d.ts +62 -0
  63. package/src/lib/server/assist-page-change.js +140 -0
  64. package/src/lib/server/assist-page-change.js.map +1 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-edit-step.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 { checkEntitlement } from '@aglyn/aglyn/app-utils/plan-entitlements'\nimport { SCREEN_KIND_TEMPLATE } from '@aglyn/aglyn/app-utils/screen-route'\nimport { screenSeoStageKey, SCREEN_SEO_TEXT_FIELDS } from '@aglyn/aglyn/app-utils/screen-seo-fields'\nimport { decodeStoredNodes, encodeStoredNodes } from '@aglyn/aglyn/app-utils/stored-nodes'\nimport { CanvasManager } from '@aglyn/aglyn/canvas-manager/canvas-manager'\nimport { FEATURE_FLAG } from '@aglyn/aglyn/foundation/constants/shared'\nimport type { EditorSession } from '@aglyn/aglyn/plugin-manager/editor-sessions'\nimport type { NodesMap } from '@aglyn/aglyn/types/nodes'\nimport { resolveOrgIdForHost } from '@aglyn/tenant-data-admin/server/organizations'\nimport { applyAssistEdit } from '../components/assist-edit-canvas'\nimport {\n AI_EDIT_COMPONENT_LEFT_OUT,\n AI_EDIT_EMAIL_COPY,\n AI_EDIT_GONE_COPY,\n AI_EDIT_NEEDS_VERSIONING_COPY,\n AI_EDIT_NO_NODES_COPY,\n AI_EDIT_NO_SITE_COPY,\n AI_EDIT_NOTHING_COPY,\n AI_EDIT_REFUSED_COPY,\n AI_EDIT_SEO_LEFT_OUT,\n AI_EDIT_STALE_COPY,\n AI_EDIT_VERSION_GONE_COPY,\n aiEditJobOutline,\n aiEditOutputNote,\n aiEditVersionName,\n parseAiEditJobInputs,\n type AiEditJobInputs,\n type AiEditPlacement,\n type AiEditTargetKind,\n} from '../model/ai-edit-job'\nimport type { AiJob, AiJobOutput } from '../model/ai-jobs.types'\nimport {\n ASSIST_EDIT_TOOL_NAME,\n type AssistEditCanvasContext,\n type AssistEditOp,\n type AssistEditProposal,\n type AssistEditTarget,\n} from '../model/assist-edit'\nimport { AI_STEP_TIERS } from '../providers/catalog'\nimport { AI_ROUTING_TABLE, aiModelForStep } from '../providers/routing'\nimport {\n runValidatedGeneration,\n validateStreamedGeneration,\n type AiCustomGenerationInput,\n} from '../runtime/ai-doctrine'\nimport { AI_PALETTE } from '../runtime/ai-palette.generated'\nimport type { AiSystemBlock } from '../runtime/ai-runtime'\nimport {\n assistEditTool,\n checkAssistEditAnswer,\n editCanvasBlock,\n editSelectionBlock,\n parseAssistEditContext,\n} from '../server/assist-edit'\nimport { registerAiJobAdmission, type AiJobAdmission, type AiJobAdmissionRefusal } from './ai-job-admission'\nimport { aiJobStepBudget } from './ai-job-budget'\nimport { aiOriginJobId } from './ai-job-draft-ids'\nimport { aiGenerationSpent, aiLimitReview, aiUnspentOutcome } from './ai-job-generation'\nimport { AI_JOB_BRIEF_MAX_CHARS, type AiJobStepOutcome, type AiJobStepRunner } from './ai-job-text-step'\nimport { registerAiJobStep } from './ai-jobs'\n\n/**\n * The `edit` step (AGL-3616): a change to a page or a layout the site already\n * has, from a description, made on the server with no editor open.\n *\n * It is the Assist edit rung, run as a job. The model is shown the stored\n * document as the rung's outline and proposes through the rung's own tool;\n * its answer is held to the rung's closed world and palette validators\n * (`checkAssistEditAnswer`) and re-asked once, naming what was left out; and\n * what survives lands through the canvas's own guarded mutators\n * (`applyAssistEdit`), on a `CanvasManager` built headless from the stored\n * nodes, inside the transaction that writes it.\n *\n * ── Where the change lands, decided before anything is spent ────────────\n *\n * Never on a version a visitor sees. With version history, a NEW version\n * beside the one it started from — a copy of that version's stored fields with\n * the changed nodes — which becomes the page's current version only when the\n * page is not published; a published page's current version, and a layout's,\n * never move. Without it, in place, on a version no visitor reaches: an\n * unpublished page's, or one that is not the current version and no schedule\n * will publish. Anything else is refused as a plan limit, charged nothing.\n *\n * ── What a job cannot do that the editor can ────────────────────────────\n *\n * The rung's chat door never offers saving part of a page as a reusable\n * component, and neither does this. A page's search title and description\n * live on the page itself, not on a version, so they are written only for a\n * page that is not published; on a published page they are left out, and the\n * output's note says so, as it says every change it left out.\n *\n * ── One version per job ──────────────────────────────────────────────────\n *\n * The new version is named by the job's id — a build's unit is named by its\n * plan item's id — so a run asked again finds what it wrote and spends\n * nothing. A change made in place stamps the version with the job's id for\n * the same reason.\n */\n\n/** The longest the step may take reading the page and writing it. */\nexport const AI_EDIT_READS_MS = 2_000\n\n/** The step's time: its answer and its re-ask at the `job.edit` ceiling, with its own reads. */\nexport const AI_JOB_EDIT_STEP_BUDGET = aiJobStepBudget({\n tier: AI_STEP_TIERS['job.edit'],\n maxTokens: AI_ROUTING_TABLE['job.edit'].maxTokens,\n lookups: 0,\n ownReadsMs: AI_EDIT_READS_MS,\n})\n\nexport const AI_JOB_EDIT_STEP_MINIMUM_MS = AI_JOB_EDIT_STEP_BUDGET.minimumMs\n\nexport function aiJobEditModel(): string {\n return aiModelForStep('job.edit')\n}\n\n/**\n * The job's own rules, byte-identical on every request, ahead of the rung's\n * protocol and catalog for the document's kind, which carries the breakpoint:\n * both are the same for every site, so a kind's whole prefix caches once.\n */\nexport const AI_JOB_EDIT_RULES: AiSystemBlock = {\n text: [\n 'You change a page or a layout the person already has, as a job that runs on its own. No editor is open, nothing is selected, and nobody can answer a question before you finish.',\n `- Make exactly the change the request asks for, and keep everything else as it is: other elements, their text, their styles and their order. Never rewrite, restyle, move or remove what the request does not ask about.`,\n '- Find the elements the request means in the canvas outline the request carries, by what they say and what they are. Name only ids that outline lists.',\n `- If the outline does not show an element the request is about, change nothing for that part: never guess at an element.`,\n `- Call ${ASSIST_EDIT_TOOL_NAME} exactly once, with every operation. Its summary is one short line saying what changed, in the language of the request; it names the version the change is saved in.`,\n \"- Fill in a page's search title or description only when the request asks for it.\",\n '- What you change is saved where no visitor sees it until the person publishes it, so never say the site or the page has changed.',\n '- The protocol below was written for Assist’s chat. In this job there is no user to ask or to press Apply, and no selection: what it says about those does not apply. Everything else in it — the operations, their fields, the elements and the style tokens — does.',\n ].join('\\n'),\n}\n\n/** The system blocks for one kind of document. */\nexport function aiJobEditInstructions(kind: AiEditTargetKind): AiSystemBlock[] {\n return [AI_JOB_EDIT_RULES, { text: editCanvasBlock(kind), cacheBreakpoint: true }]\n}\n\n/** What the change is asked from: the document's outline, then the request. */\nexport function aiJobEditPrompt(input: { brief: string; context: AssistEditCanvasContext }): string {\n return [editSelectionBlock(input.context), '', `Request: ${input.brief.slice(0, AI_JOB_BRIEF_MAX_CHARS)}`].join('\\n')\n}\n\n/** The edit as the doctrine's loop runs it: the rung's tool, held to the rung's check. */\nexport function aiJobEditGeneration(request: {\n brief: string\n context: AssistEditCanvasContext\n target: AssistEditTarget\n model: string\n signal?: AbortSignal\n}): AiCustomGenerationInput<AssistEditProposal> {\n const row = AI_ROUTING_TABLE['job.edit']\n const kind = request.target.kind as AiEditTargetKind\n return {\n step: 'job.edit',\n model: request.model,\n instructions: aiJobEditInstructions(kind),\n messages: [{ role: 'user', content: aiJobEditPrompt(request) }],\n tool: assistEditTool(kind),\n maxTokens: AI_JOB_EDIT_STEP_BUDGET.maxTokens(request.model),\n ...(row.thinking ? { thinking: row.thinking } : {}),\n ...(row.effort ? { effort: row.effort } : {}),\n ...(request.signal ? { signal: request.signal } : {}),\n check: (answer) => checkAssistEditAnswer(answer, { context: request.context, target: request.target }),\n }\n}\n\n// ── The document, headless ────────────────────────────────────────────────\n\n/**\n * The slice of the platform a headless canvas asks: whether an element takes\n * children, answered from the AI palette the edit validator reads, so the\n * canvas refuses exactly what the validator refuses.\n */\nconst AI_EDIT_CANVAS_PLATFORM = {\n components: {\n getSchema: (componentId: string) => {\n const entry = AI_PALETTE[componentId]\n if (!entry) return undefined\n return entry.acceptsChildren ? {} : { flags: { dropping: FEATURE_FLAG.DISABLED } }\n },\n getLabel: (componentId: string) => AI_PALETTE[componentId]?.displayName ?? componentId,\n },\n}\n\n/** A canvas over a stored node map, with the editor's own guarded mutators and no editor. */\nexport function aiEditCanvas(nodes: NodesMap): CanvasManager {\n const canvas = new CanvasManager(AI_EDIT_CANVAS_PLATFORM as never)\n canvas.setNodes(nodes)\n return canvas\n}\n\n/**\n * Lands a proposal on a stored node map, as the editor lands one on its open\n * canvas: checked whole first, then one batch, all or nothing. The session is\n * the version being written, which is never one a visitor sees; a page's\n * search fields are captured into `seo` only where `seo` is given.\n */\nexport function aiApplyEditToNodes(\n nodes: NodesMap,\n proposal: AssistEditProposal,\n seo?: Partial<Record<(typeof SCREEN_SEO_TEXT_FIELDS)[number], string>>,\n): { ok: true; nodes: NodesMap } | { ok: false; reason: string } {\n const canvas = aiEditCanvas(nodes)\n const fields: Record<string, (value: string) => void> = {}\n if (seo) {\n for (const field of SCREEN_SEO_TEXT_FIELDS) fields[screenSeoStageKey(field)] = (value) => (seo[field] = value)\n }\n const session: EditorSession = {\n documentKind: proposal.target.kind,\n documentId: proposal.target.documentId,\n versionId: proposal.target.versionId,\n isLiveVersion: () => false,\n ...(seo ? { fields } : {}),\n }\n const applied = applyAssistEdit(canvas, proposal, session)\n if (applied.ok === false) return { ok: false, reason: applied.reason }\n return { ok: true, nodes: canvas.toJSON().nodes as NodesMap }\n}\n\n// ── Reading the document ──────────────────────────────────────────────────\n\ntype Firestore = FirebaseFirestore.Firestore\n\nconst COLLECTIONS: Readonly<Record<AiEditTargetKind, 'screens' | 'layouts'>> = {\n screen: 'screens',\n layout: 'layouts',\n}\n\n/** The page or layout an edit job changes, as it stands before anything is spent. */\nexport interface AiEditTargetRead {\n kind: AiEditTargetKind\n id: string\n name: string\n hostSubdomain: string | null\n /** The document's current version. */\n pointerVersionId: string | null\n /** The version the change starts from. */\n sourceVersionId: string\n nodes: NodesMap\n /** Whether visitors see the document's current version: a published page, a collection template, any layout. */\n served: boolean\n /** The version a pending publish schedule will make current, if any. */\n scheduledVersionId: string | null\n /** What this job already wrote, when a run of it got that far. */\n written: { versionId: string; placement: AiEditPlacement } | null\n}\n\nexport interface AiEditTargetRefusal {\n status: 400 | 404\n error: string\n}\n\nconst text = (value: unknown): string => (typeof value === 'string' ? value : '')\n\n/** Whether visitors see a page's current version: it is routed, or it renders a collection's entries. */\nfunction screenServed(screen: FirebaseFirestore.DocumentSnapshot, host: FirebaseFirestore.DocumentSnapshot): boolean {\n if (screen.get('kind') === SCREEN_KIND_TEMPLATE) return true\n const routing = host.get('screens') as Record<string, unknown> | null | undefined\n return typeof routing?.[screen.id] === 'string'\n}\n\nfunction pendingScheduleVersion(doc: FirebaseFirestore.DocumentSnapshot): string | null {\n const schedule = doc.get('publishSchedule') as Record<string, unknown> | null | undefined\n if (!schedule || schedule['status'] !== 'pending' || schedule['action'] === 'unpublish') return null\n return text(schedule['versionId']) || null\n}\n\n/**\n * The document an edit job changes, on the job's own site: the page or\n * layout named, not deleted, not an email design, and the version it starts\n * from with a document in it. A refusal names what is wrong.\n */\nexport async function readAiEditTarget(\n firestore: Firestore,\n input: {\n orgId: string\n hostId: string\n inputs: AiEditJobInputs\n /** The job's own version id, to find what a run of it wrote; `null` at the door, before there is one. */\n jobId: string | null\n },\n): Promise<AiEditTargetRead | AiEditTargetRefusal> {\n const hostRef = firestore.collection('hosts').doc(input.hostId)\n const host = await hostRef.get()\n if (!host.exists || host.get('orgId') !== input.orgId) return { status: 404, error: AI_EDIT_NO_SITE_COPY }\n const kinds: AiEditTargetKind[] = input.inputs.targetKind ? [input.inputs.targetKind] : ['screen', 'layout']\n for (const kind of kinds) {\n const docRef = hostRef.collection(COLLECTIONS[kind]).doc(input.inputs.target)\n const doc = await docRef.get()\n if (!doc.exists) continue\n if (doc.get('deletedAt') != null) return { status: 404, error: AI_EDIT_GONE_COPY }\n if (kind === 'screen' && doc.get('kind') === 'email') return { status: 400, error: AI_EDIT_EMAIL_COPY }\n const pointerVersionId = text(doc.get('versionId')) || null\n const versions = docRef.collection('versions')\n const hostSubdomain = text(host.get('subdomain')) || null\n const name = text(doc.get('displayName')) || (kind === 'screen' ? 'Page' : 'Layout')\n // A run of this job that got as far as its write.\n const own = input.jobId ? await versions.doc(input.jobId).get() : null\n const sourceVersionId = input.inputs.versionId ?? pointerVersionId\n const base = {\n kind,\n id: doc.id,\n name,\n hostSubdomain,\n pointerVersionId,\n served: kind === 'layout' || screenServed(doc, host),\n scheduledVersionId: pendingScheduleVersion(doc),\n }\n if (own?.exists && input.jobId) {\n const placement: AiEditPlacement = pointerVersionId === input.jobId ? 'new-current' : 'new-beside'\n return { ...base, sourceVersionId: sourceVersionId ?? input.jobId, nodes: {}, written: { versionId: input.jobId, placement } }\n }\n if (!sourceVersionId) return { status: 404, error: AI_EDIT_VERSION_GONE_COPY }\n const source = await versions.doc(sourceVersionId).get()\n if (!source.exists) return { status: 404, error: AI_EDIT_VERSION_GONE_COPY }\n if (input.jobId && source.get('aiEditJobId') === input.jobId) {\n return { ...base, sourceVersionId, nodes: {}, written: { versionId: sourceVersionId, placement: 'in-place' } }\n }\n const nodes = decodeStoredNodes<NodesMap>(source.get('nodes'))\n if (!nodes || !Object.keys(nodes).length) return { status: 400, error: AI_EDIT_NO_NODES_COPY }\n return { ...base, sourceVersionId, nodes, written: null }\n }\n return { status: 404, error: AI_EDIT_GONE_COPY }\n}\n\n/**\n * Where a change may land (see the module head), or `refused` when the only\n * place is a version visitors see and the plan keeps no version history.\n */\nexport function aiEditPlacement(input: {\n versioning: boolean\n served: boolean\n sourceIsPointer: boolean\n sourceScheduled: boolean\n}): AiEditPlacement | 'refused' {\n if (input.versioning) return !input.served && input.sourceIsPointer ? 'new-current' : 'new-beside'\n const seen = input.sourceScheduled || (input.sourceIsPointer && input.served)\n return seen ? 'refused' : 'in-place'\n}\n\n// ── Writing it ────────────────────────────────────────────────────────────\n\nexport interface AiEditWriteInput {\n hostId: string\n kind: AiEditTargetKind\n id: string\n sourceVersionId: string\n /** The new version's id, which is the job's; also the stamp a change in place carries. */\n jobVersionId: string\n versioning: boolean\n proposal: AssistEditProposal\n /** Whether the page's search fields may be written: a page no visitor reaches. */\n writeSeo: boolean\n versionName: string\n uid: string\n /** The job the member started, stamped as `aiJobId` on a new version. */\n aiJobId: string\n now: Date\n}\n\nexport type AiEditWrite =\n | { ok: true; versionId: string; placement: AiEditPlacement; seoWritten: boolean }\n | { ok: false; error: string }\n\n/**\n * The change written, in one transaction that reads the document as it is\n * stored NOW — a member may have saved, published or changed plan's history\n * since the read — decides where it lands again on that, and applies the\n * proposal to the stored nodes through the canvas's guards. An element that\n * changed since the model saw it refuses the whole change, as the editor does.\n */\nexport async function writeAiEdit(firestore: Firestore, input: AiEditWriteInput): Promise<AiEditWrite> {\n const hostRef = firestore.collection('hosts').doc(input.hostId)\n const docRef = hostRef.collection(COLLECTIONS[input.kind]).doc(input.id)\n const versions = docRef.collection('versions')\n const sourceRef = versions.doc(input.sourceVersionId)\n const newRef = versions.doc(input.jobVersionId)\n return firestore.runTransaction(async (tx): Promise<AiEditWrite> => {\n const [host, doc, source, already] = await Promise.all([tx.get(hostRef), tx.get(docRef), tx.get(sourceRef), tx.get(newRef)])\n const pointer = text(doc.get('versionId')) || null\n if (already.exists) {\n return { ok: true, versionId: input.jobVersionId, placement: pointer === input.jobVersionId ? 'new-current' : 'new-beside', seoWritten: false }\n }\n if (!doc.exists || doc.get('deletedAt') != null) return { ok: false, error: AI_EDIT_GONE_COPY }\n if (!source.exists) return { ok: false, error: AI_EDIT_VERSION_GONE_COPY }\n const served = input.kind === 'layout' || screenServed(doc, host)\n const placement = aiEditPlacement({\n versioning: input.versioning,\n served,\n sourceIsPointer: pointer === input.sourceVersionId,\n sourceScheduled: pendingScheduleVersion(doc) === input.sourceVersionId,\n })\n if (placement === 'refused') return { ok: false, error: AI_EDIT_NEEDS_VERSIONING_COPY }\n const nodes = decodeStoredNodes<NodesMap>(source.get('nodes'))\n if (!nodes) return { ok: false, error: AI_EDIT_NO_NODES_COPY }\n const versionId = placement === 'in-place' ? input.sourceVersionId : input.jobVersionId\n const seo: Partial<Record<(typeof SCREEN_SEO_TEXT_FIELDS)[number], string>> = {}\n const writeSeo = input.writeSeo && input.kind === 'screen' && !served\n const proposal: AssistEditProposal = {\n ...input.proposal,\n ops: writeSeo ? input.proposal.ops : input.proposal.ops.filter((op) => op.op !== 'setSeo'),\n target: { ...input.proposal.target, versionId },\n }\n const applied = aiApplyEditToNodes(nodes, proposal, writeSeo ? seo : undefined)\n if (applied.ok === false) return { ok: false, error: applied.reason === 'stale' ? AI_EDIT_STALE_COPY : AI_EDIT_REFUSED_COPY }\n const packed = encodeStoredNodes(applied.nodes)\n if (!packed) return { ok: false, error: AI_EDIT_NO_NODES_COPY }\n const stored = Buffer.from(packed)\n const docPatch: Record<string, unknown> = {}\n if (placement === 'in-place') {\n tx.update(sourceRef, { nodes: stored, updatedAt: input.now, aiEditJobId: input.jobVersionId })\n } else {\n // A copy of the version it started from — its layout binding, its\n // declared properties, everything the editor stored — with the changed\n // tree, as the versions route copies one.\n const copy = { ...(source.data() as Record<string, unknown>) }\n delete copy['aiEditJobId']\n tx.create(newRef, {\n ...copy,\n nodes: stored,\n displayName: input.versionName,\n createdAt: input.now,\n updatedAt: input.now,\n createdBy: input.uid,\n aiJobId: input.aiJobId,\n })\n if (placement === 'new-current') docPatch['versionId'] = input.jobVersionId\n }\n for (const [field, value] of Object.entries(seo)) {\n if (value) docPatch[`seo.${field}`] = value\n }\n if (Object.keys(docPatch).length) tx.update(docRef, { ...docPatch, updatedAt: input.now })\n return { ok: true, versionId, placement, seoWritten: Object.keys(seo).length > 0 }\n })\n}\n\n// ── The runner ────────────────────────────────────────────────────────────\n\nexport interface AiJobEditStepDeps {\n readTarget?: typeof readAiEditTarget\n writeEdit?: typeof writeAiEdit\n}\n\n/** What an edit wrote, as the job's output: a link into the Besigner on the version written. */\nexport function aiEditOutput(\n read: Pick<AiEditTargetRead, 'kind' | 'id' | 'name' | 'hostSubdomain'>,\n hostId: string,\n written: { versionId: string; placement: AiEditPlacement },\n leftOut: readonly string[],\n): AiJobOutput {\n return {\n resource: read.kind,\n id: read.id,\n versionId: written.versionId,\n hostId,\n hostSubdomain: read.hostSubdomain,\n label: read.name,\n note: aiEditOutputNote({ kind: read.kind, placement: written.placement, leftOut }),\n }\n}\n\n/** The new version's id: the job's own, which a build's unit takes from its plan item. */\nexport function aiEditVersionId(job: Pick<AiJob, '$id'>): string {\n return job.$id.slice(0, 64)\n}\n\n/**\n * The ops a job may save, and why it left the rest out: never a component\n * save, and a page's search fields only where no visitor reaches the page.\n */\nexport function aiEditJobOps(\n ops: readonly AssistEditOp[],\n options: { seo: boolean },\n): { ops: AssistEditOp[]; leftOut: string[] } {\n const kept: AssistEditOp[] = []\n const leftOut = new Set<string>()\n for (const op of ops) {\n if (op.op === 'saveAsComponent') leftOut.add(AI_EDIT_COMPONENT_LEFT_OUT)\n else if (op.op === 'setSeo' && !options.seo) leftOut.add(AI_EDIT_SEO_LEFT_OUT)\n else kept.push(op)\n }\n return { ops: kept, leftOut: [...leftOut] }\n}\n\nexport function createAiJobEditStep(deps: AiJobEditStepDeps = {}): AiJobStepRunner {\n const readTarget = deps.readTarget ?? readAiEditTarget\n const writeEdit = deps.writeEdit ?? writeAiEdit\n return async ({ job, now, signal, firestore, org, modelFor }): Promise<AiJobStepOutcome> => {\n const model = modelFor?.('job.edit') ?? aiJobEditModel()\n const unspent = (failure: string): AiJobStepOutcome => ({ ...aiUnspentOutcome(model), failure })\n const inputs = parseAiEditJobInputs(job.inputs)\n if (typeof inputs === 'string') return unspent(inputs)\n const hostId = job.hostId\n if (!hostId) return unspent(AI_EDIT_NO_SITE_COPY)\n const read = await readTarget(firestore, { orgId: job.orgId, hostId, inputs, jobId: aiEditVersionId(job) })\n if ('error' in read) return unspent(read.error)\n if (read.written) return aiUnspentOutcome(model, { outputs: [aiEditOutput(read, hostId, read.written, [])] })\n\n const orgData = org ?? ((await firestore.collection('orgs').doc(job.orgId).get()).data() ?? null)\n const versioning = checkEntitlement(orgData as never, 'versioning')\n const placement = aiEditPlacement({\n versioning,\n served: read.served,\n sourceIsPointer: read.sourceVersionId === read.pointerVersionId,\n sourceScheduled: read.scheduledVersionId === read.sourceVersionId,\n })\n if (placement === 'refused') return aiUnspentOutcome(model, { review: aiLimitReview(AI_EDIT_NEEDS_VERSIONING_COPY) })\n const outline = aiEditJobOutline(read.nodes)\n const context = outline ? parseAssistEditContext(outline) : null\n if (!context) return unspent(AI_EDIT_NO_NODES_COPY)\n const target: AssistEditTarget = {\n kind: read.kind,\n documentId: read.id,\n versionId: placement === 'in-place' ? read.sourceVersionId : aiEditVersionId(job),\n hostId,\n }\n\n const generation = await runValidatedGeneration(\n 'edit',\n aiJobEditGeneration({ brief: job.brief, context, target, model, ...(signal ? { signal } : {}) }),\n )\n const spent = { ...aiGenerationSpent(generation), ...(generation.effort ? { effort: generation.effort } : {}) }\n if (generation.status === 'refused') return { ...spent, refused: true }\n // An answer its re-ask still could not make whole keeps the changes that\n // survived, as the chat door's card does, and says what it left out; one\n // with none, or one that broke a numbered rule, is nothing to save.\n let proposal: AssistEditProposal | null = generation.status === 'ok' ? generation.value : null\n const refused = generation.status === 'needs_input' ? generation.answer : null\n if (!proposal && refused) {\n const salvaged = validateStreamedGeneration('edit', {\n answer: refused,\n check: (answer) => checkAssistEditAnswer(answer, { context, target }),\n })\n proposal = salvaged.status === 'ok' ? salvaged.value : null\n }\n if (!proposal) return { ...spent, failure: AI_EDIT_NOTHING_COPY }\n\n const seo = read.kind === 'screen' && !read.served\n const kept = aiEditJobOps(proposal.ops, { seo })\n const leftOut = [...proposal.dropped, ...kept.leftOut]\n if (!kept.ops.length) {\n return { ...spent, failure: `Nothing was saved. Left out: ${leftOut.join('; ') || 'every change'}.` }\n }\n const write = await writeEdit(firestore, {\n hostId,\n kind: read.kind,\n id: read.id,\n sourceVersionId: read.sourceVersionId,\n jobVersionId: aiEditVersionId(job),\n versioning,\n proposal: { ...proposal, ops: kept.ops },\n writeSeo: seo,\n versionName: aiEditVersionName(proposal.summary),\n uid: job.createdBy,\n aiJobId: aiOriginJobId(job),\n now,\n })\n if (write.ok === false) return { ...spent, failure: write.error }\n return { ...spent, outputs: [aiEditOutput(read, hostId, write, leftOut)] }\n }\n}\n\nexport const runAiJobEditStep = createAiJobEditStep()\n\n// ── Admission ─────────────────────────────────────────────────────────────\n\nexport interface AiEditJobAdmissionDeps {\n readTarget?: typeof readAiEditTarget\n ownerOf?: typeof resolveOrgIdForHost\n}\n\n/**\n * An edit job is admitted for a page or layout of a site of its own org,\n * and only where the change has somewhere to land that no visitor sees on\n * this workspace's plan — refused in the plan's words before anything exists.\n */\nexport function createAiEditJobAdmission(deps: AiEditJobAdmissionDeps = {}): AiJobAdmission {\n const readTarget = deps.readTarget ?? readAiEditTarget\n const ownerOf = deps.ownerOf ?? resolveOrgIdForHost\n return async (context): Promise<AiJobAdmissionRefusal | null> => {\n const inputs = parseAiEditJobInputs(context.inputs)\n if (typeof inputs === 'string') return { status: 400, error: inputs }\n if (!context.hostId) return { status: 400, error: AI_EDIT_NO_SITE_COPY }\n const owner = await ownerOf(context.hostId)\n if (!owner || owner !== context.orgId) return { status: 404, error: 'Unknown site' }\n // A job asked about at its door has no run to find yet.\n const read = await readTarget(context.firestore, { orgId: context.orgId, hostId: context.hostId, inputs, jobId: null })\n if ('error' in read) return read\n const placement = aiEditPlacement({\n versioning: checkEntitlement(context.org as never, 'versioning'),\n served: read.served,\n sourceIsPointer: read.sourceVersionId === read.pointerVersionId,\n sourceScheduled: read.scheduledVersionId === read.sourceVersionId,\n })\n return placement === 'refused' ? { status: 403, error: AI_EDIT_NEEDS_VERSIONING_COPY } : null\n }\n}\n\nexport const aiEditJobAdmission = createAiEditJobAdmission()\n\n/** Registers the edit step and the check an edit job passes before it is created or resumed. */\nexport function registerAiEditJob(): void {\n registerAiJobStep('edit', runAiJobEditStep, { minimumMs: AI_JOB_EDIT_STEP_MINIMUM_MS })\n registerAiJobAdmission('edit', aiEditJobAdmission)\n}\n"],"names":["checkEntitlement","SCREEN_KIND_TEMPLATE","screenSeoStageKey","SCREEN_SEO_TEXT_FIELDS","decodeStoredNodes","encodeStoredNodes","CanvasManager","FEATURE_FLAG","resolveOrgIdForHost","applyAssistEdit","AI_EDIT_COMPONENT_LEFT_OUT","AI_EDIT_EMAIL_COPY","AI_EDIT_GONE_COPY","AI_EDIT_NEEDS_VERSIONING_COPY","AI_EDIT_NO_NODES_COPY","AI_EDIT_NO_SITE_COPY","AI_EDIT_NOTHING_COPY","AI_EDIT_REFUSED_COPY","AI_EDIT_SEO_LEFT_OUT","AI_EDIT_STALE_COPY","AI_EDIT_VERSION_GONE_COPY","aiEditJobOutline","aiEditOutputNote","aiEditVersionName","parseAiEditJobInputs","ASSIST_EDIT_TOOL_NAME","AI_STEP_TIERS","AI_ROUTING_TABLE","aiModelForStep","runValidatedGeneration","validateStreamedGeneration","AI_PALETTE","assistEditTool","checkAssistEditAnswer","editCanvasBlock","editSelectionBlock","parseAssistEditContext","registerAiJobAdmission","aiJobStepBudget","aiOriginJobId","aiGenerationSpent","aiLimitReview","aiUnspentOutcome","AI_JOB_BRIEF_MAX_CHARS","registerAiJobStep","AI_EDIT_READS_MS","AI_JOB_EDIT_STEP_BUDGET","tier","maxTokens","lookups","ownReadsMs","AI_JOB_EDIT_STEP_MINIMUM_MS","minimumMs","aiJobEditModel","AI_JOB_EDIT_RULES","text","join","aiJobEditInstructions","kind","cacheBreakpoint","aiJobEditPrompt","input","context","brief","slice","aiJobEditGeneration","request","row","target","step","model","instructions","messages","role","content","tool","thinking","effort","signal","check","answer","AI_EDIT_CANVAS_PLATFORM","components","getSchema","componentId","entry","undefined","acceptsChildren","flags","dropping","DISABLED","getLabel","displayName","aiEditCanvas","nodes","canvas","setNodes","aiApplyEditToNodes","proposal","seo","fields","field","value","session","documentKind","documentId","versionId","isLiveVersion","applied","ok","reason","toJSON","COLLECTIONS","screen","layout","screenServed","host","get","routing","id","pendingScheduleVersion","doc","schedule","readAiEditTarget","firestore","hostRef","collection","hostId","exists","orgId","status","error","kinds","inputs","targetKind","docRef","pointerVersionId","versions","hostSubdomain","name","own","jobId","sourceVersionId","base","served","scheduledVersionId","placement","written","source","Object","keys","length","aiEditPlacement","versioning","sourceIsPointer","seen","sourceScheduled","writeAiEdit","sourceRef","newRef","jobVersionId","runTransaction","tx","already","Promise","all","pointer","seoWritten","writeSeo","ops","filter","op","packed","stored","Buffer","from","docPatch","update","updatedAt","now","aiEditJobId","copy","data","create","versionName","createdAt","createdBy","uid","aiJobId","entries","aiEditOutput","read","leftOut","resource","label","note","aiEditVersionId","job","$id","aiEditJobOps","options","kept","Set","add","push","createAiJobEditStep","deps","readTarget","writeEdit","org","modelFor","unspent","failure","outputs","orgData","review","outline","generation","spent","refused","salvaged","dropped","write","summary","runAiJobEditStep","createAiEditJobAdmission","ownerOf","owner","aiEditJobAdmission","registerAiEditJob"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,gBAAgB,QAAQ,2CAA0C;AAC3E,SAASC,oBAAoB,QAAQ,sCAAqC;AAC1E,SAASC,iBAAiB,EAAEC,sBAAsB,QAAQ,2CAA0C;AACpG,SAASC,iBAAiB,EAAEC,iBAAiB,QAAQ,sCAAqC;AAC1F,SAASC,aAAa,QAAQ,6CAA4C;AAC1E,SAASC,YAAY,QAAQ,2CAA0C;AAGvE,SAASC,mBAAmB,QAAQ,gDAA+C;AACnF,SAASC,eAAe,QAAQ,sCAAkC;AAClE,SACEC,0BAA0B,EAC1BC,kBAAkB,EAClBC,iBAAiB,EACjBC,6BAA6B,EAC7BC,qBAAqB,EACrBC,oBAAoB,EACpBC,oBAAoB,EACpBC,oBAAoB,EACpBC,oBAAoB,EACpBC,kBAAkB,EAClBC,yBAAyB,EACzBC,gBAAgB,EAChBC,gBAAgB,EAChBC,iBAAiB,EACjBC,oBAAoB,QAIf,0BAAsB;AAE7B,SACEC,qBAAqB,QAKhB,0BAAsB;AAC7B,SAASC,aAAa,QAAQ,0BAAsB;AACpD,SAASC,gBAAgB,EAAEC,cAAc,QAAQ,0BAAsB;AACvE,SACEC,sBAAsB,EACtBC,0BAA0B,QAErB,4BAAwB;AAC/B,SAASC,UAAU,QAAQ,qCAAiC;AAE5D,SACEC,cAAc,EACdC,qBAAqB,EACrBC,eAAe,EACfC,kBAAkB,EAClBC,sBAAsB,QACjB,2BAAuB;AAC9B,SAASC,sBAAsB,QAAyD,wBAAoB;AAC5G,SAASC,eAAe,QAAQ,qBAAiB;AACjD,SAASC,aAAa,QAAQ,wBAAoB;AAClD,SAASC,iBAAiB,EAAEC,aAAa,EAAEC,gBAAgB,QAAQ,yBAAqB;AACxF,SAASC,sBAAsB,QAAqD,wBAAoB;AACxG,SAASC,iBAAiB,QAAQ,eAAW;AAE7C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoCC,GAED,mEAAmE,GACnE,OAAO,MAAMC,mBAAmB,KAAK;AAErC,8FAA8F,GAC9F,OAAO,MAAMC,0BAA0BR,gBAAgB;IACrDS,MAAMrB,aAAa,CAAC,WAAW;IAC/BsB,WAAWrB,gBAAgB,CAAC,WAAW,CAACqB,SAAS;IACjDC,SAAS;IACTC,YAAYL;AACd,GAAE;AAEF,OAAO,MAAMM,8BAA8BL,wBAAwBM,SAAS,CAAA;AAE5E,OAAO,SAASC;IACd,OAAOzB,eAAe;AACxB;AAEA;;;;CAIC,GACD,OAAO,MAAM0B,oBAAmC;IAC9CC,MAAM;QACJ;QACA,CAAC,wNAAwN,CAAC;QAC1N;QACA,CAAC,wHAAwH,CAAC;QAC1H,CAAC,OAAO,EAAE9B,sBAAsB,oKAAoK,CAAC;QACrM;QACA;QACA;KACD,CAAC+B,IAAI,CAAC;AACT,EAAC;AAED,gDAAgD,GAChD,OAAO,SAASC,sBAAsBC,IAAsB;IAC1D,OAAO;QAACJ;QAAmB;YAAEC,MAAMrB,gBAAgBwB;YAAOC,iBAAiB;QAAK;KAAE;AACpF;AAEA,6EAA6E,GAC7E,OAAO,SAASC,gBAAgBC,KAA0D;IACxF,OAAO;QAAC1B,mBAAmB0B,MAAMC,OAAO;QAAG;QAAI,CAAC,SAAS,EAAED,MAAME,KAAK,CAACC,KAAK,CAAC,GAAGrB,yBAAyB;KAAC,CAACa,IAAI,CAAC;AAClH;AAEA,wFAAwF,GACxF,OAAO,SAASS,oBAAoBC,OAMnC;IACC,MAAMC,MAAMxC,gBAAgB,CAAC,WAAW;IACxC,MAAM+B,OAAOQ,QAAQE,MAAM,CAACV,IAAI;IAChC,OAAO;QACLW,MAAM;QACNC,OAAOJ,QAAQI,KAAK;QACpBC,cAAcd,sBAAsBC;QACpCc,UAAU;YAAC;gBAAEC,MAAM;gBAAQC,SAASd,gBAAgBM;YAAS;SAAE;QAC/DS,MAAM3C,eAAe0B;QACrBV,WAAWF,wBAAwBE,SAAS,CAACkB,QAAQI,KAAK;OACtDH,IAAIS,QAAQ,GAAG;QAAEA,UAAUT,IAAIS,QAAQ;IAAC,IAAI,CAAC,GAC7CT,IAAIU,MAAM,GAAG;QAAEA,QAAQV,IAAIU,MAAM;IAAC,IAAI,CAAC,GACvCX,QAAQY,MAAM,GAAG;QAAEA,QAAQZ,QAAQY,MAAM;IAAC,IAAI,CAAC;QACnDC,OAAO,CAACC,SAAW/C,sBAAsB+C,QAAQ;gBAAElB,SAASI,QAAQJ,OAAO;gBAAEM,QAAQF,QAAQE,MAAM;YAAC;;AAExG;AAEA,6EAA6E;AAE7E;;;;CAIC,GACD,MAAMa,0BAA0B;IAC9BC,YAAY;QACVC,WAAW,CAACC;YACV,MAAMC,QAAQtD,UAAU,CAACqD,YAAY;YACrC,IAAI,CAACC,OAAO,OAAOC;YACnB,OAAOD,MAAME,eAAe,GAAG,CAAC,IAAI;gBAAEC,OAAO;oBAAEC,UAAUlF,aAAamF,QAAQ;gBAAC;YAAE;QACnF;QACAC,UAAU,CAACP;;gBAAwBrD;4BAAAA,0BAAAA,UAAU,CAACqD,YAAY,qBAAvBrD,wBAAyB6D,WAAW,mBAAIR;;IAC7E;AACF;AAEA,2FAA2F,GAC3F,OAAO,SAASS,aAAaC,KAAe;IAC1C,MAAMC,SAAS,IAAIzF,cAAc2E;IACjCc,OAAOC,QAAQ,CAACF;IAChB,OAAOC;AACT;AAEA;;;;;CAKC,GACD,OAAO,SAASE,mBACdH,KAAe,EACfI,QAA4B,EAC5BC,GAAsE;IAEtE,MAAMJ,SAASF,aAAaC;IAC5B,MAAMM,SAAkD,CAAC;IACzD,IAAID,KAAK;QACP,KAAK,MAAME,SAASlG,uBAAwBiG,MAAM,CAAClG,kBAAkBmG,OAAO,GAAG,CAACC,QAAWH,GAAG,CAACE,MAAM,GAAGC;IAC1G;IACA,MAAMC,UAAyB;QAC7BC,cAAcN,SAAS9B,MAAM,CAACV,IAAI;QAClC+C,YAAYP,SAAS9B,MAAM,CAACqC,UAAU;QACtCC,WAAWR,SAAS9B,MAAM,CAACsC,SAAS;QACpCC,eAAe,IAAM;OACjBR,MAAM;QAAEC;IAAO,IAAI,CAAC;IAE1B,MAAMQ,UAAUnG,gBAAgBsF,QAAQG,UAAUK;IAClD,IAAIK,QAAQC,EAAE,KAAK,OAAO,OAAO;QAAEA,IAAI;QAAOC,QAAQF,QAAQE,MAAM;IAAC;IACrE,OAAO;QAAED,IAAI;QAAMf,OAAOC,OAAOgB,MAAM,GAAGjB,KAAK;IAAa;AAC9D;AAMA,MAAMkB,cAAyE;IAC7EC,QAAQ;IACRC,QAAQ;AACV;AA0BA,MAAM3D,OAAO,CAAC+C,QAA4B,OAAOA,UAAU,WAAWA,QAAQ;AAE9E,uGAAuG,GACvG,SAASa,aAAaF,MAA0C,EAAEG,IAAwC;IACxG,IAAIH,OAAOI,GAAG,CAAC,YAAYpH,sBAAsB,OAAO;IACxD,MAAMqH,UAAUF,KAAKC,GAAG,CAAC;IACzB,OAAO,QAAOC,2BAAAA,OAAS,CAACL,OAAOM,EAAE,CAAC,MAAK;AACzC;AAEA,SAASC,uBAAuBC,GAAuC;IACrE,MAAMC,WAAWD,IAAIJ,GAAG,CAAC;IACzB,IAAI,CAACK,YAAYA,QAAQ,CAAC,SAAS,KAAK,aAAaA,QAAQ,CAAC,SAAS,KAAK,aAAa,OAAO;IAChG,OAAOnE,KAAKmE,QAAQ,CAAC,YAAY,KAAK;AACxC;AAEA;;;;CAIC,GACD,OAAO,eAAeC,iBACpBC,SAAoB,EACpB/D,KAMC;IAED,MAAMgE,UAAUD,UAAUE,UAAU,CAAC,SAASL,GAAG,CAAC5D,MAAMkE,MAAM;IAC9D,MAAMX,OAAO,MAAMS,QAAQR,GAAG;IAC9B,IAAI,CAACD,KAAKY,MAAM,IAAIZ,KAAKC,GAAG,CAAC,aAAaxD,MAAMoE,KAAK,EAAE,OAAO;QAAEC,QAAQ;QAAKC,OAAOpH;IAAqB;IACzG,MAAMqH,QAA4BvE,MAAMwE,MAAM,CAACC,UAAU,GAAG;QAACzE,MAAMwE,MAAM,CAACC,UAAU;KAAC,GAAG;QAAC;QAAU;KAAS;IAC5G,KAAK,MAAM5E,QAAQ0E,MAAO;YAYAvE;QAXxB,MAAM0E,SAASV,QAAQC,UAAU,CAACd,WAAW,CAACtD,KAAK,EAAE+D,GAAG,CAAC5D,MAAMwE,MAAM,CAACjE,MAAM;QAC5E,MAAMqD,MAAM,MAAMc,OAAOlB,GAAG;QAC5B,IAAI,CAACI,IAAIO,MAAM,EAAE;QACjB,IAAIP,IAAIJ,GAAG,CAAC,gBAAgB,MAAM,OAAO;YAAEa,QAAQ;YAAKC,OAAOvH;QAAkB;QACjF,IAAI8C,SAAS,YAAY+D,IAAIJ,GAAG,CAAC,YAAY,SAAS,OAAO;YAAEa,QAAQ;YAAKC,OAAOxH;QAAmB;QACtG,MAAM6H,mBAAmBjF,KAAKkE,IAAIJ,GAAG,CAAC,iBAAiB;QACvD,MAAMoB,WAAWF,OAAOT,UAAU,CAAC;QACnC,MAAMY,gBAAgBnF,KAAK6D,KAAKC,GAAG,CAAC,iBAAiB;QACrD,MAAMsB,OAAOpF,KAAKkE,IAAIJ,GAAG,CAAC,mBAAoB3D,CAAAA,SAAS,WAAW,SAAS,QAAO;QAClF,kDAAkD;QAClD,MAAMkF,MAAM/E,MAAMgF,KAAK,GAAG,MAAMJ,SAAShB,GAAG,CAAC5D,MAAMgF,KAAK,EAAExB,GAAG,KAAK;QAClE,MAAMyB,mBAAkBjF,0BAAAA,MAAMwE,MAAM,CAAC3B,SAAS,YAAtB7C,0BAA0B2E;QAClD,MAAMO,OAAO;YACXrF;YACA6D,IAAIE,IAAIF,EAAE;YACVoB;YACAD;YACAF;YACAQ,QAAQtF,SAAS,YAAYyD,aAAaM,KAAKL;YAC/C6B,oBAAoBzB,uBAAuBC;QAC7C;QACA,IAAImB,CAAAA,uBAAAA,IAAKZ,MAAM,KAAInE,MAAMgF,KAAK,EAAE;YAC9B,MAAMK,YAA6BV,qBAAqB3E,MAAMgF,KAAK,GAAG,gBAAgB;YACtF,OAAO,aAAKE;gBAAMD,eAAe,EAAEA,0BAAAA,kBAAmBjF,MAAMgF,KAAK;gBAAE/C,OAAO,CAAC;gBAAGqD,SAAS;oBAAEzC,WAAW7C,MAAMgF,KAAK;oBAAEK;gBAAU;;QAC7H;QACA,IAAI,CAACJ,iBAAiB,OAAO;YAAEZ,QAAQ;YAAKC,OAAO/G;QAA0B;QAC7E,MAAMgI,SAAS,MAAMX,SAAShB,GAAG,CAACqB,iBAAiBzB,GAAG;QACtD,IAAI,CAAC+B,OAAOpB,MAAM,EAAE,OAAO;YAAEE,QAAQ;YAAKC,OAAO/G;QAA0B;QAC3E,IAAIyC,MAAMgF,KAAK,IAAIO,OAAO/B,GAAG,CAAC,mBAAmBxD,MAAMgF,KAAK,EAAE;YAC5D,OAAO,aAAKE;gBAAMD;gBAAiBhD,OAAO,CAAC;gBAAGqD,SAAS;oBAAEzC,WAAWoC;oBAAiBI,WAAW;gBAAW;;QAC7G;QACA,MAAMpD,QAAQ1F,kBAA4BgJ,OAAO/B,GAAG,CAAC;QACrD,IAAI,CAACvB,SAAS,CAACuD,OAAOC,IAAI,CAACxD,OAAOyD,MAAM,EAAE,OAAO;YAAErB,QAAQ;YAAKC,OAAOrH;QAAsB;QAC7F,OAAO,aAAKiI;YAAMD;YAAiBhD;YAAOqD,SAAS;;IACrD;IACA,OAAO;QAAEjB,QAAQ;QAAKC,OAAOvH;IAAkB;AACjD;AAEA;;;CAGC,GACD,OAAO,SAAS4I,gBAAgB3F,KAK/B;IACC,IAAIA,MAAM4F,UAAU,EAAE,OAAO,CAAC5F,MAAMmF,MAAM,IAAInF,MAAM6F,eAAe,GAAG,gBAAgB;IACtF,MAAMC,OAAO9F,MAAM+F,eAAe,IAAK/F,MAAM6F,eAAe,IAAI7F,MAAMmF,MAAM;IAC5E,OAAOW,OAAO,YAAY;AAC5B;AA0BA;;;;;;CAMC,GACD,OAAO,eAAeE,YAAYjC,SAAoB,EAAE/D,KAAuB;IAC7E,MAAMgE,UAAUD,UAAUE,UAAU,CAAC,SAASL,GAAG,CAAC5D,MAAMkE,MAAM;IAC9D,MAAMQ,SAASV,QAAQC,UAAU,CAACd,WAAW,CAACnD,MAAMH,IAAI,CAAC,EAAE+D,GAAG,CAAC5D,MAAM0D,EAAE;IACvE,MAAMkB,WAAWF,OAAOT,UAAU,CAAC;IACnC,MAAMgC,YAAYrB,SAAShB,GAAG,CAAC5D,MAAMiF,eAAe;IACpD,MAAMiB,SAAStB,SAAShB,GAAG,CAAC5D,MAAMmG,YAAY;IAC9C,OAAOpC,UAAUqC,cAAc,CAAC,OAAOC;QACrC,MAAM,CAAC9C,MAAMK,KAAK2B,QAAQe,QAAQ,GAAG,MAAMC,QAAQC,GAAG,CAAC;YAACH,GAAG7C,GAAG,CAACQ;YAAUqC,GAAG7C,GAAG,CAACkB;YAAS2B,GAAG7C,GAAG,CAACyC;YAAYI,GAAG7C,GAAG,CAAC0C;SAAQ;QAC3H,MAAMO,UAAU/G,KAAKkE,IAAIJ,GAAG,CAAC,iBAAiB;QAC9C,IAAI8C,QAAQnC,MAAM,EAAE;YAClB,OAAO;gBAAEnB,IAAI;gBAAMH,WAAW7C,MAAMmG,YAAY;gBAAEd,WAAWoB,YAAYzG,MAAMmG,YAAY,GAAG,gBAAgB;gBAAcO,YAAY;YAAM;QAChJ;QACA,IAAI,CAAC9C,IAAIO,MAAM,IAAIP,IAAIJ,GAAG,CAAC,gBAAgB,MAAM,OAAO;YAAER,IAAI;YAAOsB,OAAOvH;QAAkB;QAC9F,IAAI,CAACwI,OAAOpB,MAAM,EAAE,OAAO;YAAEnB,IAAI;YAAOsB,OAAO/G;QAA0B;QACzE,MAAM4H,SAASnF,MAAMH,IAAI,KAAK,YAAYyD,aAAaM,KAAKL;QAC5D,MAAM8B,YAAYM,gBAAgB;YAChCC,YAAY5F,MAAM4F,UAAU;YAC5BT;YACAU,iBAAiBY,YAAYzG,MAAMiF,eAAe;YAClDc,iBAAiBpC,uBAAuBC,SAAS5D,MAAMiF,eAAe;QACxE;QACA,IAAII,cAAc,WAAW,OAAO;YAAErC,IAAI;YAAOsB,OAAOtH;QAA8B;QACtF,MAAMiF,QAAQ1F,kBAA4BgJ,OAAO/B,GAAG,CAAC;QACrD,IAAI,CAACvB,OAAO,OAAO;YAAEe,IAAI;YAAOsB,OAAOrH;QAAsB;QAC7D,MAAM4F,YAAYwC,cAAc,aAAarF,MAAMiF,eAAe,GAAGjF,MAAMmG,YAAY;QACvF,MAAM7D,MAAwE,CAAC;QAC/E,MAAMqE,WAAW3G,MAAM2G,QAAQ,IAAI3G,MAAMH,IAAI,KAAK,YAAY,CAACsF;QAC/D,MAAM9C,WAA+B,aAChCrC,MAAMqC,QAAQ;YACjBuE,KAAKD,WAAW3G,MAAMqC,QAAQ,CAACuE,GAAG,GAAG5G,MAAMqC,QAAQ,CAACuE,GAAG,CAACC,MAAM,CAAC,CAACC,KAAOA,GAAGA,EAAE,KAAK;YACjFvG,QAAQ,aAAKP,MAAMqC,QAAQ,CAAC9B,MAAM;gBAAEsC;;;QAEtC,MAAME,UAAUX,mBAAmBH,OAAOI,UAAUsE,WAAWrE,MAAMb;QACrE,IAAIsB,QAAQC,EAAE,KAAK,OAAO,OAAO;YAAEA,IAAI;YAAOsB,OAAOvB,QAAQE,MAAM,KAAK,UAAU3F,qBAAqBF;QAAqB;QAC5H,MAAM2J,SAASvK,kBAAkBuG,QAAQd,KAAK;QAC9C,IAAI,CAAC8E,QAAQ,OAAO;YAAE/D,IAAI;YAAOsB,OAAOrH;QAAsB;QAC9D,MAAM+J,SAASC,OAAOC,IAAI,CAACH;QAC3B,MAAMI,WAAoC,CAAC;QAC3C,IAAI9B,cAAc,YAAY;YAC5BgB,GAAGe,MAAM,CAACnB,WAAW;gBAAEhE,OAAO+E;gBAAQK,WAAWrH,MAAMsH,GAAG;gBAAEC,aAAavH,MAAMmG,YAAY;YAAC;QAC9F,OAAO;YACL,kEAAkE;YAClE,uEAAuE;YACvE,0CAA0C;YAC1C,MAAMqB,OAAO,aAAMjC,OAAOkC,IAAI;YAC9B,OAAOD,IAAI,CAAC,cAAc;YAC1BnB,GAAGqB,MAAM,CAACxB,QAAQ,aACbsB;gBACHvF,OAAO+E;gBACPjF,aAAa/B,MAAM2H,WAAW;gBAC9BC,WAAW5H,MAAMsH,GAAG;gBACpBD,WAAWrH,MAAMsH,GAAG;gBACpBO,WAAW7H,MAAM8H,GAAG;gBACpBC,SAAS/H,MAAM+H,OAAO;;YAExB,IAAI1C,cAAc,eAAe8B,QAAQ,CAAC,YAAY,GAAGnH,MAAMmG,YAAY;QAC7E;QACA,KAAK,MAAM,CAAC3D,OAAOC,MAAM,IAAI+C,OAAOwC,OAAO,CAAC1F,KAAM;YAChD,IAAIG,OAAO0E,QAAQ,CAAC,CAAC,IAAI,EAAE3E,OAAO,CAAC,GAAGC;QACxC;QACA,IAAI+C,OAAOC,IAAI,CAAC0B,UAAUzB,MAAM,EAAEW,GAAGe,MAAM,CAAC1C,QAAQ,aAAKyC;YAAUE,WAAWrH,MAAMsH,GAAG;;QACvF,OAAO;YAAEtE,IAAI;YAAMH;YAAWwC;YAAWqB,YAAYlB,OAAOC,IAAI,CAACnD,KAAKoD,MAAM,GAAG;QAAE;IACnF;AACF;AASA,8FAA8F,GAC9F,OAAO,SAASuC,aACdC,IAAsE,EACtEhE,MAAc,EACdoB,OAA0D,EAC1D6C,OAA0B;IAE1B,OAAO;QACLC,UAAUF,KAAKrI,IAAI;QACnB6D,IAAIwE,KAAKxE,EAAE;QACXb,WAAWyC,QAAQzC,SAAS;QAC5BqB;QACAW,eAAeqD,KAAKrD,aAAa;QACjCwD,OAAOH,KAAKpD,IAAI;QAChBwD,MAAM7K,iBAAiB;YAAEoC,MAAMqI,KAAKrI,IAAI;YAAEwF,WAAWC,QAAQD,SAAS;YAAE8C;QAAQ;IAClF;AACF;AAEA,wFAAwF,GACxF,OAAO,SAASI,gBAAgBC,GAAuB;IACrD,OAAOA,IAAIC,GAAG,CAACtI,KAAK,CAAC,GAAG;AAC1B;AAEA;;;CAGC,GACD,OAAO,SAASuI,aACd9B,GAA4B,EAC5B+B,OAAyB;IAEzB,MAAMC,OAAuB,EAAE;IAC/B,MAAMT,UAAU,IAAIU;IACpB,KAAK,MAAM/B,MAAMF,IAAK;QACpB,IAAIE,GAAGA,EAAE,KAAK,mBAAmBqB,QAAQW,GAAG,CAACjM;aACxC,IAAIiK,GAAGA,EAAE,KAAK,YAAY,CAAC6B,QAAQrG,GAAG,EAAE6F,QAAQW,GAAG,CAACzL;aACpDuL,KAAKG,IAAI,CAACjC;IACjB;IACA,OAAO;QAAEF,KAAKgC;QAAMT,SAAS;eAAIA;SAAQ;IAAC;AAC5C;AAEA,OAAO,SAASa,oBAAoBC,OAA0B,CAAC,CAAC;QAC3CA,kBACDA;IADlB,MAAMC,cAAaD,mBAAAA,KAAKC,UAAU,YAAfD,mBAAmBnF;IACtC,MAAMqF,aAAYF,kBAAAA,KAAKE,SAAS,YAAdF,kBAAkBjD;IACpC,OAAO,OAAO,EAAEwC,GAAG,EAAElB,GAAG,EAAErG,MAAM,EAAE8C,SAAS,EAAEqF,GAAG,EAAEC,QAAQ,EAAE;kBAWlC;QAVxB,MAAM5I,gBAAQ4I,4BAAAA,SAAW,8BAAe7J;QACxC,MAAM8J,UAAU,CAACC,UAAuC,aAAK1K,iBAAiB4B;gBAAQ8I;;QACtF,MAAM/E,SAAS7G,qBAAqB6K,IAAIhE,MAAM;QAC9C,IAAI,OAAOA,WAAW,UAAU,OAAO8E,QAAQ9E;QAC/C,MAAMN,SAASsE,IAAItE,MAAM;QACzB,IAAI,CAACA,QAAQ,OAAOoF,QAAQpM;QAC5B,MAAMgL,OAAO,MAAMgB,WAAWnF,WAAW;YAAEK,OAAOoE,IAAIpE,KAAK;YAAEF;YAAQM;YAAQQ,OAAOuD,gBAAgBC;QAAK;QACzG,IAAI,WAAWN,MAAM,OAAOoB,QAAQpB,KAAK5D,KAAK;QAC9C,IAAI4D,KAAK5C,OAAO,EAAE,OAAOzG,iBAAiB4B,OAAO;YAAE+I,SAAS;gBAACvB,aAAaC,MAAMhE,QAAQgE,KAAK5C,OAAO,EAAE,EAAE;aAAE;QAAC;QAE3G,MAAMmE,UAAUL,cAAAA,OAAQ,QAAA,AAAC,CAAA,MAAMrF,UAAUE,UAAU,CAAC,QAAQL,GAAG,CAAC4E,IAAIpE,KAAK,EAAEZ,GAAG,EAAC,EAAGiE,IAAI,cAA9D,QAAoE;QAC5F,MAAM7B,aAAazJ,iBAAiBsN,SAAkB;QACtD,MAAMpE,YAAYM,gBAAgB;YAChCC;YACAT,QAAQ+C,KAAK/C,MAAM;YACnBU,iBAAiBqC,KAAKjD,eAAe,KAAKiD,KAAKvD,gBAAgB;YAC/DoB,iBAAiBmC,KAAK9C,kBAAkB,KAAK8C,KAAKjD,eAAe;QACnE;QACA,IAAII,cAAc,WAAW,OAAOxG,iBAAiB4B,OAAO;YAAEiJ,QAAQ9K,cAAc5B;QAA+B;QACnH,MAAM2M,UAAUnM,iBAAiB0K,KAAKjG,KAAK;QAC3C,MAAMhC,UAAU0J,UAAUpL,uBAAuBoL,WAAW;QAC5D,IAAI,CAAC1J,SAAS,OAAOqJ,QAAQrM;QAC7B,MAAMsD,SAA2B;YAC/BV,MAAMqI,KAAKrI,IAAI;YACf+C,YAAYsF,KAAKxE,EAAE;YACnBb,WAAWwC,cAAc,aAAa6C,KAAKjD,eAAe,GAAGsD,gBAAgBC;YAC7EtE;QACF;QAEA,MAAM0F,aAAa,MAAM5L,uBACvB,QACAoC,oBAAoB;YAAEF,OAAOsI,IAAItI,KAAK;YAAED;YAASM;YAAQE;WAAWQ,SAAS;YAAEA;QAAO,IAAI,CAAC;QAE7F,MAAM4I,QAAQ,aAAKlL,kBAAkBiL,aAAiBA,WAAW5I,MAAM,GAAG;YAAEA,QAAQ4I,WAAW5I,MAAM;QAAC,IAAI,CAAC;QAC3G,IAAI4I,WAAWvF,MAAM,KAAK,WAAW,OAAO,aAAKwF;YAAOC,SAAS;;QACjE,yEAAyE;QACzE,yEAAyE;QACzE,oEAAoE;QACpE,IAAIzH,WAAsCuH,WAAWvF,MAAM,KAAK,OAAOuF,WAAWnH,KAAK,GAAG;QAC1F,MAAMqH,UAAUF,WAAWvF,MAAM,KAAK,gBAAgBuF,WAAWzI,MAAM,GAAG;QAC1E,IAAI,CAACkB,YAAYyH,SAAS;YACxB,MAAMC,WAAW9L,2BAA2B,QAAQ;gBAClDkD,QAAQ2I;gBACR5I,OAAO,CAACC,SAAW/C,sBAAsB+C,QAAQ;wBAAElB;wBAASM;oBAAO;YACrE;YACA8B,WAAW0H,SAAS1F,MAAM,KAAK,OAAO0F,SAAStH,KAAK,GAAG;QACzD;QACA,IAAI,CAACJ,UAAU,OAAO,aAAKwH;YAAON,SAASpM;;QAE3C,MAAMmF,MAAM4F,KAAKrI,IAAI,KAAK,YAAY,CAACqI,KAAK/C,MAAM;QAClD,MAAMyD,OAAOF,aAAarG,SAASuE,GAAG,EAAE;YAAEtE;QAAI;QAC9C,MAAM6F,UAAU;eAAI9F,SAAS2H,OAAO;eAAKpB,KAAKT,OAAO;SAAC;QACtD,IAAI,CAACS,KAAKhC,GAAG,CAAClB,MAAM,EAAE;YACpB,OAAO,aAAKmE;gBAAON,SAAS,CAAC,6BAA6B,EAAEpB,QAAQxI,IAAI,CAAC,SAAS,eAAe,CAAC,CAAC;;QACrG;QACA,MAAMsK,QAAQ,MAAMd,UAAUpF,WAAW;YACvCG;YACArE,MAAMqI,KAAKrI,IAAI;YACf6D,IAAIwE,KAAKxE,EAAE;YACXuB,iBAAiBiD,KAAKjD,eAAe;YACrCkB,cAAcoC,gBAAgBC;YAC9B5C;YACAvD,UAAU,aAAKA;gBAAUuE,KAAKgC,KAAKhC,GAAG;;YACtCD,UAAUrE;YACVqF,aAAajK,kBAAkB2E,SAAS6H,OAAO;YAC/CpC,KAAKU,IAAIX,SAAS;YAClBE,SAASrJ,cAAc8J;YACvBlB;QACF;QACA,IAAI2C,MAAMjH,EAAE,KAAK,OAAO,OAAO,aAAK6G;YAAON,SAASU,MAAM3F,KAAK;;QAC/D,OAAO,aAAKuF;YAAOL,SAAS;gBAACvB,aAAaC,MAAMhE,QAAQ+F,OAAO9B;aAAS;;IAC1E;AACF;AAEA,OAAO,MAAMgC,mBAAmBnB,sBAAqB;AASrD;;;;CAIC,GACD,OAAO,SAASoB,yBAAyBnB,OAA+B,CAAC,CAAC;QACrDA,kBACHA;IADhB,MAAMC,cAAaD,mBAAAA,KAAKC,UAAU,YAAfD,mBAAmBnF;IACtC,MAAMuG,WAAUpB,gBAAAA,KAAKoB,OAAO,YAAZpB,gBAAgBtM;IAChC,OAAO,OAAOsD;QACZ,MAAMuE,SAAS7G,qBAAqBsC,QAAQuE,MAAM;QAClD,IAAI,OAAOA,WAAW,UAAU,OAAO;YAAEH,QAAQ;YAAKC,OAAOE;QAAO;QACpE,IAAI,CAACvE,QAAQiE,MAAM,EAAE,OAAO;YAAEG,QAAQ;YAAKC,OAAOpH;QAAqB;QACvE,MAAMoN,QAAQ,MAAMD,QAAQpK,QAAQiE,MAAM;QAC1C,IAAI,CAACoG,SAASA,UAAUrK,QAAQmE,KAAK,EAAE,OAAO;YAAEC,QAAQ;YAAKC,OAAO;QAAe;QACnF,wDAAwD;QACxD,MAAM4D,OAAO,MAAMgB,WAAWjJ,QAAQ8D,SAAS,EAAE;YAAEK,OAAOnE,QAAQmE,KAAK;YAAEF,QAAQjE,QAAQiE,MAAM;YAAEM;YAAQQ,OAAO;QAAK;QACrH,IAAI,WAAWkD,MAAM,OAAOA;QAC5B,MAAM7C,YAAYM,gBAAgB;YAChCC,YAAYzJ,iBAAiB8D,QAAQmJ,GAAG,EAAW;YACnDjE,QAAQ+C,KAAK/C,MAAM;YACnBU,iBAAiBqC,KAAKjD,eAAe,KAAKiD,KAAKvD,gBAAgB;YAC/DoB,iBAAiBmC,KAAK9C,kBAAkB,KAAK8C,KAAKjD,eAAe;QACnE;QACA,OAAOI,cAAc,YAAY;YAAEhB,QAAQ;YAAKC,OAAOtH;QAA8B,IAAI;IAC3F;AACF;AAEA,OAAO,MAAMuN,qBAAqBH,2BAA0B;AAE5D,8FAA8F,GAC9F,OAAO,SAASI;IACdzL,kBAAkB,QAAQoL,kBAAkB;QAAE5K,WAAWD;IAA4B;IACrFd,uBAAuB,QAAQ+L;AACjC"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-edit-step.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 { checkEntitlement } from '@aglyn/aglyn/app-utils/plan-entitlements'\nimport { SCREEN_KIND_TEMPLATE } from '@aglyn/aglyn/app-utils/screen-route'\nimport { screenSeoStageKey, SCREEN_SEO_TEXT_FIELDS } from '@aglyn/aglyn/app-utils/screen-seo-fields'\nimport { hasConcurrentWrite, versionStamp } from '@aglyn/aglyn/app-utils/guarded-version-save'\nimport { decodeStoredNodes, encodeStoredNodes } from '@aglyn/aglyn/app-utils/stored-nodes'\nimport { coeditMirrorRoomPath, VERSION_DRAFT_COLLECTION, VERSION_DRAFT_DOC_ID } from '@aglyn/aglyn/app-utils/version-draft'\nimport { CanvasManager } from '@aglyn/aglyn/canvas-manager/canvas-manager'\nimport { FEATURE_FLAG } from '@aglyn/aglyn/foundation/constants/shared'\nimport type { EditorSession } from '@aglyn/aglyn/plugin-manager/editor-sessions'\nimport type { NodesMap } from '@aglyn/aglyn/types/nodes'\nimport { firebaseAdmin } from '@aglyn/tenant-data-admin/server/firebase-admin'\nimport { resolveOrgIdForHost } from '@aglyn/tenant-data-admin/server/organizations'\nimport { applyAssistEdit } from '../components/assist-edit-canvas'\nimport {\n AI_EDIT_COMPONENT_LEFT_OUT,\n AI_EDIT_EMAIL_COPY,\n AI_EDIT_GONE_COPY,\n AI_EDIT_NO_NODES_COPY,\n AI_EDIT_NO_SITE_COPY,\n AI_EDIT_NOTHING_COPY,\n AI_EDIT_REFUSED_COPY,\n AI_EDIT_SEO_LEFT_OUT,\n AI_EDIT_STALE_COPY,\n AI_EDIT_VERSION_GONE_COPY,\n aiEditJobOutline,\n aiEditOutputNote,\n aiEditVersionName,\n parseAiEditJobInputs,\n type AiEditJobInputs,\n type AiEditPlacement,\n type AiEditTargetKind,\n} from '../model/ai-edit-job'\nimport type { AiJob, AiJobOutput } from '../model/ai-jobs.types'\nimport {\n ASSIST_EDIT_TOOL_NAME,\n type AssistEditCanvasContext,\n type AssistEditOp,\n type AssistEditProposal,\n type AssistEditTarget,\n} from '../model/assist-edit'\nimport { AI_STEP_TIERS } from '../providers/catalog'\nimport { AI_ROUTING_TABLE, aiModelForStep } from '../providers/routing'\nimport {\n runValidatedGeneration,\n validateStreamedGeneration,\n type AiCustomGenerationInput,\n} from '../runtime/ai-doctrine'\nimport { AI_PALETTE } from '../runtime/ai-palette.generated'\nimport type { AiSystemBlock } from '../runtime/ai-runtime'\nimport { readAiSiteDatasets } from '../runtime/site-inventory'\nimport {\n assistEditTool,\n checkAssistEditAnswer,\n editCanvasBlock,\n editDatasetsBlock,\n editSelectionBlock,\n parseAssistEditContext,\n type AssistEditDataset,\n type AssistEditDatasetScope,\n} from '../server/assist-edit'\nimport { registerAiJobAdmission, type AiJobAdmission, type AiJobAdmissionRefusal } from './ai-job-admission'\nimport { aiJobStepBudget } from './ai-job-budget'\nimport { aiOriginJobId } from './ai-job-draft-ids'\nimport { aiGenerationSpent, aiUnspentOutcome } from './ai-job-generation'\nimport { AI_JOB_BRIEF_MAX_CHARS, type AiJobStepOutcome, type AiJobStepRunner } from './ai-job-text-step'\nimport { registerAiJobStep } from './ai-jobs'\n\n/**\n * The `edit` step (AGL-3616): a change to a page or a layout the site already\n * has, from a description, made on the server with no editor open.\n *\n * It is the Assist edit rung, run as a job. The model is shown the stored\n * document as the rung's outline and proposes through the rung's own tool;\n * its answer is held to the rung's closed world and palette validators\n * (`checkAssistEditAnswer`) and re-asked once, naming what was left out; and\n * what survives lands through the canvas's own guarded mutators\n * (`applyAssistEdit`), on a `CanvasManager` built headless from the stored\n * nodes, inside the transaction that writes it.\n *\n * ── Where the change lands, decided before anything is spent ────────────\n *\n * Never on a version a visitor sees. By default, on every plan, in the\n * WORKING DRAFT of the version it started from (AGL-4116): the one document\n * the Besigner saves to as an author works and offers with Open draft, which\n * publishing moves into the version. The change is built on the draft the\n * author already has, when the Besigner would still offer it, so their work\n * in progress is never thrown away; otherwise on the version's own tree.\n *\n * Only when the request asks for a new version, and the plan keeps version\n * history, a NEW version beside the one it started from — a copy of that\n * version's stored fields with the changed nodes — which becomes the page's\n * current version only when the page is not published; a published page's\n * current version, and a layout's, never move. Asked for without version\n * history, the change goes into the draft and the output says why.\n *\n * ── What a job cannot do that the editor can ────────────────────────────\n *\n * The rung's chat door never offers saving part of a page as a reusable\n * component, and neither does this. A page's search title and description\n * live on the page itself, not on a version, so they are written only for a\n * page that is not published; on a published page they are left out, and the\n * output's note says so, as it says every change it left out.\n *\n * ── One version per job ──────────────────────────────────────────────────\n *\n * The new version is named by the job's id — a build's unit is named by its\n * plan item's id — so a run asked again finds what it wrote and spends\n * nothing. A draft the job writes is stamped with the job's id for the same\n * reason.\n */\n\n/** The longest the step may take reading the page and writing it. */\nexport const AI_EDIT_READS_MS = 2_000\n\n/** The step's time: its answer and its re-ask at the `job.edit` ceiling, with its own reads. */\nexport const AI_JOB_EDIT_STEP_BUDGET = aiJobStepBudget({\n tier: AI_STEP_TIERS['job.edit'],\n maxTokens: AI_ROUTING_TABLE['job.edit'].maxTokens,\n lookups: 0,\n ownReadsMs: AI_EDIT_READS_MS,\n})\n\nexport const AI_JOB_EDIT_STEP_MINIMUM_MS = AI_JOB_EDIT_STEP_BUDGET.minimumMs\n\nexport function aiJobEditModel(): string {\n return aiModelForStep('job.edit')\n}\n\n/**\n * The job's own rules, byte-identical on every request, ahead of the rung's\n * protocol and catalog for the document's kind, which carries the breakpoint:\n * both are the same for every site, so a kind's whole prefix caches once.\n */\nexport const AI_JOB_EDIT_RULES: AiSystemBlock = {\n text: [\n 'You change a page or a layout the person already has, as a job that runs on its own. No editor is open, nothing is selected, and nobody can answer a question before you finish.',\n `- Make exactly the change the request asks for, and keep everything else as it is: other elements, their text, their styles and their order. Never rewrite, restyle, move or remove what the request does not ask about, beyond what making that change needs.`,\n '- Find the elements the request means in the outline of the whole document the request carries, by what they say, what they are and where they sit on the page. Name only ids that outline lists.',\n `- If the outline does not show an element the request is about, change nothing for that part: never guess at an element.`,\n `- Call ${ASSIST_EDIT_TOOL_NAME} exactly once, with every operation. Its summary is one short line saying what changed, in the language of the request; it names the version the change is saved in.`,\n \"- Fill in a page's search title or description only when the request asks for it.\",\n '- What you change is saved where no visitor sees it until the person publishes it, so never say the site or the page has changed.',\n '- The protocol below was written for Assist’s chat. In this job there is no user to ask or to press Apply, and no selection: what it says about those does not apply. Everything else in it — the operations, their fields, the elements and the style tokens — does.',\n ].join('\\n'),\n}\n\n/** The system blocks for one kind of document. */\nexport function aiJobEditInstructions(kind: AiEditTargetKind): AiSystemBlock[] {\n return [AI_JOB_EDIT_RULES, { text: editCanvasBlock(kind), cacheBreakpoint: true }]\n}\n\n/**\n * What the change is asked from: the document's outline, the site's datasets\n * an element may show the records of (AGL-3616), then the request.\n */\nexport function aiJobEditPrompt(input: {\n brief: string\n context: AssistEditCanvasContext\n datasets?: readonly AssistEditDataset[]\n}): string {\n const datasets = editDatasetsBlock(input.datasets ?? [])\n return [\n editSelectionBlock(input.context, { job: true }),\n ...(datasets ? ['', datasets] : []),\n '',\n `Request: ${input.brief.slice(0, AI_JOB_BRIEF_MAX_CHARS)}`,\n ].join('\\n')\n}\n\n/**\n * The datasets an edit to this document knows (AGL-3616): every one the site\n * holds, and the one whose record page the document is, if it is one.\n */\nexport function aiEditDatasetScope(\n datasets: ReadonlyArray<AssistEditDataset & { recordScreenId?: string }>,\n read: Pick<AiEditTargetRead, 'kind' | 'id'>,\n): Required<AssistEditDatasetScope> {\n const record = read.kind === 'screen' ? datasets.find((dataset) => dataset.recordScreenId === read.id) : undefined\n return {\n datasets: datasets.map(({ id, name, fields, fieldIds, imageField }) => ({\n id,\n name,\n fields,\n ...(fieldIds ? { fieldIds } : {}),\n ...(imageField ? { imageField } : {}),\n })),\n recordDatasetId: record?.id ?? null,\n }\n}\n\n/** The edit as the doctrine's loop runs it: the rung's tool, held to the rung's check. */\nexport function aiJobEditGeneration(request: {\n brief: string\n context: AssistEditCanvasContext\n target: AssistEditTarget\n model: string\n signal?: AbortSignal\n} & AssistEditDatasetScope): AiCustomGenerationInput<AssistEditProposal> {\n const row = AI_ROUTING_TABLE['job.edit']\n const kind = request.target.kind as AiEditTargetKind\n return {\n step: 'job.edit',\n model: request.model,\n instructions: aiJobEditInstructions(kind),\n messages: [{ role: 'user', content: aiJobEditPrompt(request) }],\n tool: assistEditTool(kind),\n maxTokens: AI_JOB_EDIT_STEP_BUDGET.maxTokens(request.model),\n ...(row.thinking ? { thinking: row.thinking } : {}),\n ...(row.effort ? { effort: row.effort } : {}),\n ...(request.signal ? { signal: request.signal } : {}),\n check: (answer) =>\n checkAssistEditAnswer(answer, {\n context: request.context,\n target: request.target,\n ...(request.datasets ? { datasets: request.datasets } : {}),\n ...(request.recordDatasetId ? { recordDatasetId: request.recordDatasetId } : {}),\n }),\n }\n}\n\n// ── The document, headless ────────────────────────────────────────────────\n\n/**\n * The slice of the platform a headless canvas asks: whether an element takes\n * children, answered from the AI palette the edit validator reads, so the\n * canvas refuses exactly what the validator refuses.\n */\nconst AI_EDIT_CANVAS_PLATFORM = {\n components: {\n getSchema: (componentId: string) => {\n const entry = AI_PALETTE[componentId]\n if (!entry) return undefined\n return entry.acceptsChildren ? {} : { flags: { dropping: FEATURE_FLAG.DISABLED } }\n },\n getLabel: (componentId: string) => AI_PALETTE[componentId]?.displayName ?? componentId,\n },\n}\n\n/** A canvas over a stored node map, with the editor's own guarded mutators and no editor. */\nexport function aiEditCanvas(nodes: NodesMap): CanvasManager {\n const canvas = new CanvasManager(AI_EDIT_CANVAS_PLATFORM as never)\n canvas.setNodes(nodes)\n return canvas\n}\n\n/**\n * Lands a proposal on a stored node map, as the editor lands one on its open\n * canvas: checked whole first, then one batch, all or nothing. The session is\n * the version being written, which is never one a visitor sees; a page's\n * search fields are captured into `seo` only where `seo` is given.\n */\nexport function aiApplyEditToNodes(\n nodes: NodesMap,\n proposal: AssistEditProposal,\n seo?: Partial<Record<(typeof SCREEN_SEO_TEXT_FIELDS)[number], string>>,\n): { ok: true; nodes: NodesMap } | { ok: false; reason: string } {\n const canvas = aiEditCanvas(nodes)\n const fields: Record<string, (value: string) => void> = {}\n if (seo) {\n for (const field of SCREEN_SEO_TEXT_FIELDS) fields[screenSeoStageKey(field)] = (value) => (seo[field] = value)\n }\n const session: EditorSession = {\n documentKind: proposal.target.kind,\n documentId: proposal.target.documentId,\n versionId: proposal.target.versionId,\n isLiveVersion: () => false,\n ...(seo ? { fields } : {}),\n }\n const applied = applyAssistEdit(canvas, proposal, session)\n if (applied.ok === false) return { ok: false, reason: applied.reason }\n return { ok: true, nodes: canvas.toJSON().nodes as NodesMap }\n}\n\n// ── Reading the document ──────────────────────────────────────────────────\n\ntype Firestore = FirebaseFirestore.Firestore\n\nconst COLLECTIONS: Readonly<Record<AiEditTargetKind, 'screens' | 'layouts'>> = {\n screen: 'screens',\n layout: 'layouts',\n}\n\n/** The page or layout an edit job changes, as it stands before anything is spent. */\nexport interface AiEditTargetRead {\n kind: AiEditTargetKind\n id: string\n name: string\n hostSubdomain: string | null\n /** The document's current version. */\n pointerVersionId: string | null\n /** The version the change starts from. */\n sourceVersionId: string\n /** The version's own tree. */\n nodes: NodesMap\n /**\n * The version's working draft, when it has one the Besigner would still\n * offer: an author's saved work in progress, which a change to the draft\n * is built on. Null when there is none, or the version was saved since.\n */\n draft: { nodes: NodesMap } | null\n /** Whether visitors see the document's current version: a published page, a collection template, any layout. */\n served: boolean\n /** The version a pending publish schedule will make current, if any. */\n scheduledVersionId: string | null\n /** What this job already wrote, when a run of it got that far. */\n written: { versionId: string; placement: AiEditPlacement } | null\n}\n\nexport interface AiEditTargetRefusal {\n status: 400 | 404\n error: string\n}\n\nconst text = (value: unknown): string => (typeof value === 'string' ? value : '')\n\n/** Whether visitors see a page's current version: it is routed, or it renders a collection's entries. */\nfunction screenServed(screen: FirebaseFirestore.DocumentSnapshot, host: FirebaseFirestore.DocumentSnapshot): boolean {\n if (screen.get('kind') === SCREEN_KIND_TEMPLATE) return true\n const routing = host.get('screens') as Record<string, unknown> | null | undefined\n return typeof routing?.[screen.id] === 'string'\n}\n\nfunction pendingScheduleVersion(doc: FirebaseFirestore.DocumentSnapshot): string | null {\n const schedule = doc.get('publishSchedule') as Record<string, unknown> | null | undefined\n if (!schedule || schedule['status'] !== 'pending' || schedule['action'] === 'unpublish') return null\n return text(schedule['versionId']) || null\n}\n\n/**\n * A version's working draft as the Besigner would offer it: a tree, taken\n * against the version as it is stored now. A draft the version has been\n * saved past is not offered there (it would undo that save), so a change is\n * not built on it here either. `aiEditJobId` names the job that wrote it.\n */\nfunction usableDraft(\n draft: FirebaseFirestore.DocumentSnapshot,\n version: FirebaseFirestore.DocumentSnapshot,\n): { nodes: NodesMap; aiEditJobId: string | null } | null {\n if (!draft.exists) return null\n const nodes = decodeStoredNodes<NodesMap>(draft.get('nodes'))\n if (!nodes || !Object.keys(nodes).length) return null\n const baseStamp = typeof draft.get('baseStamp') === 'string' ? (draft.get('baseStamp') as string) : null\n if (hasConcurrentWrite(baseStamp, versionStamp(version.get('updatedAt')))) return null\n return { nodes, aiEditJobId: text(draft.get('aiEditJobId')) || null }\n}\n\n/** A version's working draft document: the Besigner's, at the core's one address. */\nfunction draftRef(versions: FirebaseFirestore.CollectionReference, versionId: string): FirebaseFirestore.DocumentReference {\n return versions.doc(versionId).collection(VERSION_DRAFT_COLLECTION).doc(VERSION_DRAFT_DOC_ID)\n}\n\n/**\n * The document an edit job changes, on the job's own site: the page or\n * layout named, not deleted, not an email design, and the version it starts\n * from with a document in it. A refusal names what is wrong.\n */\nexport async function readAiEditTarget(\n firestore: Firestore,\n input: {\n orgId: string\n hostId: string\n inputs: AiEditJobInputs\n /** The job's own version id, to find what a run of it wrote; `null` at the door, before there is one. */\n jobId: string | null\n },\n): Promise<AiEditTargetRead | AiEditTargetRefusal> {\n const hostRef = firestore.collection('hosts').doc(input.hostId)\n const host = await hostRef.get()\n if (!host.exists || host.get('orgId') !== input.orgId) return { status: 404, error: AI_EDIT_NO_SITE_COPY }\n const kinds: AiEditTargetKind[] = input.inputs.targetKind ? [input.inputs.targetKind] : ['screen', 'layout']\n for (const kind of kinds) {\n const docRef = hostRef.collection(COLLECTIONS[kind]).doc(input.inputs.target)\n const doc = await docRef.get()\n if (!doc.exists) continue\n if (doc.get('deletedAt') != null) return { status: 404, error: AI_EDIT_GONE_COPY }\n if (kind === 'screen' && doc.get('kind') === 'email') return { status: 400, error: AI_EDIT_EMAIL_COPY }\n const pointerVersionId = text(doc.get('versionId')) || null\n const versions = docRef.collection('versions')\n const hostSubdomain = text(host.get('subdomain')) || null\n const name = text(doc.get('displayName')) || (kind === 'screen' ? 'Page' : 'Layout')\n // A run of this job that got as far as its write.\n const own = input.jobId ? await versions.doc(input.jobId).get() : null\n const sourceVersionId = input.inputs.versionId ?? pointerVersionId\n const base = {\n kind,\n id: doc.id,\n name,\n hostSubdomain,\n pointerVersionId,\n served: kind === 'layout' || screenServed(doc, host),\n scheduledVersionId: pendingScheduleVersion(doc),\n }\n if (own?.exists && input.jobId) {\n const placement: AiEditPlacement = pointerVersionId === input.jobId ? 'new-current' : 'new-beside'\n return { ...base, sourceVersionId: sourceVersionId ?? input.jobId, nodes: {}, draft: null, written: { versionId: input.jobId, placement } }\n }\n if (!sourceVersionId) return { status: 404, error: AI_EDIT_VERSION_GONE_COPY }\n const [source, draftDoc] = await Promise.all([versions.doc(sourceVersionId).get(), draftRef(versions, sourceVersionId).get()])\n if (!source.exists) return { status: 404, error: AI_EDIT_VERSION_GONE_COPY }\n if (input.jobId && source.get('aiEditJobId') === input.jobId) {\n return { ...base, sourceVersionId, nodes: {}, draft: null, written: { versionId: sourceVersionId, placement: 'in-place' } }\n }\n const draft = usableDraft(draftDoc, source)\n if (input.jobId && draft?.aiEditJobId === input.jobId) {\n return { ...base, sourceVersionId, nodes: {}, draft: null, written: { versionId: sourceVersionId, placement: 'draft' } }\n }\n const nodes = decodeStoredNodes<NodesMap>(source.get('nodes'))\n if (!nodes || !Object.keys(nodes).length) return { status: 400, error: AI_EDIT_NO_NODES_COPY }\n return { ...base, sourceVersionId, nodes, draft: draft ? { nodes: draft.nodes } : null, written: null }\n }\n return { status: 404, error: AI_EDIT_GONE_COPY }\n}\n\n/**\n * Where a change may land (see the module head), or `refused` when the only\n * place is a version visitors see and the plan keeps no version history.\n */\nexport function aiEditPlacement(input: {\n versioning: boolean\n served: boolean\n sourceIsPointer: boolean\n sourceScheduled: boolean\n}): AiEditPlacement | 'refused' {\n if (input.versioning) return !input.served && input.sourceIsPointer ? 'new-current' : 'new-beside'\n const seen = input.sourceScheduled || (input.sourceIsPointer && input.served)\n return seen ? 'refused' : 'in-place'\n}\n\n/**\n * Where an edit job's change lands (AGL-4116): the version's working draft,\n * unless the request asked for a new version and the plan keeps version\n * history — then where {@link aiEditPlacement} puts a new one. Never refused:\n * a draft is on every plan, and no visitor sees it.\n */\nexport function aiEditJobPlacement(input: {\n newVersion: boolean\n versioning: boolean\n served: boolean\n sourceIsPointer: boolean\n sourceScheduled: boolean\n}): AiEditPlacement {\n if (!input.newVersion || !input.versioning) return 'draft'\n const placed = aiEditPlacement(input)\n return placed === 'refused' ? 'draft' : placed\n}\n\n// ── Writing it ────────────────────────────────────────────────────────────\n\nexport interface AiEditWriteInput {\n hostId: string\n kind: AiEditTargetKind\n id: string\n sourceVersionId: string\n /** The new version's id, which is the job's; also the stamp a draft it writes carries. */\n jobVersionId: string\n versioning: boolean\n /** The request asked for a new version (see {@link aiEditJobPlacement}). */\n newVersion: boolean\n proposal: AssistEditProposal\n /** Whether the page's search fields may be written: a page no visitor reaches. */\n writeSeo: boolean\n versionName: string\n uid: string\n /** The job the member started, stamped as `aiJobId` on a new version. */\n aiJobId: string\n now: Date\n}\n\nexport type AiEditWrite =\n | { ok: true; versionId: string; placement: AiEditPlacement; seoWritten: boolean }\n | { ok: false; error: string }\n\n/**\n * The change written, in one transaction that reads the document as it is\n * stored NOW — a member may have saved, published or changed plan's history\n * since the read — decides where it lands again on that, and applies the\n * proposal to the stored nodes through the canvas's guards. An element that\n * changed since the model saw it refuses the whole change, as the editor does.\n */\nexport async function writeAiEdit(firestore: Firestore, input: AiEditWriteInput): Promise<AiEditWrite> {\n const hostRef = firestore.collection('hosts').doc(input.hostId)\n const docRef = hostRef.collection(COLLECTIONS[input.kind]).doc(input.id)\n const versions = docRef.collection('versions')\n const sourceRef = versions.doc(input.sourceVersionId)\n const newRef = versions.doc(input.jobVersionId)\n const sourceDraftRef = draftRef(versions, input.sourceVersionId)\n return firestore.runTransaction(async (tx): Promise<AiEditWrite> => {\n const [host, doc, source, already, draftDoc] = await Promise.all([\n tx.get(hostRef),\n tx.get(docRef),\n tx.get(sourceRef),\n tx.get(newRef),\n tx.get(sourceDraftRef),\n ])\n const pointer = text(doc.get('versionId')) || null\n if (already.exists) {\n return { ok: true, versionId: input.jobVersionId, placement: pointer === input.jobVersionId ? 'new-current' : 'new-beside', seoWritten: false }\n }\n if (!doc.exists || doc.get('deletedAt') != null) return { ok: false, error: AI_EDIT_GONE_COPY }\n if (!source.exists) return { ok: false, error: AI_EDIT_VERSION_GONE_COPY }\n const served = input.kind === 'layout' || screenServed(doc, host)\n const placement = aiEditJobPlacement({\n newVersion: input.newVersion,\n versioning: input.versioning,\n served,\n sourceIsPointer: pointer === input.sourceVersionId,\n sourceScheduled: pendingScheduleVersion(doc) === input.sourceVersionId,\n })\n // Read again here, as the version is: an author who saved their draft,\n // or published it, since the read is what the change is built on now.\n const draft = placement === 'draft' ? usableDraft(draftDoc, source) : null\n if (draft?.aiEditJobId === input.jobVersionId) {\n return { ok: true, versionId: input.sourceVersionId, placement, seoWritten: false }\n }\n const nodes = draft?.nodes ?? decodeStoredNodes<NodesMap>(source.get('nodes'))\n if (!nodes) return { ok: false, error: AI_EDIT_NO_NODES_COPY }\n const versionId = placement === 'draft' ? input.sourceVersionId : input.jobVersionId\n const seo: Partial<Record<(typeof SCREEN_SEO_TEXT_FIELDS)[number], string>> = {}\n const writeSeo = input.writeSeo && input.kind === 'screen' && !served\n const proposal: AssistEditProposal = {\n ...input.proposal,\n ops: writeSeo ? input.proposal.ops : input.proposal.ops.filter((op) => op.op !== 'setSeo'),\n target: { ...input.proposal.target, versionId },\n }\n const applied = aiApplyEditToNodes(nodes, proposal, writeSeo ? seo : undefined)\n if (applied.ok === false) return { ok: false, error: applied.reason === 'stale' ? AI_EDIT_STALE_COPY : AI_EDIT_REFUSED_COPY }\n const packed = encodeStoredNodes(applied.nodes)\n if (!packed) return { ok: false, error: AI_EDIT_NO_NODES_COPY }\n const stored = Buffer.from(packed)\n const docPatch: Record<string, unknown> = {}\n if (placement === 'draft') {\n // The whole working tree, as the Besigner writes a draft, taken against\n // the version as stored so the Besigner offers it with Open draft.\n tx.set(sourceDraftRef, {\n nodes: stored,\n baseStamp: versionStamp(source.get('updatedAt')),\n updatedByUid: input.uid,\n updatedByEmail: null,\n updatedAt: input.now,\n aiEditJobId: input.jobVersionId,\n })\n } else {\n // A copy of the version it started from — its layout binding, its\n // declared properties, everything the editor stored — with the changed\n // tree, as the versions route copies one.\n const copy = { ...(source.data() as Record<string, unknown>) }\n delete copy['aiEditJobId']\n tx.create(newRef, {\n ...copy,\n nodes: stored,\n displayName: input.versionName,\n createdAt: input.now,\n updatedAt: input.now,\n createdBy: input.uid,\n aiJobId: input.aiJobId,\n })\n if (placement === 'new-current') docPatch['versionId'] = input.jobVersionId\n }\n for (const [field, value] of Object.entries(seo)) {\n if (value) docPatch[`seo.${field}`] = value\n }\n if (Object.keys(docPatch).length) tx.update(docRef, { ...docPatch, updatedAt: input.now })\n return { ok: true, versionId, placement, seoWritten: Object.keys(seo).length > 0 }\n })\n}\n\n// ── The runner ────────────────────────────────────────────────────────────\n\nexport interface AiJobEditStepDeps {\n readTarget?: typeof readAiEditTarget\n writeEdit?: typeof writeAiEdit\n readDatasets?: typeof readAiSiteDatasets\n clearMirror?: typeof clearAiEditMirror\n}\n\n/**\n * Clears the co-edit mirror of the version whose draft a job just wrote\n * (AGL-4116), as the Besigner's own Save draft does.\n *\n * The mirror holds unsaved canvas work and replays everything newer than the\n * version's last save to whoever opens it. A draft write does not move the\n * version's save, so without this the canvas of someone who had opened the\n * older draft replays over the change the job just saved, and a room with\n * replayed work offers no draft at all: the newer draft can only be reached by\n * a Discard that deletes it. Best effort: a mirror that cannot be cleared\n * leaves the draft saved, and the job reports what it wrote.\n */\nexport async function clearAiEditMirror(input: {\n orgId: string\n hostId: string\n kind: AiEditTargetKind\n id: string\n versionId: string\n}): Promise<void> {\n await firebaseAdmin\n .database()\n .ref(coeditMirrorRoomPath({ orgId: input.orgId, hostId: input.hostId, docType: input.kind, docId: input.id, versionId: input.versionId }))\n .remove()\n}\n\n/** What an edit wrote, as the job's output: a link into the Besigner on the version written. */\nexport function aiEditOutput(\n read: Pick<AiEditTargetRead, 'kind' | 'id' | 'name' | 'hostSubdomain'>,\n hostId: string,\n written: { versionId: string; placement: AiEditPlacement },\n leftOut: readonly string[],\n options: { versionsUnavailable?: boolean } = {},\n): AiJobOutput {\n return {\n resource: read.kind,\n id: read.id,\n versionId: written.versionId,\n hostId,\n hostSubdomain: read.hostSubdomain,\n label: read.name,\n ...(written.placement === 'draft' ? { inDraft: true as const } : {}),\n note: aiEditOutputNote({\n kind: read.kind,\n placement: written.placement,\n leftOut,\n ...(options.versionsUnavailable ? { versionsUnavailable: true } : {}),\n }),\n }\n}\n\n/** The new version's id: the job's own, which a build's unit takes from its plan item. */\nexport function aiEditVersionId(job: Pick<AiJob, '$id'>): string {\n return job.$id.slice(0, 64)\n}\n\n/**\n * The ops a job may save, and why it left the rest out: never a component\n * save, and a page's search fields only where no visitor reaches the page.\n */\nexport function aiEditJobOps(\n ops: readonly AssistEditOp[],\n options: { seo: boolean },\n): { ops: AssistEditOp[]; leftOut: string[] } {\n const kept: AssistEditOp[] = []\n const leftOut = new Set<string>()\n for (const op of ops) {\n if (op.op === 'saveAsComponent') leftOut.add(AI_EDIT_COMPONENT_LEFT_OUT)\n else if (op.op === 'setSeo' && !options.seo) leftOut.add(AI_EDIT_SEO_LEFT_OUT)\n else kept.push(op)\n }\n return { ops: kept, leftOut: [...leftOut] }\n}\n\nexport function createAiJobEditStep(deps: AiJobEditStepDeps = {}): AiJobStepRunner {\n const readTarget = deps.readTarget ?? readAiEditTarget\n const writeEdit = deps.writeEdit ?? writeAiEdit\n const readDatasets = deps.readDatasets ?? readAiSiteDatasets\n const clearMirror = deps.clearMirror ?? clearAiEditMirror\n return async ({ job, now, signal, firestore, org, modelFor }): Promise<AiJobStepOutcome> => {\n const model = modelFor?.('job.edit') ?? aiJobEditModel()\n const unspent = (failure: string): AiJobStepOutcome => ({ ...aiUnspentOutcome(model), failure })\n const inputs = parseAiEditJobInputs(job.inputs)\n if (typeof inputs === 'string') return unspent(inputs)\n const hostId = job.hostId\n if (!hostId) return unspent(AI_EDIT_NO_SITE_COPY)\n const read = await readTarget(firestore, { orgId: job.orgId, hostId, inputs, jobId: aiEditVersionId(job) })\n if ('error' in read) return unspent(read.error)\n if (read.written) return aiUnspentOutcome(model, { outputs: [aiEditOutput(read, hostId, read.written, [])] })\n\n const orgData = org ?? ((await firestore.collection('orgs').doc(job.orgId).get()).data() ?? null)\n const versioning = checkEntitlement(orgData as never, 'versioning')\n const placement = aiEditJobPlacement({\n newVersion: inputs.newVersion,\n versioning,\n served: read.served,\n sourceIsPointer: read.sourceVersionId === read.pointerVersionId,\n sourceScheduled: read.scheduledVersionId === read.sourceVersionId,\n })\n // A change to the draft is made on the draft the author already has.\n const outline = aiEditJobOutline(placement === 'draft' && read.draft ? read.draft.nodes : read.nodes)\n const context = outline ? parseAssistEditContext(outline, { whole: true }) : null\n if (!context) return unspent(AI_EDIT_NO_NODES_COPY)\n const target: AssistEditTarget = {\n kind: read.kind,\n documentId: read.id,\n versionId: placement === 'draft' ? read.sourceVersionId : aiEditVersionId(job),\n hostId,\n }\n\n // The site's datasets, so a request to show records binds a repeat to a\n // real one instead of writing tokens nothing fills (AGL-3616).\n const datasets = aiEditDatasetScope(await readDatasets(job.orgId, hostId), read)\n\n const generation = await runValidatedGeneration(\n 'edit',\n aiJobEditGeneration({ brief: job.brief, context, target, model, ...datasets, ...(signal ? { signal } : {}) }),\n )\n const spent = { ...aiGenerationSpent(generation), ...(generation.effort ? { effort: generation.effort } : {}) }\n if (generation.status === 'refused') return { ...spent, refused: true }\n // An answer its re-ask still could not make whole keeps the changes that\n // survived, as the chat door's card does, and says what it left out; one\n // with none, or one that broke a numbered rule, is nothing to save.\n let proposal: AssistEditProposal | null = generation.status === 'ok' ? generation.value : null\n const refused = generation.status === 'needs_input' ? generation.answer : null\n if (!proposal && refused) {\n const salvaged = validateStreamedGeneration('edit', {\n answer: refused,\n check: (answer) => checkAssistEditAnswer(answer, { context, target, ...datasets }),\n })\n proposal = salvaged.status === 'ok' ? salvaged.value : null\n }\n if (!proposal) return { ...spent, failure: AI_EDIT_NOTHING_COPY }\n\n const seo = read.kind === 'screen' && !read.served\n const kept = aiEditJobOps(proposal.ops, { seo })\n const leftOut = [...proposal.dropped, ...kept.leftOut]\n if (!kept.ops.length) {\n return { ...spent, failure: `Nothing was saved. Left out: ${leftOut.join('; ') || 'every change'}.` }\n }\n const write = await writeEdit(firestore, {\n hostId,\n kind: read.kind,\n id: read.id,\n sourceVersionId: read.sourceVersionId,\n jobVersionId: aiEditVersionId(job),\n versioning,\n newVersion: inputs.newVersion,\n proposal: { ...proposal, ops: kept.ops },\n writeSeo: seo,\n versionName: aiEditVersionName(proposal.summary),\n uid: job.createdBy,\n aiJobId: aiOriginJobId(job),\n now,\n })\n if (write.ok === false) return { ...spent, failure: write.error }\n if (write.placement === 'draft') {\n await clearMirror({ orgId: job.orgId, hostId, kind: read.kind, id: read.id, versionId: write.versionId }).catch((error: unknown) =>\n console.warn('[ai edit] the co-edit mirror could not be cleared', { jobId: job.$id, error }),\n )\n }\n const versionsUnavailable = inputs.newVersion && !versioning\n return { ...spent, outputs: [aiEditOutput(read, hostId, write, leftOut, { versionsUnavailable })] }\n }\n}\n\nexport const runAiJobEditStep = createAiJobEditStep()\n\n// ── Admission ─────────────────────────────────────────────────────────────\n\nexport interface AiEditJobAdmissionDeps {\n readTarget?: typeof readAiEditTarget\n ownerOf?: typeof resolveOrgIdForHost\n}\n\n/**\n * An edit job is admitted for a page or layout of a site of its own org that\n * has a version to start from. Every plan has somewhere to put the change\n * that no visitor sees — the version's draft — so no plan is refused.\n */\nexport function createAiEditJobAdmission(deps: AiEditJobAdmissionDeps = {}): AiJobAdmission {\n const readTarget = deps.readTarget ?? readAiEditTarget\n const ownerOf = deps.ownerOf ?? resolveOrgIdForHost\n return async (context): Promise<AiJobAdmissionRefusal | null> => {\n const inputs = parseAiEditJobInputs(context.inputs)\n if (typeof inputs === 'string') return { status: 400, error: inputs }\n if (!context.hostId) return { status: 400, error: AI_EDIT_NO_SITE_COPY }\n const owner = await ownerOf(context.hostId)\n if (!owner || owner !== context.orgId) return { status: 404, error: 'Unknown site' }\n // A job asked about at its door has no run to find yet.\n const read = await readTarget(context.firestore, { orgId: context.orgId, hostId: context.hostId, inputs, jobId: null })\n // Every plan has somewhere to put the change: the page's draft (AGL-4116).\n return 'error' in read ? read : null\n }\n}\n\nexport const aiEditJobAdmission = createAiEditJobAdmission()\n\n/** Registers the edit step and the check an edit job passes before it is created or resumed. */\nexport function registerAiEditJob(): void {\n registerAiJobStep('edit', runAiJobEditStep, { minimumMs: AI_JOB_EDIT_STEP_MINIMUM_MS })\n registerAiJobAdmission('edit', aiEditJobAdmission)\n}\n"],"names":["checkEntitlement","SCREEN_KIND_TEMPLATE","screenSeoStageKey","SCREEN_SEO_TEXT_FIELDS","hasConcurrentWrite","versionStamp","decodeStoredNodes","encodeStoredNodes","coeditMirrorRoomPath","VERSION_DRAFT_COLLECTION","VERSION_DRAFT_DOC_ID","CanvasManager","FEATURE_FLAG","firebaseAdmin","resolveOrgIdForHost","applyAssistEdit","AI_EDIT_COMPONENT_LEFT_OUT","AI_EDIT_EMAIL_COPY","AI_EDIT_GONE_COPY","AI_EDIT_NO_NODES_COPY","AI_EDIT_NO_SITE_COPY","AI_EDIT_NOTHING_COPY","AI_EDIT_REFUSED_COPY","AI_EDIT_SEO_LEFT_OUT","AI_EDIT_STALE_COPY","AI_EDIT_VERSION_GONE_COPY","aiEditJobOutline","aiEditOutputNote","aiEditVersionName","parseAiEditJobInputs","ASSIST_EDIT_TOOL_NAME","AI_STEP_TIERS","AI_ROUTING_TABLE","aiModelForStep","runValidatedGeneration","validateStreamedGeneration","AI_PALETTE","readAiSiteDatasets","assistEditTool","checkAssistEditAnswer","editCanvasBlock","editDatasetsBlock","editSelectionBlock","parseAssistEditContext","registerAiJobAdmission","aiJobStepBudget","aiOriginJobId","aiGenerationSpent","aiUnspentOutcome","AI_JOB_BRIEF_MAX_CHARS","registerAiJobStep","AI_EDIT_READS_MS","AI_JOB_EDIT_STEP_BUDGET","tier","maxTokens","lookups","ownReadsMs","AI_JOB_EDIT_STEP_MINIMUM_MS","minimumMs","aiJobEditModel","AI_JOB_EDIT_RULES","text","join","aiJobEditInstructions","kind","cacheBreakpoint","aiJobEditPrompt","input","datasets","context","job","brief","slice","aiEditDatasetScope","read","record","find","dataset","recordScreenId","id","undefined","map","name","fields","fieldIds","imageField","recordDatasetId","aiJobEditGeneration","request","row","target","step","model","instructions","messages","role","content","tool","thinking","effort","signal","check","answer","AI_EDIT_CANVAS_PLATFORM","components","getSchema","componentId","entry","acceptsChildren","flags","dropping","DISABLED","getLabel","displayName","aiEditCanvas","nodes","canvas","setNodes","aiApplyEditToNodes","proposal","seo","field","value","session","documentKind","documentId","versionId","isLiveVersion","applied","ok","reason","toJSON","COLLECTIONS","screen","layout","screenServed","host","get","routing","pendingScheduleVersion","doc","schedule","usableDraft","draft","version","exists","Object","keys","length","baseStamp","aiEditJobId","draftRef","versions","collection","readAiEditTarget","firestore","hostRef","hostId","orgId","status","error","kinds","inputs","targetKind","docRef","pointerVersionId","hostSubdomain","own","jobId","sourceVersionId","base","served","scheduledVersionId","placement","written","source","draftDoc","Promise","all","aiEditPlacement","versioning","sourceIsPointer","seen","sourceScheduled","aiEditJobPlacement","newVersion","placed","writeAiEdit","sourceRef","newRef","jobVersionId","sourceDraftRef","runTransaction","tx","already","pointer","seoWritten","writeSeo","ops","filter","op","packed","stored","Buffer","from","docPatch","set","updatedByUid","uid","updatedByEmail","updatedAt","now","copy","data","create","versionName","createdAt","createdBy","aiJobId","entries","update","clearAiEditMirror","database","ref","docType","docId","remove","aiEditOutput","leftOut","options","resource","label","inDraft","note","versionsUnavailable","aiEditVersionId","$id","aiEditJobOps","kept","Set","add","push","createAiJobEditStep","deps","readTarget","writeEdit","readDatasets","clearMirror","org","modelFor","unspent","failure","outputs","orgData","outline","whole","generation","spent","refused","salvaged","dropped","write","summary","catch","console","warn","runAiJobEditStep","createAiEditJobAdmission","ownerOf","owner","aiEditJobAdmission","registerAiEditJob"],"mappings":"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"}
@@ -297,7 +297,8 @@ export function createAiJobNavigationStep(deps = {}) {
297
297
  inputs: {
298
298
  target: plan.layoutId,
299
299
  targetKind: 'layout',
300
- versionId: null
300
+ versionId: null,
301
+ newVersion: false
301
302
  },
302
303
  jobId: versionId
303
304
  });
@@ -409,7 +410,8 @@ export const runAiJobNavigationStep = createAiJobNavigationStep();
409
410
  inputs: {
410
411
  target: plan.layoutId,
411
412
  targetKind: 'layout',
412
- versionId: null
413
+ versionId: null,
414
+ newVersion: false
413
415
  },
414
416
  jobId: null
415
417
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-navigation-step.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 { checkEntitlement } from '@aglyn/aglyn/app-utils/plan-entitlements'\nimport { holdsSiteKey } from '@aglyn/aglyn/app-utils/host-permissions'\nimport { orgPermissionLabel } from '@aglyn/aglyn/app-utils/org-permissions'\nimport { decodeStoredNodes, encodeStoredNodes } from '@aglyn/aglyn/app-utils/stored-nodes'\nimport { getLockdownVerdict } from '@aglyn/tenant-data-admin/server/lockdown'\nimport { resolveOrgIdForHost } from '@aglyn/tenant-data-admin/server/organizations'\nimport type { Firestore } from 'firebase-admin/firestore'\nimport { AI_EDIT_GONE_COPY, AI_EDIT_NEEDS_VERSIONING_COPY, AI_EDIT_NO_NODES_COPY, AI_EDIT_VERSION_GONE_COPY } from '../model/ai-edit-job'\nimport { isAiPlanNewRef } from '../model/ai-build-plan'\nimport { AI_BUILD_MADE_INPUT, type AiBuildMade } from '../model/ai-build-job'\nimport { aiBuildStoreLayoutOf } from '../model/ai-build-store-pages'\nimport type { AiJob, AiJobOutput } from '../model/ai-jobs.types'\nimport {\n aiLayoutWithMenuChanges,\n aiMenuChangesEmpty,\n aiMenuChangesOf,\n aiMenusOf,\n type AiMenuChanges,\n type AiMenuName,\n type AiMenuTarget,\n} from '../model/ai-layout-menus'\nimport { registerAiJobAdmission, type AiJobAdmission, type AiJobAdmissionRefusal } from './ai-job-admission'\nimport { aiOriginJobId } from './ai-job-draft-ids'\nimport { aiEditPlacement, aiEditVersionId, aiJobEditModel, readAiEditTarget } from './ai-job-edit-step'\nimport { aiLimitReview, aiUnspentOutcome } from './ai-job-generation'\nimport type { AiJobStepOutcome, AiJobStepRunner } from './ai-job-text-step'\nimport { registerAiJobStep } from './ai-jobs'\n\n/**\n * The `navigation` step (AGL-3728): a change to a site's header or footer\n * menu — add, take out, rename, relink or reorder its items — as a\n * first-class build operation, so Assist's one plan card can say \"add About\n * and Pricing to the menu, and take Blog out\" and have it done.\n *\n * NO MODEL. The planner already wrote the change as the item's arguments, in\n * words (`aiMenuChangesOf`); this applies it to the layout's own Besigner\n * elements in code (`aiLayoutWithMenuChanges`), so it costs no credits and\n * cannot invent a link: every destination is one of the site's pages, a page\n * this build made, a path on the site or an https address.\n *\n * ── Where it lands ───────────────────────────────────────────────────────\n *\n * As the `edit` step's change to a layout does: a NEW version beside the\n * layout's current one, which visitors keep seeing until a person publishes\n * the new one. A layout is always served, so without version history there is\n * nowhere for it to land and it is refused as a plan limit, charged nothing.\n * The version is named by the job's id, so a pass asked again finds what it\n * wrote.\n *\n * ── Who may ──────────────────────────────────────────────────────────────\n *\n * The editor's own rule for a layout's versions (`canWriteHostContent` and\n * `hostWritesFrozen` in the Firestore rules): a member with a write role on\n * the site (admin, editor or author), while no lockdown freezes the site, its\n * org or the platform. Asked at the door, again before the write, and the\n * role once more inside the transaction that writes.\n *\n * ── Audit ────────────────────────────────────────────────────────────────\n *\n * The layout it wrote is the job's output, which the job machine records as\n * an `ai.job.output` activity row on the site, as every other operation's is.\n */\n\nexport const AI_NAVIGATION_KIND = 'navigation' as const\n\n/**\n * ASSUMED: the least time the step needs — the site's pages, the layout and\n * its version, the org, and the one transaction that writes. No model, so it\n * fits an inline door.\n */\nexport const AI_JOB_NAVIGATION_STEP_MINIMUM_MS = 5_000\n\nexport const AI_NAVIGATION_NO_SITE_COPY = 'Open the site whose menu should change.'\n/** Names \"Edit site content\" (`site.edit`, AGL-4047), never a role. */\nexport const AI_NAVIGATION_ROLE_COPY = `Changing the site’s menus needs the \"${orgPermissionLabel('site.edit')}\" permission on this site — ask an organization admin.`\nexport const AI_NAVIGATION_LOCKED_COPY = 'This site is not accepting changes right now.'\nexport const AI_NAVIGATION_NO_LAYOUT_COPY = 'This site has no layout with a header or footer to change. Make one in Layouts first.'\nexport const AI_NAVIGATION_NOTHING_COPY = 'The request named no menu change this could make.'\n\n/** The name the new version carries in the layout's versions. */\nexport const AI_NAVIGATION_VERSION_NAME = 'Menu changes'\n\nconst text = (value: unknown): string => (typeof value === 'string' ? value.trim() : '')\n\n/** A path on the site, as a menu item may link it. */\nconst SITE_PATH = /^\\/(?:[A-Za-z0-9._~!$&'()*+,;=:@%-]+(?:\\/[A-Za-z0-9._~!$&'()*+,;=:@%-]+)*)?\\/?(?:[?#][^\\s]*)?$/\nconst SCREEN_ID = /^[A-Za-z0-9_-]{1,128}$/\n\nfunction buildMadeOf(inputs: Readonly<Record<string, unknown>> | null | undefined): AiBuildMade {\n const raw = (inputs?.[AI_BUILD_MADE_INPUT] ?? {}) as Partial<Record<'pages' | 'layouts', unknown>>\n const rows = (value: unknown, name: 'title' | 'name') =>\n (Array.isArray(value) ? value : []).flatMap((entry) => {\n const row = (entry ?? {}) as Record<string, unknown>\n return text(row[name]) && SCREEN_ID.test(text(row['id'])) ? [{ [name]: text(row[name]), id: text(row['id']) }] : []\n })\n return { pages: rows(raw.pages, 'title') as AiBuildMade['pages'], layouts: rows(raw.layouts, 'name') as AiBuildMade['layouts'] }\n}\n\n/** The site's pages a menu may link, by id, with the layout each renders inside. */\nexport interface AiNavigationSitePage {\n id: string\n title: string\n slug: string\n template: boolean\n layoutId: string | null\n}\n\n/** The site's pages: not deleted, not an email design. */\nexport async function readAiNavigationSitePages(firestore: Firestore, hostId: string): Promise<AiNavigationSitePage[]> {\n const snapshot = await firestore.collection('hosts').doc(hostId).collection('screens').select('displayName', 'slug', 'kind', 'deletedAt', 'layoutId').get()\n return snapshot.docs.flatMap((doc) => {\n const kind = doc.get('kind')\n if (doc.get('deletedAt') || (kind && kind !== 'template')) return []\n const layoutId = doc.get('layoutId')\n return [\n {\n id: doc.id,\n slug: String(doc.get('slug') ?? ''),\n title: String(doc.get('displayName') ?? ''),\n template: kind === 'template',\n layoutId: typeof layoutId === 'string' && layoutId ? layoutId : null,\n },\n ]\n })\n}\n\n/**\n * What a destination in an item's words links (AGL-3728): a page this build\n * made (`new:<title>`), one of the site's pages by id (`page:<id>` or the bare\n * id), a path on the site, or an https address; `null` for anything else.\n */\nexport function aiNavigationTarget(\n destination: string,\n context: { pages: readonly Pick<AiNavigationSitePage, 'id' | 'template'>[]; made: AiBuildMade },\n): AiMenuTarget | null {\n const value = destination.trim()\n const pageIds = new Set(context.pages.filter((page) => !page.template).map((page) => page.id))\n if (isAiPlanNewRef(value as string | null)) {\n const title = value.slice(value.indexOf(':') + 1).trim().toLowerCase()\n const made = context.made.pages.find((page) => page.title.trim().toLowerCase() === title)\n // A page this build was to make and did not is no destination.\n return made && pageIds.has(made.id) ? { screenId: made.id } : null\n }\n const id = value.replace(/^page:/i, '')\n if (pageIds.has(id)) return { screenId: id }\n if (value.startsWith('/') && !value.startsWith('//') && SITE_PATH.test(value)) return { href: value }\n if (/^https:\\/\\/[^\\s/?#]+\\.[^\\s/?#]+(?:[/?#]\\S*)?$/i.test(value)) return { href: value }\n return null\n}\n\n/** The layout an item changes: the one it names, else the one the site's pages render inside. */\nexport function aiNavigationLayoutOf(\n named: string,\n context: { pages: readonly AiNavigationSitePage[]; made: AiBuildMade },\n): string | null {\n if (isAiPlanNewRef(named as string | null)) {\n const name = named.slice(named.indexOf(':') + 1).trim().toLowerCase()\n return context.made.layouts.find((layout) => layout.name.trim().toLowerCase() === name)?.id ?? null\n }\n if (SCREEN_ID.test(named)) return named\n return aiBuildStoreLayoutOf(null, context.pages)\n}\n\n/** Why this member may not change this site's menus now, or `null`. */\nexport async function aiNavigationAccessRefusal(\n input: { host: FirebaseFirestore.DocumentSnapshot; uid: string; org: object | null },\n deps: { lockdown?: typeof getLockdownVerdict } = {},\n): Promise<string | null> {\n if (!holdsSiteKey(input.host, input.uid, 'site.edit')) return AI_NAVIGATION_ROLE_COPY\n const locked = await (deps.lockdown ?? getLockdownVerdict)({\n staff: false,\n uid: input.uid,\n org: input.org as never,\n host: input.host.data() as never,\n intent: 'write',\n })\n return locked ? AI_NAVIGATION_LOCKED_COPY : null\n}\n\n/** The new version's output note: what was done, what was not, and that it waits to be published. */\nexport function aiNavigationOutputNote(done: readonly string[], notDone: readonly string[]): string {\n const did = done.length ? `${done.join(' ')} ` : ''\n const not = notDone.length ? ` Not done: ${notDone.join(' ')}` : ''\n return `${did}Saved as a new version of the layout beside the one visitors see: open it to review it, and publish it when it is ready.${not}`.slice(0, 1_000)\n}\n\nexport type AiNavigationWrite =\n | { ok: true; versionId: string; done: string[]; notDone: string[] }\n | { ok: false; error: string }\n\n/**\n * The change written, in one transaction that reads the layout as it is\n * stored NOW: it still exists, its version is still there, the member still\n * holds a write role, and the plan still keeps version history. The change is\n * applied again to that version's nodes, so a save made since is kept.\n */\nexport async function writeAiNavigation(\n firestore: Firestore,\n input: {\n hostId: string\n layoutId: string\n sourceVersionId: string\n versionId: string\n menus: readonly AiMenuName[]\n changes: AiMenuChanges\n uid: string\n aiJobId: string\n versioning: boolean\n now: Date\n },\n): Promise<AiNavigationWrite> {\n const hostRef = firestore.collection('hosts').doc(input.hostId)\n const docRef = hostRef.collection('layouts').doc(input.layoutId)\n const sourceRef = docRef.collection('versions').doc(input.sourceVersionId)\n const newRef = docRef.collection('versions').doc(input.versionId)\n return firestore.runTransaction(async (tx): Promise<AiNavigationWrite> => {\n const [host, doc, source, already] = await Promise.all([tx.get(hostRef), tx.get(docRef), tx.get(sourceRef), tx.get(newRef)])\n if (already.exists) return { ok: true, versionId: input.versionId, done: [], notDone: [] }\n if (!doc.exists || doc.get('deletedAt') != null) return { ok: false, error: AI_EDIT_GONE_COPY }\n if (!source.exists) return { ok: false, error: AI_EDIT_VERSION_GONE_COPY }\n if (!holdsSiteKey(host, input.uid, 'site.edit')) return { ok: false, error: AI_NAVIGATION_ROLE_COPY }\n const pointer = text(doc.get('versionId')) || null\n const placement = aiEditPlacement({\n versioning: input.versioning,\n served: true,\n sourceIsPointer: pointer === input.sourceVersionId,\n sourceScheduled: false,\n })\n if (placement === 'refused') return { ok: false, error: AI_EDIT_NEEDS_VERSIONING_COPY }\n const nodes = decodeStoredNodes<Record<string, unknown>>(source.get('nodes'))\n if (!nodes) return { ok: false, error: AI_EDIT_NO_NODES_COPY }\n const applied = aiLayoutWithMenuChanges(nodes, input.menus, input.changes)\n if (!applied.nodes) return { ok: false, error: applied.notDone.join(' ') || AI_NAVIGATION_NOTHING_COPY }\n const packed = encodeStoredNodes(applied.nodes as never)\n if (!packed) return { ok: false, error: AI_EDIT_NO_NODES_COPY }\n // A copy of the version it started from — everything the editor stored —\n // with the changed menus, as the versions route copies one.\n const copy = { ...(source.data() as Record<string, unknown>) }\n delete copy['aiEditJobId']\n tx.create(newRef, {\n ...copy,\n nodes: Buffer.from(packed),\n displayName: AI_NAVIGATION_VERSION_NAME,\n createdAt: input.now,\n updatedAt: input.now,\n createdBy: input.uid,\n aiJobId: input.aiJobId,\n })\n return { ok: true, versionId: input.versionId, done: applied.done, notDone: applied.notDone }\n })\n}\n\nexport interface AiJobNavigationStepDeps {\n readTarget?: typeof readAiEditTarget\n readPages?: typeof readAiNavigationSitePages\n write?: typeof writeAiNavigation\n lockdown?: typeof getLockdownVerdict\n}\n\n/** What the step decided before writing: the layout, its version, the change and what was read away. */\ninterface AiNavigationPlan {\n layoutId: string\n menus: AiMenuName[]\n changes: AiMenuChanges\n problems: string[]\n}\n\nasync function navigationPlanOf(\n firestore: Firestore,\n job: Pick<AiJob, 'hostId' | 'inputs'>,\n readPages: typeof readAiNavigationSitePages,\n): Promise<AiNavigationPlan | string> {\n if (!job.hostId) return AI_NAVIGATION_NO_SITE_COPY\n const inputs = job.inputs ?? {}\n const pages = await readPages(firestore, job.hostId)\n const made = buildMadeOf(inputs)\n const layoutId = aiNavigationLayoutOf(text(inputs['layout']), { pages, made })\n if (!layoutId) return AI_NAVIGATION_NO_LAYOUT_COPY\n const read = aiMenuChangesOf(inputs, (destination) => aiNavigationTarget(destination, { pages, made }))\n if (aiMenuChangesEmpty(read.changes)) return [AI_NAVIGATION_NOTHING_COPY, ...read.problems].join(' ')\n return { layoutId, menus: aiMenusOf(inputs['menu']), changes: read.changes, problems: read.problems }\n}\n\nexport function createAiJobNavigationStep(deps: AiJobNavigationStepDeps = {}): AiJobStepRunner {\n const readTarget = deps.readTarget ?? readAiEditTarget\n const readPages = deps.readPages ?? readAiNavigationSitePages\n const write = deps.write ?? writeAiNavigation\n return async ({ job, now, firestore, org, modelFor }): Promise<AiJobStepOutcome> => {\n // No model is asked: the outcome records the edit step's at no cost.\n const model = modelFor?.('job.edit') ?? aiJobEditModel()\n const unspent = (failure: string): AiJobStepOutcome => ({ ...aiUnspentOutcome(model), failure })\n const hostId = job.hostId\n if (!hostId) return unspent(AI_NAVIGATION_NO_SITE_COPY)\n const plan = await navigationPlanOf(firestore, job, readPages)\n if (typeof plan === 'string') return unspent(plan)\n const versionId = aiEditVersionId(job)\n const read = await readTarget(firestore, {\n orgId: job.orgId,\n hostId,\n inputs: { target: plan.layoutId, targetKind: 'layout', versionId: null },\n jobId: versionId,\n })\n if ('error' in read) return unspent(read.error)\n const output = (done: readonly string[], notDone: readonly string[]): AiJobOutput => ({\n resource: 'layout',\n id: read.id,\n versionId,\n hostId,\n hostSubdomain: read.hostSubdomain,\n label: read.name,\n note: aiNavigationOutputNote(done, notDone),\n })\n if (read.written) return aiUnspentOutcome(model, { outputs: [output([], [])] })\n const orgData = org ?? ((await firestore.collection('orgs').doc(job.orgId).get()).data() ?? null)\n const host = await firestore.collection('hosts').doc(hostId).get()\n const refused = await aiNavigationAccessRefusal({ host, uid: job.createdBy, org: orgData }, { lockdown: deps.lockdown })\n if (refused) return aiUnspentOutcome(model, { review: aiLimitReview(refused) })\n const versioning = checkEntitlement(orgData as never, 'versioning')\n const placement = aiEditPlacement({\n versioning,\n served: true,\n sourceIsPointer: read.sourceVersionId === read.pointerVersionId,\n sourceScheduled: read.scheduledVersionId === read.sourceVersionId,\n })\n if (placement === 'refused') return aiUnspentOutcome(model, { review: aiLimitReview(AI_EDIT_NEEDS_VERSIONING_COPY) })\n const written = await write(firestore, {\n hostId,\n layoutId: read.id,\n sourceVersionId: read.sourceVersionId,\n versionId,\n menus: plan.menus,\n changes: plan.changes,\n uid: job.createdBy,\n aiJobId: aiOriginJobId(job),\n versioning,\n now,\n })\n if (written.ok === false) return unspent(written.error)\n return aiUnspentOutcome(model, { outputs: [output(written.done, [...written.notDone, ...plan.problems])] })\n }\n}\n\nexport const runAiJobNavigationStep = createAiJobNavigationStep()\n\n// ── Admission ─────────────────────────────────────────────────────────────\n\nexport interface AiNavigationJobAdmissionDeps {\n ownerOf?: typeof resolveOrgIdForHost\n readTarget?: typeof readAiEditTarget\n readPages?: typeof readAiNavigationSitePages\n lockdown?: typeof getLockdownVerdict\n}\n\n/**\n * A navigation change is admitted on a site of the job's own org, for a\n * member who may write its content now, where the site has a layout and the\n * change has somewhere to land that no visitor sees on this plan. Refused in\n * those words before anything exists.\n */\nexport function createAiNavigationJobAdmission(deps: AiNavigationJobAdmissionDeps = {}): AiJobAdmission {\n const ownerOf = deps.ownerOf ?? resolveOrgIdForHost\n const readTarget = deps.readTarget ?? readAiEditTarget\n const readPages = deps.readPages ?? readAiNavigationSitePages\n return async (context): Promise<AiJobAdmissionRefusal | null> => {\n if (!context.hostId) return { status: 400, error: AI_NAVIGATION_NO_SITE_COPY }\n const owner = await ownerOf(context.hostId)\n if (!owner || owner !== context.orgId) return { status: 404, error: 'Unknown site' }\n const host = await context.firestore.collection('hosts').doc(context.hostId).get()\n const refused = await aiNavigationAccessRefusal({ host, uid: context.uid ?? '', org: context.org }, { lockdown: deps.lockdown })\n if (refused) return { status: 403, error: refused }\n const plan = await navigationPlanOf(context.firestore as Firestore, { hostId: context.hostId, inputs: context.inputs as Record<string, unknown> }, readPages)\n if (typeof plan === 'string') return { status: 400, error: plan }\n const read = await readTarget(context.firestore, {\n orgId: context.orgId,\n hostId: context.hostId,\n inputs: { target: plan.layoutId, targetKind: 'layout', versionId: null },\n jobId: null,\n })\n if ('error' in read) return read\n const placement = aiEditPlacement({\n versioning: checkEntitlement(context.org as never, 'versioning'),\n served: true,\n sourceIsPointer: read.sourceVersionId === read.pointerVersionId,\n sourceScheduled: read.scheduledVersionId === read.sourceVersionId,\n })\n return placement === 'refused' ? { status: 403, error: AI_EDIT_NEEDS_VERSIONING_COPY } : null\n }\n}\n\nexport const aiNavigationJobAdmission = createAiNavigationJobAdmission()\n\n/** Registers the navigation step and its door (AGL-3728). */\nexport function registerAiNavigationJob(): void {\n registerAiJobStep(AI_NAVIGATION_KIND, runAiJobNavigationStep, { minimumMs: AI_JOB_NAVIGATION_STEP_MINIMUM_MS })\n registerAiJobAdmission(AI_NAVIGATION_KIND, aiNavigationJobAdmission)\n}\n"],"names":["checkEntitlement","holdsSiteKey","orgPermissionLabel","decodeStoredNodes","encodeStoredNodes","getLockdownVerdict","resolveOrgIdForHost","AI_EDIT_GONE_COPY","AI_EDIT_NEEDS_VERSIONING_COPY","AI_EDIT_NO_NODES_COPY","AI_EDIT_VERSION_GONE_COPY","isAiPlanNewRef","AI_BUILD_MADE_INPUT","aiBuildStoreLayoutOf","aiLayoutWithMenuChanges","aiMenuChangesEmpty","aiMenuChangesOf","aiMenusOf","registerAiJobAdmission","aiOriginJobId","aiEditPlacement","aiEditVersionId","aiJobEditModel","readAiEditTarget","aiLimitReview","aiUnspentOutcome","registerAiJobStep","AI_NAVIGATION_KIND","AI_JOB_NAVIGATION_STEP_MINIMUM_MS","AI_NAVIGATION_NO_SITE_COPY","AI_NAVIGATION_ROLE_COPY","AI_NAVIGATION_LOCKED_COPY","AI_NAVIGATION_NO_LAYOUT_COPY","AI_NAVIGATION_NOTHING_COPY","AI_NAVIGATION_VERSION_NAME","text","value","trim","SITE_PATH","SCREEN_ID","buildMadeOf","inputs","raw","rows","name","Array","isArray","flatMap","entry","row","test","id","pages","layouts","readAiNavigationSitePages","firestore","hostId","snapshot","collection","doc","select","get","docs","kind","layoutId","slug","String","title","template","aiNavigationTarget","destination","context","pageIds","Set","filter","page","map","slice","indexOf","toLowerCase","made","find","has","screenId","replace","startsWith","href","aiNavigationLayoutOf","named","layout","aiNavigationAccessRefusal","input","deps","host","uid","locked","lockdown","staff","org","data","intent","aiNavigationOutputNote","done","notDone","did","length","join","not","writeAiNavigation","hostRef","docRef","sourceRef","sourceVersionId","newRef","versionId","runTransaction","tx","source","already","Promise","all","exists","ok","error","pointer","placement","versioning","served","sourceIsPointer","sourceScheduled","nodes","applied","menus","changes","packed","copy","create","Buffer","from","displayName","createdAt","now","updatedAt","createdBy","aiJobId","navigationPlanOf","job","readPages","read","problems","createAiJobNavigationStep","readTarget","write","modelFor","model","unspent","failure","plan","orgId","target","targetKind","jobId","output","resource","hostSubdomain","label","note","written","outputs","orgData","refused","review","pointerVersionId","scheduledVersionId","runAiJobNavigationStep","createAiNavigationJobAdmission","ownerOf","status","owner","aiNavigationJobAdmission","registerAiNavigationJob","minimumMs"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-navigation-step.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 { checkEntitlement } from '@aglyn/aglyn/app-utils/plan-entitlements'\nimport { holdsSiteKey } from '@aglyn/aglyn/app-utils/host-permissions'\nimport { orgPermissionLabel } from '@aglyn/aglyn/app-utils/org-permissions'\nimport { decodeStoredNodes, encodeStoredNodes } from '@aglyn/aglyn/app-utils/stored-nodes'\nimport { getLockdownVerdict } from '@aglyn/tenant-data-admin/server/lockdown'\nimport { resolveOrgIdForHost } from '@aglyn/tenant-data-admin/server/organizations'\nimport type { Firestore } from 'firebase-admin/firestore'\nimport { AI_EDIT_GONE_COPY, AI_EDIT_NEEDS_VERSIONING_COPY, AI_EDIT_NO_NODES_COPY, AI_EDIT_VERSION_GONE_COPY } from '../model/ai-edit-job'\nimport { isAiPlanNewRef } from '../model/ai-build-plan'\nimport { AI_BUILD_MADE_INPUT, type AiBuildMade } from '../model/ai-build-job'\nimport { aiBuildStoreLayoutOf } from '../model/ai-build-store-pages'\nimport type { AiJob, AiJobOutput } from '../model/ai-jobs.types'\nimport {\n aiLayoutWithMenuChanges,\n aiMenuChangesEmpty,\n aiMenuChangesOf,\n aiMenusOf,\n type AiMenuChanges,\n type AiMenuName,\n type AiMenuTarget,\n} from '../model/ai-layout-menus'\nimport { registerAiJobAdmission, type AiJobAdmission, type AiJobAdmissionRefusal } from './ai-job-admission'\nimport { aiOriginJobId } from './ai-job-draft-ids'\nimport { aiEditPlacement, aiEditVersionId, aiJobEditModel, readAiEditTarget } from './ai-job-edit-step'\nimport { aiLimitReview, aiUnspentOutcome } from './ai-job-generation'\nimport type { AiJobStepOutcome, AiJobStepRunner } from './ai-job-text-step'\nimport { registerAiJobStep } from './ai-jobs'\n\n/**\n * The `navigation` step (AGL-3728): a change to a site's header or footer\n * menu — add, take out, rename, relink or reorder its items — as a\n * first-class build operation, so Assist's one plan card can say \"add About\n * and Pricing to the menu, and take Blog out\" and have it done.\n *\n * NO MODEL. The planner already wrote the change as the item's arguments, in\n * words (`aiMenuChangesOf`); this applies it to the layout's own Besigner\n * elements in code (`aiLayoutWithMenuChanges`), so it costs no credits and\n * cannot invent a link: every destination is one of the site's pages, a page\n * this build made, a path on the site or an https address.\n *\n * ── Where it lands ───────────────────────────────────────────────────────\n *\n * As the `edit` step's change to a layout does: a NEW version beside the\n * layout's current one, which visitors keep seeing until a person publishes\n * the new one. A layout is always served, so without version history there is\n * nowhere for it to land and it is refused as a plan limit, charged nothing.\n * The version is named by the job's id, so a pass asked again finds what it\n * wrote.\n *\n * ── Who may ──────────────────────────────────────────────────────────────\n *\n * The editor's own rule for a layout's versions (`canWriteHostContent` and\n * `hostWritesFrozen` in the Firestore rules): a member with a write role on\n * the site (admin, editor or author), while no lockdown freezes the site, its\n * org or the platform. Asked at the door, again before the write, and the\n * role once more inside the transaction that writes.\n *\n * ── Audit ────────────────────────────────────────────────────────────────\n *\n * The layout it wrote is the job's output, which the job machine records as\n * an `ai.job.output` activity row on the site, as every other operation's is.\n */\n\nexport const AI_NAVIGATION_KIND = 'navigation' as const\n\n/**\n * ASSUMED: the least time the step needs — the site's pages, the layout and\n * its version, the org, and the one transaction that writes. No model, so it\n * fits an inline door.\n */\nexport const AI_JOB_NAVIGATION_STEP_MINIMUM_MS = 5_000\n\nexport const AI_NAVIGATION_NO_SITE_COPY = 'Open the site whose menu should change.'\n/** Names \"Edit site content\" (`site.edit`, AGL-4047), never a role. */\nexport const AI_NAVIGATION_ROLE_COPY = `Changing the site’s menus needs the \"${orgPermissionLabel('site.edit')}\" permission on this site — ask an organization admin.`\nexport const AI_NAVIGATION_LOCKED_COPY = 'This site is not accepting changes right now.'\nexport const AI_NAVIGATION_NO_LAYOUT_COPY = 'This site has no layout with a header or footer to change. Make one in Layouts first.'\nexport const AI_NAVIGATION_NOTHING_COPY = 'The request named no menu change this could make.'\n\n/** The name the new version carries in the layout's versions. */\nexport const AI_NAVIGATION_VERSION_NAME = 'Menu changes'\n\nconst text = (value: unknown): string => (typeof value === 'string' ? value.trim() : '')\n\n/** A path on the site, as a menu item may link it. */\nconst SITE_PATH = /^\\/(?:[A-Za-z0-9._~!$&'()*+,;=:@%-]+(?:\\/[A-Za-z0-9._~!$&'()*+,;=:@%-]+)*)?\\/?(?:[?#][^\\s]*)?$/\nconst SCREEN_ID = /^[A-Za-z0-9_-]{1,128}$/\n\nfunction buildMadeOf(inputs: Readonly<Record<string, unknown>> | null | undefined): AiBuildMade {\n const raw = (inputs?.[AI_BUILD_MADE_INPUT] ?? {}) as Partial<Record<'pages' | 'layouts', unknown>>\n const rows = (value: unknown, name: 'title' | 'name') =>\n (Array.isArray(value) ? value : []).flatMap((entry) => {\n const row = (entry ?? {}) as Record<string, unknown>\n return text(row[name]) && SCREEN_ID.test(text(row['id'])) ? [{ [name]: text(row[name]), id: text(row['id']) }] : []\n })\n return { pages: rows(raw.pages, 'title') as AiBuildMade['pages'], layouts: rows(raw.layouts, 'name') as AiBuildMade['layouts'] }\n}\n\n/** The site's pages a menu may link, by id, with the layout each renders inside. */\nexport interface AiNavigationSitePage {\n id: string\n title: string\n slug: string\n template: boolean\n layoutId: string | null\n}\n\n/** The site's pages: not deleted, not an email design. */\nexport async function readAiNavigationSitePages(firestore: Firestore, hostId: string): Promise<AiNavigationSitePage[]> {\n const snapshot = await firestore.collection('hosts').doc(hostId).collection('screens').select('displayName', 'slug', 'kind', 'deletedAt', 'layoutId').get()\n return snapshot.docs.flatMap((doc) => {\n const kind = doc.get('kind')\n if (doc.get('deletedAt') || (kind && kind !== 'template')) return []\n const layoutId = doc.get('layoutId')\n return [\n {\n id: doc.id,\n slug: String(doc.get('slug') ?? ''),\n title: String(doc.get('displayName') ?? ''),\n template: kind === 'template',\n layoutId: typeof layoutId === 'string' && layoutId ? layoutId : null,\n },\n ]\n })\n}\n\n/**\n * What a destination in an item's words links (AGL-3728): a page this build\n * made (`new:<title>`), one of the site's pages by id (`page:<id>` or the bare\n * id), a path on the site, or an https address; `null` for anything else.\n */\nexport function aiNavigationTarget(\n destination: string,\n context: { pages: readonly Pick<AiNavigationSitePage, 'id' | 'template'>[]; made: AiBuildMade },\n): AiMenuTarget | null {\n const value = destination.trim()\n const pageIds = new Set(context.pages.filter((page) => !page.template).map((page) => page.id))\n if (isAiPlanNewRef(value as string | null)) {\n const title = value.slice(value.indexOf(':') + 1).trim().toLowerCase()\n const made = context.made.pages.find((page) => page.title.trim().toLowerCase() === title)\n // A page this build was to make and did not is no destination.\n return made && pageIds.has(made.id) ? { screenId: made.id } : null\n }\n const id = value.replace(/^page:/i, '')\n if (pageIds.has(id)) return { screenId: id }\n if (value.startsWith('/') && !value.startsWith('//') && SITE_PATH.test(value)) return { href: value }\n if (/^https:\\/\\/[^\\s/?#]+\\.[^\\s/?#]+(?:[/?#]\\S*)?$/i.test(value)) return { href: value }\n return null\n}\n\n/** The layout an item changes: the one it names, else the one the site's pages render inside. */\nexport function aiNavigationLayoutOf(\n named: string,\n context: { pages: readonly AiNavigationSitePage[]; made: AiBuildMade },\n): string | null {\n if (isAiPlanNewRef(named as string | null)) {\n const name = named.slice(named.indexOf(':') + 1).trim().toLowerCase()\n return context.made.layouts.find((layout) => layout.name.trim().toLowerCase() === name)?.id ?? null\n }\n if (SCREEN_ID.test(named)) return named\n return aiBuildStoreLayoutOf(null, context.pages)\n}\n\n/** Why this member may not change this site's menus now, or `null`. */\nexport async function aiNavigationAccessRefusal(\n input: { host: FirebaseFirestore.DocumentSnapshot; uid: string; org: object | null },\n deps: { lockdown?: typeof getLockdownVerdict } = {},\n): Promise<string | null> {\n if (!holdsSiteKey(input.host, input.uid, 'site.edit')) return AI_NAVIGATION_ROLE_COPY\n const locked = await (deps.lockdown ?? getLockdownVerdict)({\n staff: false,\n uid: input.uid,\n org: input.org as never,\n host: input.host.data() as never,\n intent: 'write',\n })\n return locked ? AI_NAVIGATION_LOCKED_COPY : null\n}\n\n/** The new version's output note: what was done, what was not, and that it waits to be published. */\nexport function aiNavigationOutputNote(done: readonly string[], notDone: readonly string[]): string {\n const did = done.length ? `${done.join(' ')} ` : ''\n const not = notDone.length ? ` Not done: ${notDone.join(' ')}` : ''\n return `${did}Saved as a new version of the layout beside the one visitors see: open it to review it, and publish it when it is ready.${not}`.slice(0, 1_000)\n}\n\nexport type AiNavigationWrite =\n | { ok: true; versionId: string; done: string[]; notDone: string[] }\n | { ok: false; error: string }\n\n/**\n * The change written, in one transaction that reads the layout as it is\n * stored NOW: it still exists, its version is still there, the member still\n * holds a write role, and the plan still keeps version history. The change is\n * applied again to that version's nodes, so a save made since is kept.\n */\nexport async function writeAiNavigation(\n firestore: Firestore,\n input: {\n hostId: string\n layoutId: string\n sourceVersionId: string\n versionId: string\n menus: readonly AiMenuName[]\n changes: AiMenuChanges\n uid: string\n aiJobId: string\n versioning: boolean\n now: Date\n },\n): Promise<AiNavigationWrite> {\n const hostRef = firestore.collection('hosts').doc(input.hostId)\n const docRef = hostRef.collection('layouts').doc(input.layoutId)\n const sourceRef = docRef.collection('versions').doc(input.sourceVersionId)\n const newRef = docRef.collection('versions').doc(input.versionId)\n return firestore.runTransaction(async (tx): Promise<AiNavigationWrite> => {\n const [host, doc, source, already] = await Promise.all([tx.get(hostRef), tx.get(docRef), tx.get(sourceRef), tx.get(newRef)])\n if (already.exists) return { ok: true, versionId: input.versionId, done: [], notDone: [] }\n if (!doc.exists || doc.get('deletedAt') != null) return { ok: false, error: AI_EDIT_GONE_COPY }\n if (!source.exists) return { ok: false, error: AI_EDIT_VERSION_GONE_COPY }\n if (!holdsSiteKey(host, input.uid, 'site.edit')) return { ok: false, error: AI_NAVIGATION_ROLE_COPY }\n const pointer = text(doc.get('versionId')) || null\n const placement = aiEditPlacement({\n versioning: input.versioning,\n served: true,\n sourceIsPointer: pointer === input.sourceVersionId,\n sourceScheduled: false,\n })\n if (placement === 'refused') return { ok: false, error: AI_EDIT_NEEDS_VERSIONING_COPY }\n const nodes = decodeStoredNodes<Record<string, unknown>>(source.get('nodes'))\n if (!nodes) return { ok: false, error: AI_EDIT_NO_NODES_COPY }\n const applied = aiLayoutWithMenuChanges(nodes, input.menus, input.changes)\n if (!applied.nodes) return { ok: false, error: applied.notDone.join(' ') || AI_NAVIGATION_NOTHING_COPY }\n const packed = encodeStoredNodes(applied.nodes as never)\n if (!packed) return { ok: false, error: AI_EDIT_NO_NODES_COPY }\n // A copy of the version it started from — everything the editor stored —\n // with the changed menus, as the versions route copies one.\n const copy = { ...(source.data() as Record<string, unknown>) }\n delete copy['aiEditJobId']\n tx.create(newRef, {\n ...copy,\n nodes: Buffer.from(packed),\n displayName: AI_NAVIGATION_VERSION_NAME,\n createdAt: input.now,\n updatedAt: input.now,\n createdBy: input.uid,\n aiJobId: input.aiJobId,\n })\n return { ok: true, versionId: input.versionId, done: applied.done, notDone: applied.notDone }\n })\n}\n\nexport interface AiJobNavigationStepDeps {\n readTarget?: typeof readAiEditTarget\n readPages?: typeof readAiNavigationSitePages\n write?: typeof writeAiNavigation\n lockdown?: typeof getLockdownVerdict\n}\n\n/** What the step decided before writing: the layout, its version, the change and what was read away. */\ninterface AiNavigationPlan {\n layoutId: string\n menus: AiMenuName[]\n changes: AiMenuChanges\n problems: string[]\n}\n\nasync function navigationPlanOf(\n firestore: Firestore,\n job: Pick<AiJob, 'hostId' | 'inputs'>,\n readPages: typeof readAiNavigationSitePages,\n): Promise<AiNavigationPlan | string> {\n if (!job.hostId) return AI_NAVIGATION_NO_SITE_COPY\n const inputs = job.inputs ?? {}\n const pages = await readPages(firestore, job.hostId)\n const made = buildMadeOf(inputs)\n const layoutId = aiNavigationLayoutOf(text(inputs['layout']), { pages, made })\n if (!layoutId) return AI_NAVIGATION_NO_LAYOUT_COPY\n const read = aiMenuChangesOf(inputs, (destination) => aiNavigationTarget(destination, { pages, made }))\n if (aiMenuChangesEmpty(read.changes)) return [AI_NAVIGATION_NOTHING_COPY, ...read.problems].join(' ')\n return { layoutId, menus: aiMenusOf(inputs['menu']), changes: read.changes, problems: read.problems }\n}\n\nexport function createAiJobNavigationStep(deps: AiJobNavigationStepDeps = {}): AiJobStepRunner {\n const readTarget = deps.readTarget ?? readAiEditTarget\n const readPages = deps.readPages ?? readAiNavigationSitePages\n const write = deps.write ?? writeAiNavigation\n return async ({ job, now, firestore, org, modelFor }): Promise<AiJobStepOutcome> => {\n // No model is asked: the outcome records the edit step's at no cost.\n const model = modelFor?.('job.edit') ?? aiJobEditModel()\n const unspent = (failure: string): AiJobStepOutcome => ({ ...aiUnspentOutcome(model), failure })\n const hostId = job.hostId\n if (!hostId) return unspent(AI_NAVIGATION_NO_SITE_COPY)\n const plan = await navigationPlanOf(firestore, job, readPages)\n if (typeof plan === 'string') return unspent(plan)\n const versionId = aiEditVersionId(job)\n const read = await readTarget(firestore, {\n orgId: job.orgId,\n hostId,\n inputs: { target: plan.layoutId, targetKind: 'layout', versionId: null, newVersion: false },\n jobId: versionId,\n })\n if ('error' in read) return unspent(read.error)\n const output = (done: readonly string[], notDone: readonly string[]): AiJobOutput => ({\n resource: 'layout',\n id: read.id,\n versionId,\n hostId,\n hostSubdomain: read.hostSubdomain,\n label: read.name,\n note: aiNavigationOutputNote(done, notDone),\n })\n if (read.written) return aiUnspentOutcome(model, { outputs: [output([], [])] })\n const orgData = org ?? ((await firestore.collection('orgs').doc(job.orgId).get()).data() ?? null)\n const host = await firestore.collection('hosts').doc(hostId).get()\n const refused = await aiNavigationAccessRefusal({ host, uid: job.createdBy, org: orgData }, { lockdown: deps.lockdown })\n if (refused) return aiUnspentOutcome(model, { review: aiLimitReview(refused) })\n const versioning = checkEntitlement(orgData as never, 'versioning')\n const placement = aiEditPlacement({\n versioning,\n served: true,\n sourceIsPointer: read.sourceVersionId === read.pointerVersionId,\n sourceScheduled: read.scheduledVersionId === read.sourceVersionId,\n })\n if (placement === 'refused') return aiUnspentOutcome(model, { review: aiLimitReview(AI_EDIT_NEEDS_VERSIONING_COPY) })\n const written = await write(firestore, {\n hostId,\n layoutId: read.id,\n sourceVersionId: read.sourceVersionId,\n versionId,\n menus: plan.menus,\n changes: plan.changes,\n uid: job.createdBy,\n aiJobId: aiOriginJobId(job),\n versioning,\n now,\n })\n if (written.ok === false) return unspent(written.error)\n return aiUnspentOutcome(model, { outputs: [output(written.done, [...written.notDone, ...plan.problems])] })\n }\n}\n\nexport const runAiJobNavigationStep = createAiJobNavigationStep()\n\n// ── Admission ─────────────────────────────────────────────────────────────\n\nexport interface AiNavigationJobAdmissionDeps {\n ownerOf?: typeof resolveOrgIdForHost\n readTarget?: typeof readAiEditTarget\n readPages?: typeof readAiNavigationSitePages\n lockdown?: typeof getLockdownVerdict\n}\n\n/**\n * A navigation change is admitted on a site of the job's own org, for a\n * member who may write its content now, where the site has a layout and the\n * change has somewhere to land that no visitor sees on this plan. Refused in\n * those words before anything exists.\n */\nexport function createAiNavigationJobAdmission(deps: AiNavigationJobAdmissionDeps = {}): AiJobAdmission {\n const ownerOf = deps.ownerOf ?? resolveOrgIdForHost\n const readTarget = deps.readTarget ?? readAiEditTarget\n const readPages = deps.readPages ?? readAiNavigationSitePages\n return async (context): Promise<AiJobAdmissionRefusal | null> => {\n if (!context.hostId) return { status: 400, error: AI_NAVIGATION_NO_SITE_COPY }\n const owner = await ownerOf(context.hostId)\n if (!owner || owner !== context.orgId) return { status: 404, error: 'Unknown site' }\n const host = await context.firestore.collection('hosts').doc(context.hostId).get()\n const refused = await aiNavigationAccessRefusal({ host, uid: context.uid ?? '', org: context.org }, { lockdown: deps.lockdown })\n if (refused) return { status: 403, error: refused }\n const plan = await navigationPlanOf(context.firestore as Firestore, { hostId: context.hostId, inputs: context.inputs as Record<string, unknown> }, readPages)\n if (typeof plan === 'string') return { status: 400, error: plan }\n const read = await readTarget(context.firestore, {\n orgId: context.orgId,\n hostId: context.hostId,\n inputs: { target: plan.layoutId, targetKind: 'layout', versionId: null, newVersion: false },\n jobId: null,\n })\n if ('error' in read) return read\n const placement = aiEditPlacement({\n versioning: checkEntitlement(context.org as never, 'versioning'),\n served: true,\n sourceIsPointer: read.sourceVersionId === read.pointerVersionId,\n sourceScheduled: read.scheduledVersionId === read.sourceVersionId,\n })\n return placement === 'refused' ? { status: 403, error: AI_EDIT_NEEDS_VERSIONING_COPY } : null\n }\n}\n\nexport const aiNavigationJobAdmission = createAiNavigationJobAdmission()\n\n/** Registers the navigation step and its door (AGL-3728). */\nexport function registerAiNavigationJob(): void {\n registerAiJobStep(AI_NAVIGATION_KIND, runAiJobNavigationStep, { minimumMs: AI_JOB_NAVIGATION_STEP_MINIMUM_MS })\n registerAiJobAdmission(AI_NAVIGATION_KIND, aiNavigationJobAdmission)\n}\n"],"names":["checkEntitlement","holdsSiteKey","orgPermissionLabel","decodeStoredNodes","encodeStoredNodes","getLockdownVerdict","resolveOrgIdForHost","AI_EDIT_GONE_COPY","AI_EDIT_NEEDS_VERSIONING_COPY","AI_EDIT_NO_NODES_COPY","AI_EDIT_VERSION_GONE_COPY","isAiPlanNewRef","AI_BUILD_MADE_INPUT","aiBuildStoreLayoutOf","aiLayoutWithMenuChanges","aiMenuChangesEmpty","aiMenuChangesOf","aiMenusOf","registerAiJobAdmission","aiOriginJobId","aiEditPlacement","aiEditVersionId","aiJobEditModel","readAiEditTarget","aiLimitReview","aiUnspentOutcome","registerAiJobStep","AI_NAVIGATION_KIND","AI_JOB_NAVIGATION_STEP_MINIMUM_MS","AI_NAVIGATION_NO_SITE_COPY","AI_NAVIGATION_ROLE_COPY","AI_NAVIGATION_LOCKED_COPY","AI_NAVIGATION_NO_LAYOUT_COPY","AI_NAVIGATION_NOTHING_COPY","AI_NAVIGATION_VERSION_NAME","text","value","trim","SITE_PATH","SCREEN_ID","buildMadeOf","inputs","raw","rows","name","Array","isArray","flatMap","entry","row","test","id","pages","layouts","readAiNavigationSitePages","firestore","hostId","snapshot","collection","doc","select","get","docs","kind","layoutId","slug","String","title","template","aiNavigationTarget","destination","context","pageIds","Set","filter","page","map","slice","indexOf","toLowerCase","made","find","has","screenId","replace","startsWith","href","aiNavigationLayoutOf","named","layout","aiNavigationAccessRefusal","input","deps","host","uid","locked","lockdown","staff","org","data","intent","aiNavigationOutputNote","done","notDone","did","length","join","not","writeAiNavigation","hostRef","docRef","sourceRef","sourceVersionId","newRef","versionId","runTransaction","tx","source","already","Promise","all","exists","ok","error","pointer","placement","versioning","served","sourceIsPointer","sourceScheduled","nodes","applied","menus","changes","packed","copy","create","Buffer","from","displayName","createdAt","now","updatedAt","createdBy","aiJobId","navigationPlanOf","job","readPages","read","problems","createAiJobNavigationStep","readTarget","write","modelFor","model","unspent","failure","plan","orgId","target","targetKind","newVersion","jobId","output","resource","hostSubdomain","label","note","written","outputs","orgData","refused","review","pointerVersionId","scheduledVersionId","runAiJobNavigationStep","createAiNavigationJobAdmission","ownerOf","status","owner","aiNavigationJobAdmission","registerAiNavigationJob","minimumMs"],"mappings":"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"}
@@ -23,13 +23,17 @@
23
23
  * — against the stored document instead of an open canvas, and the change is
24
24
  * saved where no visitor sees it:
25
25
  *
26
- * - on a plan with version history, as a NEW version beside the one it
27
- * started from, which becomes the page's current version only when the
28
- * page is not published (no visitor reaches it); a published page's and a
29
- * layout's current version never move;
30
- * - on a plan without it, in place, and only on a version no visitor
31
- * reaches — an unpublished page's, or a version that is not the current
32
- * one. Anything else is refused, before anything is spent.
26
+ * - by default, on every plan, into the WORKING DRAFT of the version it
27
+ * started from (AGL-4116) — the draft the Besigner saves to as an author
28
+ * works and offers with Open draft, so the author reviews the change and
29
+ * publishes it there exactly as they would their own. A draft the author
30
+ * already has is built on, never replaced;
31
+ * - when the request asks for a new version and the plan keeps version
32
+ * history, as a NEW version beside the one it started from, which becomes
33
+ * the page's current version only when the page is not published; a
34
+ * published page's and a layout's current version never move. Asked for
35
+ * on a plan without version history, the change goes into the draft and
36
+ * the output says why.
33
37
  *
34
38
  * Pure: the inputs, the sentences a person reads, and the outline the model
35
39
  * is shown. The runner is `jobs/ai-job-edit-step.ts`.
@@ -44,6 +48,8 @@ export interface AiEditJobInputs {
44
48
  targetKind: AiEditTargetKind | null;
45
49
  /** The version to start from; the document's current version when absent. */
46
50
  versionId: string | null;
51
+ /** The request asked for a new version; otherwise the change goes into the draft. */
52
+ newVersion: boolean;
47
53
  }
48
54
  export declare const AI_EDIT_TARGET_REQUIRED_COPY = "Name the page or layout to change.";
49
55
  export declare const AI_EDIT_TARGET_KIND_COPY = "inputs.targetKind must be screen or layout";
@@ -78,13 +84,22 @@ export type AiEditPlacement =
78
84
  'new-current'
79
85
  /** A new version beside the one it started from, which is unchanged. */
80
86
  | 'new-beside'
81
- /** The version it started from, changed in place: one no visitor reaches. */
82
- | 'in-place';
87
+ /** The version it started from, changed in place: one no visitor reaches. Written before AGL-4116. */
88
+ | 'in-place'
89
+ /** The working draft of the version it started from, which the Besigner offers with Open draft. */
90
+ | 'draft';
91
+ /**
92
+ * Why a change asked for as a new version went into the draft: the plan
93
+ * keeps no version history. The versions route's own words for the plan.
94
+ */
95
+ export declare const AI_EDIT_NO_VERSIONS_DRAFT_COPY = "A new version needs version history, which requires a Pro plan, so the change went into the draft instead.";
83
96
  /** The output's note: where the change is, that nothing visitors see moved, and what was left out. */
84
97
  export declare function aiEditOutputNote(input: {
85
98
  kind: AiEditTargetKind;
86
99
  placement: AiEditPlacement;
87
100
  leftOut: readonly string[];
101
+ /** A new version was asked for on a plan without version history. */
102
+ versionsUnavailable?: boolean;
88
103
  }): string;
89
104
  /** What the output says of search fields a published page keeps on the page itself. */
90
105
  export declare const AI_EDIT_SEO_LEFT_OUT = "the search title and description, which a published page uses as soon as they are saved \u2014 change them in Page properties";
@@ -92,15 +107,17 @@ export declare const AI_EDIT_SEO_LEFT_OUT = "the search title and description, w
92
107
  export declare const AI_EDIT_COMPONENT_LEFT_OUT = "saving part of the page as a reusable component, which is done in the editor";
93
108
  /**
94
109
  * The stored document as the outline the edit rung's protocol reads
95
- * (`parseAssistEditContext` holds it to its limits): the document root, then
96
- * every element level by level — each after its parent, so every ancestor
97
- * chain the check walks is whole — until the rung's cap.
110
+ * (`parseAssistEditContext` with `whole` holds it to its limits): the
111
+ * document root, then every element in the order it reads on the page — each
112
+ * after its parent, so every ancestor chain the check walks is whole — with
113
+ * its settings and its styles, up to the whole-document cap.
98
114
  *
99
115
  * In the editor, an outline is the neighborhood of what the author selected.
100
- * A job has no selection, so it is the TOP of the document, read breadth
101
- * first: every section, and as deep inside each as the cap reaches. Each
102
- * element keeps its real child count and the document its real size, so the
103
- * prompt can say how much the outline leaves out. `null` without a root.
116
+ * A job has no selection and nobody to ask, so it is the WHOLE document: a
117
+ * request about a picture five levels down is about an element the model can
118
+ * see, and a restyle starts from the styles the element has. Each element
119
+ * keeps its real child count and the document its real size, so the prompt
120
+ * can say what a document past the cap leaves out. `null` without a root.
104
121
  */
105
122
  export declare function aiEditJobOutline(nodes: Readonly<Record<string, unknown>>): {
106
123
  selectedId: null;