@aglyn/plugins-ai 1.0.0-beta.272 → 1.0.0-beta.274
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +17 -17
- package/src/lib/components/ai-site-start-card.component.js +237 -234
- package/src/lib/components/ai-site-start-card.component.js.map +1 -1
- package/src/lib/jobs/ai-empty-site-sweep.d.ts +12 -1
- package/src/lib/jobs/ai-empty-site-sweep.js +15 -3
- package/src/lib/jobs/ai-empty-site-sweep.js.map +1 -1
- package/src/lib/jobs/ai-job-page-step.js +21 -2
- package/src/lib/jobs/ai-job-page-step.js.map +1 -1
- package/src/lib/jobs/ai-job-seo-step.js +8 -4
- package/src/lib/jobs/ai-job-seo-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-datasets.d.ts +39 -1
- package/src/lib/jobs/ai-job-site-datasets.js +111 -15
- package/src/lib/jobs/ai-job-site-datasets.js.map +1 -1
- package/src/lib/jobs/ai-job-site-step.d.ts +12 -3
- package/src/lib/jobs/ai-job-site-step.js +85 -11
- package/src/lib/jobs/ai-job-site-step.js.map +1 -1
- package/src/lib/jobs/ai-job-text-step.d.ts +8 -0
- package/src/lib/jobs/ai-job-text-step.js.map +1 -1
- package/src/lib/jobs/ai-jobs.d.ts +3 -0
- package/src/lib/jobs/ai-jobs.js +4 -0
- package/src/lib/jobs/ai-jobs.js.map +1 -1
- package/src/lib/jobs/ai-layout-owner-photos.js +4 -4
- package/src/lib/jobs/ai-layout-owner-photos.js.map +1 -1
- package/src/lib/jobs/ai-layout-stock-photos.d.ts +35 -0
- package/src/lib/jobs/ai-layout-stock-photos.js +114 -43
- package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
- package/src/lib/jobs/ai-site-late-photos.d.ts +143 -0
- package/src/lib/jobs/ai-site-late-photos.js +486 -0
- package/src/lib/jobs/ai-site-late-photos.js.map +1 -0
- package/src/lib/jobs/ai-site-publish.d.ts +18 -0
- package/src/lib/jobs/ai-site-publish.js +123 -5
- package/src/lib/jobs/ai-site-publish.js.map +1 -1
- package/src/lib/jobs/fixtures/ai-site-late-photos-laurel-fork.d.ts +83 -0
- package/src/lib/jobs/fixtures/ai-site-late-photos-laurel-fork.js +1553 -0
- package/src/lib/jobs/fixtures/ai-site-late-photos-laurel-fork.js.map +1 -0
- package/src/lib/layout-language/ai-layout-pictures.d.ts +50 -13
- package/src/lib/layout-language/ai-layout-pictures.js +81 -19
- package/src/lib/layout-language/ai-layout-pictures.js.map +1 -1
- package/src/lib/model/ai-jobs.types.d.ts +10 -0
- package/src/lib/model/ai-jobs.types.js.map +1 -1
- package/src/lib/model/ai-site-photos.d.ts +119 -0
- package/src/lib/model/ai-site-photos.js +128 -0
- package/src/lib/model/ai-site-photos.js.map +1 -0
- package/src/lib/runtime/ai-doctrine-validators.d.ts +3 -1
- package/src/lib/runtime/ai-doctrine-validators.js +3 -1
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/seo-fields.d.ts +29 -4
- package/src/lib/runtime/seo-fields.js +58 -11
- package/src/lib/runtime/seo-fields.js.map +1 -1
- package/src/lib/tools/ai-seo-tool.d.ts +25 -1
- package/src/lib/tools/ai-seo-tool.js +61 -0
- package/src/lib/tools/ai-seo-tool.js.map +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-jobs.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\n/**\n * `FieldValue` straight from the SDK rather than through `./firebase-admin`,\n * for the reason `assist-usage.ts` gives: the default-app initialization\n * would ride into every unit test that touches a job, and the statics need\n * no app at all.\n */\nimport { FieldValue } from 'firebase-admin/firestore'\nimport { createResourceUid } from '@aglyn/aglyn/app-utils/create-resource-uid'\nimport { checkEntitlement } from '@aglyn/aglyn/server'\nimport {\n assistCreditsFromUsd,\n assistFreeTasteRefusalText,\n assistHardCapRefusalText,\n assistRefusedByHardCap,\n} from '../usage/assist-credits'\nimport {\n AI_JOB_PLAN_STEP,\n AI_JOB_TERMINAL_STATUSES,\n type AiJob,\n type AiJobApplied,\n type AiJobCancelRequest,\n type AiJobInputRequest,\n type AiJobItemLedger,\n type AiJobKind,\n type AiJobOutput,\n type AiJobPlan,\n type AiJobReview,\n type AiJobSitePublish,\n type AiJobStatus,\n type AiJobStep,\n type AiJobStepTokens,\n type AiJobSummary,\n} from '../model/ai-jobs.types'\nimport { aiJobInputProblems, aiPlanWithInput } from '../model/ai-job-input'\nimport type { AiJobCreditsConfirmation } from '../model/ai-credit-estimate'\nimport type { AglynOrgBilling } from '@aglyn/aglyn/foundation/definitions/org-billing.types'\nimport { resolveEffectivePlan } from '@aglyn/aglyn/app-utils/plan-entitlements'\nimport { aiOverageReservationRefusal } from '../billing/ai-overage-gate'\nimport { aiAllotmentRefusalText } from '../model/ai-allotments'\nimport { AI_OFF_FOR_SITE_COPY, isAiOffForSite } from '../model/ai-site-switch'\nimport { resolveAiModelChoice } from '../providers/model-choice'\nimport { aiOutputTargetType } from '../activity/ai-activity-actions'\nimport {\n logAiJobCanceled,\n logAiJobCreated,\n logAiJobNeedsInput,\n logAiJobOutput,\n type AiActivityActor,\n} from '../activity/ai-activity'\nimport {\n AI_UPSTREAM_FAILURE_COPY,\n AiUpstreamError,\n type AiEffort,\n type AiUsage,\n} from '../runtime/ai-runtime'\nimport { recordUserAiRefusal } from '../usage/ai-usage-by-user'\nimport { AI_JOB_INLINE_BUDGET_MS, AI_JOB_SWEEP_BUDGET_MS } from './ai-job-budget'\nimport { aiMintJobDraftIds } from './ai-job-draft-ids'\nimport {\n AI_JOB_TEXT_STEP_MINIMUM_MS,\n runAiJobTextStep,\n type AiJobStepOutcome,\n type AiJobStepRunner,\n} from './ai-job-text-step'\nimport { AI_JOB_SEO_STEP_MINIMUM_MS } from './ai-job-seo-budget'\nimport { AI_JOB_THEME_STEP_MINIMUM_MS } from './ai-job-theme-budget'\nimport {\n assistExchangeExpiry,\n assistUsageDay,\n assistUsageMonth,\n recordAssistCost,\n releaseAssistMessage,\n reserveAssistMessage,\n type AssistReservation,\n} from '../usage/assist-usage'\nimport {\n aiJobItemRefundKey,\n aiJobRefundCredits,\n refundJobCredits,\n type AiJobRefundReason,\n} from '../usage/assist-job-refund'\nimport {\n aiBuildSettlement,\n aiBuildUnits,\n aiBuildRetryLedger,\n aiBuildCreditEstimate,\n aiLedgerUnits,\n aiJobCreditEstimate,\n} from '../model/ai-build-job'\nimport type { AiJobItemOutcome } from './ai-job-text-step'\nimport { freeAssistAccount, type FreeAssistAccount } from '../usage/assist-free-taste'\nimport { aiJobAutoConfirms } from './ai-job-auto-confirm'\nimport {\n AI_SITE_GUIDED_BUILD_FAILED_COPY,\n aiCustomerSafeCopy,\n aiCustomerSafeItem,\n aiCustomerSafeReview,\n} from '../model/ai-job-failure-copy'\nimport { aiSiteStartInputsOf } from '../model/ai-site-start'\nimport { aiSiteImportWire, type AiSiteImportState } from '../model/ai-site-import'\nimport { addAdminAudit } from '@aglyn/tenant-data-admin/server/admin-audit-write'\nimport {\n AI_JOB_CANCELED_ITEM_NOTE,\n AiJobCanceledError,\n aiJobCancelPending,\n isAiJobCanceledError,\n watchAiJobCancel,\n} from './ai-job-cancel'\nimport { clearAiEmptySiteSweep, scheduleAiEmptySiteSweep } from './ai-empty-site-sweep'\n\n/**\n * AI generation jobs — the Firestore state machine (AGL-2904).\n *\n * One document per job under the org, written only by this module through\n * the Admin SDK; the rules let a member READ their org's jobs and nobody\n * write them. The console route creates a job and runs its first step\n * inline when it can; the AI jobs beat, a console route of its own, claims\n * and runs whatever is queued, across every org, inside a wall-clock budget\n * (`ai-jobs-beat.ts`). Both go through\n * `runAiJobStep`, so there is exactly one place a step is claimed, metered,\n * run and recorded.\n *\n * ── Credits, per step ────────────────────────────────────────────────────\n *\n * Every step is an assist message to the meter: `reserveAssistMessage`\n * BEFORE the provider is called — the same read-and-increment transaction\n * the chat route takes, so a job cannot slip past the org's monthly ceiling\n * by being asynchronous — and `recordAssistCost` after, at the serving\n * model's rates, so the usage rollup and the invoice see a job's tokens\n * exactly as they see a chat turn's. The credit figure on the job is that\n * cost at the plan's credit rate, rounded up like every other credit.\n *\n * A refused reservation is NOT a failure. The org is out of credits, over\n * its cap, or has switched overage off — all things a member can change —\n * so the job parks as `needs_input` with the refusal in customer-safe words\n * and the beat tries it again once it has rested. `failed` is reserved for\n * a step that cannot succeed by waiting.\n *\n * ── What a step may not do ───────────────────────────────────────────────\n *\n * A step runner writes drafts and returns. It does not touch the job\n * document, the meter or the lease: the machine owns those, so a runner\n * written next month cannot forget the reservation or record itself done\n * while the provider is still generating.\n */\n\nexport const AI_JOBS_COLLECTION = 'aiJobs'\n\n/**\n * How long a claim holds the step: longer than the longest any holder can\n * still be running it, which is the beat route's 300 s function ceiling\n * (AGL-3026). A lease that ran out under a live holder would let the next\n * beat claim the same step and call the provider for it a second time, and\n * the beat fires every minute while a sweep may run for most of five, so\n * this is what keeps overlapping beats apart. A step abandoned by a process\n * that died is recovered once it runs out, by the first beat after that.\n */\nexport const AI_JOB_LEASE_MS = 330_000\n\n/** A step handed back by a retryable provider failure this many times fails. */\nexport const AI_JOB_STEP_MAX_ATTEMPTS = 3\n\n/**\n * The most passes one step may take by asking to continue (AGL-2910), for a\n * kind that registers no bound of its own. A site audit's passes are its\n * units of generated work; this bounds a runner that never finishes, not a\n * real site.\n */\nexport const AI_JOB_STEP_MAX_PASSES = 40\n\n/**\n * The nominal credit figure shown as held per outstanding step. Not a\n * bound — the reservation and the monthly ceiling are — but the console\n * must show a running job as costing something before its bill is known.\n *\n * NOMINAL, and nothing reads it as more (AGL-3030): the band, the Free\n * taste's account allowance and every refusal are measured on recorded\n * spend, and the one real reservation a step takes is a message, counted\n * when the step is claimed and settled — metered or handed back — before the\n * step is recorded. So a job holds nothing real between steps, and a job\n * parked for a person shows nothing held either: `recordStep` releases the\n * figure when a step parks for review, and `resumeAiJob` holds it again for\n * the steps a person's resume queues.\n */\nexport const AI_JOB_STEP_RESERVE_CREDITS = 50\n\n/**\n * The inline doors' budget and the beat's, declared beside the time a step is\n * planned against (`ai-job-budget.ts`, AGL-3036) so a step's least time and\n * the budgets it has to fit are read in one place.\n */\nexport { AI_JOB_INLINE_BUDGET_MS, AI_JOB_SWEEP_BUDGET_MS }\n\n/** Jobs one beat reads as candidates; the budget usually stops it first. */\nexport const AI_JOB_SWEEP_MAX_JOBS = 25\n\n/** Parked jobs one beat re-queues, on top of the active candidates. */\nexport const AI_JOB_SWEEP_MAX_PARKED = 5\n\n/** How long a `needs_input` job rests before the beat tries its reservation again. */\nexport const AI_JOB_NEEDS_INPUT_RETRY_MS = 60 * 60_000\n\nexport const AI_JOB_NOT_AVAILABLE_COPY =\n 'This kind of AI job is not available yet.'\nexport const AI_JOB_REFUSED_COPY =\n 'The model declined this brief. Try rephrasing it.'\n\ntype Firestore = FirebaseFirestore.Firestore\ntype Transaction = FirebaseFirestore.Transaction\n\n/** A stored instant as the SDK returns it, or as this module wrote it. */\ntype Instant = Date | { toMillis(): number } | number | string | null | undefined\n\nfunction toMillis(value: Instant): number | null {\n if (value == null) return null\n if (value instanceof Date) return value.getTime()\n if (typeof value === 'number') return value\n if (typeof value === 'string') {\n const parsed = Date.parse(value)\n return Number.isFinite(parsed) ? parsed : null\n }\n return typeof value.toMillis === 'function' ? value.toMillis() : null\n}\n\nfunction toIso(value: Instant): string | null {\n const millis = toMillis(value)\n return millis === null ? null : new Date(millis).toISOString()\n}\n\nexport function isAiJobTerminal(status: AiJobStatus): boolean {\n return AI_JOB_TERMINAL_STATUSES.includes(status)\n}\n\n// ── The step runner registry ──────────────────────────────────────────────\n\n// The step a planned kind runs first (AGL-2935), named in the model so the\n// console reads a job's phase off its steps without loading this machine.\nexport { AI_JOB_PLAN_STEP }\n\n/**\n * The kinds that build site structure, and so plan before they generate\n * (AGL-2935): the plan their first step proposes is what their generation\n * step executes, once a person has confirmed it.\n */\nexport const AI_PLANNED_JOB_KINDS: readonly AiJobKind[] = [\n 'page',\n 'site',\n 'component',\n 'layout',\n 'template',\n 'form',\n 'email',\n 'build',\n]\n\nconst stepRunners = new Map<AiJobKind, AiJobStepRunner>()\nconst stepMinimums = new Map<AiJobKind, number>()\nconst stepRunMinimums = new Map<AiJobKind, (job: AiJob) => number>()\nlet planStepRunner: AiJobStepRunner | null = null\n/** The plan step's least time: one step every planned kind shares, so kept by the step, not by a kind. */\nlet planStepMinimumMs = 0\nconst stepPassCaps = new Map<AiJobKind, number>()\n\nexport interface AiJobStepRegistration {\n /**\n * The least time, in milliseconds, one run of the kind's step needs before\n * it starts (AGL-2907): its generation's worst case at the rates\n * `ai-job-budget.ts` assumes — every model call it may make, with the step's\n * own reads and writes (AGL-3036). The beat leaves the step queued rather\n * than start it with less time left, and an inline door leaves it for the\n * beat, because a provider call the caller's budget cuts off is still\n * generated and billed upstream while the meter records nothing.\n *\n * Every generation step the plugin registers declares one (AGL-3035), and a\n * spec over the console surface's registrations fails on a step that does\n * not. Absent, the step starts whenever it is claimed.\n */\n minimumMs?: number\n /**\n * For a step whose runs need different times, the least time the run this\n * job would make next needs (AGL-3035): a page job's pass that builds a\n * layout needs a layout's time, where its section passes need a section's,\n * and a scaffold's pass needs what the unit it hands on needs. Never read as\n * less than `minimumMs`, which is then the least any run of the step needs.\n */\n minimumMsFor?: (job: AiJob) => number\n}\n\n/** Idempotent per kind; last registration wins. */\nexport function registerAiJobStep(\n kind: AiJobKind,\n runner: AiJobStepRunner,\n registration: AiJobStepRegistration = {},\n): void {\n stepRunners.set(kind, runner)\n if (registration.minimumMs && registration.minimumMs > 0) {\n stepMinimums.set(kind, registration.minimumMs)\n } else {\n stepMinimums.delete(kind)\n }\n if (registration.minimumMsFor) {\n stepRunMinimums.set(kind, registration.minimumMsFor)\n } else {\n stepRunMinimums.delete(kind)\n }\n}\n\n/**\n * The least time a step needs before it starts: the plan step's own minimum\n * for the plan step of any kind (AGL-3026), and the kind's registered\n * minimum for the kind's own step — the least any run of it needs.\n */\nexport function aiJobStepMinimumMs(kind: AiJobKind, stepName: string): number {\n return stepName === AI_JOB_PLAN_STEP ? planStepMinimumMs : (stepMinimums.get(kind) ?? 0)\n}\n\n/**\n * The least time the next run of a job's own step needs (AGL-3035): what the\n * kind registered, or more where the kind says this job's next run needs\n * more. A step that delegates a unit to another kind — a page job's creation,\n * a scaffold's page — asks this of the job it derives for the unit.\n */\nexport function aiJobStepRunMinimumMs(job: AiJob): number {\n const least = stepMinimums.get(job.kind) ?? 0\n const forRun = stepRunMinimums.get(job.kind)\n return forRun ? Math.max(least, forRun(job)) : least\n}\n\n/** The least time the job's next step needs; 0 when no step is left to run. */\nexport function aiJobNextStepMinimumMs(job: AiJob): number {\n const index = nextStepIndex(job)\n if (index === -1) return 0\n return job.steps[index].name === AI_JOB_PLAN_STEP ? planStepMinimumMs : aiJobStepRunMinimumMs(job)\n}\n\nexport function aiJobStepRunnerFor(kind: AiJobKind): AiJobStepRunner | null {\n return stepRunners.get(kind) ?? null\n}\n\n/**\n * A kind whose step works through more units than the default bounds\n * (AGL-2911). A scaffold's unit is a whole page and its site is eight of\n * them, where an audit's is one page's listing, so the runaway bound the\n * default gives an audit would cut a real site short. The kind registers its\n * own beside its runner, so the machine keeps no list of kinds and the bound\n * sits with the plan that explains it.\n */\nexport function registerAiJobStepPasses(kind: AiJobKind, maxPasses: number): void {\n stepPassCaps.set(kind, Math.max(1, Math.floor(maxPasses)))\n}\n\n/** The passes a kind's step may take; the default where it registered none. */\nexport function aiJobStepMaxPasses(kind: AiJobKind): number {\n return stepPassCaps.get(kind) ?? AI_JOB_STEP_MAX_PASSES\n}\n\n/**\n * The plan step every planned kind runs first. Registered by its own module\n * (`ai-job-plan-step.ts`), which the plugin's server surface loads, so this\n * machine — and every spec that drives it — never loads the inventory\n * reader and the Admin SDK behind it. `null` unregisters it, minimum and all.\n *\n * `minimumMs` means for the plan step what it means for a kind's step\n * (AGL-3026): a plan answered at its ceiling takes longer than an inline\n * door's budget, so the step says so, and both doors leave it for the beat.\n */\nexport function registerAiJobPlanStep(\n runner: AiJobStepRunner | null,\n registration: AiJobStepRegistration = {},\n): void {\n planStepRunner = runner\n planStepMinimumMs =\n runner && registration.minimumMs && registration.minimumMs > 0 ? registration.minimumMs : 0\n}\n\n/**\n * Whether AI generation is paused for a workspace, as the jobs doors read it\n * (AGL-3037). `staff` is a verified staff caller on an inline door; the beat\n * has none.\n */\nexport type AiJobPauseReader = (input: { orgId: string; staff: boolean }) => Promise<boolean>\n\nlet pauseReader: AiJobPauseReader | null = null\n\n/**\n * The reader `runAiJobStep` asks before it claims a step. Registered by its\n * own module (`ai-jobs-pause.ts`), which the plugin's console surface calls,\n * so this machine — and every spec that drives it — never loads the Admin SDK\n * the lockdown reads run on. `null` unregisters it; a machine with no reader\n * runs every job, as a process with no lockdown carrier would.\n */\nexport function registerAiJobPauseReader(reader: AiJobPauseReader | null): void {\n pauseReader = reader\n}\n\n// ── Telling the person (AGL-3593) ──────────────────────────────────────────\n\n/**\n * A change a person is told about: the job's plan waits for them (or a step\n * stopped for their decision), the job finished, it stopped, or it PAUSED\n * because the meter refused its next step (AGL-3660) — out of credits, at a\n * cap — which a person resolves by getting more and resuming the same job.\n */\nexport type AiJobTransition = 'needs-review' | 'done' | 'failed' | 'paused'\n\n/**\n * Why a job failed, for staff only (never shown to the customer): whether\n * the failure was ours — a provider error, a step that produced nothing, a\n * build that delivered nothing — rather than the model declining the brief\n * or the site having AI switched off, and what the runner actually said.\n */\nexport interface AiJobFailureCause {\n ours: boolean\n stepIndex: number | null\n error: string | null\n}\n\nexport type AiJobTransitionListener = (input: {\n job: AiJob\n to: AiJobTransition\n /** Present on `failed` only. */\n failure?: AiJobFailureCause\n}) => Promise<void> | void\n\nlet transitionListener: AiJobTransitionListener | null = null\n\n/**\n * The listener told each time a job ENTERS `needs_review`, `done` or `failed`,\n * or is paused by the meter (`needs_input`, once per reason)\n * — once per entry, from the write that made it, so a stream that re-reads\n * the job or a beat that sees it again tells nobody twice. Registered by its\n * own module (`ai-jobs-notify.ts`) from the console's server surface, as the\n * pause reader is, so this machine never loads the notification fan-out.\n * `null` unregisters it.\n */\nexport function registerAiJobTransitionListener(listener: AiJobTransitionListener | null): void {\n transitionListener = listener\n}\n\n/** Whether a transition listener is registered: what a surface that runs jobs must have. */\nexport function aiJobTransitionListenerRegistered(): boolean {\n return transitionListener !== null\n}\n\n/**\n * A job just ENDED `canceled` or `failed`, in the write the caller made\n * (AGL-3936): a site start that built nothing schedules the removal of the\n * empty site it was for, an hour out (`ai-empty-site-sweep.ts`). Every write\n * that ends a job calls this once, after its transaction commits. Never\n * throws.\n */\nasync function noteAiJobEnded(firestore: Firestore, job: AiJob | null, now: Date): Promise<void> {\n if (!job || (job.status !== 'canceled' && job.status !== 'failed')) return\n await scheduleAiEmptySiteSweep(firestore, job, now)\n}\n\n/** Tells the listener; a listener that throws never fails the write it follows. */\nasync function announceAiJobTransition(\n job: AiJob,\n to: AiJobTransition,\n failure?: AiJobFailureCause,\n): Promise<void> {\n if (!transitionListener) return\n try {\n await transitionListener(failure ? { job, to, failure } : { job, to })\n } catch (error) {\n console.error('ai job transition listener failed', { orgId: job.orgId, jobId: job.$id, to, error })\n }\n}\n\n/** Whether a pause reader is registered: what a surface that runs jobs must have. */\nexport function aiJobPauseReaderRegistered(): boolean {\n return pauseReader !== null\n}\n\n/**\n * The registered reader's answer. A read that throws is not a pause: the\n * lockdown carriers fail open on an unreachable Firestore, and so does this.\n */\nasync function aiJobPausedFor(orgId: string, jobId: string, staff: boolean): Promise<boolean> {\n if (!pauseReader) return false\n try {\n return await pauseReader({ orgId, staff })\n } catch (error) {\n console.error('ai job pause read failed', { orgId, jobId, error })\n return false\n }\n}\n\n/** The runner for one step of one job: the plan step by its name, else the kind's own. */\nexport function aiJobRunnerForStep(kind: AiJobKind, stepName: string): AiJobStepRunner | null {\n return stepName === AI_JOB_PLAN_STEP ? planStepRunner : aiJobStepRunnerFor(kind)\n}\n\n/**\n * The step names a job of this kind carries, in order. A planned kind plans\n * only once something can build the plan: until its generation runner is\n * registered, its job fails fast and free, rather than spending on a plan\n * nothing will execute.\n */\nexport function aiJobStepNames(kind: AiJobKind): string[] {\n if (kind === 'text') return ['draft']\n return AI_PLANNED_JOB_KINDS.includes(kind) && planStepRunner && stepRunners.has(kind)\n ? [AI_JOB_PLAN_STEP, 'generate']\n : ['generate']\n}\n\n/**\n * The text step (AGL-2904). Every kind with no runner refuses until its issue\n * lands. Its least time fits an inline door's budget (AGL-3035), so the doors\n * run a text job where it is asked for.\n */\nregisterAiJobStep('text', runAiJobTextStep, { minimumMs: AI_JOB_TEXT_STEP_MINIMUM_MS })\n\n/**\n * The theme step (AGL-2938), loaded the first time a theme job runs rather\n * than when this module does: it builds the brand themes it measures\n * contrast against and may read a site's logo, and a process that never runs\n * a theme job should pay for neither. Its least time is declared apart from\n * the step (`ai-job-theme-budget.ts`, AGL-3035) for the same reason.\n */\nregisterAiJobStep(\n 'theme',\n async (context) => {\n const { runAiJobThemeStep } = await import('./ai-job-theme-step')\n return runAiJobThemeStep(context)\n },\n { minimumMs: AI_JOB_THEME_STEP_MINIMUM_MS },\n)\n\n/**\n * The SEO step (AGL-2910), loaded the first time an SEO job runs rather than\n * when this module does: it reads pages, versions and layouts, and a process\n * that never runs one should load none of that. Its least time is declared\n * apart from the step (`ai-job-seo-budget.ts`, AGL-3035) for the same reason.\n */\nregisterAiJobStep(\n 'seo',\n async (context) => {\n const { runAiJobSeoStep } = await import('./ai-job-seo-step')\n return runAiJobSeoStep(context)\n },\n { minimumMs: AI_JOB_SEO_STEP_MINIMUM_MS },\n)\n\n// ── Documents ─────────────────────────────────────────────────────────────\n\nfunction jobsCollection(firestore: Firestore, orgId: string) {\n return firestore.collection('orgs').doc(orgId).collection(AI_JOBS_COLLECTION)\n}\n\nfunction jobFrom(\n snapshot: FirebaseFirestore.DocumentSnapshot,\n): AiJob | null {\n if (!snapshot.exists) return null\n const data = snapshot.data() as Omit<AiJob, '$id'>\n return { ...data, $id: snapshot.id }\n}\n\nexport interface CreateAiJobInput {\n orgId: string\n hostId?: string | null\n kind: AiJobKind\n brief: string\n inputs?: Record<string, unknown>\n /** The model the creator picked (AGL-2942); absent or `null` for Auto. */\n model?: string | null\n createdBy: string\n /** The creator's address, for the activity row; the document stores the uid only. */\n createdByEmail?: string | null\n /** A Free start the creator chose to build past what was left (AGL-3722). */\n creditsConfirmed?: AiJobCreditsConfirmation | null\n}\n\n/**\n * A new job, `queued`, with its step plan laid out and nothing run.\n *\n * The activity row is written once the document is, so the feed never names\n * a job that does not exist. The brief's length stands in for the brief,\n * which is the customer's own text and is not logged (AGL-2929).\n */\nexport async function createAiJob(\n firestore: Firestore,\n input: CreateAiJobInput,\n now = new Date(),\n): Promise<AiJob> {\n const brief = input.brief.trim()\n if (!brief) throw new Error('an AI job needs a brief')\n // The drafts the kind always writes are named before any step runs, on the\n // step that writes them, so a run repeated after its write finds them.\n const draftIds = aiMintJobDraftIds(input.kind)\n const steps: AiJobStep[] = aiJobStepNames(input.kind).map((name) => ({\n name,\n status: 'pending',\n startedAt: null,\n endedAt: null,\n creditsSpent: 0,\n error: null,\n attempts: 0,\n ...(draftIds && name !== AI_JOB_PLAN_STEP ? { draftIds } : {}),\n }))\n // A job is a resource the console lists and routes to, named as one.\n const ref = jobsCollection(firestore, input.orgId).doc(createResourceUid())\n const data = {\n orgId: input.orgId,\n hostId: input.hostId ?? null,\n kind: input.kind,\n status: 'queued' as AiJobStatus,\n brief,\n inputs: input.inputs ?? {},\n model: input.model ?? null,\n steps,\n outputs: [] as AiJobOutput[],\n creditsReserved: steps.length * AI_JOB_STEP_RESERVE_CREDITS,\n creditsSpent: 0,\n createdBy: input.createdBy,\n createdAt: now,\n updatedAt: now,\n // The brief is verbatim customer text, so it expires on the same clock\n // as an assist exchange; the TTL policy keys on this field.\n expiresAt: assistExchangeExpiry(now),\n error: null,\n lease: null,\n ...(input.creditsConfirmed ? { creditsConfirmed: input.creditsConfirmed } : {}),\n }\n await ref.set(data)\n // A new job on the site — a guided start begun again — keeps it (AGL-3936).\n await clearAiEmptySiteSweep(firestore, input.hostId)\n await logAiJobCreated(\n input.orgId,\n { uid: input.createdBy, email: input.createdByEmail ?? null },\n {\n jobId: ref.id,\n kind: input.kind,\n briefLength: brief.length,\n hostId: input.hostId ?? null,\n },\n )\n return { ...(data as unknown as Omit<AiJob, '$id'>), $id: ref.id }\n}\n\nexport async function getAiJob(\n firestore: Firestore,\n orgId: string,\n jobId: string,\n): Promise<AiJob | null> {\n return jobFrom(await jobsCollection(firestore, orgId).doc(jobId).get())\n}\n\nexport interface ListAiJobsOptions {\n status?: AiJobStatus\n /**\n * Any of these statuses (AGL-3593): the console's indicator asks for every\n * job not yet settled in one read. Ignored when `status` is given.\n */\n statuses?: readonly AiJobStatus[]\n /**\n * One site's jobs (AGL-3596), for the site's AI jobs page; on the\n * (hostId, createdAt) index. Not combined with a status filter, which no\n * reader asks for and no index answers.\n */\n hostId?: string\n limit?: number\n}\n\n/**\n * Newest first. Needs the (status, createdAt) index when filtered, which\n * answers an `in` over statuses as it answers one status.\n */\nexport async function listAiJobs(\n firestore: Firestore,\n orgId: string,\n options: ListAiJobsOptions = {},\n): Promise<AiJob[]> {\n const limit = Math.min(Math.max(1, Math.floor(options.limit ?? 20)), 100)\n let query: FirebaseFirestore.Query = jobsCollection(firestore, orgId)\n if (options.status) query = query.where('status', '==', options.status)\n else if (options.statuses?.length) query = query.where('status', 'in', [...options.statuses])\n else if (options.hostId) query = query.where('hostId', '==', options.hostId)\n const read = async (from: FirebaseFirestore.Query, count: number) =>\n (await from.orderBy('createdAt', 'desc').limit(count).get()).docs\n .map((doc) => jobFrom(doc))\n .filter((job): job is AiJob => job !== null)\n try {\n return await read(query, limit)\n } catch (error) {\n // A site's list while its (hostId, createdAt) index is still building\n // (AGL-3596): Firestore refuses the query with FAILED_PRECONDITION until\n // the index is ready, which answered the site's AI jobs page with a 500.\n // The org's own newest jobs — the index every org list already uses —\n // filtered to the site in memory stand in, bounded to\n // `AI_JOBS_HOST_FALLBACK_SCAN` jobs, so a site whose jobs are older than\n // that lists fewer until the index is ready rather than none.\n if (!options.hostId || options.status || options.statuses?.length || !isFailedPrecondition(error)) throw error\n console.warn('ai jobs: host index not ready; listing from the org', { orgId, hostId: options.hostId })\n const recent = await read(jobsCollection(firestore, orgId), AI_JOBS_HOST_FALLBACK_SCAN)\n return recent.filter((job) => job.hostId === options.hostId).slice(0, limit)\n }\n}\n\n/** The most of an org's newest jobs a site's list scans while its index builds (AGL-3596). */\nexport const AI_JOBS_HOST_FALLBACK_SCAN = 300\n\n/** Firestore's \"this query needs an index that is not ready\" (gRPC 9). */\nfunction isFailedPrecondition(error: unknown): boolean {\n const code = (error as { code?: unknown } | null)?.code\n return code === 9 || code === 'failed-precondition' || code === 'FAILED_PRECONDITION'\n}\n\n/** The wire form. `running` is the lease, read against `now`. */\nexport function aiJobSummary(job: AiJob, now = new Date()): AiJobSummary {\n const leaseUntil = toMillis(job.lease?.until as Instant)\n return {\n id: job.$id,\n orgId: job.orgId,\n hostId: job.hostId ?? null,\n kind: job.kind,\n status: job.status,\n brief: job.brief,\n batch: typeof job.inputs?.['batchId'] === 'string' ? job.inputs['batchId'] : null,\n steps: (job.steps ?? []).map((step) => ({\n name: step.name,\n status: step.status,\n startedAt: toIso(step.startedAt as Instant),\n endedAt: toIso(step.endedAt as Instant),\n creditsSpent: step.creditsSpent ?? 0,\n error: aiCustomerSafeCopy(step.error ?? null),\n })),\n outputs: job.outputs ?? [],\n creditsReserved: job.creditsReserved ?? 0,\n creditsSpent: job.creditsSpent ?? 0,\n refundedCredits: job.refundedCredits ?? 0,\n createdBy: job.createdBy,\n createdAt: toIso(job.createdAt as Instant) ?? new Date(0).toISOString(),\n updatedAt: toIso(job.updatedAt as Instant) ?? new Date(0).toISOString(),\n // The doctrine's own words never reach a customer (AGL-3596).\n error: aiCustomerSafeCopy(job.error ?? null, { page: job.kind === 'page' }),\n running:\n job.status === 'running' &&\n leaseUntil !== null &&\n leaseUntil > now.getTime(),\n plan: job.plan\n ? {\n ...job.plan,\n labels: job.plan.labels ?? {},\n proposedAt: toIso(job.plan.proposedAt as Instant),\n confirmedAt: toIso(job.plan.confirmedAt as Instant),\n confirmedBy: job.plan.confirmedBy ?? null,\n }\n : null,\n review: job.review ? aiCustomerSafeReview(job.review, { page: job.kind === 'page' }) : null,\n ...(job.status === 'needs_input' && job.input?.fields?.length ? { input: job.input } : {}),\n applied: job.applied\n ? {\n at: toIso(job.applied.at as Instant),\n by: job.applied.by,\n versions: job.applied.versions ?? {},\n staged: job.applied.staged ?? [],\n }\n : null,\n ...(job.sitePublish ? { sitePublish: job.sitePublish } : {}),\n ...(job.kind === 'site' ? { siteInputs: aiSiteStartInputsOf(job.inputs) } : {}),\n ...(aiJobAutoConfirms(job) ? { autoConfirm: true } : {}),\n ...(job.items?.length ? { items: job.items.map(aiCustomerSafeItem) } : {}),\n ...(job.orchestration ? { orchestration: job.orchestration } : {}),\n ...(job.kind === 'build' && job.inputs?.['publish'] === true ? { publishAsked: true } : {}),\n ...(aiJobCancelPending(job) ? { cancelRequested: true } : {}),\n ...(job.siteRemoved ? { siteRemoved: true } : {}),\n // A site import's waves (AGL-3976), without the page text its waves read.\n ...(job.siteImport ? { siteImport: aiSiteImportWire(job.siteImport) } : {}),\n }\n}\n\n// ── The lease ─────────────────────────────────────────────────────────────\n\nexport interface ClaimedAiJobStep {\n job: AiJob\n stepIndex: number\n}\n\nfunction leaseIsLive(job: AiJob, owner: string, nowMs: number): boolean {\n const lease = job.lease\n if (!lease) return false\n if (lease.owner === owner) return false\n const until = toMillis(lease.until as Instant)\n return until !== null && until > nowMs\n}\n\n/** Whether ANY owner's lease on the job is still live: a step is in flight. */\nfunction anyLeaseIsLive(job: AiJob, nowMs: number): boolean {\n const until = toMillis(job.lease?.until as Instant)\n return Boolean(job.lease) && until !== null && until > nowMs\n}\n\n/**\n * The write that ends a job `canceled` (AGL-3616): it holds nothing and no\n * lease, a step left running goes back to `pending` (it did not finish), and\n * every item not yet built is skipped with the cancel's note. What the job\n * built, spent and was given back stays as it is: drafts stay drafts, and\n * credits for what ran stay charged.\n */\nfunction aiJobCanceledPatch(job: AiJob, now: Date, request: AiJobCancelRequest): Record<string, unknown> {\n return {\n status: 'canceled',\n cancelRequested: request,\n creditsReserved: 0,\n lease: null,\n error: null,\n steps: (job.steps ?? []).map((step) =>\n step.status === 'running' ? { ...step, status: 'pending', endedAt: null } : step,\n ),\n ...(job.items?.length ? { items: aiCanceledItems(job.items) } : {}),\n updatedAt: now,\n }\n}\n\nfunction aiCanceledItems(items: readonly AiJobItemLedger[]): AiJobItemLedger[] {\n return items.map((row) =>\n row.status === 'pending' || row.status === 'running'\n ? { ...row, status: 'skipped', note: AI_JOB_CANCELED_ITEM_NOTE }\n : row,\n )\n}\n\n/** The cancel a pending-cancel job carries, or one made now for a job that has none. */\nfunction cancelRequestOf(job: AiJob, now: Date): AiJobCancelRequest {\n return job.cancelRequested ?? aiJobCancelRequest(now, null)\n}\n\n/** A cancel asked now by `by`, as the job stores it (the instant is written as a Date). */\nfunction aiJobCancelRequest(now: Date, by: string | null): AiJobCancelRequest {\n return { at: now as unknown as AiJobCancelRequest['at'], by }\n}\n\nfunction nextStepIndex(job: AiJob): number {\n return (job.steps ?? []).findIndex(\n (step) => step.status === 'pending' || step.status === 'running',\n )\n}\n\n/**\n * The step a claim by `owner` would take, or -1 when there is none to take:\n * the job is terminal or parked, has no step left, or ANOTHER owner's lease\n * has not yet expired.\n */\nfunction claimableStepIndex(job: AiJob, owner: string, nowMs: number): number {\n if (job.status !== 'queued' && job.status !== 'running') return -1\n if (leaseIsLive(job, owner, nowMs)) return -1\n return nextStepIndex(job)\n}\n\n/**\n * Take the lease on the job's next step, in one transaction.\n *\n * Refuses — `null` — when the job is terminal or parked, when it has no\n * step left, or when ANOTHER owner's lease has not yet expired. That last\n * clause is the whole point: the console route and two overlapping beats\n * can all look at one job inside the same minute, and only one of them may\n * spend the reservation for its step. An expired lease is claimable, which\n * is how a step abandoned mid-flight by a frozen process is recovered — the\n * stale lease is released by being taken, not by a sweep of its own.\n */\nexport async function claimNextStep(\n firestore: Firestore,\n orgId: string,\n jobId: string,\n owner: string,\n now = new Date(),\n): Promise<ClaimedAiJobStep | null> {\n const ref = jobsCollection(firestore, orgId).doc(jobId)\n let ended: AiJob | null = null\n const claimed = await firestore.runTransaction<ClaimedAiJobStep | null>(async (tx: Transaction) => {\n ended = null\n const job = jobFrom(await tx.get(ref))\n if (!job) return null\n // A cancel asked while a step ran, whose runner never came back to end\n // it (AGL-3616): the lease ran out, so the job ends here, unclaimed.\n if (aiJobCancelPending(job)) {\n if (!leaseIsLive(job, owner, now.getTime())) {\n const patch = aiJobCanceledPatch(job, now, cancelRequestOf(job, now))\n tx.set(ref, patch, { merge: true })\n ended = { ...job, ...(patch as unknown as Partial<AiJob>) } as AiJob\n }\n return null\n }\n const stepIndex = claimableStepIndex(job, owner, now.getTime())\n if (stepIndex === -1) return null\n const steps = job.steps.map((step, index) =>\n index === stepIndex\n ? {\n ...step,\n status: 'running' as const,\n startedAt: step.startedAt ?? now,\n attempts: (step.attempts ?? 0) + 1,\n }\n : step,\n )\n const lease = { owner, until: new Date(now.getTime() + AI_JOB_LEASE_MS) }\n tx.set(\n ref,\n { status: 'running', steps, lease, updatedAt: now },\n { merge: true },\n )\n return {\n job: {\n ...job,\n status: 'running',\n steps: steps as AiJob['steps'],\n lease: lease as unknown as AiJob['lease'],\n updatedAt: now as unknown as AiJob['updatedAt'],\n },\n stepIndex,\n }\n })\n await noteAiJobEnded(firestore, ended, now)\n return claimed\n}\n\n/**\n * Extend the lease while a long step is still running. `false` when the\n * caller no longer holds it — the step was recovered by someone else, and\n * the caller's result must not overwrite theirs.\n */\nexport async function heartbeatStep(\n firestore: Firestore,\n orgId: string,\n jobId: string,\n owner: string,\n now = new Date(),\n): Promise<boolean> {\n const ref = jobsCollection(firestore, orgId).doc(jobId)\n return firestore.runTransaction(async (tx: Transaction) => {\n const job = jobFrom(await tx.get(ref))\n if (!job || job.status !== 'running' || job.lease?.owner !== owner) {\n return false\n }\n tx.set(\n ref,\n {\n lease: { owner, until: new Date(now.getTime() + AI_JOB_LEASE_MS) },\n updatedAt: now,\n },\n { merge: true },\n )\n return true\n })\n}\n\n/**\n * Leave a job whose workspace's AI is paused where it is (AGL-3037), moved to\n * the back of the beat's queue. Nothing is claimed: its steps, their attempts\n * and its lease stay exactly as they were, so resuming AI runs the job from\n * where it stopped. Only `updatedAt` moves — the beat's queue is ordered by it,\n * and a paused workspace's jobs left at the front would fill every beat's\n * candidates and starve every other workspace's.\n *\n * `null`, writing nothing, for a job a claim would not take either: terminal,\n * parked, no step left, or another owner's live lease.\n */\nexport async function holdPausedAiJob(\n firestore: Firestore,\n orgId: string,\n jobId: string,\n owner: string,\n now = new Date(),\n): Promise<AiJob | null> {\n const ref = jobsCollection(firestore, orgId).doc(jobId)\n let ended: AiJob | null = null\n const held = await firestore.runTransaction<AiJob | null>(async (tx: Transaction) => {\n ended = null\n const job = jobFrom(await tx.get(ref))\n if (!job) return null\n // A paused workspace's job a person canceled ends canceled (AGL-3616).\n if (aiJobCancelPending(job)) {\n if (!leaseIsLive(job, owner, now.getTime())) {\n const patch = aiJobCanceledPatch(job, now, cancelRequestOf(job, now))\n tx.set(ref, patch, { merge: true })\n ended = { ...job, ...(patch as unknown as Partial<AiJob>) } as AiJob\n }\n return null\n }\n if (claimableStepIndex(job, owner, now.getTime()) === -1) return null\n tx.set(ref, { updatedAt: now }, { merge: true })\n return { ...job, updatedAt: now as unknown as AiJob['updatedAt'] }\n })\n await noteAiJobEnded(firestore, ended, now)\n return held\n}\n\nexport interface RecordStepInput {\n /** `pending` hands the step back for another attempt. */\n status: 'done' | 'failed' | 'pending'\n creditsSpent: number\n error?: string | null\n outputs?: AiJobOutput[]\n /** The plan the step proposed, kept on the job (AGL-2935). */\n plan?: AiJobPlan\n /** The step stopped for a person: the job parks `needs_review` in this same write. */\n review?: AiJobReview\n /**\n * With `review`: the person must answer these first (AGL-3978), so the job\n * parks `needs_input` with them, the review kept for after the answer.\n */\n input?: AiJobInputRequest\n /**\n * With `pending`: the step is handed back because it made progress and\n * asked to continue (AGL-2910), not because an attempt failed. Its\n * attempts count again from zero and its passes by one.\n */\n continued?: boolean\n /** What the run cost in tokens and time (AGL-2937), added to the step's totals. */\n tokens?: AiJobStepTokenRun\n /** What a guided site start put live (AGL-3596), kept on the job. */\n sitePublish?: AiJobSitePublish\n /**\n * A park the caller announces itself (AGL-3596): after the give-back it\n * owes is recorded, so the notice can say it, or never, for a park the\n * caller resolves at once.\n */\n quiet?: boolean\n /**\n * A `build` job's ledger to start from (AGL-3616), kept in this write;\n * absent keeps the one the job has.\n */\n items?: AiJobItemLedger[]\n /**\n * What this pass came to for the `build` item it ran (AGL-3616): the pass's\n * `creditsSpent` is that item's, and lands on its row in this same\n * transaction, with what was given back for it.\n */\n item?: AiJobItemRecord\n /** An `import` job's waves after this pass (AGL-3976), kept in this write. */\n siteImport?: AiSiteImportState\n}\n\n/** One `build` pass as its item's row records it (AGL-3616). */\nexport interface AiJobItemRecord extends AiJobItemOutcome {\n /** Given back for this pass's failure, before the record. */\n creditsRefunded?: number\n refundKey?: string\n}\n\n/**\n * A build's ledger with one pass applied (AGL-3616): the pass's credits on\n * the item's row, its status, failure, note and outputs, and what was given\n * back for it. Pure; the machine applies it inside `recordStep`.\n */\nexport function aiApplyJobItemRecord(\n ledger: readonly AiJobItemLedger[],\n record: AiJobItemRecord,\n credits: number,\n now?: Date,\n): AiJobItemLedger[] {\n return ledger.map((row) =>\n row.slot !== record.slot\n ? row\n : {\n ...row,\n status: record.status,\n creditsSpent: (row.creditsSpent ?? 0) + credits,\n attemptCredits: (row.attemptCredits ?? 0) + credits,\n creditsRefunded: (row.creditsRefunded ?? 0) + (record.creditsRefunded ?? 0),\n ...(record.refundKey ? { refundKey: record.refundKey } : {}),\n ...(record.failure !== undefined ? { failure: record.failure } : {}),\n ...(record.note !== undefined ? { note: record.note } : {}),\n ...(record.degradedBy?.length ? { degradedBy: record.degradedBy } : {}),\n // When it settled, so the next item's row counts from there (AGL-3596).\n ...(now && record.status !== 'running' && record.status !== 'pending' ? { settledAt: now.toISOString() } : {}),\n outputs: [...(row.outputs ?? []), ...(record.outputs ?? []).filter((id) => !(row.outputs ?? []).includes(id))],\n },\n )\n}\n\n/** One run of a step's runner, as the machine measured it (AGL-2937). */\nexport interface AiJobStepTokenRun {\n usage: AiUsage\n model: string\n effort: AiEffort | null\n latencyMs: number\n /** Why the run's last model call stopped (AGL-3042). */\n stopReason: string | null\n}\n\n/**\n * The runs whose stop reasons a step's record keeps (AGL-3042), newest last.\n * A page job that builds a layout, a form and a component, then five sections\n * and its last pass, runs its generation step nine times: twelve keep such a\n * page whole with room for three retries.\n *\n * What it costs the document: an entry is at most 67 bytes as Firestore sizes\n * it (the two field names, 11 and 7; a stop reason, a provider's ASCII word of\n * at most `AI_JOB_STEP_STOP_REASON_MAX_CHARS` characters, 41; an integer, 8),\n * so a step's list is at most 813 bytes with its field name, and a job's two\n * steps at most 1,626 of the 1,048,576 bytes a document may hold.\n */\nexport const AI_JOB_STEP_LAST_RUNS = 12\n\n/** The longest stop reason a run's entry keeps; every one a provider names today is shorter. */\nexport const AI_JOB_STEP_STOP_REASON_MAX_CHARS = 40\n\n/**\n * A step's measure with one more run added: the four counts and the time\n * summed, the model and effort taken from the run, and why it stopped\n * appended to the latest runs (AGL-3042).\n */\nexport function addAiJobStepTokens(\n current: AiJobStepTokens | null | undefined,\n run: AiJobStepTokenRun,\n): AiJobStepTokens {\n const count = (value: unknown): number => {\n const parsed = Number(value ?? 0)\n return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : 0\n }\n const kept = Array.isArray(current?.lastRuns) ? current.lastRuns : []\n const stopReason =\n typeof run.stopReason === 'string' && run.stopReason\n ? run.stopReason.slice(0, AI_JOB_STEP_STOP_REASON_MAX_CHARS)\n : null\n return {\n input: count(current?.input) + count(run.usage.inputTokens),\n cachedRead: count(current?.cachedRead) + count(run.usage.cacheReadTokens),\n cacheWrite: count(current?.cacheWrite) + count(run.usage.cacheWriteTokens),\n output: count(current?.output) + count(run.usage.outputTokens),\n model: run.model || current?.model || null,\n effort: run.effort ?? null,\n latencyMs: count(current?.latencyMs) + count(run.latencyMs),\n runs: count(current?.runs) + 1,\n lastRuns: [...kept, { stopReason, output: count(run.usage.outputTokens) }].slice(-AI_JOB_STEP_LAST_RUNS),\n }\n}\n\nexport interface RecordedStep {\n job: AiJob\n /** Steps still to run after this one. */\n remaining: number\n /** The job entered `needs_review` in this write. */\n parked?: boolean\n}\n\n/**\n * Record what a step cost and produced, and hand the lease back.\n *\n * Credits and outputs are recorded whatever the job's status is by now —\n * a job canceled while its provider call was in flight still spent the\n * tokens, and hiding that would make the bill disagree with the document.\n * The status transition is the caller's next call (`completeAiJob`,\n * `failAiJob`), except that a step handed back or a step with more steps\n * behind it re-queues here, so the beat sees it without another write —\n * and a step that stopped for a person parks the job `needs_review` here,\n * in the same transaction, so no beat can claim the next step in between.\n */\nexport async function recordStep(\n firestore: Firestore,\n orgId: string,\n jobId: string,\n owner: string,\n stepIndex: number,\n input: RecordStepInput,\n now = new Date(),\n): Promise<RecordedStep> {\n const ref = jobsCollection(firestore, orgId).doc(jobId)\n const recorded = await firestore.runTransaction(async (tx: Transaction) => {\n const job = jobFrom(await tx.get(ref))\n if (!job) throw new Error(`ai job ${orgId}/${jobId} vanished`)\n const steps = job.steps.map((step, index) =>\n index === stepIndex\n ? {\n ...step,\n status: input.status,\n endedAt: input.status === 'pending' ? null : now,\n creditsSpent: (step.creditsSpent ?? 0) + input.creditsSpent,\n error: input.error ?? null,\n ...(input.continued && input.status === 'pending'\n ? { attempts: 0, passes: (step.passes ?? 0) + 1 }\n : {}),\n ...(input.tokens ? { tokens: addAiJobStepTokens(step.tokens, input.tokens) } : {}),\n }\n : step,\n )\n const remaining = steps.filter(\n (step) => step.status === 'pending' || step.status === 'running',\n ).length\n // A build's item ledger moves with its spend, in this one write (AGL-3616).\n const ledger = input.items ?? job.items ?? null\n const items =\n ledger && input.item ? aiApplyJobItemRecord(ledger, input.item, input.creditsSpent, now) : input.items ? ledger : null\n const settled = input.status !== 'pending'\n const ownsLease = job.lease?.owner === owner\n // A cancel asked while this step ran ends the job here (AGL-3616), in the\n // write that records what the step cost and built: nothing after it runs.\n const canceling = aiJobCancelPending(job)\n const parksForReview = Boolean(input.review) && !isAiJobTerminal(job.status) && !canceling\n // A plan only the person can complete waits for their answer (AGL-3978).\n const parkedAs: AiJobStatus = input.input ? 'needs_input' : 'needs_review'\n const status: AiJobStatus = isAiJobTerminal(job.status)\n ? job.status\n : canceling\n ? 'canceled'\n : job.status === 'needs_input'\n ? job.status\n : parksForReview\n ? parkedAs\n : input.status === 'failed'\n ? 'running'\n : remaining > 0\n ? 'queued'\n : 'running'\n const patch = {\n status,\n steps,\n outputs: [...(job.outputs ?? []), ...(input.outputs ?? [])],\n creditsSpent: (job.creditsSpent ?? 0) + input.creditsSpent,\n // A job parked for a person holds nothing: no step of it can run until\n // someone resumes it, and the resume holds the figure again (AGL-3030).\n creditsReserved: parksForReview\n ? 0\n : settled\n ? Math.max(0, (job.creditsReserved ?? 0) - AI_JOB_STEP_RESERVE_CREDITS)\n : (job.creditsReserved ?? 0),\n updatedAt: now,\n ...(ownsLease ? { lease: null } : {}),\n // A plan the step produced is kept whatever the job's status is by\n // now, for the reason its credits are.\n ...(input.plan ? { plan: input.plan } : {}),\n ...(input.sitePublish ? { sitePublish: input.sitePublish } : {}),\n ...(input.siteImport ? { siteImport: input.siteImport } : {}),\n ...(items ? { items } : {}),\n ...(input.item?.creditsRefunded\n ? { refundedCredits: (job.refundedCredits ?? 0) + input.item.creditsRefunded }\n : {}),\n // A doctrine or limit review's sentence is the job's customer-safe\n // error, as a meter park's is; a plan waiting to be confirmed is not an\n // error.\n // A job waiting for an answer says its question there too (AGL-3978).\n ...(parksForReview && input.review\n ? {\n review: input.review,\n // A plan to confirm and a site import's wave to review (AGL-3976)\n // are decisions waiting, not errors.\n error: input.input\n ? input.input.message\n : input.review.reason === 'plan' || input.review.reason === 'wave'\n ? null\n : input.review.message,\n input: input.input ?? null,\n }\n : {}),\n ...(canceling\n ? {\n creditsReserved: 0,\n lease: null,\n error: null,\n steps: steps.map((step) => (step.status === 'running' ? { ...step, status: 'pending' as const } : step)),\n ...((items ?? job.items)?.length ? { items: aiCanceledItems(items ?? job.items ?? []) } : {}),\n }\n : {}),\n }\n tx.set(ref, patch, { merge: true })\n return {\n job: { ...job, ...(patch as unknown as Partial<AiJob>) } as AiJob,\n remaining,\n // Entered `needs_review` (or `needs_input` for an answer) in this write, rather than parked again.\n parked: parksForReview && job.status !== parkedAs,\n // Ended `canceled` in this write (AGL-3936).\n ended: canceling && !isAiJobTerminal(job.status),\n }\n })\n if (recorded.ended) await noteAiJobEnded(firestore, recorded.job, now)\n if (recorded.parked && !input.quiet) await announceAiJobTransition(recorded.job, 'needs-review')\n return { job: recorded.job, remaining: recorded.remaining, parked: recorded.parked }\n}\n\nasync function transition(\n firestore: Firestore,\n orgId: string,\n jobId: string,\n apply: (job: AiJob) => Record<string, unknown> | null,\n): Promise<{ job: AiJob; changed: boolean }> {\n const ref = jobsCollection(firestore, orgId).doc(jobId)\n return firestore.runTransaction(async (tx: Transaction) => {\n const job = jobFrom(await tx.get(ref))\n if (!job) throw new Error(`ai job ${orgId}/${jobId} vanished`)\n const patch = apply(job)\n if (!patch) return { job, changed: false }\n tx.set(ref, patch, { merge: true })\n return {\n job: { ...job, ...(patch as unknown as Partial<AiJob>) } as AiJob,\n changed: true,\n }\n })\n}\n\n/** Every step done: the job is `done` and holds nothing. */\nexport async function completeAiJob(\n firestore: Firestore,\n orgId: string,\n jobId: string,\n now = new Date(),\n): Promise<AiJob> {\n const { job, changed } = await transition(firestore, orgId, jobId, (current) =>\n isAiJobTerminal(current.status)\n ? null\n : // A cancel asked before the finish commits wins (AGL-3616).\n aiJobCancelPending(current)\n ? aiJobCanceledPatch(current, now, cancelRequestOf(current, now))\n : {\n status: 'done',\n creditsReserved: 0,\n lease: null,\n error: null,\n updatedAt: now,\n },\n )\n if (changed && job.status === 'done') await announceAiJobTransition(job, 'done')\n if (changed) await noteAiJobEnded(firestore, job, now)\n return job\n}\n\n/**\n * Terminal failure. `message` is the only thing a customer reads, so it is\n * a fixed sentence; whatever the provider or the runner actually said goes\n * to the log beside the ids an operator would search for.\n */\nexport async function failAiJob(\n firestore: Firestore,\n orgId: string,\n jobId: string,\n message: string,\n detail?: { stepIndex?: number; error?: unknown; ours?: boolean },\n now = new Date(),\n): Promise<AiJob> {\n console.error('ai job failed', {\n orgId,\n jobId,\n stepIndex: detail?.stepIndex ?? null,\n error: detail?.error instanceof Error ? detail.error.message : detail?.error ?? null,\n })\n const { job, changed } = await transition(firestore, orgId, jobId, (current) =>\n isAiJobTerminal(current.status)\n ? null\n : // A person's cancel is not a failure (AGL-3616): no alert, no notice.\n aiJobCancelPending(current)\n ? aiJobCanceledPatch(current, now, cancelRequestOf(current, now))\n : {\n status: 'failed',\n error: message,\n creditsReserved: 0,\n lease: null,\n steps: current.steps.map((step) =>\n step.status === 'running'\n ? { ...step, status: 'failed', endedAt: now, error: message }\n : step,\n ),\n updatedAt: now,\n },\n )\n if (changed && job.status === 'failed') {\n await announceAiJobTransition(job, 'failed', {\n ours: detail?.ours ?? false,\n stepIndex: detail?.stepIndex ?? null,\n error: detail?.error instanceof Error ? detail.error.message : detail?.error == null ? null : String(detail.error),\n })\n }\n if (changed) await noteAiJobEnded(firestore, job, now)\n return job\n}\n\n/**\n * Park the job for something only the workspace can change. The running\n * step goes back to `pending` so the same step is what runs when the beat\n * tries again; its attempt is not counted, because nothing was attempted.\n */\nexport async function markAiJobNeedsInput(\n firestore: Firestore,\n orgId: string,\n jobId: string,\n reason: string,\n now = new Date(),\n): Promise<AiJob> {\n const { job, changed } = await transition(firestore, orgId, jobId, (current) =>\n isAiJobTerminal(current.status)\n ? null\n : aiJobCancelPending(current)\n ? aiJobCanceledPatch(current, now, cancelRequestOf(current, now))\n : {\n status: 'needs_input',\n error: reason,\n lease: null,\n steps: current.steps.map((step) =>\n step.status === 'running'\n ? {\n ...step,\n status: 'pending',\n attempts: Math.max(0, (step.attempts ?? 0) - 1),\n }\n : step,\n ),\n updatedAt: now,\n },\n )\n if (changed) await noteAiJobEnded(firestore, job, now)\n return job\n}\n\n/** What a cancel did (AGL-3616). */\nexport interface AiJobCancelResult {\n job: AiJob\n /** The cancel was new: it ended the job, or asked the step in flight to stop. */\n changed: boolean\n}\n\n/**\n * Cancel (AGL-2904, AGL-3616). One transaction decides it against whatever\n * else commits, so a cancel racing a finish resolves to whichever committed\n * first: a job already `done`, `failed` or `canceled` is returned unchanged.\n *\n * - **No step in flight** (queued, parked for a person or the meter, or a\n * lease that ran out): the job ends `canceled` in this write. It holds\n * nothing, its unbuilt items are skipped, and nothing it built is touched.\n * - **A step in flight** (a live lease): the cancel is recorded as\n * `cancelRequested` and the job keeps its status until the runner holding\n * the lease comes back. The runner watches for it and aborts its provider\n * call; whatever the step had spent is metered, and the machine's next\n * write ends the job `canceled` (`recordStep`, or the finish or failure it\n * was about to write, which a pending cancel turns into the cancel).\n *\n * Either way a canceled job is never failed: no `ai.jobFailed` alert, no\n * failure notice, no give-back (what ran stays charged), no publish.\n *\n * The activity row is written once, when the cancel is first asked: a second\n * cancel of the same job is not a second act. `actor` is the member who\n * canceled; a cancel with nobody named is recorded as nobody's rather than\n * as the creator's.\n */\nexport async function cancelAiJob(\n firestore: Firestore,\n orgId: string,\n jobId: string,\n now = new Date(),\n actor: AiActivityActor | null = null,\n): Promise<AiJobCancelResult> {\n const request = aiJobCancelRequest(now, actor?.uid ?? null)\n let firstAsk = false\n const result = await transition(firestore, orgId, jobId, (current) => {\n if (isAiJobTerminal(current.status)) return null\n const inFlight = anyLeaseIsLive(current, now.getTime())\n if (current.cancelRequested) {\n // Asked already: nothing new while its step is still in flight; a\n // step whose runner never came back is ended now.\n return inFlight ? null : aiJobCanceledPatch(current, now, current.cancelRequested)\n }\n firstAsk = true\n return inFlight\n ? { cancelRequested: request, updatedAt: now }\n : aiJobCanceledPatch(current, now, request)\n })\n if (result.changed && firstAsk) {\n await logAiJobCanceled(orgId, actor ?? { uid: null }, {\n jobId,\n kind: result.job.kind,\n })\n }\n if (result.changed) await noteAiJobEnded(firestore, result.job, now)\n return result\n}\n\n/**\n * Gives back what a job spent when it failed on our side (AGL-3594), and\n * records the give-back on the job — `refundedCredits`, `refundReason` and\n * the count its next key is numbered by. Everything the job spent and has not\n * given back where it delivered nothing; the failing step's own spend where\n * it delivered drafts, which it keeps. `assist-job-refund.ts` has the rule\n * and the bound. Never throws: a give-back that cannot be made leaves the job\n * as the failure left it, metered as every other step.\n */\nasync function refundOurFailure(\n firestore: Firestore,\n orgId: string,\n job: AiJob,\n input: {\n reason: AiJobRefundReason\n /** The failing step's own credits, as the step was recorded. */\n stepCredits: number\n /** The Free taste's attribution, where the caller holds the reservation that decided it. */\n free?: FreeAssistAccount | null\n now: Date\n },\n): Promise<AiJob> {\n try {\n const delivered = (job.outputs ?? []).some((output) => output.resource !== 'seo')\n const credits = aiJobRefundCredits({\n delivered,\n jobCredits: job.creditsSpent ?? 0,\n stepCredits: input.stepCredits,\n alreadyRefunded: job.refundedCredits ?? 0,\n })\n if (credits <= 0) return job\n const free =\n input.free !== undefined\n ? input.free\n : freeAssistAccount(\n ((await firestore.collection('orgs').doc(orgId).get()).data() ?? {}) as Partial<AglynOrgBilling>,\n )\n const ordinal = job.refunds ?? 0\n const refund = await refundJobCredits(firestore, {\n orgId,\n free,\n jobId: job.$id,\n ordinal,\n credits,\n month: assistUsageMonth(input.now),\n day: assistUsageDay(input.now),\n reason: input.reason,\n })\n if (refund.status !== 'returned') return job\n const { job: refunded } = await transition(firestore, orgId, job.$id, (current) => ({\n refundedCredits: (current.refundedCredits ?? 0) + credits,\n refundReason: input.reason,\n refunds: (current.refunds ?? 0) + 1,\n updatedAt: input.now,\n }))\n return refunded\n } catch (error) {\n console.error('ai job refund failed', { orgId, jobId: job.$id, error })\n return job\n }\n}\n\n/**\n * A failure on our side, given back BEFORE the job is failed (AGL-3596): the\n * failed state is the last one a follower of the job reads and the one its\n * notice is written from, so it already carries `refundedCredits`. A job that\n * ended meanwhile — a person's cancel, which pays for what ran — is left as it\n * ended and given nothing back.\n */\nasync function failOurFailure(\n firestore: Firestore,\n orgId: string,\n jobId: string,\n message: string,\n detail: { stepIndex?: number; error?: unknown; ours?: boolean },\n refund: { reason: AiJobRefundReason; stepCredits: number; free?: FreeAssistAccount | null },\n now: Date,\n): Promise<AiJob> {\n const current = await getAiJob(firestore, orgId, jobId)\n // A person's cancel asked while the step ran is the job's end (AGL-3616):\n // not our failure, so nothing is given back and nobody is told.\n if (current && aiJobCancelPending(current)) {\n return failAiJob(firestore, orgId, jobId, message, { ...detail, ours: false }, now)\n }\n // A build that got as far as its items settles item by item (AGL-3616):\n // what it delivered stands, and what it did not is given back per item.\n if (current && aiJobSettlesByItem(current.kind) && current.items?.length && !isAiJobTerminal(current.status)) {\n console.error('ai build stopped', { orgId, jobId, stepIndex: detail.stepIndex ?? null, error: detail.error })\n return settleAiBuildJob(firestore, orgId, jobId, {\n now,\n free: refund.free,\n stopped: { message, reason: refund.reason },\n })\n }\n if (current && !isAiJobTerminal(current.status)) {\n await refundOurFailure(firestore, orgId, current, { ...refund, now })\n }\n // Given back as ours either way; ANNOUNCED as ours unless the step said the\n // failure is the customer's own limit (`customerLimit`), which pages no one.\n return failAiJob(firestore, orgId, jobId, message, { ...detail, ours: detail.ours ?? true }, now)\n}\n\n// ── A build's settlement (AGL-3616) ──────────────────────────────────────\n\n/**\n * The kinds settled item by item (AGL-3616): a build, and a site scaffold,\n * which is the build's preset. Their step writes an item ledger; a failed\n * item gives back its own spend and the rest goes on.\n */\nexport function aiJobSettlesByItem(kind: AiJobKind): boolean {\n // A site import's pages are its items too (AGL-3976): one that fails on\n // our side gives back its own spend, and its wave goes on.\n return kind === 'build' || kind === 'site' || kind === 'import'\n}\n\n/**\n * Whether Try again may run a job's failed items: a finished build, done or\n * failed; a site scaffold that delivered part of its site — one that built\n * nothing starts over from its own answers instead.\n */\nexport function aiBuildRetryable(job: Pick<AiJob, 'kind' | 'status'>): boolean {\n if (job.kind === 'build') return job.status === 'done' || job.status === 'failed'\n return job.kind === 'site' && job.status === 'done'\n}\n\n/** What a build that delivered nothing says; its planning was given back too. */\nexport const AI_BUILD_NOTHING_BUILT_COPY =\n 'Nothing in this request could be built. Try again, or ask for less at once.'\n\n/** What a site import that brought no page in says (AGL-3976); every page's spend was given back. */\nexport const AI_SITE_IMPORT_NOTHING_BUILT_COPY =\n 'No page of your site could be brought in. Nothing was charged for the pages that failed; try again later.'\n\n/** What an item the build never reached says when the build stopped early. */\nexport const AI_BUILD_NOT_REACHED_COPY = 'Not built: the build stopped before it reached this.'\n\n/**\n * Gives back one build item's spend, or its planning's (`slot: 'plan'`),\n * under the item key (job, slot, attempt), so a replayed pass gives back once\n * and a later attempt is a give-back of its own. The credits given back and\n * the key, or `null` when nothing was. Never throws.\n */\nasync function refundBuildCredits(\n firestore: Firestore,\n orgId: string,\n job: AiJob,\n input: {\n slot: string\n attempt: number\n credits: number\n reason: AiJobRefundReason\n free?: FreeAssistAccount | null\n now: Date\n },\n): Promise<{ credits: number; key: string } | null> {\n const credits = Math.max(0, Math.floor(input.credits))\n if (credits <= 0) return null\n try {\n const free =\n input.free !== undefined\n ? input.free\n : freeAssistAccount(\n ((await firestore.collection('orgs').doc(orgId).get()).data() ?? {}) as Partial<AglynOrgBilling>,\n )\n const created = toMillis(job.createdAt as Instant)\n const jobDay = assistUsageDay(created === null ? input.now : new Date(created))\n const refund = await refundJobCredits(firestore, {\n orgId,\n free,\n jobId: job.$id,\n ordinal: 0,\n credits,\n month: assistUsageMonth(input.now),\n day: assistUsageDay(input.now),\n reason: input.reason,\n item: { slot: input.slot, attempt: input.attempt, jobDay },\n })\n // A key the month already holds was given back by an earlier run of this pass.\n if (refund.status !== 'returned' && refund.status !== 'duplicate') return null\n return {\n credits,\n key: aiJobItemRefundKey({ day: jobDay, jobId: job.$id, slot: input.slot, attempt: input.attempt }),\n }\n } catch (error) {\n console.error('ai build refund failed', { orgId, jobId: job.$id, slot: input.slot, error })\n return null\n }\n}\n\n/** What the build's plan step spent: its orchestration. */\nfunction aiBuildPlanCredits(job: Pick<AiJob, 'steps'>): number {\n return job.steps.find((step) => step.name === AI_JOB_PLAN_STEP)?.creditsSpent ?? 0\n}\n\n/**\n * Ends a build (AGL-3616), once nothing is left to build or the machine\n * stopped it. A build that STOPPED — a provider failure, its attempts used up\n * — fails the item it was on and gives back that attempt's spend, and leaves\n * the items it never reached `skipped`. Then: anything delivered, and its\n * planning is charged and the job is `done`, failed items and all; nothing\n * delivered, and its planning is given back too and the job is `failed`.\n */\nexport async function settleAiBuildJob(\n firestore: Firestore,\n orgId: string,\n jobId: string,\n input: {\n now: Date\n free?: FreeAssistAccount | null\n stopped?: { message: string; reason: AiJobRefundReason }\n },\n): Promise<AiJob> {\n const { now } = input\n let job = await getAiJob(firestore, orgId, jobId)\n if (!job || isAiJobTerminal(job.status)) return job as AiJob\n // A cancel asked before the build settled is how it ends (AGL-3616).\n if (aiJobCancelPending(job)) return completeAiJob(firestore, orgId, jobId, now)\n const stopped = input.stopped\n if (stopped && (job.items ?? []).some((row) => row.status === 'pending' || row.status === 'running')) {\n const refunds = new Map<string, { credits: number; key: string }>()\n for (const row of job.items ?? []) {\n if (row.status !== 'running') continue\n const refund = await refundBuildCredits(firestore, orgId, job, {\n slot: row.slot,\n attempt: row.attempt,\n credits: row.attemptCredits ?? 0,\n reason: stopped.reason,\n free: input.free,\n now,\n })\n if (refund) refunds.set(row.slot, refund)\n }\n job = (\n await transition(firestore, orgId, jobId, (current) => {\n if (isAiJobTerminal(current.status)) return null\n let given = 0\n const items = (current.items ?? []).map((row): AiJobItemLedger => {\n if (row.status === 'running') {\n const refund = refunds.get(row.slot)\n given += refund?.credits ?? 0\n return {\n ...row,\n status: 'failed',\n failure: { ours: true, reason: stopped.reason, message: stopped.message },\n creditsRefunded: (row.creditsRefunded ?? 0) + (refund?.credits ?? 0),\n ...(refund ? { refundKey: refund.key } : {}),\n }\n }\n return row.status === 'pending' ? { ...row, status: 'skipped', note: AI_BUILD_NOT_REACHED_COPY } : row\n })\n return {\n items,\n refundedCredits: (current.refundedCredits ?? 0) + given,\n updatedAt: now,\n }\n })\n ).job\n }\n // A site is delivered when a page is (AGL-3616): a form alone is not a\n // site. A scaffold that owed no page (a deployment without the page kind)\n // is delivered by whatever it did build.\n const sitePages = job.kind === 'site' ? (job.items ?? []).filter((row) => row.op === 'page') : []\n const delivered = sitePages.length\n ? sitePages.some((row) => row.status === 'succeeded' || row.status === 'degraded')\n : aiBuildSettlement(job.items ?? []).delivered\n const planCredits = aiBuildPlanCredits(job)\n if (delivered) {\n await transition(firestore, orgId, jobId, (current) =>\n isAiJobTerminal(current.status)\n ? null\n : { orchestration: { creditsSpent: planCredits, settled: 'charged' }, updatedAt: now },\n )\n return completeAiJob(firestore, orgId, jobId, now)\n }\n // A site that built no page gives back what its other parts cost too: it\n // was not built, and its Try again starts over from its answers.\n const parts = new Map<string, { credits: number; key: string }>()\n if (job.kind === 'site') {\n for (const row of job.items ?? []) {\n const owed = Math.max(0, (row.creditsSpent ?? 0) - (row.creditsRefunded ?? 0))\n if (owed <= 0 || row.status === 'failed') continue\n const given = await refundBuildCredits(firestore, orgId, job, {\n slot: row.slot,\n attempt: row.attempt,\n credits: owed,\n reason: stopped?.reason ?? 'step-failure',\n free: input.free,\n now,\n })\n if (given) parts.set(row.slot, given)\n }\n }\n const refund =\n job.orchestration?.settled === 'refunded'\n ? null\n : await refundBuildCredits(firestore, orgId, job, {\n slot: 'plan',\n attempt: 1,\n credits: planCredits,\n reason: stopped?.reason ?? 'step-failure',\n free: input.free,\n now,\n })\n await transition(firestore, orgId, jobId, (current) =>\n isAiJobTerminal(current.status)\n ? null\n : {\n orchestration: {\n creditsSpent: planCredits,\n settled: refund || current.orchestration?.settled === 'refunded' ? 'refunded' : 'charged',\n },\n ...(refund || parts.size\n ? {\n refundedCredits:\n (current.refundedCredits ?? 0) +\n (refund?.credits ?? 0) +\n [...parts.values()].reduce((total, part) => total + part.credits, 0),\n refundReason: stopped?.reason ?? 'step-failure',\n }\n : {}),\n ...(parts.size\n ? {\n items: (current.items ?? []).map((row) => {\n const part = parts.get(row.slot)\n return part ? { ...row, creditsRefunded: (row.creditsRefunded ?? 0) + part.credits, refundKey: part.key } : row\n }),\n }\n : {}),\n updatedAt: now,\n },\n )\n const nothing =\n job.kind === 'site'\n ? AI_SITE_GUIDED_BUILD_FAILED_COPY\n : job.kind === 'import'\n ? AI_SITE_IMPORT_NOTHING_BUILT_COPY\n : AI_BUILD_NOTHING_BUILT_COPY\n return failAiJob(firestore, orgId, jobId, stopped?.message ?? nothing, {\n error: 'build delivered nothing',\n // Ours unless every item that failed was the model declining (AGL-3616).\n ours: Boolean(stopped) || (job.items ?? []).some((row) => row.status === 'failed' && row.failure?.ours),\n }, now)\n}\n\n/**\n * Try again on a build (AGL-3616): its failed items back to pending on their\n * next attempt, with what they left unbuilt (`aiBuildRetryLedger`), and its\n * generation step queued again. Never re-runs an item that succeeded, never\n * re-asks the plan. Held at what the retried items are estimated to cost.\n * `changed: false` for a job that is not a finished build, or has nothing to\n * try again.\n */\nexport async function retryAiBuildJob(\n firestore: Firestore,\n orgId: string,\n jobId: string,\n now = new Date(),\n options: {\n /** A Free Try again the person chose to run past what was left (AGL-3722). */\n creditsConfirmed?: AiJobCreditsConfirmation | null\n } = {},\n): Promise<{ job: AiJob; changed: boolean; retried: string[] }> {\n let retried: string[] = []\n const result = await transition(firestore, orgId, jobId, (current) => {\n if (!aiBuildRetryable(current)) return null\n const plan = current.plan?.status === 'confirmed' ? current.plan : null\n if (!plan || !current.items?.length) return null\n const next = aiBuildRetryLedger(current.items, current.kind === 'site' ? aiLedgerUnits(current.items) : aiBuildUnits(plan))\n if (!next.ledger.some((row) => row.status === 'pending')) return null\n retried = next.retried\n return {\n status: 'queued',\n items: next.ledger,\n steps: current.steps.map((step) =>\n step.name === AI_JOB_PLAN_STEP\n ? step\n : { ...step, status: 'pending', attempts: 0, passes: 0, endedAt: null, error: null },\n ),\n review: null,\n error: null,\n lease: null,\n creditsReserved: Math.max(\n AI_JOB_STEP_RESERVE_CREDITS,\n current.kind === 'site'\n ? next.retried.length * AI_JOB_STEP_RESERVE_CREDITS\n : aiBuildCreditEstimate(plan, { slots: new Set(next.retried) }),\n ),\n ...(options.creditsConfirmed ? { creditsConfirmed: options.creditsConfirmed } : {}),\n updatedAt: now,\n }\n })\n // A site start tried again keeps its site (AGL-3936).\n if (result.changed) await clearAiEmptySiteSweep(firestore, result.job.hostId)\n return { ...result, retried: result.changed ? retried : [] }\n}\n\n/**\n * Resume a job that stopped for a person (AGL-2935): confirm its plan, or\n * try again the step whose answer broke a building rule — or carry on a job\n * the meter paused, from its paused step (AGL-3660). One transaction,\n * so two confirmations land once; anything but a `needs_review` job comes\n * back unchanged. The pending step's attempts start over — a person asking\n * again is not a provider failing again — and a confirmed plan with no step\n * left behind it completes the job rather than queueing nothing. The steps\n * it queues are held at the nominal figure again, which the park released —\n * and a confirmed plan is held at what the whole job is estimated to cost\n * (AGL-3031), creations included, where that is more.\n */\nexport async function resumeAiJob(\n firestore: Firestore,\n orgId: string,\n jobId: string,\n actor: { uid: string },\n now = new Date(),\n options: {\n /**\n * A build whose request asked to publish, confirmed with the plan card's\n * box ticked (AGL-3616): kept on its inputs, which is what lets its last\n * pass put its pages live.\n */\n publishConfirmed?: boolean\n /**\n * A Free build or start the person chose to build past what was left\n * (AGL-3722), kept on the job: who, when, what was left.\n */\n creditsConfirmed?: AiJobCreditsConfirmation | null\n /**\n * The smaller first build the person chose instead (AGL-3722): its plan\n * — the home page and what it needs — is the one confirmed, in place of\n * the plan the job proposed. Only while that plan waits for confirmation.\n */\n reducePlan?: (plan: AiJobPlan) => AiJobPlan | null\n /** The answers a job waiting in `needs_input` asked for (AGL-3978), by `aiJobInputKey`. */\n input?: Readonly<Record<string, unknown>> | null\n } = {},\n): Promise<{ job: AiJob; changed: boolean }> {\n const resumed = await transition(firestore, orgId, jobId, (current) => {\n // A job the meter paused (AGL-3660) carries on from the step it paused\n // on: that step is already pending, so it is queued again with nothing\n // else changed. Whether the credits are there now is the reservation's\n // to say, at the door and again at the step.\n // A job waiting for a person's answer (AGL-3978) takes it only once every\n // field reads (`aiJobInputProblems`, which the door asks first): the\n // answer is written to the plan's item and the job returns to its plan\n // card, where Confirm builds it as any plan.\n if (current.status === 'needs_input' && current.input?.fields?.length) {\n const values = options.input\n if (!values || !current.plan || Object.keys(aiJobInputProblems(current.input, values)).length) return null\n return {\n status: 'needs_review',\n plan: aiPlanWithInput(current.plan, current.input, values),\n input: null,\n error: null,\n lease: null,\n updatedAt: now,\n }\n }\n if (current.status === 'needs_input') {\n return { status: 'queued', error: null, lease: null, updatedAt: now }\n }\n if (current.status !== 'needs_review') return null\n const steps = current.steps.map((step) =>\n step.status === 'pending' ? { ...step, attempts: 0 } : step,\n )\n const outstanding = steps.filter((step) => step.status === 'pending').length\n const pending = outstanding > 0\n const proposed = current.review?.reason === 'plan' && current.plan ? current.plan : null\n const confirming = proposed && options.reducePlan ? (options.reducePlan(proposed) ?? proposed) : proposed\n const confirmed = confirming\n ? {\n plan: {\n ...confirming,\n status: 'confirmed',\n confirmedAt: now,\n confirmedBy: actor.uid,\n },\n }\n : {}\n const publish =\n confirming && current.kind === 'build' && options.publishConfirmed && current.inputs?.['publish'] === true\n ? { inputs: { ...current.inputs, publishConfirmed: true } }\n : {}\n return {\n status: pending ? 'queued' : 'done',\n steps,\n review: null,\n error: null,\n ...confirmed,\n ...publish,\n ...(options.creditsConfirmed ? { creditsConfirmed: options.creditsConfirmed } : {}),\n creditsReserved: pending\n ? Math.max(\n outstanding * AI_JOB_STEP_RESERVE_CREDITS,\n confirming ? aiJobCreditEstimate(current.kind, confirming) : 0,\n )\n : 0,\n updatedAt: now,\n }\n })\n // A site start resumed keeps its site (AGL-3936).\n if (resumed.changed) await clearAiEmptySiteSweep(firestore, resumed.job.hostId)\n return resumed\n}\n\n/**\n * Record what a person applied from a finished job's outputs (AGL-2910): the\n * versions an apply opened and the listings it staged. The apply door calls\n * this after its own writes, so the record names only what exists. It never\n * changes the job's status, its steps or its spend.\n */\nexport async function recordAiJobApplied(\n firestore: Firestore,\n orgId: string,\n jobId: string,\n applied: AiJobApplied,\n now = new Date(),\n): Promise<AiJob> {\n const { job } = await transition(firestore, orgId, jobId, () => ({\n applied,\n updatedAt: now,\n }))\n return job\n}\n\n// ── Audit ─────────────────────────────────────────────────────────────────\n\nexport type AiJobAuditAction = 'ai.job.output' | 'ai.job.cancel' | 'ai.job.resume' | 'ai.job.apply'\n\nexport interface AiJobAuditEntry {\n action: AiJobAuditAction\n actorUid: string\n actorEmail?: string | null\n orgId: string\n jobId: string\n after: Record<string, unknown>\n}\n\n/**\n * One `adminAudit` row, the same writer shape the billing routes use. Never\n * throws: an audit row that fails to land is logged, not turned into a\n * failed job the customer paid for.\n */\nexport async function writeAiJobAudit(\n firestore: Firestore,\n entry: AiJobAuditEntry,\n): Promise<void> {\n try {\n await addAdminAudit(firestore, {\n actorUid: entry.actorUid,\n actorEmail: entry.actorEmail ?? null,\n action: entry.action,\n target: `orgs/${entry.orgId}/${AI_JOBS_COLLECTION}/${entry.jobId}`,\n after: entry.after,\n at: FieldValue.serverTimestamp(),\n })\n } catch (error) {\n console.error('ai job audit write failed', {\n orgId: entry.orgId,\n jobId: entry.jobId,\n action: entry.action,\n error,\n })\n }\n}\n\n// ── Running a step ────────────────────────────────────────────────────────\n\n/**\n * The refusal in customer-safe words. The org's own wall names the switch\n * (AGL-2653); a cap, a band and a budget are told apart because one is a\n * figure the org set, one is what its plan sold, and one is the operator's\n * backstop; the message cap resets on a clock the others do not.\n */\nexport function aiJobRefusalText(\n org: Partial<AglynOrgBilling> | null,\n reservation: Pick<\n AssistReservation,\n | 'refusedBy'\n | 'budgetUsd'\n | 'allotment'\n | 'capReason'\n | 'overageLimitUsd'\n | 'overageUnpaidUsd'\n >,\n): string {\n const refusedBy = reservation.refusedBy\n // AGLYN'S OWN OVERAGE GUARDS (AGL-3011). First, because a `cap` refusal\n // that carries a reason knows exactly which limit stopped the job, where\n // the `case 'cap'` below can only name the workspace's own.\n const overage = aiOverageReservationRefusal(reservation)\n if (overage) return overage.text\n // A hard allotment (AGL-2942) — the creator's, theirs on the job's site,\n // or the site's — in the sentence every door gives, naming who can raise\n // it. A member can change that, so the job parks rather than fails.\n if (refusedBy === 'allotment') {\n return aiAllotmentRefusalText(reservation.allotment?.refusal?.scope)\n }\n if (assistRefusedByHardCap(org, refusedBy)) {\n return assistHardCapRefusalText(org)\n }\n // The Free taste's own precautions (AGL-2925), in the sentences every\n // other door uses — each names a clock or an upgrade.\n const taste = assistFreeTasteRefusalText(refusedBy)\n if (taste) return taste\n switch (refusedBy) {\n case 'cap':\n return 'This workspace reached the AI spending cap it set for the month'\n case 'band':\n return 'This workspace used its AI credits for the month'\n case 'budget':\n return reservation.budgetUsd === null\n ? 'This workspace reached its AI spending limit for the month'\n : 'This workspace used its AI credits for the month'\n default:\n return 'This workspace reached its AI limit for the month'\n }\n}\n\nexport interface RunAiJobStepOptions {\n /** Who is claiming: a request id or a beat id. */\n owner: string\n /** How often the running step re-reads its job for a cancel (AGL-3616); specs shorten it. */\n cancelPollMs?: number\n now?: Date\n /**\n * A reservation the caller already holds for THIS step — the console\n * route's gate ladder takes one before the job exists. Every later step\n * reserves for itself.\n */\n reservation?: AssistReservation\n /** The org billing document when the caller has it; read otherwise. */\n org?: Partial<AglynOrgBilling> | null\n /** Ends the provider call when the caller's budget does. */\n signal?: AbortSignal\n /**\n * The person on the request when a route runs the step inline. The beat\n * has none: what a step produces is then credited to the job's creator,\n * whose brief it is, never to nobody (AGL-2929).\n */\n actor?: AiActivityActor\n /**\n * A verified staff claim on the request when a route runs the step inline.\n * The workspace's AI pause lets staff through here as the gate ladder does,\n * so staff can verify a pause with one generation (AGL-3037). The beat has\n * no caller and never passes it.\n */\n staff?: boolean\n}\n\nexport type AiJobStepRun =\n /** Nothing to do: terminal, parked, no step left, or another owner's lease. */\n | { outcome: 'not-claimable' }\n /** The reservation was refused; the job is parked with the reason. */\n | { outcome: 'needs_input'; job: AiJob }\n /** The step stopped for a person (AGL-2935): its plan waits, or its answer broke a rule twice. */\n | { outcome: 'needs_review'; job: AiJob }\n /** The step ran and was recorded; `job.status` says whether more remain. */\n | { outcome: 'done'; job: AiJob }\n | { outcome: 'failed'; job: AiJob }\n /** A retryable provider failure; the step is pending again. */\n | { outcome: 'requeued'; job: AiJob }\n /**\n * AI is paused for the job's workspace (AGL-3037): nothing was claimed,\n * reserved or run, and the job waits where it stopped, at the back of the\n * beat's queue, until staff resume AI.\n */\n | { outcome: 'paused'; job: AiJob }\n /**\n * A person canceled the job while this step held it (AGL-3616): the step\n * finished or aborted, what it spent is recorded, and the job is canceled.\n */\n | { outcome: 'canceled'; job: AiJob }\n\n/**\n * A budget that ended before the provider answered. `fetch` rejects with\n * the signal's reason, which is an `AbortError` for a manual abort and a\n * `TimeoutError` for `AbortSignal.timeout` — the one the route and the\n * sweep actually hand in.\n */\nfunction isAbort(error: unknown): boolean {\n const name = (error as { name?: string } | null)?.name\n return name === 'AbortError' || name === 'TimeoutError'\n}\n\n/** A step outcome that carries no tokens and no cost: the provider was never reached. */\nexport function aiJobStepSpentNothing(\n outcome: Pick<AiJobStepOutcome, 'usage' | 'estCostUsd'>,\n): boolean {\n const { usage } = outcome\n return (\n outcome.estCostUsd === 0 &&\n usage.inputTokens === 0 &&\n usage.outputTokens === 0 &&\n usage.cacheReadTokens === 0 &&\n usage.cacheWriteTokens === 0\n )\n}\n\n/**\n * Pause, claim, reserve, run, meter, record — one step of one job.\n *\n * The order is the invariant. The workspace's AI pause is asked BEFORE the\n * claim, so a paused workspace's job is neither claimed nor reserved for.\n * The reservation is taken AFTER the claim so a refused claim costs nothing,\n * and BEFORE the runner so the org's ceiling binds before a token is spent.\n * The cost is recorded before the step is, so a process cut off between the\n * two leaves the bill right and the step recoverable, never the other way\n * round.\n */\nexport async function runAiJobStep(\n firestore: Firestore,\n orgId: string,\n jobId: string,\n options: RunAiJobStepOptions,\n): Promise<AiJobStepRun> {\n const now = options.now ?? new Date()\n\n // A reservation the caller took before the job existed is a message\n // counted against the org; a step that fails before the provider is\n // reached spends nothing, so the message goes back rather than standing\n // as capacity the org paid for and never used.\n const releaseHeld = async () => {\n if (!options.reservation) return\n await releaseAssistMessage(firestore, orgId, options.reservation).catch(\n (releaseError) =>\n console.error('ai job release failed', { orgId, jobId, releaseError }),\n )\n }\n\n // A WORKSPACE WHOSE AI STAFF HAVE PAUSED runs none of its jobs (AGL-3037),\n // queued before the pause or not: the pause is a spend stop, and a job the\n // beat kept running would spend through it. The pause is lifted by staff\n // or by its own expiry and leaves the plan, the add-on and every\n // entitlement as they were, so a paused job is held, not failed: nothing is\n // claimed, reserved or run, and resuming AI runs it from where it stopped.\n // Read through the same verdict the jobs doors climb, before the claim.\n if (await aiJobPausedFor(orgId, jobId, options.staff === true)) {\n const held = await holdPausedAiJob(firestore, orgId, jobId, options.owner, now)\n if (!held) return { outcome: 'not-claimable' }\n await releaseHeld()\n return { outcome: 'paused', job: held }\n }\n\n const claimed = await claimNextStep(firestore, orgId, jobId, options.owner, now)\n if (!claimed) return { outcome: 'not-claimable' }\n const { job, stepIndex } = claimed\n const step = job.steps[stepIndex]\n\n const runner = aiJobRunnerForStep(job.kind, step.name)\n if (!runner) {\n await releaseHeld()\n return {\n outcome: 'failed',\n job: await failOurFailure(firestore, orgId, jobId, AI_JOB_NOT_AVAILABLE_COPY, {\n stepIndex,\n error: `no runner registered for kind ${job.kind}`,\n }, { reason: 'unavailable', stepCredits: 0 }, now),\n }\n }\n if ((step.attempts ?? 0) > AI_JOB_STEP_MAX_ATTEMPTS) {\n await releaseHeld()\n return {\n outcome: 'failed',\n job: await failOurFailure(firestore, orgId, jobId, AI_UPSTREAM_FAILURE_COPY, {\n stepIndex,\n error: `step ${step.name} exhausted ${AI_JOB_STEP_MAX_ATTEMPTS} attempts`,\n }, { reason: 'timeout', stepCredits: 0 }, now),\n }\n }\n\n const org =\n options.org ??\n (((await firestore.collection('orgs').doc(orgId).get()).data() ??\n {}) as Partial<AglynOrgBilling>)\n\n // A site that switched AI off runs none of its jobs (AGL-3028), queued\n // before the switch or not. Asked before the reservation, so the job fails\n // in words a member can act on and nothing is reserved, run or metered.\n if (await isAiOffForSite(firestore, org, job.hostId)) {\n await releaseHeld()\n return {\n outcome: 'failed',\n job: await failAiJob(firestore, orgId, jobId, AI_OFF_FOR_SITE_COPY, {\n stepIndex,\n error: `ai is switched off for site ${job.hostId}`,\n }, now),\n }\n }\n\n const entitled =\n checkEntitlement(org, 'aiAssist') || checkEntitlement(org, 'aiGenerative')\n\n let reservation: AssistReservation\n if (options.reservation) {\n reservation = options.reservation\n } else {\n try {\n // The creator's allotments on the job's site (AGL-2942): the step is\n // their spend, as the meter below attributes it.\n reservation = await reserveAssistMessage(firestore, orgId, entitled, now, org, {\n uid: job.createdBy,\n hostId: job.hostId ?? null,\n })\n } catch (error) {\n // The meter is unreachable, not refusing. Hand the step back; the\n // next beat asks again.\n console.error('ai job reservation failed', { orgId, jobId, error })\n const { job: requeued } = await recordStep(\n firestore, orgId, jobId, options.owner, stepIndex,\n { status: 'pending', creditsSpent: 0 },\n now,\n )\n return { outcome: 'requeued', job: requeued }\n }\n }\n // A refused step is the creator's refusal (AGL-2928), on their month,\n // beside the org counter the reservation moved; an admitted one records\n // nothing.\n recordUserAiRefusal(firestore, orgId, job.createdBy, reservation)\n if (!reservation.allowed) {\n const refusal = aiJobRefusalText(org, reservation)\n const parked = await markAiJobNeedsInput(firestore, orgId, jobId, refusal, now)\n // A cancel asked meanwhile ended the job instead of parking it (AGL-3616).\n if (parked.status === 'canceled') return { outcome: 'canceled', job: parked }\n // Nobody parked the job — the meter did — so the row carries no actor\n // rather than the creator's name on an act they did not perform. Only\n // when the job was not already parked for this: the reason the last\n // park stored survives the beat's re-queue, so a job the meter refuses\n // again on its hourly retry is one row, not one an hour; a different\n // ceiling is a new row. The meter names a ceiling on every refusal; the\n // guard narrows the type.\n if (reservation.refusedBy && job.error !== refusal) {\n await logAiJobNeedsInput(orgId, { uid: null }, {\n jobId,\n kind: job.kind,\n reason: reservation.refusedBy,\n })\n // The person is told once per pause (AGL-3660), not once an hour: a\n // job the beat re-queues and the meter refuses again keeps its reason.\n await announceAiJobTransition(parked, 'paused')\n }\n return { outcome: 'needs_input', job: parked }\n }\n\n // The model each step runs on (AGL-2942): the creator's pick where the\n // plan and the allotment allowlists the reservation read allow it, the\n // routing table otherwise. Handed to the runner as a function of the step\n // kind, because the runner — not the machine — knows what kind it runs.\n const bounds = {\n plan: resolveEffectivePlan(org),\n allotmentModels: reservation.allotment?.models ?? null,\n orgModels: reservation.allotment?.orgModels ?? null,\n }\n const modelFor = (kind: Parameters<typeof resolveAiModelChoice>[0]) =>\n resolveAiModelChoice(kind, job.model ?? null, bounds)?.model\n\n // A person's cancel stops the step (AGL-3616): the step's signal is the\n // caller's budget AND a watch on the job, so a cancel aborts the provider\n // call in flight, stops one about to start, and stops a write the step\n // asks `throwIfAiJobCanceled` about first.\n const stepSignal = new AbortController()\n const forwardBudget = () => stepSignal.abort(options.signal?.reason)\n if (options.signal?.aborted) forwardBudget()\n else options.signal?.addEventListener('abort', forwardBudget, { once: true })\n const stopWatch = watchAiJobCancel(\n () => getAiJob(firestore, orgId, jobId),\n stepSignal,\n options.cancelPollMs,\n )\n const canceledMidStep = () =>\n stepSignal.signal.aborted && isAiJobCanceledError(stepSignal.signal.reason)\n\n let outcome: AiJobStepOutcome\n // The runner's wall-clock time, recorded on the step beside its tokens\n // (AGL-2937): measured here, around the call, rather than asked of it.\n const runStarted = Date.now()\n try {\n outcome = await runner({\n job, stepIndex, now, signal: stepSignal.signal, firestore, org, modelFor,\n })\n } catch (error) {\n // The provider refused the request or the budget ended before it\n // answered: no usage came back, so nothing is metered and the message\n // goes back to the org. The provider's own words are already in the\n // log under the request id (AGL-2815).\n await releaseAssistMessage(firestore, orgId, reservation).catch((releaseError) =>\n console.error('ai job release failed', { orgId, jobId, releaseError }),\n )\n // Canceled mid-step (AGL-3616): the step is handed back unrun and the\n // record ends the job canceled — never failed, never retried.\n if (isAiJobCanceledError(error) || canceledMidStep()) {\n const { job: canceled } = await recordStep(\n firestore, orgId, jobId, options.owner, stepIndex,\n { status: 'pending', creditsSpent: 0 },\n now,\n )\n return { outcome: 'canceled', job: canceled }\n }\n const retryable =\n (error instanceof AiUpstreamError && error.retryable) || isAbort(error)\n if (retryable) {\n const { job: requeued } = await recordStep(\n firestore, orgId, jobId, options.owner, stepIndex,\n { status: 'pending', creditsSpent: 0 },\n now,\n )\n return { outcome: 'requeued', job: requeued }\n }\n return {\n outcome: 'failed',\n job: await failOurFailure(firestore, orgId, jobId, AI_UPSTREAM_FAILURE_COPY, {\n stepIndex,\n error,\n }, { reason: 'provider', stepCredits: 0, free: reservation.free ?? null }, now),\n }\n } finally {\n stopWatch()\n options.signal?.removeEventListener('abort', forwardBudget)\n }\n\n const latencyMs = Math.max(0, Date.now() - runStarted)\n\n // A step that failed before it reached the provider — the site it builds\n // from could not be read, say (AGL-2938) — spent nothing: its message goes\n // back and nothing is metered, exactly as for a request the provider\n // refused.\n if (outcome.failure && aiJobStepSpentNothing(outcome)) {\n await releaseAssistMessage(firestore, orgId, reservation).catch((releaseError) =>\n console.error('ai job release failed', { orgId, jobId, releaseError }),\n )\n await recordStep(\n firestore, orgId, jobId, options.owner, stepIndex,\n { status: 'failed', creditsSpent: 0, error: outcome.failure },\n now,\n )\n return {\n outcome: 'failed',\n job: await failOurFailure(firestore, orgId, jobId, outcome.failure, {\n stepIndex,\n error: `step failure before the provider: ${outcome.failure}`,\n ...(outcome.customerLimit ? { ours: false } : {}),\n }, { reason: 'step-failure', stepCredits: 0, free: reservation.free ?? null }, now),\n }\n }\n\n const tokens: AiJobStepTokenRun = {\n usage: outcome.usage,\n model: outcome.model,\n effort: outcome.effort ?? null,\n latencyMs,\n stopReason: outcome.stopReason,\n }\n\n // A step that stopped before the provider without failing — a site with no\n // room for the draft (AGL-2909), say — spent nothing too: its message goes\n // back and there is nothing to meter. What it returns is still recorded\n // below, as any step's is.\n const spentNothing = aiJobStepSpentNothing(outcome)\n if (spentNothing) {\n await releaseAssistMessage(firestore, orgId, reservation).catch((releaseError) =>\n console.error('ai job release failed', { orgId, jobId, releaseError }),\n )\n } else {\n // Tokens were spent, so the bill is written first and its failure is a\n // log line, never a failed job — the same rule the chat route keeps.\n try {\n await recordAssistCost(\n firestore,\n orgId,\n {\n route: 'ai/jobs',\n hostId: job.hostId ?? null,\n model: outcome.model,\n tier: entitled ? 'entitled' : 'free',\n usage: outcome.usage,\n docsPaths: [],\n stopReason: outcome.stopReason,\n deflected: false,\n // The account this job drew on, as the reservation decided it\n // (AGL-2925): a Free job lands on the owner's allowance and the\n // platform's day; a paid one on neither.\n free: reservation.free ?? null,\n // The step is the creator's spend, under the job's kind (AGL-2928).\n uid: job.createdBy,\n kind: job.kind,\n },\n now,\n )\n } catch (error) {\n console.error('ai job cost record failed', { orgId, jobId, error })\n }\n }\n const credits = assistCreditsFromUsd(outcome.estCostUsd)\n\n // A model that declined, or a step that got nothing usable out of the\n // model's answer (AGL-2938), fails the job with its own sentence. The\n // tokens are already on the bill above.\n if (outcome.refused || outcome.failure) {\n const message = outcome.refused ? AI_JOB_REFUSED_COPY : (outcome.failure as string)\n await recordStep(\n firestore, orgId, jobId, options.owner, stepIndex,\n { status: 'failed', creditsSpent: credits, error: message, tokens },\n now,\n )\n const detail = {\n stepIndex,\n error: outcome.refused ? 'stop_reason refusal' : `step failure: ${message}`,\n ...(outcome.customerLimit ? { ours: false } : {}),\n }\n // A model declining the brief is not our failure; a step's own is (AGL-3594).\n return {\n outcome: 'failed',\n job: outcome.refused\n ? await failAiJob(firestore, orgId, jobId, message, detail, now)\n : await failOurFailure(firestore, orgId, jobId, message, detail, {\n reason: 'step-failure', stepCredits: credits, free: reservation.free ?? null,\n }, now),\n }\n }\n\n // A guided site start whose step broke a building rule after its re-ask\n // fails, given back whole (AGL-3596): the person never saw the plan, so a\n // review asking them to look at it again is a review of nothing. The rule\n // and its findings go to the log for staff; the person reads one sentence,\n // and Try again starts a fresh job.\n if (outcome.review?.reason === 'doctrine' && aiJobAutoConfirms(job) && step.name !== AI_JOB_PLAN_STEP) {\n await recordStep(\n firestore, orgId, jobId, options.owner, stepIndex,\n {\n status: 'failed',\n creditsSpent: credits,\n error: AI_SITE_GUIDED_BUILD_FAILED_COPY,\n outputs: outcome.outputs,\n ...(spentNothing ? {} : { tokens }),\n },\n now,\n )\n return {\n outcome: 'failed',\n job: await failOurFailure(firestore, orgId, jobId, AI_SITE_GUIDED_BUILD_FAILED_COPY, {\n stepIndex,\n error: `doctrine refusal after the re-ask: ${JSON.stringify(outcome.review.findings ?? [])} ${outcome.review.message ?? ''}`,\n }, { reason: 'doctrine-refused', stepCredits: credits, free: reservation.free ?? null }, now),\n }\n }\n\n // A step that stopped for a person parks the job in the same write that\n // records it (AGL-2935): a plan review completes the step, so confirming\n // runs the next one; a doctrine or limit review hands it back, so trying\n // again runs the same one.\n //\n // A step that asks to continue (AGL-2910) is recorded as this pass — its\n // cost, its credits, its outputs, its audit rows — and handed back as\n // `pending`, so the next claim runs the same step again. A step that stopped\n // for a person never continues: the review is what it waits on. The pass\n // cap is what stops a runner that never finishes: past it the pass is\n // recorded as the step's last, and what it produced stands.\n // A build item that failed on our side gives back its own attempt's spend\n // before its row is written (AGL-3616), so the row records what was given.\n let itemRecord: AiJobItemRecord | undefined = aiJobSettlesByItem(job.kind) ? outcome.item : undefined\n // An item a person's cancel cut short is not our failure (AGL-3616): it is\n // given nothing back, and its row is the cancel's.\n if (itemRecord?.status === 'failed' && itemRecord.failure?.ours && !canceledMidStep()) {\n const slot = itemRecord.slot\n const row = (outcome.items ?? job.items ?? []).find((one) => one.slot === slot)\n const refund = await refundBuildCredits(firestore, orgId, job, {\n slot,\n attempt: row?.attempt ?? 1,\n credits: (row?.attemptCredits ?? 0) + credits,\n reason: itemRecord.failure.reason as AiJobRefundReason,\n free: reservation.free ?? null,\n now,\n })\n if (refund) itemRecord = { ...itemRecord, creditsRefunded: refund.credits, refundKey: refund.key }\n }\n\n const review = outcome.review\n const continuing =\n !review &&\n Boolean(outcome.continue) &&\n (step.passes ?? 0) + 1 < aiJobStepMaxPasses(job.kind)\n if (!review && outcome.continue && !continuing) {\n console.warn('ai job step reached its pass cap', { orgId, jobId, stepIndex })\n }\n const recorded = await recordStep(\n firestore, orgId, jobId, options.owner, stepIndex,\n {\n status: continuing || (review && review.reason !== 'plan') ? 'pending' : 'done',\n ...(continuing ? { continued: true } : {}),\n creditsSpent: credits,\n // A step that spent nothing ran no model, so it adds no run to the measure.\n ...(spentNothing ? {} : { tokens }),\n outputs: outcome.outputs,\n ...(outcome.plan ? { plan: outcome.plan } : {}),\n ...(review ? { review } : {}),\n ...(review && outcome.input ? { input: outcome.input } : {}),\n ...(outcome.sitePublish ? { sitePublish: outcome.sitePublish } : {}),\n ...(job.kind === 'import' && outcome.siteImport ? { siteImport: outcome.siteImport } : {}),\n ...(aiJobSettlesByItem(job.kind) && outcome.items ? { items: outcome.items } : {}),\n ...(itemRecord ? { item: itemRecord } : {}),\n // A park this call resolves at once (an auto-confirmed plan) or gives\n // back before it is told (a refusal on our side) is announced below.\n ...(review ? { quiet: true } : {}),\n },\n now,\n )\n const actor: AiActivityActor = options.actor ?? { uid: job.createdBy }\n for (const output of outcome.outputs) {\n await writeAiJobAudit(firestore, {\n action: 'ai.job.output',\n actorUid: job.createdBy,\n orgId,\n jobId,\n after: {\n resource: output.resource,\n id: output.id,\n versionId: output.versionId ?? null,\n hostId: output.hostId,\n label: output.label,\n credits,\n },\n })\n // The customer-visible row, one per output: the label names the thing,\n // never its text, which is the site's content.\n await logAiJobOutput(orgId, actor, {\n jobId,\n hostId: output.hostId,\n resource: {\n type: aiOutputTargetType(output.resource),\n id: output.id,\n name: output.label,\n versionId: output.versionId ?? null,\n },\n })\n }\n // A cancel asked while the step ran ended the job in that record (AGL-3616).\n if (recorded.job.status === 'canceled') return { outcome: 'canceled', job: recorded.job }\n if (review?.reason === 'plan' && recorded.job.status === 'needs_review' && aiJobAutoConfirms(job)) {\n // A guided site start confirms its own plan (AGL-3594): the resume\n // door's confirmation, by the job's creator, and the build queued.\n const resumed = await resumeAiJob(firestore, orgId, jobId, { uid: job.createdBy }, now)\n await writeAiJobAudit(firestore, {\n action: 'ai.job.resume',\n actorUid: job.createdBy,\n orgId,\n jobId,\n after: { status: resumed.job.status, confirmedPlan: true, autoConfirm: true },\n })\n return { outcome: 'done', job: resumed.job }\n }\n // A plan waiting for an answer only the person can give (AGL-3978): kept,\n // charged as any plan is, and asked of them; nothing about it failed.\n if (review && outcome.input && recorded.job.status === 'needs_input') {\n await logAiJobNeedsInput(orgId, { uid: null }, { jobId, kind: job.kind, reason: 'input' })\n if (recorded.parked) await announceAiJobTransition(recorded.job, 'needs-review')\n return { outcome: 'needs_input', job: recorded.job }\n }\n if (review && recorded.job.status === 'needs_review') {\n // Nobody parked the job but its own step, so the row names no actor, as\n // the meter's park does.\n await logAiJobNeedsInput(orgId, { uid: null }, {\n jobId,\n kind: job.kind,\n reason: review.reason,\n })\n // A plan the plan rules still refused, and a step that still broke a\n // building rule after its re-ask, are our failures (AGL-3594, AGL-3596):\n // given back before the person is told, so the notice can say so. Trying\n // again spends anew, and a second refusal gives back again, within the\n // day's bound.\n const ours = outcome.uncredited ? 'plan-refused' : review.reason === 'doctrine' ? 'doctrine-refused' : null\n const parked = ours\n ? await refundOurFailure(firestore, orgId, recorded.job, {\n reason: ours, stepCredits: credits, free: reservation.free ?? null, now,\n })\n : recorded.job\n if (recorded.parked) await announceAiJobTransition(parked, 'needs-review')\n return { outcome: 'needs_review', job: parked }\n }\n if (recorded.remaining > 0 || isAiJobTerminal(recorded.job.status)) {\n return { outcome: 'done', job: recorded.job }\n }\n // A build ends by its items (AGL-3616): done when anything was delivered.\n if (aiJobSettlesByItem(job.kind) && step.name !== AI_JOB_PLAN_STEP && recorded.job.items?.length) {\n // Items still open here are ones the pass cap cut off: the build stopped.\n const open = aiBuildSettlement(recorded.job.items ?? []).open\n const settled = await settleAiBuildJob(firestore, orgId, jobId, {\n now,\n free: reservation.free ?? null,\n ...(open ? { stopped: { message: AI_UPSTREAM_FAILURE_COPY, reason: 'timeout' as const } } : {}),\n })\n return { outcome: settled.status === 'failed' ? 'failed' : 'done', job: settled }\n }\n return {\n outcome: 'done',\n job: await completeAiJob(firestore, orgId, jobId, now),\n }\n}\n\n// ── The beat's sweep ──────────────────────────────────────────────────────\n\nexport interface DueAiJob {\n orgId: string\n jobId: string\n}\n\nexport interface SweepAiJobsOptions {\n firestore: Firestore\n /** The beat's identity for its leases. */\n owner: string\n now?: () => number\n budgetMs?: number\n maxJobs?: number\n /** Test seams; the defaults query Firestore and run the real step. */\n listDue?: () => Promise<DueAiJob[]>\n runStep?: (\n orgId: string,\n jobId: string,\n options: RunAiJobStepOptions,\n ) => Promise<AiJobStepRun>\n}\n\nexport interface SweepAiJobsResult {\n /** Candidates the queries returned. */\n due: number\n /** Steps this sweep actually ran to a recorded result. */\n ran: number\n /** Claims another owner held, or jobs with nothing left. */\n skipped: number\n /**\n * Jobs whose workspace's AI staff have paused (AGL-3037): held where they\n * stopped and moved to the back of the queue, to run once AI is resumed.\n */\n paused: number\n /** Candidates left untouched because the wall clock ran out. */\n remaining: number\n budgetExhausted: boolean\n}\n\n/**\n * Every job the beat should look at, oldest first: queued and running\n * across every org, then `needs_input` jobs that have rested long enough\n * for the org's standing to have changed. Two queries rather than one, so\n * a workspace with many parked jobs cannot fill the candidate list and\n * starve the queue behind it. Both use the collection-group index on\n * (status, updatedAt).\n *\n * The ordering is the cursor. A job this sweep touches has its `updatedAt`\n * moved and goes to the back of the next query; one it did not reach keeps\n * its place at the front. So there is no stored position to fall behind\n * or skip past — the same shape `media-move.ts` gives its budgeted loop,\n * where what was not done is simply what is asked for next.\n */\nexport async function listDueAiJobs(\n firestore: Firestore,\n nowMs: number,\n maxJobs = AI_JOB_SWEEP_MAX_JOBS,\n maxParked = AI_JOB_SWEEP_MAX_PARKED,\n): Promise<DueAiJob[]> {\n const group = firestore.collectionGroup(AI_JOBS_COLLECTION)\n const [active, parked] = await Promise.all([\n group\n .where('status', 'in', ['queued', 'running'])\n .orderBy('updatedAt', 'asc')\n .limit(maxJobs)\n .get(),\n group\n .where('status', '==', 'needs_input')\n .where('updatedAt', '<=', new Date(nowMs - AI_JOB_NEEDS_INPUT_RETRY_MS))\n .orderBy('updatedAt', 'asc')\n .limit(maxParked)\n .get(),\n ])\n return [...active.docs, ...parked.docs]\n .map((doc) => ({ orgId: String(doc.get('orgId') ?? ''), jobId: doc.id }))\n .filter((due) => due.orgId !== '')\n}\n\n/**\n * Run due steps until the budget is spent, and account for every candidate.\n *\n * The bound is wall clock, not a count, for the reason `media-move.ts`\n * gives: one step is one provider round trip of unknown length, so any\n * fixed number would be wrong for some brief. The budget is checked BETWEEN\n * jobs, and the step in flight gets the time that is left as its abort\n * signal, so the sweep still finishes whichever step it has started. At\n * least one candidate is always attempted, so a beat that yields having\n * done nothing cannot starve a queue behind a slow clock.\n *\n * A `needs_input` job is re-queued before its step is claimed, because the\n * claim admits only queued and running jobs; the reservation inside the\n * step is what decides whether the org's standing has changed.\n */\nexport async function sweepAiJobs(\n options: SweepAiJobsOptions,\n): Promise<SweepAiJobsResult> {\n const now = options.now ?? Date.now\n const budgetMs = options.budgetMs ?? AI_JOB_SWEEP_BUDGET_MS\n const startedAt = now()\n const listDue =\n options.listDue ??\n (() => listDueAiJobs(options.firestore, now(), options.maxJobs))\n const runStep =\n options.runStep ??\n ((orgId: string, jobId: string, stepOptions: RunAiJobStepOptions) =>\n runAiJobStep(options.firestore, orgId, jobId, stepOptions))\n\n const due = await listDue()\n const result: SweepAiJobsResult = {\n due: due.length,\n ran: 0,\n skipped: 0,\n paused: 0,\n remaining: 0,\n budgetExhausted: false,\n }\n // Jobs a run left with more of a timed step to do, in the order they ran.\n const again: DueAiJob[] = []\n const attempt = async (candidate: DueAiJob, left: number, first: boolean): Promise<void> => {\n const { orgId, jobId } = candidate\n try {\n const current = await getAiJob(options.firestore, orgId, jobId)\n // A step that needs more time than is left is not started (AGL-2907): a\n // provider call the abort cuts off is billed upstream and metered\n // nowhere. The job is left as it is, so it keeps its place at the front\n // of the next beat's queue.\n const minimumMs = current ? aiJobNextStepMinimumMs(current) : 0\n if (minimumMs > left) {\n if (first) result.remaining += 1\n return\n }\n // A job waiting for a person's answer (AGL-3978) is theirs to resume:\n // it rests again, at the back of the parked queue, so it never holds a\n // place a meter park could use.\n if (current?.status === 'needs_input' && current.input?.fields?.length) {\n await transition(options.firestore, orgId, jobId, (job) =>\n job.status === 'needs_input' && job.input?.fields?.length ? { updatedAt: new Date(now()) } : null,\n )\n if (first) result.skipped += 1\n return\n }\n if (current?.status === 'needs_input') {\n await transition(options.firestore, orgId, jobId, (job) =>\n job.status === 'needs_input' ? { status: 'queued', updatedAt: new Date(now()) } : null,\n )\n }\n const run = await runStep(orgId, jobId, {\n owner: options.owner,\n now: new Date(now()),\n signal: AbortSignal.timeout(left),\n })\n if (run.outcome === 'not-claimable') {\n if (first) result.skipped += 1\n return\n }\n if (run.outcome === 'paused') {\n if (first) result.paused += 1\n return\n }\n result.ran += 1\n // Only a step that says how long it needs is run again in this sweep:\n // one that does not could start with too little time left.\n if (\n run.outcome === 'done' &&\n run.job.status === 'queued' &&\n aiJobNextStepMinimumMs(run.job) > 0\n ) {\n again.push(candidate)\n }\n } catch (error) {\n // One job's fault is isolated, as the runner isolates one job's.\n console.error('ai job sweep step failed', { orgId, jobId, error })\n if (first) result.skipped += 1\n }\n }\n\n for (let index = 0; index < due.length; index += 1) {\n const elapsed = now() - startedAt\n if (index > 0 && elapsed >= budgetMs) {\n result.budgetExhausted = true\n result.remaining += due.length - index\n return result\n }\n await attempt(due[index], Math.max(1_000, budgetMs - elapsed), true)\n }\n // Once every due job has had its turn, a job whose timed step has more to\n // do runs again while the time it needs is left (AGL-2907): a page built a\n // section a pass otherwise waits a beat between sections. Bounded by the\n // budget, and by the sweep's candidate count so no clock is trusted alone.\n for (let round = 0; again.length && round < AI_JOB_SWEEP_MAX_JOBS; round += 1) {\n const elapsed = now() - startedAt\n if (elapsed >= budgetMs) {\n result.budgetExhausted = true\n break\n }\n await attempt(again.shift() as DueAiJob, budgetMs - elapsed, false)\n }\n return result\n}\n"],"names":["FieldValue","createResourceUid","checkEntitlement","assistCreditsFromUsd","assistFreeTasteRefusalText","assistHardCapRefusalText","assistRefusedByHardCap","AI_JOB_PLAN_STEP","AI_JOB_TERMINAL_STATUSES","aiJobInputProblems","aiPlanWithInput","resolveEffectivePlan","aiOverageReservationRefusal","aiAllotmentRefusalText","AI_OFF_FOR_SITE_COPY","isAiOffForSite","resolveAiModelChoice","aiOutputTargetType","logAiJobCanceled","logAiJobCreated","logAiJobNeedsInput","logAiJobOutput","AI_UPSTREAM_FAILURE_COPY","AiUpstreamError","recordUserAiRefusal","AI_JOB_INLINE_BUDGET_MS","AI_JOB_SWEEP_BUDGET_MS","aiMintJobDraftIds","AI_JOB_TEXT_STEP_MINIMUM_MS","runAiJobTextStep","AI_JOB_SEO_STEP_MINIMUM_MS","AI_JOB_THEME_STEP_MINIMUM_MS","assistExchangeExpiry","assistUsageDay","assistUsageMonth","recordAssistCost","releaseAssistMessage","reserveAssistMessage","aiJobItemRefundKey","aiJobRefundCredits","refundJobCredits","aiBuildSettlement","aiBuildUnits","aiBuildRetryLedger","aiBuildCreditEstimate","aiLedgerUnits","aiJobCreditEstimate","freeAssistAccount","aiJobAutoConfirms","AI_SITE_GUIDED_BUILD_FAILED_COPY","aiCustomerSafeCopy","aiCustomerSafeItem","aiCustomerSafeReview","aiSiteStartInputsOf","aiSiteImportWire","addAdminAudit","AI_JOB_CANCELED_ITEM_NOTE","aiJobCancelPending","isAiJobCanceledError","watchAiJobCancel","clearAiEmptySiteSweep","scheduleAiEmptySiteSweep","AI_JOBS_COLLECTION","AI_JOB_LEASE_MS","AI_JOB_STEP_MAX_ATTEMPTS","AI_JOB_STEP_MAX_PASSES","AI_JOB_STEP_RESERVE_CREDITS","AI_JOB_SWEEP_MAX_JOBS","AI_JOB_SWEEP_MAX_PARKED","AI_JOB_NEEDS_INPUT_RETRY_MS","AI_JOB_NOT_AVAILABLE_COPY","AI_JOB_REFUSED_COPY","toMillis","value","Date","getTime","parsed","parse","Number","isFinite","toIso","millis","toISOString","isAiJobTerminal","status","includes","AI_PLANNED_JOB_KINDS","stepRunners","Map","stepMinimums","stepRunMinimums","planStepRunner","planStepMinimumMs","stepPassCaps","registerAiJobStep","kind","runner","registration","set","minimumMs","delete","minimumMsFor","aiJobStepMinimumMs","stepName","get","aiJobStepRunMinimumMs","job","least","forRun","Math","max","aiJobNextStepMinimumMs","index","nextStepIndex","steps","name","aiJobStepRunnerFor","registerAiJobStepPasses","maxPasses","floor","aiJobStepMaxPasses","registerAiJobPlanStep","pauseReader","registerAiJobPauseReader","reader","transitionListener","registerAiJobTransitionListener","listener","aiJobTransitionListenerRegistered","noteAiJobEnded","firestore","now","announceAiJobTransition","to","failure","error","console","orgId","jobId","$id","aiJobPauseReaderRegistered","aiJobPausedFor","staff","aiJobRunnerForStep","aiJobStepNames","has","context","runAiJobThemeStep","runAiJobSeoStep","jobsCollection","collection","doc","jobFrom","snapshot","exists","data","id","createAiJob","input","brief","trim","Error","draftIds","map","startedAt","endedAt","creditsSpent","attempts","ref","hostId","inputs","model","outputs","creditsReserved","length","createdBy","createdAt","updatedAt","expiresAt","lease","creditsConfirmed","uid","email","createdByEmail","briefLength","getAiJob","listAiJobs","options","limit","min","query","where","statuses","read","from","count","orderBy","docs","filter","isFailedPrecondition","warn","recent","AI_JOBS_HOST_FALLBACK_SCAN","slice","code","aiJobSummary","leaseUntil","until","batch","step","refundedCredits","page","running","plan","labels","proposedAt","confirmedAt","confirmedBy","review","fields","applied","at","by","versions","staged","sitePublish","siteInputs","autoConfirm","items","orchestration","publishAsked","cancelRequested","siteRemoved","siteImport","leaseIsLive","owner","nowMs","anyLeaseIsLive","Boolean","aiJobCanceledPatch","request","aiCanceledItems","row","note","cancelRequestOf","aiJobCancelRequest","findIndex","claimableStepIndex","claimNextStep","ended","claimed","runTransaction","tx","patch","merge","stepIndex","heartbeatStep","holdPausedAiJob","held","aiApplyJobItemRecord","ledger","record","credits","slot","attemptCredits","creditsRefunded","refundKey","undefined","degradedBy","settledAt","AI_JOB_STEP_LAST_RUNS","AI_JOB_STEP_STOP_REASON_MAX_CHARS","addAiJobStepTokens","current","run","kept","Array","isArray","lastRuns","stopReason","usage","inputTokens","cachedRead","cacheReadTokens","cacheWrite","cacheWriteTokens","output","outputTokens","effort","latencyMs","runs","recordStep","recorded","continued","passes","tokens","remaining","item","settled","ownsLease","canceling","parksForReview","parkedAs","message","reason","parked","quiet","transition","apply","changed","completeAiJob","failAiJob","detail","ours","String","markAiJobNeedsInput","cancelAiJob","actor","firstAsk","result","inFlight","refundOurFailure","delivered","some","resource","jobCredits","stepCredits","alreadyRefunded","free","ordinal","refunds","refund","month","day","refunded","refundReason","failOurFailure","aiJobSettlesByItem","settleAiBuildJob","stopped","aiBuildRetryable","AI_BUILD_NOTHING_BUILT_COPY","AI_SITE_IMPORT_NOTHING_BUILT_COPY","AI_BUILD_NOT_REACHED_COPY","refundBuildCredits","created","jobDay","attempt","key","aiBuildPlanCredits","find","given","sitePages","op","planCredits","parts","owed","size","values","reduce","total","part","nothing","retryAiBuildJob","retried","next","slots","Set","resumeAiJob","resumed","Object","keys","outstanding","pending","proposed","confirming","reducePlan","confirmed","publish","publishConfirmed","recordAiJobApplied","writeAiJobAudit","entry","actorUid","actorEmail","action","target","after","serverTimestamp","aiJobRefusalText","org","reservation","refusedBy","overage","text","allotment","refusal","scope","taste","budgetUsd","isAbort","aiJobStepSpentNothing","outcome","estCostUsd","runAiJobStep","itemRecord","releaseHeld","catch","releaseError","entitled","requeued","allowed","bounds","allotmentModels","models","orgModels","modelFor","stepSignal","AbortController","forwardBudget","abort","signal","aborted","addEventListener","once","stopWatch","cancelPollMs","canceledMidStep","runStarted","canceled","retryable","removeEventListener","customerLimit","spentNothing","route","tier","docsPaths","deflected","refused","JSON","stringify","findings","one","continuing","continue","versionId","label","type","confirmedPlan","uncredited","open","listDueAiJobs","maxJobs","maxParked","group","collectionGroup","active","Promise","all","due","sweepAiJobs","budgetMs","listDue","runStep","stepOptions","ran","skipped","paused","budgetExhausted","again","candidate","left","first","AbortSignal","timeout","push","elapsed","round","shift"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-jobs.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\n/**\n * `FieldValue` straight from the SDK rather than through `./firebase-admin`,\n * for the reason `assist-usage.ts` gives: the default-app initialization\n * would ride into every unit test that touches a job, and the statics need\n * no app at all.\n */\nimport { FieldValue } from 'firebase-admin/firestore'\nimport { createResourceUid } from '@aglyn/aglyn/app-utils/create-resource-uid'\nimport { checkEntitlement } from '@aglyn/aglyn/server'\nimport {\n assistCreditsFromUsd,\n assistFreeTasteRefusalText,\n assistHardCapRefusalText,\n assistRefusedByHardCap,\n} from '../usage/assist-credits'\nimport {\n AI_JOB_PLAN_STEP,\n AI_JOB_TERMINAL_STATUSES,\n type AiJob,\n type AiJobApplied,\n type AiJobCancelRequest,\n type AiJobInputRequest,\n type AiJobItemLedger,\n type AiJobKind,\n type AiJobOutput,\n type AiJobPlan,\n type AiJobReview,\n type AiJobSitePublish,\n type AiJobStatus,\n type AiJobStep,\n type AiJobStepTokens,\n type AiJobSummary,\n} from '../model/ai-jobs.types'\nimport { aiJobInputProblems, aiPlanWithInput } from '../model/ai-job-input'\nimport type { AiJobCreditsConfirmation } from '../model/ai-credit-estimate'\nimport type { AglynOrgBilling } from '@aglyn/aglyn/foundation/definitions/org-billing.types'\nimport { resolveEffectivePlan } from '@aglyn/aglyn/app-utils/plan-entitlements'\nimport { aiOverageReservationRefusal } from '../billing/ai-overage-gate'\nimport { aiAllotmentRefusalText } from '../model/ai-allotments'\nimport { AI_OFF_FOR_SITE_COPY, isAiOffForSite } from '../model/ai-site-switch'\nimport { resolveAiModelChoice } from '../providers/model-choice'\nimport { aiOutputTargetType } from '../activity/ai-activity-actions'\nimport {\n logAiJobCanceled,\n logAiJobCreated,\n logAiJobNeedsInput,\n logAiJobOutput,\n type AiActivityActor,\n} from '../activity/ai-activity'\nimport {\n AI_UPSTREAM_FAILURE_COPY,\n AiUpstreamError,\n type AiEffort,\n type AiUsage,\n} from '../runtime/ai-runtime'\nimport { recordUserAiRefusal } from '../usage/ai-usage-by-user'\nimport { AI_JOB_INLINE_BUDGET_MS, AI_JOB_SWEEP_BUDGET_MS } from './ai-job-budget'\nimport { aiMintJobDraftIds } from './ai-job-draft-ids'\nimport {\n AI_JOB_TEXT_STEP_MINIMUM_MS,\n runAiJobTextStep,\n type AiJobStepOutcome,\n type AiJobStepRunner,\n} from './ai-job-text-step'\nimport { AI_JOB_SEO_STEP_MINIMUM_MS } from './ai-job-seo-budget'\nimport { AI_JOB_THEME_STEP_MINIMUM_MS } from './ai-job-theme-budget'\nimport {\n assistExchangeExpiry,\n assistUsageDay,\n assistUsageMonth,\n recordAssistCost,\n releaseAssistMessage,\n reserveAssistMessage,\n type AssistReservation,\n} from '../usage/assist-usage'\nimport {\n aiJobItemRefundKey,\n aiJobRefundCredits,\n refundJobCredits,\n type AiJobRefundReason,\n} from '../usage/assist-job-refund'\nimport {\n aiBuildSettlement,\n aiBuildUnits,\n aiBuildRetryLedger,\n aiBuildCreditEstimate,\n aiLedgerUnits,\n aiJobCreditEstimate,\n} from '../model/ai-build-job'\nimport type { AiJobItemOutcome } from './ai-job-text-step'\nimport { freeAssistAccount, type FreeAssistAccount } from '../usage/assist-free-taste'\nimport { aiJobAutoConfirms } from './ai-job-auto-confirm'\nimport {\n AI_SITE_GUIDED_BUILD_FAILED_COPY,\n aiCustomerSafeCopy,\n aiCustomerSafeItem,\n aiCustomerSafeReview,\n} from '../model/ai-job-failure-copy'\nimport { aiSiteStartInputsOf } from '../model/ai-site-start'\nimport { aiSiteImportWire, type AiSiteImportState } from '../model/ai-site-import'\nimport type { AiSitePhotos } from '../model/ai-site-photos'\nimport { addAdminAudit } from '@aglyn/tenant-data-admin/server/admin-audit-write'\nimport {\n AI_JOB_CANCELED_ITEM_NOTE,\n AiJobCanceledError,\n aiJobCancelPending,\n isAiJobCanceledError,\n watchAiJobCancel,\n} from './ai-job-cancel'\nimport { clearAiEmptySiteSweep, scheduleAiEmptySiteSweep } from './ai-empty-site-sweep'\n\n/**\n * AI generation jobs — the Firestore state machine (AGL-2904).\n *\n * One document per job under the org, written only by this module through\n * the Admin SDK; the rules let a member READ their org's jobs and nobody\n * write them. The console route creates a job and runs its first step\n * inline when it can; the AI jobs beat, a console route of its own, claims\n * and runs whatever is queued, across every org, inside a wall-clock budget\n * (`ai-jobs-beat.ts`). Both go through\n * `runAiJobStep`, so there is exactly one place a step is claimed, metered,\n * run and recorded.\n *\n * ── Credits, per step ────────────────────────────────────────────────────\n *\n * Every step is an assist message to the meter: `reserveAssistMessage`\n * BEFORE the provider is called — the same read-and-increment transaction\n * the chat route takes, so a job cannot slip past the org's monthly ceiling\n * by being asynchronous — and `recordAssistCost` after, at the serving\n * model's rates, so the usage rollup and the invoice see a job's tokens\n * exactly as they see a chat turn's. The credit figure on the job is that\n * cost at the plan's credit rate, rounded up like every other credit.\n *\n * A refused reservation is NOT a failure. The org is out of credits, over\n * its cap, or has switched overage off — all things a member can change —\n * so the job parks as `needs_input` with the refusal in customer-safe words\n * and the beat tries it again once it has rested. `failed` is reserved for\n * a step that cannot succeed by waiting.\n *\n * ── What a step may not do ───────────────────────────────────────────────\n *\n * A step runner writes drafts and returns. It does not touch the job\n * document, the meter or the lease: the machine owns those, so a runner\n * written next month cannot forget the reservation or record itself done\n * while the provider is still generating.\n */\n\nexport const AI_JOBS_COLLECTION = 'aiJobs'\n\n/**\n * How long a claim holds the step: longer than the longest any holder can\n * still be running it, which is the beat route's 300 s function ceiling\n * (AGL-3026). A lease that ran out under a live holder would let the next\n * beat claim the same step and call the provider for it a second time, and\n * the beat fires every minute while a sweep may run for most of five, so\n * this is what keeps overlapping beats apart. A step abandoned by a process\n * that died is recovered once it runs out, by the first beat after that.\n */\nexport const AI_JOB_LEASE_MS = 330_000\n\n/** A step handed back by a retryable provider failure this many times fails. */\nexport const AI_JOB_STEP_MAX_ATTEMPTS = 3\n\n/**\n * The most passes one step may take by asking to continue (AGL-2910), for a\n * kind that registers no bound of its own. A site audit's passes are its\n * units of generated work; this bounds a runner that never finishes, not a\n * real site.\n */\nexport const AI_JOB_STEP_MAX_PASSES = 40\n\n/**\n * The nominal credit figure shown as held per outstanding step. Not a\n * bound — the reservation and the monthly ceiling are — but the console\n * must show a running job as costing something before its bill is known.\n *\n * NOMINAL, and nothing reads it as more (AGL-3030): the band, the Free\n * taste's account allowance and every refusal are measured on recorded\n * spend, and the one real reservation a step takes is a message, counted\n * when the step is claimed and settled — metered or handed back — before the\n * step is recorded. So a job holds nothing real between steps, and a job\n * parked for a person shows nothing held either: `recordStep` releases the\n * figure when a step parks for review, and `resumeAiJob` holds it again for\n * the steps a person's resume queues.\n */\nexport const AI_JOB_STEP_RESERVE_CREDITS = 50\n\n/**\n * The inline doors' budget and the beat's, declared beside the time a step is\n * planned against (`ai-job-budget.ts`, AGL-3036) so a step's least time and\n * the budgets it has to fit are read in one place.\n */\nexport { AI_JOB_INLINE_BUDGET_MS, AI_JOB_SWEEP_BUDGET_MS }\n\n/** Jobs one beat reads as candidates; the budget usually stops it first. */\nexport const AI_JOB_SWEEP_MAX_JOBS = 25\n\n/** Parked jobs one beat re-queues, on top of the active candidates. */\nexport const AI_JOB_SWEEP_MAX_PARKED = 5\n\n/** How long a `needs_input` job rests before the beat tries its reservation again. */\nexport const AI_JOB_NEEDS_INPUT_RETRY_MS = 60 * 60_000\n\nexport const AI_JOB_NOT_AVAILABLE_COPY =\n 'This kind of AI job is not available yet.'\nexport const AI_JOB_REFUSED_COPY =\n 'The model declined this brief. Try rephrasing it.'\n\ntype Firestore = FirebaseFirestore.Firestore\ntype Transaction = FirebaseFirestore.Transaction\n\n/** A stored instant as the SDK returns it, or as this module wrote it. */\ntype Instant = Date | { toMillis(): number } | number | string | null | undefined\n\nfunction toMillis(value: Instant): number | null {\n if (value == null) return null\n if (value instanceof Date) return value.getTime()\n if (typeof value === 'number') return value\n if (typeof value === 'string') {\n const parsed = Date.parse(value)\n return Number.isFinite(parsed) ? parsed : null\n }\n return typeof value.toMillis === 'function' ? value.toMillis() : null\n}\n\nfunction toIso(value: Instant): string | null {\n const millis = toMillis(value)\n return millis === null ? null : new Date(millis).toISOString()\n}\n\nexport function isAiJobTerminal(status: AiJobStatus): boolean {\n return AI_JOB_TERMINAL_STATUSES.includes(status)\n}\n\n// ── The step runner registry ──────────────────────────────────────────────\n\n// The step a planned kind runs first (AGL-2935), named in the model so the\n// console reads a job's phase off its steps without loading this machine.\nexport { AI_JOB_PLAN_STEP }\n\n/**\n * The kinds that build site structure, and so plan before they generate\n * (AGL-2935): the plan their first step proposes is what their generation\n * step executes, once a person has confirmed it.\n */\nexport const AI_PLANNED_JOB_KINDS: readonly AiJobKind[] = [\n 'page',\n 'site',\n 'component',\n 'layout',\n 'template',\n 'form',\n 'email',\n 'build',\n]\n\nconst stepRunners = new Map<AiJobKind, AiJobStepRunner>()\nconst stepMinimums = new Map<AiJobKind, number>()\nconst stepRunMinimums = new Map<AiJobKind, (job: AiJob) => number>()\nlet planStepRunner: AiJobStepRunner | null = null\n/** The plan step's least time: one step every planned kind shares, so kept by the step, not by a kind. */\nlet planStepMinimumMs = 0\nconst stepPassCaps = new Map<AiJobKind, number>()\n\nexport interface AiJobStepRegistration {\n /**\n * The least time, in milliseconds, one run of the kind's step needs before\n * it starts (AGL-2907): its generation's worst case at the rates\n * `ai-job-budget.ts` assumes — every model call it may make, with the step's\n * own reads and writes (AGL-3036). The beat leaves the step queued rather\n * than start it with less time left, and an inline door leaves it for the\n * beat, because a provider call the caller's budget cuts off is still\n * generated and billed upstream while the meter records nothing.\n *\n * Every generation step the plugin registers declares one (AGL-3035), and a\n * spec over the console surface's registrations fails on a step that does\n * not. Absent, the step starts whenever it is claimed.\n */\n minimumMs?: number\n /**\n * For a step whose runs need different times, the least time the run this\n * job would make next needs (AGL-3035): a page job's pass that builds a\n * layout needs a layout's time, where its section passes need a section's,\n * and a scaffold's pass needs what the unit it hands on needs. Never read as\n * less than `minimumMs`, which is then the least any run of the step needs.\n */\n minimumMsFor?: (job: AiJob) => number\n}\n\n/** Idempotent per kind; last registration wins. */\nexport function registerAiJobStep(\n kind: AiJobKind,\n runner: AiJobStepRunner,\n registration: AiJobStepRegistration = {},\n): void {\n stepRunners.set(kind, runner)\n if (registration.minimumMs && registration.minimumMs > 0) {\n stepMinimums.set(kind, registration.minimumMs)\n } else {\n stepMinimums.delete(kind)\n }\n if (registration.minimumMsFor) {\n stepRunMinimums.set(kind, registration.minimumMsFor)\n } else {\n stepRunMinimums.delete(kind)\n }\n}\n\n/**\n * The least time a step needs before it starts: the plan step's own minimum\n * for the plan step of any kind (AGL-3026), and the kind's registered\n * minimum for the kind's own step — the least any run of it needs.\n */\nexport function aiJobStepMinimumMs(kind: AiJobKind, stepName: string): number {\n return stepName === AI_JOB_PLAN_STEP ? planStepMinimumMs : (stepMinimums.get(kind) ?? 0)\n}\n\n/**\n * The least time the next run of a job's own step needs (AGL-3035): what the\n * kind registered, or more where the kind says this job's next run needs\n * more. A step that delegates a unit to another kind — a page job's creation,\n * a scaffold's page — asks this of the job it derives for the unit.\n */\nexport function aiJobStepRunMinimumMs(job: AiJob): number {\n const least = stepMinimums.get(job.kind) ?? 0\n const forRun = stepRunMinimums.get(job.kind)\n return forRun ? Math.max(least, forRun(job)) : least\n}\n\n/** The least time the job's next step needs; 0 when no step is left to run. */\nexport function aiJobNextStepMinimumMs(job: AiJob): number {\n const index = nextStepIndex(job)\n if (index === -1) return 0\n return job.steps[index].name === AI_JOB_PLAN_STEP ? planStepMinimumMs : aiJobStepRunMinimumMs(job)\n}\n\nexport function aiJobStepRunnerFor(kind: AiJobKind): AiJobStepRunner | null {\n return stepRunners.get(kind) ?? null\n}\n\n/**\n * A kind whose step works through more units than the default bounds\n * (AGL-2911). A scaffold's unit is a whole page and its site is eight of\n * them, where an audit's is one page's listing, so the runaway bound the\n * default gives an audit would cut a real site short. The kind registers its\n * own beside its runner, so the machine keeps no list of kinds and the bound\n * sits with the plan that explains it.\n */\nexport function registerAiJobStepPasses(kind: AiJobKind, maxPasses: number): void {\n stepPassCaps.set(kind, Math.max(1, Math.floor(maxPasses)))\n}\n\n/** The passes a kind's step may take; the default where it registered none. */\nexport function aiJobStepMaxPasses(kind: AiJobKind): number {\n return stepPassCaps.get(kind) ?? AI_JOB_STEP_MAX_PASSES\n}\n\n/**\n * The plan step every planned kind runs first. Registered by its own module\n * (`ai-job-plan-step.ts`), which the plugin's server surface loads, so this\n * machine — and every spec that drives it — never loads the inventory\n * reader and the Admin SDK behind it. `null` unregisters it, minimum and all.\n *\n * `minimumMs` means for the plan step what it means for a kind's step\n * (AGL-3026): a plan answered at its ceiling takes longer than an inline\n * door's budget, so the step says so, and both doors leave it for the beat.\n */\nexport function registerAiJobPlanStep(\n runner: AiJobStepRunner | null,\n registration: AiJobStepRegistration = {},\n): void {\n planStepRunner = runner\n planStepMinimumMs =\n runner && registration.minimumMs && registration.minimumMs > 0 ? registration.minimumMs : 0\n}\n\n/**\n * Whether AI generation is paused for a workspace, as the jobs doors read it\n * (AGL-3037). `staff` is a verified staff caller on an inline door; the beat\n * has none.\n */\nexport type AiJobPauseReader = (input: { orgId: string; staff: boolean }) => Promise<boolean>\n\nlet pauseReader: AiJobPauseReader | null = null\n\n/**\n * The reader `runAiJobStep` asks before it claims a step. Registered by its\n * own module (`ai-jobs-pause.ts`), which the plugin's console surface calls,\n * so this machine — and every spec that drives it — never loads the Admin SDK\n * the lockdown reads run on. `null` unregisters it; a machine with no reader\n * runs every job, as a process with no lockdown carrier would.\n */\nexport function registerAiJobPauseReader(reader: AiJobPauseReader | null): void {\n pauseReader = reader\n}\n\n// ── Telling the person (AGL-3593) ──────────────────────────────────────────\n\n/**\n * A change a person is told about: the job's plan waits for them (or a step\n * stopped for their decision), the job finished, it stopped, or it PAUSED\n * because the meter refused its next step (AGL-3660) — out of credits, at a\n * cap — which a person resolves by getting more and resuming the same job.\n */\nexport type AiJobTransition = 'needs-review' | 'done' | 'failed' | 'paused'\n\n/**\n * Why a job failed, for staff only (never shown to the customer): whether\n * the failure was ours — a provider error, a step that produced nothing, a\n * build that delivered nothing — rather than the model declining the brief\n * or the site having AI switched off, and what the runner actually said.\n */\nexport interface AiJobFailureCause {\n ours: boolean\n stepIndex: number | null\n error: string | null\n}\n\nexport type AiJobTransitionListener = (input: {\n job: AiJob\n to: AiJobTransition\n /** Present on `failed` only. */\n failure?: AiJobFailureCause\n}) => Promise<void> | void\n\nlet transitionListener: AiJobTransitionListener | null = null\n\n/**\n * The listener told each time a job ENTERS `needs_review`, `done` or `failed`,\n * or is paused by the meter (`needs_input`, once per reason)\n * — once per entry, from the write that made it, so a stream that re-reads\n * the job or a beat that sees it again tells nobody twice. Registered by its\n * own module (`ai-jobs-notify.ts`) from the console's server surface, as the\n * pause reader is, so this machine never loads the notification fan-out.\n * `null` unregisters it.\n */\nexport function registerAiJobTransitionListener(listener: AiJobTransitionListener | null): void {\n transitionListener = listener\n}\n\n/** Whether a transition listener is registered: what a surface that runs jobs must have. */\nexport function aiJobTransitionListenerRegistered(): boolean {\n return transitionListener !== null\n}\n\n/**\n * A job just ENDED `canceled` or `failed`, in the write the caller made\n * (AGL-3936): a site start that built nothing schedules the removal of the\n * empty site it was for, an hour out (`ai-empty-site-sweep.ts`). Every write\n * that ends a job calls this once, after its transaction commits. Never\n * throws.\n */\nasync function noteAiJobEnded(firestore: Firestore, job: AiJob | null, now: Date): Promise<void> {\n if (!job || (job.status !== 'canceled' && job.status !== 'failed')) return\n await scheduleAiEmptySiteSweep(firestore, job, now)\n}\n\n/** Tells the listener; a listener that throws never fails the write it follows. */\nasync function announceAiJobTransition(\n job: AiJob,\n to: AiJobTransition,\n failure?: AiJobFailureCause,\n): Promise<void> {\n if (!transitionListener) return\n try {\n await transitionListener(failure ? { job, to, failure } : { job, to })\n } catch (error) {\n console.error('ai job transition listener failed', { orgId: job.orgId, jobId: job.$id, to, error })\n }\n}\n\n/** Whether a pause reader is registered: what a surface that runs jobs must have. */\nexport function aiJobPauseReaderRegistered(): boolean {\n return pauseReader !== null\n}\n\n/**\n * The registered reader's answer. A read that throws is not a pause: the\n * lockdown carriers fail open on an unreachable Firestore, and so does this.\n */\nasync function aiJobPausedFor(orgId: string, jobId: string, staff: boolean): Promise<boolean> {\n if (!pauseReader) return false\n try {\n return await pauseReader({ orgId, staff })\n } catch (error) {\n console.error('ai job pause read failed', { orgId, jobId, error })\n return false\n }\n}\n\n/** The runner for one step of one job: the plan step by its name, else the kind's own. */\nexport function aiJobRunnerForStep(kind: AiJobKind, stepName: string): AiJobStepRunner | null {\n return stepName === AI_JOB_PLAN_STEP ? planStepRunner : aiJobStepRunnerFor(kind)\n}\n\n/**\n * The step names a job of this kind carries, in order. A planned kind plans\n * only once something can build the plan: until its generation runner is\n * registered, its job fails fast and free, rather than spending on a plan\n * nothing will execute.\n */\nexport function aiJobStepNames(kind: AiJobKind): string[] {\n if (kind === 'text') return ['draft']\n return AI_PLANNED_JOB_KINDS.includes(kind) && planStepRunner && stepRunners.has(kind)\n ? [AI_JOB_PLAN_STEP, 'generate']\n : ['generate']\n}\n\n/**\n * The text step (AGL-2904). Every kind with no runner refuses until its issue\n * lands. Its least time fits an inline door's budget (AGL-3035), so the doors\n * run a text job where it is asked for.\n */\nregisterAiJobStep('text', runAiJobTextStep, { minimumMs: AI_JOB_TEXT_STEP_MINIMUM_MS })\n\n/**\n * The theme step (AGL-2938), loaded the first time a theme job runs rather\n * than when this module does: it builds the brand themes it measures\n * contrast against and may read a site's logo, and a process that never runs\n * a theme job should pay for neither. Its least time is declared apart from\n * the step (`ai-job-theme-budget.ts`, AGL-3035) for the same reason.\n */\nregisterAiJobStep(\n 'theme',\n async (context) => {\n const { runAiJobThemeStep } = await import('./ai-job-theme-step')\n return runAiJobThemeStep(context)\n },\n { minimumMs: AI_JOB_THEME_STEP_MINIMUM_MS },\n)\n\n/**\n * The SEO step (AGL-2910), loaded the first time an SEO job runs rather than\n * when this module does: it reads pages, versions and layouts, and a process\n * that never runs one should load none of that. Its least time is declared\n * apart from the step (`ai-job-seo-budget.ts`, AGL-3035) for the same reason.\n */\nregisterAiJobStep(\n 'seo',\n async (context) => {\n const { runAiJobSeoStep } = await import('./ai-job-seo-step')\n return runAiJobSeoStep(context)\n },\n { minimumMs: AI_JOB_SEO_STEP_MINIMUM_MS },\n)\n\n// ── Documents ─────────────────────────────────────────────────────────────\n\nfunction jobsCollection(firestore: Firestore, orgId: string) {\n return firestore.collection('orgs').doc(orgId).collection(AI_JOBS_COLLECTION)\n}\n\nfunction jobFrom(\n snapshot: FirebaseFirestore.DocumentSnapshot,\n): AiJob | null {\n if (!snapshot.exists) return null\n const data = snapshot.data() as Omit<AiJob, '$id'>\n return { ...data, $id: snapshot.id }\n}\n\nexport interface CreateAiJobInput {\n orgId: string\n hostId?: string | null\n kind: AiJobKind\n brief: string\n inputs?: Record<string, unknown>\n /** The model the creator picked (AGL-2942); absent or `null` for Auto. */\n model?: string | null\n createdBy: string\n /** The creator's address, for the activity row; the document stores the uid only. */\n createdByEmail?: string | null\n /** A Free start the creator chose to build past what was left (AGL-3722). */\n creditsConfirmed?: AiJobCreditsConfirmation | null\n}\n\n/**\n * A new job, `queued`, with its step plan laid out and nothing run.\n *\n * The activity row is written once the document is, so the feed never names\n * a job that does not exist. The brief's length stands in for the brief,\n * which is the customer's own text and is not logged (AGL-2929).\n */\nexport async function createAiJob(\n firestore: Firestore,\n input: CreateAiJobInput,\n now = new Date(),\n): Promise<AiJob> {\n const brief = input.brief.trim()\n if (!brief) throw new Error('an AI job needs a brief')\n // The drafts the kind always writes are named before any step runs, on the\n // step that writes them, so a run repeated after its write finds them.\n const draftIds = aiMintJobDraftIds(input.kind)\n const steps: AiJobStep[] = aiJobStepNames(input.kind).map((name) => ({\n name,\n status: 'pending',\n startedAt: null,\n endedAt: null,\n creditsSpent: 0,\n error: null,\n attempts: 0,\n ...(draftIds && name !== AI_JOB_PLAN_STEP ? { draftIds } : {}),\n }))\n // A job is a resource the console lists and routes to, named as one.\n const ref = jobsCollection(firestore, input.orgId).doc(createResourceUid())\n const data = {\n orgId: input.orgId,\n hostId: input.hostId ?? null,\n kind: input.kind,\n status: 'queued' as AiJobStatus,\n brief,\n inputs: input.inputs ?? {},\n model: input.model ?? null,\n steps,\n outputs: [] as AiJobOutput[],\n creditsReserved: steps.length * AI_JOB_STEP_RESERVE_CREDITS,\n creditsSpent: 0,\n createdBy: input.createdBy,\n createdAt: now,\n updatedAt: now,\n // The brief is verbatim customer text, so it expires on the same clock\n // as an assist exchange; the TTL policy keys on this field.\n expiresAt: assistExchangeExpiry(now),\n error: null,\n lease: null,\n ...(input.creditsConfirmed ? { creditsConfirmed: input.creditsConfirmed } : {}),\n }\n await ref.set(data)\n // A new job on the site — a guided start begun again — keeps it (AGL-3936).\n await clearAiEmptySiteSweep(firestore, input.hostId)\n await logAiJobCreated(\n input.orgId,\n { uid: input.createdBy, email: input.createdByEmail ?? null },\n {\n jobId: ref.id,\n kind: input.kind,\n briefLength: brief.length,\n hostId: input.hostId ?? null,\n },\n )\n return { ...(data as unknown as Omit<AiJob, '$id'>), $id: ref.id }\n}\n\nexport async function getAiJob(\n firestore: Firestore,\n orgId: string,\n jobId: string,\n): Promise<AiJob | null> {\n return jobFrom(await jobsCollection(firestore, orgId).doc(jobId).get())\n}\n\nexport interface ListAiJobsOptions {\n status?: AiJobStatus\n /**\n * Any of these statuses (AGL-3593): the console's indicator asks for every\n * job not yet settled in one read. Ignored when `status` is given.\n */\n statuses?: readonly AiJobStatus[]\n /**\n * One site's jobs (AGL-3596), for the site's AI jobs page; on the\n * (hostId, createdAt) index. Not combined with a status filter, which no\n * reader asks for and no index answers.\n */\n hostId?: string\n limit?: number\n}\n\n/**\n * Newest first. Needs the (status, createdAt) index when filtered, which\n * answers an `in` over statuses as it answers one status.\n */\nexport async function listAiJobs(\n firestore: Firestore,\n orgId: string,\n options: ListAiJobsOptions = {},\n): Promise<AiJob[]> {\n const limit = Math.min(Math.max(1, Math.floor(options.limit ?? 20)), 100)\n let query: FirebaseFirestore.Query = jobsCollection(firestore, orgId)\n if (options.status) query = query.where('status', '==', options.status)\n else if (options.statuses?.length) query = query.where('status', 'in', [...options.statuses])\n else if (options.hostId) query = query.where('hostId', '==', options.hostId)\n const read = async (from: FirebaseFirestore.Query, count: number) =>\n (await from.orderBy('createdAt', 'desc').limit(count).get()).docs\n .map((doc) => jobFrom(doc))\n .filter((job): job is AiJob => job !== null)\n try {\n return await read(query, limit)\n } catch (error) {\n // A site's list while its (hostId, createdAt) index is still building\n // (AGL-3596): Firestore refuses the query with FAILED_PRECONDITION until\n // the index is ready, which answered the site's AI jobs page with a 500.\n // The org's own newest jobs — the index every org list already uses —\n // filtered to the site in memory stand in, bounded to\n // `AI_JOBS_HOST_FALLBACK_SCAN` jobs, so a site whose jobs are older than\n // that lists fewer until the index is ready rather than none.\n if (!options.hostId || options.status || options.statuses?.length || !isFailedPrecondition(error)) throw error\n console.warn('ai jobs: host index not ready; listing from the org', { orgId, hostId: options.hostId })\n const recent = await read(jobsCollection(firestore, orgId), AI_JOBS_HOST_FALLBACK_SCAN)\n return recent.filter((job) => job.hostId === options.hostId).slice(0, limit)\n }\n}\n\n/** The most of an org's newest jobs a site's list scans while its index builds (AGL-3596). */\nexport const AI_JOBS_HOST_FALLBACK_SCAN = 300\n\n/** Firestore's \"this query needs an index that is not ready\" (gRPC 9). */\nfunction isFailedPrecondition(error: unknown): boolean {\n const code = (error as { code?: unknown } | null)?.code\n return code === 9 || code === 'failed-precondition' || code === 'FAILED_PRECONDITION'\n}\n\n/** The wire form. `running` is the lease, read against `now`. */\nexport function aiJobSummary(job: AiJob, now = new Date()): AiJobSummary {\n const leaseUntil = toMillis(job.lease?.until as Instant)\n return {\n id: job.$id,\n orgId: job.orgId,\n hostId: job.hostId ?? null,\n kind: job.kind,\n status: job.status,\n brief: job.brief,\n batch: typeof job.inputs?.['batchId'] === 'string' ? job.inputs['batchId'] : null,\n steps: (job.steps ?? []).map((step) => ({\n name: step.name,\n status: step.status,\n startedAt: toIso(step.startedAt as Instant),\n endedAt: toIso(step.endedAt as Instant),\n creditsSpent: step.creditsSpent ?? 0,\n error: aiCustomerSafeCopy(step.error ?? null),\n })),\n outputs: job.outputs ?? [],\n creditsReserved: job.creditsReserved ?? 0,\n creditsSpent: job.creditsSpent ?? 0,\n refundedCredits: job.refundedCredits ?? 0,\n createdBy: job.createdBy,\n createdAt: toIso(job.createdAt as Instant) ?? new Date(0).toISOString(),\n updatedAt: toIso(job.updatedAt as Instant) ?? new Date(0).toISOString(),\n // The doctrine's own words never reach a customer (AGL-3596).\n error: aiCustomerSafeCopy(job.error ?? null, { page: job.kind === 'page' }),\n running:\n job.status === 'running' &&\n leaseUntil !== null &&\n leaseUntil > now.getTime(),\n plan: job.plan\n ? {\n ...job.plan,\n labels: job.plan.labels ?? {},\n proposedAt: toIso(job.plan.proposedAt as Instant),\n confirmedAt: toIso(job.plan.confirmedAt as Instant),\n confirmedBy: job.plan.confirmedBy ?? null,\n }\n : null,\n review: job.review ? aiCustomerSafeReview(job.review, { page: job.kind === 'page' }) : null,\n ...(job.status === 'needs_input' && job.input?.fields?.length ? { input: job.input } : {}),\n applied: job.applied\n ? {\n at: toIso(job.applied.at as Instant),\n by: job.applied.by,\n versions: job.applied.versions ?? {},\n staged: job.applied.staged ?? [],\n }\n : null,\n ...(job.sitePublish ? { sitePublish: job.sitePublish } : {}),\n ...(job.kind === 'site' ? { siteInputs: aiSiteStartInputsOf(job.inputs) } : {}),\n ...(aiJobAutoConfirms(job) ? { autoConfirm: true } : {}),\n ...(job.items?.length ? { items: job.items.map(aiCustomerSafeItem) } : {}),\n ...(job.orchestration ? { orchestration: job.orchestration } : {}),\n ...(job.kind === 'build' && job.inputs?.['publish'] === true ? { publishAsked: true } : {}),\n ...(aiJobCancelPending(job) ? { cancelRequested: true } : {}),\n ...(job.siteRemoved ? { siteRemoved: true } : {}),\n // A site import's waves (AGL-3976), without the page text its waves read.\n ...(job.siteImport ? { siteImport: aiSiteImportWire(job.siteImport) } : {}),\n }\n}\n\n// ── The lease ─────────────────────────────────────────────────────────────\n\nexport interface ClaimedAiJobStep {\n job: AiJob\n stepIndex: number\n}\n\nfunction leaseIsLive(job: AiJob, owner: string, nowMs: number): boolean {\n const lease = job.lease\n if (!lease) return false\n if (lease.owner === owner) return false\n const until = toMillis(lease.until as Instant)\n return until !== null && until > nowMs\n}\n\n/** Whether ANY owner's lease on the job is still live: a step is in flight. */\nfunction anyLeaseIsLive(job: AiJob, nowMs: number): boolean {\n const until = toMillis(job.lease?.until as Instant)\n return Boolean(job.lease) && until !== null && until > nowMs\n}\n\n/**\n * The write that ends a job `canceled` (AGL-3616): it holds nothing and no\n * lease, a step left running goes back to `pending` (it did not finish), and\n * every item not yet built is skipped with the cancel's note. What the job\n * built, spent and was given back stays as it is: drafts stay drafts, and\n * credits for what ran stay charged.\n */\nfunction aiJobCanceledPatch(job: AiJob, now: Date, request: AiJobCancelRequest): Record<string, unknown> {\n return {\n status: 'canceled',\n cancelRequested: request,\n creditsReserved: 0,\n lease: null,\n error: null,\n steps: (job.steps ?? []).map((step) =>\n step.status === 'running' ? { ...step, status: 'pending', endedAt: null } : step,\n ),\n ...(job.items?.length ? { items: aiCanceledItems(job.items) } : {}),\n updatedAt: now,\n }\n}\n\nfunction aiCanceledItems(items: readonly AiJobItemLedger[]): AiJobItemLedger[] {\n return items.map((row) =>\n row.status === 'pending' || row.status === 'running'\n ? { ...row, status: 'skipped', note: AI_JOB_CANCELED_ITEM_NOTE }\n : row,\n )\n}\n\n/** The cancel a pending-cancel job carries, or one made now for a job that has none. */\nfunction cancelRequestOf(job: AiJob, now: Date): AiJobCancelRequest {\n return job.cancelRequested ?? aiJobCancelRequest(now, null)\n}\n\n/** A cancel asked now by `by`, as the job stores it (the instant is written as a Date). */\nfunction aiJobCancelRequest(now: Date, by: string | null): AiJobCancelRequest {\n return { at: now as unknown as AiJobCancelRequest['at'], by }\n}\n\nfunction nextStepIndex(job: AiJob): number {\n return (job.steps ?? []).findIndex(\n (step) => step.status === 'pending' || step.status === 'running',\n )\n}\n\n/**\n * The step a claim by `owner` would take, or -1 when there is none to take:\n * the job is terminal or parked, has no step left, or ANOTHER owner's lease\n * has not yet expired.\n */\nfunction claimableStepIndex(job: AiJob, owner: string, nowMs: number): number {\n if (job.status !== 'queued' && job.status !== 'running') return -1\n if (leaseIsLive(job, owner, nowMs)) return -1\n return nextStepIndex(job)\n}\n\n/**\n * Take the lease on the job's next step, in one transaction.\n *\n * Refuses — `null` — when the job is terminal or parked, when it has no\n * step left, or when ANOTHER owner's lease has not yet expired. That last\n * clause is the whole point: the console route and two overlapping beats\n * can all look at one job inside the same minute, and only one of them may\n * spend the reservation for its step. An expired lease is claimable, which\n * is how a step abandoned mid-flight by a frozen process is recovered — the\n * stale lease is released by being taken, not by a sweep of its own.\n */\nexport async function claimNextStep(\n firestore: Firestore,\n orgId: string,\n jobId: string,\n owner: string,\n now = new Date(),\n): Promise<ClaimedAiJobStep | null> {\n const ref = jobsCollection(firestore, orgId).doc(jobId)\n let ended: AiJob | null = null\n const claimed = await firestore.runTransaction<ClaimedAiJobStep | null>(async (tx: Transaction) => {\n ended = null\n const job = jobFrom(await tx.get(ref))\n if (!job) return null\n // A cancel asked while a step ran, whose runner never came back to end\n // it (AGL-3616): the lease ran out, so the job ends here, unclaimed.\n if (aiJobCancelPending(job)) {\n if (!leaseIsLive(job, owner, now.getTime())) {\n const patch = aiJobCanceledPatch(job, now, cancelRequestOf(job, now))\n tx.set(ref, patch, { merge: true })\n ended = { ...job, ...(patch as unknown as Partial<AiJob>) } as AiJob\n }\n return null\n }\n const stepIndex = claimableStepIndex(job, owner, now.getTime())\n if (stepIndex === -1) return null\n const steps = job.steps.map((step, index) =>\n index === stepIndex\n ? {\n ...step,\n status: 'running' as const,\n startedAt: step.startedAt ?? now,\n attempts: (step.attempts ?? 0) + 1,\n }\n : step,\n )\n const lease = { owner, until: new Date(now.getTime() + AI_JOB_LEASE_MS) }\n tx.set(\n ref,\n { status: 'running', steps, lease, updatedAt: now },\n { merge: true },\n )\n return {\n job: {\n ...job,\n status: 'running',\n steps: steps as AiJob['steps'],\n lease: lease as unknown as AiJob['lease'],\n updatedAt: now as unknown as AiJob['updatedAt'],\n },\n stepIndex,\n }\n })\n await noteAiJobEnded(firestore, ended, now)\n return claimed\n}\n\n/**\n * Extend the lease while a long step is still running. `false` when the\n * caller no longer holds it — the step was recovered by someone else, and\n * the caller's result must not overwrite theirs.\n */\nexport async function heartbeatStep(\n firestore: Firestore,\n orgId: string,\n jobId: string,\n owner: string,\n now = new Date(),\n): Promise<boolean> {\n const ref = jobsCollection(firestore, orgId).doc(jobId)\n return firestore.runTransaction(async (tx: Transaction) => {\n const job = jobFrom(await tx.get(ref))\n if (!job || job.status !== 'running' || job.lease?.owner !== owner) {\n return false\n }\n tx.set(\n ref,\n {\n lease: { owner, until: new Date(now.getTime() + AI_JOB_LEASE_MS) },\n updatedAt: now,\n },\n { merge: true },\n )\n return true\n })\n}\n\n/**\n * Leave a job whose workspace's AI is paused where it is (AGL-3037), moved to\n * the back of the beat's queue. Nothing is claimed: its steps, their attempts\n * and its lease stay exactly as they were, so resuming AI runs the job from\n * where it stopped. Only `updatedAt` moves — the beat's queue is ordered by it,\n * and a paused workspace's jobs left at the front would fill every beat's\n * candidates and starve every other workspace's.\n *\n * `null`, writing nothing, for a job a claim would not take either: terminal,\n * parked, no step left, or another owner's live lease.\n */\nexport async function holdPausedAiJob(\n firestore: Firestore,\n orgId: string,\n jobId: string,\n owner: string,\n now = new Date(),\n): Promise<AiJob | null> {\n const ref = jobsCollection(firestore, orgId).doc(jobId)\n let ended: AiJob | null = null\n const held = await firestore.runTransaction<AiJob | null>(async (tx: Transaction) => {\n ended = null\n const job = jobFrom(await tx.get(ref))\n if (!job) return null\n // A paused workspace's job a person canceled ends canceled (AGL-3616).\n if (aiJobCancelPending(job)) {\n if (!leaseIsLive(job, owner, now.getTime())) {\n const patch = aiJobCanceledPatch(job, now, cancelRequestOf(job, now))\n tx.set(ref, patch, { merge: true })\n ended = { ...job, ...(patch as unknown as Partial<AiJob>) } as AiJob\n }\n return null\n }\n if (claimableStepIndex(job, owner, now.getTime()) === -1) return null\n tx.set(ref, { updatedAt: now }, { merge: true })\n return { ...job, updatedAt: now as unknown as AiJob['updatedAt'] }\n })\n await noteAiJobEnded(firestore, ended, now)\n return held\n}\n\nexport interface RecordStepInput {\n /** `pending` hands the step back for another attempt. */\n status: 'done' | 'failed' | 'pending'\n creditsSpent: number\n error?: string | null\n outputs?: AiJobOutput[]\n /** The plan the step proposed, kept on the job (AGL-2935). */\n plan?: AiJobPlan\n /** The step stopped for a person: the job parks `needs_review` in this same write. */\n review?: AiJobReview\n /**\n * With `review`: the person must answer these first (AGL-3978), so the job\n * parks `needs_input` with them, the review kept for after the answer.\n */\n input?: AiJobInputRequest\n /**\n * With `pending`: the step is handed back because it made progress and\n * asked to continue (AGL-2910), not because an attempt failed. Its\n * attempts count again from zero and its passes by one.\n */\n continued?: boolean\n /** What the run cost in tokens and time (AGL-2937), added to the step's totals. */\n tokens?: AiJobStepTokenRun\n /** What a guided site start put live (AGL-3596), kept on the job. */\n sitePublish?: AiJobSitePublish\n /**\n * A park the caller announces itself (AGL-3596): after the give-back it\n * owes is recorded, so the notice can say it, or never, for a park the\n * caller resolves at once.\n */\n quiet?: boolean\n /**\n * A `build` job's ledger to start from (AGL-3616), kept in this write;\n * absent keeps the one the job has.\n */\n items?: AiJobItemLedger[]\n /**\n * What this pass came to for the `build` item it ran (AGL-3616): the pass's\n * `creditsSpent` is that item's, and lands on its row in this same\n * transaction, with what was given back for it.\n */\n item?: AiJobItemRecord\n /** An `import` job's waves after this pass (AGL-3976), kept in this write. */\n siteImport?: AiSiteImportState\n /** A `site` job's pictures left on a starter after this pass (AGL-3988), kept in this write. */\n sitePhotos?: AiSitePhotos\n}\n\n/** One `build` pass as its item's row records it (AGL-3616). */\nexport interface AiJobItemRecord extends AiJobItemOutcome {\n /** Given back for this pass's failure, before the record. */\n creditsRefunded?: number\n refundKey?: string\n}\n\n/**\n * A build's ledger with one pass applied (AGL-3616): the pass's credits on\n * the item's row, its status, failure, note and outputs, and what was given\n * back for it. Pure; the machine applies it inside `recordStep`.\n */\nexport function aiApplyJobItemRecord(\n ledger: readonly AiJobItemLedger[],\n record: AiJobItemRecord,\n credits: number,\n now?: Date,\n): AiJobItemLedger[] {\n return ledger.map((row) =>\n row.slot !== record.slot\n ? row\n : {\n ...row,\n status: record.status,\n creditsSpent: (row.creditsSpent ?? 0) + credits,\n attemptCredits: (row.attemptCredits ?? 0) + credits,\n creditsRefunded: (row.creditsRefunded ?? 0) + (record.creditsRefunded ?? 0),\n ...(record.refundKey ? { refundKey: record.refundKey } : {}),\n ...(record.failure !== undefined ? { failure: record.failure } : {}),\n ...(record.note !== undefined ? { note: record.note } : {}),\n ...(record.degradedBy?.length ? { degradedBy: record.degradedBy } : {}),\n // When it settled, so the next item's row counts from there (AGL-3596).\n ...(now && record.status !== 'running' && record.status !== 'pending' ? { settledAt: now.toISOString() } : {}),\n outputs: [...(row.outputs ?? []), ...(record.outputs ?? []).filter((id) => !(row.outputs ?? []).includes(id))],\n },\n )\n}\n\n/** One run of a step's runner, as the machine measured it (AGL-2937). */\nexport interface AiJobStepTokenRun {\n usage: AiUsage\n model: string\n effort: AiEffort | null\n latencyMs: number\n /** Why the run's last model call stopped (AGL-3042). */\n stopReason: string | null\n}\n\n/**\n * The runs whose stop reasons a step's record keeps (AGL-3042), newest last.\n * A page job that builds a layout, a form and a component, then five sections\n * and its last pass, runs its generation step nine times: twelve keep such a\n * page whole with room for three retries.\n *\n * What it costs the document: an entry is at most 67 bytes as Firestore sizes\n * it (the two field names, 11 and 7; a stop reason, a provider's ASCII word of\n * at most `AI_JOB_STEP_STOP_REASON_MAX_CHARS` characters, 41; an integer, 8),\n * so a step's list is at most 813 bytes with its field name, and a job's two\n * steps at most 1,626 of the 1,048,576 bytes a document may hold.\n */\nexport const AI_JOB_STEP_LAST_RUNS = 12\n\n/** The longest stop reason a run's entry keeps; every one a provider names today is shorter. */\nexport const AI_JOB_STEP_STOP_REASON_MAX_CHARS = 40\n\n/**\n * A step's measure with one more run added: the four counts and the time\n * summed, the model and effort taken from the run, and why it stopped\n * appended to the latest runs (AGL-3042).\n */\nexport function addAiJobStepTokens(\n current: AiJobStepTokens | null | undefined,\n run: AiJobStepTokenRun,\n): AiJobStepTokens {\n const count = (value: unknown): number => {\n const parsed = Number(value ?? 0)\n return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : 0\n }\n const kept = Array.isArray(current?.lastRuns) ? current.lastRuns : []\n const stopReason =\n typeof run.stopReason === 'string' && run.stopReason\n ? run.stopReason.slice(0, AI_JOB_STEP_STOP_REASON_MAX_CHARS)\n : null\n return {\n input: count(current?.input) + count(run.usage.inputTokens),\n cachedRead: count(current?.cachedRead) + count(run.usage.cacheReadTokens),\n cacheWrite: count(current?.cacheWrite) + count(run.usage.cacheWriteTokens),\n output: count(current?.output) + count(run.usage.outputTokens),\n model: run.model || current?.model || null,\n effort: run.effort ?? null,\n latencyMs: count(current?.latencyMs) + count(run.latencyMs),\n runs: count(current?.runs) + 1,\n lastRuns: [...kept, { stopReason, output: count(run.usage.outputTokens) }].slice(-AI_JOB_STEP_LAST_RUNS),\n }\n}\n\nexport interface RecordedStep {\n job: AiJob\n /** Steps still to run after this one. */\n remaining: number\n /** The job entered `needs_review` in this write. */\n parked?: boolean\n}\n\n/**\n * Record what a step cost and produced, and hand the lease back.\n *\n * Credits and outputs are recorded whatever the job's status is by now —\n * a job canceled while its provider call was in flight still spent the\n * tokens, and hiding that would make the bill disagree with the document.\n * The status transition is the caller's next call (`completeAiJob`,\n * `failAiJob`), except that a step handed back or a step with more steps\n * behind it re-queues here, so the beat sees it without another write —\n * and a step that stopped for a person parks the job `needs_review` here,\n * in the same transaction, so no beat can claim the next step in between.\n */\nexport async function recordStep(\n firestore: Firestore,\n orgId: string,\n jobId: string,\n owner: string,\n stepIndex: number,\n input: RecordStepInput,\n now = new Date(),\n): Promise<RecordedStep> {\n const ref = jobsCollection(firestore, orgId).doc(jobId)\n const recorded = await firestore.runTransaction(async (tx: Transaction) => {\n const job = jobFrom(await tx.get(ref))\n if (!job) throw new Error(`ai job ${orgId}/${jobId} vanished`)\n const steps = job.steps.map((step, index) =>\n index === stepIndex\n ? {\n ...step,\n status: input.status,\n endedAt: input.status === 'pending' ? null : now,\n creditsSpent: (step.creditsSpent ?? 0) + input.creditsSpent,\n error: input.error ?? null,\n ...(input.continued && input.status === 'pending'\n ? { attempts: 0, passes: (step.passes ?? 0) + 1 }\n : {}),\n ...(input.tokens ? { tokens: addAiJobStepTokens(step.tokens, input.tokens) } : {}),\n }\n : step,\n )\n const remaining = steps.filter(\n (step) => step.status === 'pending' || step.status === 'running',\n ).length\n // A build's item ledger moves with its spend, in this one write (AGL-3616).\n const ledger = input.items ?? job.items ?? null\n const items =\n ledger && input.item ? aiApplyJobItemRecord(ledger, input.item, input.creditsSpent, now) : input.items ? ledger : null\n const settled = input.status !== 'pending'\n const ownsLease = job.lease?.owner === owner\n // A cancel asked while this step ran ends the job here (AGL-3616), in the\n // write that records what the step cost and built: nothing after it runs.\n const canceling = aiJobCancelPending(job)\n const parksForReview = Boolean(input.review) && !isAiJobTerminal(job.status) && !canceling\n // A plan only the person can complete waits for their answer (AGL-3978).\n const parkedAs: AiJobStatus = input.input ? 'needs_input' : 'needs_review'\n const status: AiJobStatus = isAiJobTerminal(job.status)\n ? job.status\n : canceling\n ? 'canceled'\n : job.status === 'needs_input'\n ? job.status\n : parksForReview\n ? parkedAs\n : input.status === 'failed'\n ? 'running'\n : remaining > 0\n ? 'queued'\n : 'running'\n const patch = {\n status,\n steps,\n outputs: [...(job.outputs ?? []), ...(input.outputs ?? [])],\n creditsSpent: (job.creditsSpent ?? 0) + input.creditsSpent,\n // A job parked for a person holds nothing: no step of it can run until\n // someone resumes it, and the resume holds the figure again (AGL-3030).\n creditsReserved: parksForReview\n ? 0\n : settled\n ? Math.max(0, (job.creditsReserved ?? 0) - AI_JOB_STEP_RESERVE_CREDITS)\n : (job.creditsReserved ?? 0),\n updatedAt: now,\n ...(ownsLease ? { lease: null } : {}),\n // A plan the step produced is kept whatever the job's status is by\n // now, for the reason its credits are.\n ...(input.plan ? { plan: input.plan } : {}),\n ...(input.sitePublish ? { sitePublish: input.sitePublish } : {}),\n ...(input.siteImport ? { siteImport: input.siteImport } : {}),\n ...(input.sitePhotos ? { sitePhotos: input.sitePhotos } : {}),\n ...(items ? { items } : {}),\n ...(input.item?.creditsRefunded\n ? { refundedCredits: (job.refundedCredits ?? 0) + input.item.creditsRefunded }\n : {}),\n // A doctrine or limit review's sentence is the job's customer-safe\n // error, as a meter park's is; a plan waiting to be confirmed is not an\n // error.\n // A job waiting for an answer says its question there too (AGL-3978).\n ...(parksForReview && input.review\n ? {\n review: input.review,\n // A plan to confirm and a site import's wave to review (AGL-3976)\n // are decisions waiting, not errors.\n error: input.input\n ? input.input.message\n : input.review.reason === 'plan' || input.review.reason === 'wave'\n ? null\n : input.review.message,\n input: input.input ?? null,\n }\n : {}),\n ...(canceling\n ? {\n creditsReserved: 0,\n lease: null,\n error: null,\n steps: steps.map((step) => (step.status === 'running' ? { ...step, status: 'pending' as const } : step)),\n ...((items ?? job.items)?.length ? { items: aiCanceledItems(items ?? job.items ?? []) } : {}),\n }\n : {}),\n }\n tx.set(ref, patch, { merge: true })\n return {\n job: { ...job, ...(patch as unknown as Partial<AiJob>) } as AiJob,\n remaining,\n // Entered `needs_review` (or `needs_input` for an answer) in this write, rather than parked again.\n parked: parksForReview && job.status !== parkedAs,\n // Ended `canceled` in this write (AGL-3936).\n ended: canceling && !isAiJobTerminal(job.status),\n }\n })\n if (recorded.ended) await noteAiJobEnded(firestore, recorded.job, now)\n if (recorded.parked && !input.quiet) await announceAiJobTransition(recorded.job, 'needs-review')\n return { job: recorded.job, remaining: recorded.remaining, parked: recorded.parked }\n}\n\nasync function transition(\n firestore: Firestore,\n orgId: string,\n jobId: string,\n apply: (job: AiJob) => Record<string, unknown> | null,\n): Promise<{ job: AiJob; changed: boolean }> {\n const ref = jobsCollection(firestore, orgId).doc(jobId)\n return firestore.runTransaction(async (tx: Transaction) => {\n const job = jobFrom(await tx.get(ref))\n if (!job) throw new Error(`ai job ${orgId}/${jobId} vanished`)\n const patch = apply(job)\n if (!patch) return { job, changed: false }\n tx.set(ref, patch, { merge: true })\n return {\n job: { ...job, ...(patch as unknown as Partial<AiJob>) } as AiJob,\n changed: true,\n }\n })\n}\n\n/** Every step done: the job is `done` and holds nothing. */\nexport async function completeAiJob(\n firestore: Firestore,\n orgId: string,\n jobId: string,\n now = new Date(),\n): Promise<AiJob> {\n const { job, changed } = await transition(firestore, orgId, jobId, (current) =>\n isAiJobTerminal(current.status)\n ? null\n : // A cancel asked before the finish commits wins (AGL-3616).\n aiJobCancelPending(current)\n ? aiJobCanceledPatch(current, now, cancelRequestOf(current, now))\n : {\n status: 'done',\n creditsReserved: 0,\n lease: null,\n error: null,\n updatedAt: now,\n },\n )\n if (changed && job.status === 'done') await announceAiJobTransition(job, 'done')\n if (changed) await noteAiJobEnded(firestore, job, now)\n return job\n}\n\n/**\n * Terminal failure. `message` is the only thing a customer reads, so it is\n * a fixed sentence; whatever the provider or the runner actually said goes\n * to the log beside the ids an operator would search for.\n */\nexport async function failAiJob(\n firestore: Firestore,\n orgId: string,\n jobId: string,\n message: string,\n detail?: { stepIndex?: number; error?: unknown; ours?: boolean },\n now = new Date(),\n): Promise<AiJob> {\n console.error('ai job failed', {\n orgId,\n jobId,\n stepIndex: detail?.stepIndex ?? null,\n error: detail?.error instanceof Error ? detail.error.message : detail?.error ?? null,\n })\n const { job, changed } = await transition(firestore, orgId, jobId, (current) =>\n isAiJobTerminal(current.status)\n ? null\n : // A person's cancel is not a failure (AGL-3616): no alert, no notice.\n aiJobCancelPending(current)\n ? aiJobCanceledPatch(current, now, cancelRequestOf(current, now))\n : {\n status: 'failed',\n error: message,\n creditsReserved: 0,\n lease: null,\n steps: current.steps.map((step) =>\n step.status === 'running'\n ? { ...step, status: 'failed', endedAt: now, error: message }\n : step,\n ),\n updatedAt: now,\n },\n )\n if (changed && job.status === 'failed') {\n await announceAiJobTransition(job, 'failed', {\n ours: detail?.ours ?? false,\n stepIndex: detail?.stepIndex ?? null,\n error: detail?.error instanceof Error ? detail.error.message : detail?.error == null ? null : String(detail.error),\n })\n }\n if (changed) await noteAiJobEnded(firestore, job, now)\n return job\n}\n\n/**\n * Park the job for something only the workspace can change. The running\n * step goes back to `pending` so the same step is what runs when the beat\n * tries again; its attempt is not counted, because nothing was attempted.\n */\nexport async function markAiJobNeedsInput(\n firestore: Firestore,\n orgId: string,\n jobId: string,\n reason: string,\n now = new Date(),\n): Promise<AiJob> {\n const { job, changed } = await transition(firestore, orgId, jobId, (current) =>\n isAiJobTerminal(current.status)\n ? null\n : aiJobCancelPending(current)\n ? aiJobCanceledPatch(current, now, cancelRequestOf(current, now))\n : {\n status: 'needs_input',\n error: reason,\n lease: null,\n steps: current.steps.map((step) =>\n step.status === 'running'\n ? {\n ...step,\n status: 'pending',\n attempts: Math.max(0, (step.attempts ?? 0) - 1),\n }\n : step,\n ),\n updatedAt: now,\n },\n )\n if (changed) await noteAiJobEnded(firestore, job, now)\n return job\n}\n\n/** What a cancel did (AGL-3616). */\nexport interface AiJobCancelResult {\n job: AiJob\n /** The cancel was new: it ended the job, or asked the step in flight to stop. */\n changed: boolean\n}\n\n/**\n * Cancel (AGL-2904, AGL-3616). One transaction decides it against whatever\n * else commits, so a cancel racing a finish resolves to whichever committed\n * first: a job already `done`, `failed` or `canceled` is returned unchanged.\n *\n * - **No step in flight** (queued, parked for a person or the meter, or a\n * lease that ran out): the job ends `canceled` in this write. It holds\n * nothing, its unbuilt items are skipped, and nothing it built is touched.\n * - **A step in flight** (a live lease): the cancel is recorded as\n * `cancelRequested` and the job keeps its status until the runner holding\n * the lease comes back. The runner watches for it and aborts its provider\n * call; whatever the step had spent is metered, and the machine's next\n * write ends the job `canceled` (`recordStep`, or the finish or failure it\n * was about to write, which a pending cancel turns into the cancel).\n *\n * Either way a canceled job is never failed: no `ai.jobFailed` alert, no\n * failure notice, no give-back (what ran stays charged), no publish.\n *\n * The activity row is written once, when the cancel is first asked: a second\n * cancel of the same job is not a second act. `actor` is the member who\n * canceled; a cancel with nobody named is recorded as nobody's rather than\n * as the creator's.\n */\nexport async function cancelAiJob(\n firestore: Firestore,\n orgId: string,\n jobId: string,\n now = new Date(),\n actor: AiActivityActor | null = null,\n): Promise<AiJobCancelResult> {\n const request = aiJobCancelRequest(now, actor?.uid ?? null)\n let firstAsk = false\n const result = await transition(firestore, orgId, jobId, (current) => {\n if (isAiJobTerminal(current.status)) return null\n const inFlight = anyLeaseIsLive(current, now.getTime())\n if (current.cancelRequested) {\n // Asked already: nothing new while its step is still in flight; a\n // step whose runner never came back is ended now.\n return inFlight ? null : aiJobCanceledPatch(current, now, current.cancelRequested)\n }\n firstAsk = true\n return inFlight\n ? { cancelRequested: request, updatedAt: now }\n : aiJobCanceledPatch(current, now, request)\n })\n if (result.changed && firstAsk) {\n await logAiJobCanceled(orgId, actor ?? { uid: null }, {\n jobId,\n kind: result.job.kind,\n })\n }\n if (result.changed) await noteAiJobEnded(firestore, result.job, now)\n return result\n}\n\n/**\n * Gives back what a job spent when it failed on our side (AGL-3594), and\n * records the give-back on the job — `refundedCredits`, `refundReason` and\n * the count its next key is numbered by. Everything the job spent and has not\n * given back where it delivered nothing; the failing step's own spend where\n * it delivered drafts, which it keeps. `assist-job-refund.ts` has the rule\n * and the bound. Never throws: a give-back that cannot be made leaves the job\n * as the failure left it, metered as every other step.\n */\nasync function refundOurFailure(\n firestore: Firestore,\n orgId: string,\n job: AiJob,\n input: {\n reason: AiJobRefundReason\n /** The failing step's own credits, as the step was recorded. */\n stepCredits: number\n /** The Free taste's attribution, where the caller holds the reservation that decided it. */\n free?: FreeAssistAccount | null\n now: Date\n },\n): Promise<AiJob> {\n try {\n const delivered = (job.outputs ?? []).some((output) => output.resource !== 'seo')\n const credits = aiJobRefundCredits({\n delivered,\n jobCredits: job.creditsSpent ?? 0,\n stepCredits: input.stepCredits,\n alreadyRefunded: job.refundedCredits ?? 0,\n })\n if (credits <= 0) return job\n const free =\n input.free !== undefined\n ? input.free\n : freeAssistAccount(\n ((await firestore.collection('orgs').doc(orgId).get()).data() ?? {}) as Partial<AglynOrgBilling>,\n )\n const ordinal = job.refunds ?? 0\n const refund = await refundJobCredits(firestore, {\n orgId,\n free,\n jobId: job.$id,\n ordinal,\n credits,\n month: assistUsageMonth(input.now),\n day: assistUsageDay(input.now),\n reason: input.reason,\n })\n if (refund.status !== 'returned') return job\n const { job: refunded } = await transition(firestore, orgId, job.$id, (current) => ({\n refundedCredits: (current.refundedCredits ?? 0) + credits,\n refundReason: input.reason,\n refunds: (current.refunds ?? 0) + 1,\n updatedAt: input.now,\n }))\n return refunded\n } catch (error) {\n console.error('ai job refund failed', { orgId, jobId: job.$id, error })\n return job\n }\n}\n\n/**\n * A failure on our side, given back BEFORE the job is failed (AGL-3596): the\n * failed state is the last one a follower of the job reads and the one its\n * notice is written from, so it already carries `refundedCredits`. A job that\n * ended meanwhile — a person's cancel, which pays for what ran — is left as it\n * ended and given nothing back.\n */\nasync function failOurFailure(\n firestore: Firestore,\n orgId: string,\n jobId: string,\n message: string,\n detail: { stepIndex?: number; error?: unknown; ours?: boolean },\n refund: { reason: AiJobRefundReason; stepCredits: number; free?: FreeAssistAccount | null },\n now: Date,\n): Promise<AiJob> {\n const current = await getAiJob(firestore, orgId, jobId)\n // A person's cancel asked while the step ran is the job's end (AGL-3616):\n // not our failure, so nothing is given back and nobody is told.\n if (current && aiJobCancelPending(current)) {\n return failAiJob(firestore, orgId, jobId, message, { ...detail, ours: false }, now)\n }\n // A build that got as far as its items settles item by item (AGL-3616):\n // what it delivered stands, and what it did not is given back per item.\n if (current && aiJobSettlesByItem(current.kind) && current.items?.length && !isAiJobTerminal(current.status)) {\n console.error('ai build stopped', { orgId, jobId, stepIndex: detail.stepIndex ?? null, error: detail.error })\n return settleAiBuildJob(firestore, orgId, jobId, {\n now,\n free: refund.free,\n stopped: { message, reason: refund.reason },\n })\n }\n if (current && !isAiJobTerminal(current.status)) {\n await refundOurFailure(firestore, orgId, current, { ...refund, now })\n }\n // Given back as ours either way; ANNOUNCED as ours unless the step said the\n // failure is the customer's own limit (`customerLimit`), which pages no one.\n return failAiJob(firestore, orgId, jobId, message, { ...detail, ours: detail.ours ?? true }, now)\n}\n\n// ── A build's settlement (AGL-3616) ──────────────────────────────────────\n\n/**\n * The kinds settled item by item (AGL-3616): a build, and a site scaffold,\n * which is the build's preset. Their step writes an item ledger; a failed\n * item gives back its own spend and the rest goes on.\n */\nexport function aiJobSettlesByItem(kind: AiJobKind): boolean {\n // A site import's pages are its items too (AGL-3976): one that fails on\n // our side gives back its own spend, and its wave goes on.\n return kind === 'build' || kind === 'site' || kind === 'import'\n}\n\n/**\n * Whether Try again may run a job's failed items: a finished build, done or\n * failed; a site scaffold that delivered part of its site — one that built\n * nothing starts over from its own answers instead.\n */\nexport function aiBuildRetryable(job: Pick<AiJob, 'kind' | 'status'>): boolean {\n if (job.kind === 'build') return job.status === 'done' || job.status === 'failed'\n return job.kind === 'site' && job.status === 'done'\n}\n\n/** What a build that delivered nothing says; its planning was given back too. */\nexport const AI_BUILD_NOTHING_BUILT_COPY =\n 'Nothing in this request could be built. Try again, or ask for less at once.'\n\n/** What a site import that brought no page in says (AGL-3976); every page's spend was given back. */\nexport const AI_SITE_IMPORT_NOTHING_BUILT_COPY =\n 'No page of your site could be brought in. Nothing was charged for the pages that failed; try again later.'\n\n/** What an item the build never reached says when the build stopped early. */\nexport const AI_BUILD_NOT_REACHED_COPY = 'Not built: the build stopped before it reached this.'\n\n/**\n * Gives back one build item's spend, or its planning's (`slot: 'plan'`),\n * under the item key (job, slot, attempt), so a replayed pass gives back once\n * and a later attempt is a give-back of its own. The credits given back and\n * the key, or `null` when nothing was. Never throws.\n */\nasync function refundBuildCredits(\n firestore: Firestore,\n orgId: string,\n job: AiJob,\n input: {\n slot: string\n attempt: number\n credits: number\n reason: AiJobRefundReason\n free?: FreeAssistAccount | null\n now: Date\n },\n): Promise<{ credits: number; key: string } | null> {\n const credits = Math.max(0, Math.floor(input.credits))\n if (credits <= 0) return null\n try {\n const free =\n input.free !== undefined\n ? input.free\n : freeAssistAccount(\n ((await firestore.collection('orgs').doc(orgId).get()).data() ?? {}) as Partial<AglynOrgBilling>,\n )\n const created = toMillis(job.createdAt as Instant)\n const jobDay = assistUsageDay(created === null ? input.now : new Date(created))\n const refund = await refundJobCredits(firestore, {\n orgId,\n free,\n jobId: job.$id,\n ordinal: 0,\n credits,\n month: assistUsageMonth(input.now),\n day: assistUsageDay(input.now),\n reason: input.reason,\n item: { slot: input.slot, attempt: input.attempt, jobDay },\n })\n // A key the month already holds was given back by an earlier run of this pass.\n if (refund.status !== 'returned' && refund.status !== 'duplicate') return null\n return {\n credits,\n key: aiJobItemRefundKey({ day: jobDay, jobId: job.$id, slot: input.slot, attempt: input.attempt }),\n }\n } catch (error) {\n console.error('ai build refund failed', { orgId, jobId: job.$id, slot: input.slot, error })\n return null\n }\n}\n\n/** What the build's plan step spent: its orchestration. */\nfunction aiBuildPlanCredits(job: Pick<AiJob, 'steps'>): number {\n return job.steps.find((step) => step.name === AI_JOB_PLAN_STEP)?.creditsSpent ?? 0\n}\n\n/**\n * Ends a build (AGL-3616), once nothing is left to build or the machine\n * stopped it. A build that STOPPED — a provider failure, its attempts used up\n * — fails the item it was on and gives back that attempt's spend, and leaves\n * the items it never reached `skipped`. Then: anything delivered, and its\n * planning is charged and the job is `done`, failed items and all; nothing\n * delivered, and its planning is given back too and the job is `failed`.\n */\nexport async function settleAiBuildJob(\n firestore: Firestore,\n orgId: string,\n jobId: string,\n input: {\n now: Date\n free?: FreeAssistAccount | null\n stopped?: { message: string; reason: AiJobRefundReason }\n },\n): Promise<AiJob> {\n const { now } = input\n let job = await getAiJob(firestore, orgId, jobId)\n if (!job || isAiJobTerminal(job.status)) return job as AiJob\n // A cancel asked before the build settled is how it ends (AGL-3616).\n if (aiJobCancelPending(job)) return completeAiJob(firestore, orgId, jobId, now)\n const stopped = input.stopped\n if (stopped && (job.items ?? []).some((row) => row.status === 'pending' || row.status === 'running')) {\n const refunds = new Map<string, { credits: number; key: string }>()\n for (const row of job.items ?? []) {\n if (row.status !== 'running') continue\n const refund = await refundBuildCredits(firestore, orgId, job, {\n slot: row.slot,\n attempt: row.attempt,\n credits: row.attemptCredits ?? 0,\n reason: stopped.reason,\n free: input.free,\n now,\n })\n if (refund) refunds.set(row.slot, refund)\n }\n job = (\n await transition(firestore, orgId, jobId, (current) => {\n if (isAiJobTerminal(current.status)) return null\n let given = 0\n const items = (current.items ?? []).map((row): AiJobItemLedger => {\n if (row.status === 'running') {\n const refund = refunds.get(row.slot)\n given += refund?.credits ?? 0\n return {\n ...row,\n status: 'failed',\n failure: { ours: true, reason: stopped.reason, message: stopped.message },\n creditsRefunded: (row.creditsRefunded ?? 0) + (refund?.credits ?? 0),\n ...(refund ? { refundKey: refund.key } : {}),\n }\n }\n return row.status === 'pending' ? { ...row, status: 'skipped', note: AI_BUILD_NOT_REACHED_COPY } : row\n })\n return {\n items,\n refundedCredits: (current.refundedCredits ?? 0) + given,\n updatedAt: now,\n }\n })\n ).job\n }\n // A site is delivered when a page is (AGL-3616): a form alone is not a\n // site. A scaffold that owed no page (a deployment without the page kind)\n // is delivered by whatever it did build.\n const sitePages = job.kind === 'site' ? (job.items ?? []).filter((row) => row.op === 'page') : []\n const delivered = sitePages.length\n ? sitePages.some((row) => row.status === 'succeeded' || row.status === 'degraded')\n : aiBuildSettlement(job.items ?? []).delivered\n const planCredits = aiBuildPlanCredits(job)\n if (delivered) {\n await transition(firestore, orgId, jobId, (current) =>\n isAiJobTerminal(current.status)\n ? null\n : { orchestration: { creditsSpent: planCredits, settled: 'charged' }, updatedAt: now },\n )\n return completeAiJob(firestore, orgId, jobId, now)\n }\n // A site that built no page gives back what its other parts cost too: it\n // was not built, and its Try again starts over from its answers.\n const parts = new Map<string, { credits: number; key: string }>()\n if (job.kind === 'site') {\n for (const row of job.items ?? []) {\n const owed = Math.max(0, (row.creditsSpent ?? 0) - (row.creditsRefunded ?? 0))\n if (owed <= 0 || row.status === 'failed') continue\n const given = await refundBuildCredits(firestore, orgId, job, {\n slot: row.slot,\n attempt: row.attempt,\n credits: owed,\n reason: stopped?.reason ?? 'step-failure',\n free: input.free,\n now,\n })\n if (given) parts.set(row.slot, given)\n }\n }\n const refund =\n job.orchestration?.settled === 'refunded'\n ? null\n : await refundBuildCredits(firestore, orgId, job, {\n slot: 'plan',\n attempt: 1,\n credits: planCredits,\n reason: stopped?.reason ?? 'step-failure',\n free: input.free,\n now,\n })\n await transition(firestore, orgId, jobId, (current) =>\n isAiJobTerminal(current.status)\n ? null\n : {\n orchestration: {\n creditsSpent: planCredits,\n settled: refund || current.orchestration?.settled === 'refunded' ? 'refunded' : 'charged',\n },\n ...(refund || parts.size\n ? {\n refundedCredits:\n (current.refundedCredits ?? 0) +\n (refund?.credits ?? 0) +\n [...parts.values()].reduce((total, part) => total + part.credits, 0),\n refundReason: stopped?.reason ?? 'step-failure',\n }\n : {}),\n ...(parts.size\n ? {\n items: (current.items ?? []).map((row) => {\n const part = parts.get(row.slot)\n return part ? { ...row, creditsRefunded: (row.creditsRefunded ?? 0) + part.credits, refundKey: part.key } : row\n }),\n }\n : {}),\n updatedAt: now,\n },\n )\n const nothing =\n job.kind === 'site'\n ? AI_SITE_GUIDED_BUILD_FAILED_COPY\n : job.kind === 'import'\n ? AI_SITE_IMPORT_NOTHING_BUILT_COPY\n : AI_BUILD_NOTHING_BUILT_COPY\n return failAiJob(firestore, orgId, jobId, stopped?.message ?? nothing, {\n error: 'build delivered nothing',\n // Ours unless every item that failed was the model declining (AGL-3616).\n ours: Boolean(stopped) || (job.items ?? []).some((row) => row.status === 'failed' && row.failure?.ours),\n }, now)\n}\n\n/**\n * Try again on a build (AGL-3616): its failed items back to pending on their\n * next attempt, with what they left unbuilt (`aiBuildRetryLedger`), and its\n * generation step queued again. Never re-runs an item that succeeded, never\n * re-asks the plan. Held at what the retried items are estimated to cost.\n * `changed: false` for a job that is not a finished build, or has nothing to\n * try again.\n */\nexport async function retryAiBuildJob(\n firestore: Firestore,\n orgId: string,\n jobId: string,\n now = new Date(),\n options: {\n /** A Free Try again the person chose to run past what was left (AGL-3722). */\n creditsConfirmed?: AiJobCreditsConfirmation | null\n } = {},\n): Promise<{ job: AiJob; changed: boolean; retried: string[] }> {\n let retried: string[] = []\n const result = await transition(firestore, orgId, jobId, (current) => {\n if (!aiBuildRetryable(current)) return null\n const plan = current.plan?.status === 'confirmed' ? current.plan : null\n if (!plan || !current.items?.length) return null\n const next = aiBuildRetryLedger(current.items, current.kind === 'site' ? aiLedgerUnits(current.items) : aiBuildUnits(plan))\n if (!next.ledger.some((row) => row.status === 'pending')) return null\n retried = next.retried\n return {\n status: 'queued',\n items: next.ledger,\n steps: current.steps.map((step) =>\n step.name === AI_JOB_PLAN_STEP\n ? step\n : { ...step, status: 'pending', attempts: 0, passes: 0, endedAt: null, error: null },\n ),\n review: null,\n error: null,\n lease: null,\n creditsReserved: Math.max(\n AI_JOB_STEP_RESERVE_CREDITS,\n current.kind === 'site'\n ? next.retried.length * AI_JOB_STEP_RESERVE_CREDITS\n : aiBuildCreditEstimate(plan, { slots: new Set(next.retried) }),\n ),\n ...(options.creditsConfirmed ? { creditsConfirmed: options.creditsConfirmed } : {}),\n updatedAt: now,\n }\n })\n // A site start tried again keeps its site (AGL-3936).\n if (result.changed) await clearAiEmptySiteSweep(firestore, result.job.hostId)\n return { ...result, retried: result.changed ? retried : [] }\n}\n\n/**\n * Resume a job that stopped for a person (AGL-2935): confirm its plan, or\n * try again the step whose answer broke a building rule — or carry on a job\n * the meter paused, from its paused step (AGL-3660). One transaction,\n * so two confirmations land once; anything but a `needs_review` job comes\n * back unchanged. The pending step's attempts start over — a person asking\n * again is not a provider failing again — and a confirmed plan with no step\n * left behind it completes the job rather than queueing nothing. The steps\n * it queues are held at the nominal figure again, which the park released —\n * and a confirmed plan is held at what the whole job is estimated to cost\n * (AGL-3031), creations included, where that is more.\n */\nexport async function resumeAiJob(\n firestore: Firestore,\n orgId: string,\n jobId: string,\n actor: { uid: string },\n now = new Date(),\n options: {\n /**\n * A build whose request asked to publish, confirmed with the plan card's\n * box ticked (AGL-3616): kept on its inputs, which is what lets its last\n * pass put its pages live.\n */\n publishConfirmed?: boolean\n /**\n * A Free build or start the person chose to build past what was left\n * (AGL-3722), kept on the job: who, when, what was left.\n */\n creditsConfirmed?: AiJobCreditsConfirmation | null\n /**\n * The smaller first build the person chose instead (AGL-3722): its plan\n * — the home page and what it needs — is the one confirmed, in place of\n * the plan the job proposed. Only while that plan waits for confirmation.\n */\n reducePlan?: (plan: AiJobPlan) => AiJobPlan | null\n /** The answers a job waiting in `needs_input` asked for (AGL-3978), by `aiJobInputKey`. */\n input?: Readonly<Record<string, unknown>> | null\n } = {},\n): Promise<{ job: AiJob; changed: boolean }> {\n const resumed = await transition(firestore, orgId, jobId, (current) => {\n // A job the meter paused (AGL-3660) carries on from the step it paused\n // on: that step is already pending, so it is queued again with nothing\n // else changed. Whether the credits are there now is the reservation's\n // to say, at the door and again at the step.\n // A job waiting for a person's answer (AGL-3978) takes it only once every\n // field reads (`aiJobInputProblems`, which the door asks first): the\n // answer is written to the plan's item and the job returns to its plan\n // card, where Confirm builds it as any plan.\n if (current.status === 'needs_input' && current.input?.fields?.length) {\n const values = options.input\n if (!values || !current.plan || Object.keys(aiJobInputProblems(current.input, values)).length) return null\n return {\n status: 'needs_review',\n plan: aiPlanWithInput(current.plan, current.input, values),\n input: null,\n error: null,\n lease: null,\n updatedAt: now,\n }\n }\n if (current.status === 'needs_input') {\n return { status: 'queued', error: null, lease: null, updatedAt: now }\n }\n if (current.status !== 'needs_review') return null\n const steps = current.steps.map((step) =>\n step.status === 'pending' ? { ...step, attempts: 0 } : step,\n )\n const outstanding = steps.filter((step) => step.status === 'pending').length\n const pending = outstanding > 0\n const proposed = current.review?.reason === 'plan' && current.plan ? current.plan : null\n const confirming = proposed && options.reducePlan ? (options.reducePlan(proposed) ?? proposed) : proposed\n const confirmed = confirming\n ? {\n plan: {\n ...confirming,\n status: 'confirmed',\n confirmedAt: now,\n confirmedBy: actor.uid,\n },\n }\n : {}\n const publish =\n confirming && current.kind === 'build' && options.publishConfirmed && current.inputs?.['publish'] === true\n ? { inputs: { ...current.inputs, publishConfirmed: true } }\n : {}\n return {\n status: pending ? 'queued' : 'done',\n steps,\n review: null,\n error: null,\n ...confirmed,\n ...publish,\n ...(options.creditsConfirmed ? { creditsConfirmed: options.creditsConfirmed } : {}),\n creditsReserved: pending\n ? Math.max(\n outstanding * AI_JOB_STEP_RESERVE_CREDITS,\n confirming ? aiJobCreditEstimate(current.kind, confirming) : 0,\n )\n : 0,\n updatedAt: now,\n }\n })\n // A site start resumed keeps its site (AGL-3936).\n if (resumed.changed) await clearAiEmptySiteSweep(firestore, resumed.job.hostId)\n return resumed\n}\n\n/**\n * Record what a person applied from a finished job's outputs (AGL-2910): the\n * versions an apply opened and the listings it staged. The apply door calls\n * this after its own writes, so the record names only what exists. It never\n * changes the job's status, its steps or its spend.\n */\nexport async function recordAiJobApplied(\n firestore: Firestore,\n orgId: string,\n jobId: string,\n applied: AiJobApplied,\n now = new Date(),\n): Promise<AiJob> {\n const { job } = await transition(firestore, orgId, jobId, () => ({\n applied,\n updatedAt: now,\n }))\n return job\n}\n\n// ── Audit ─────────────────────────────────────────────────────────────────\n\nexport type AiJobAuditAction = 'ai.job.output' | 'ai.job.cancel' | 'ai.job.resume' | 'ai.job.apply'\n\nexport interface AiJobAuditEntry {\n action: AiJobAuditAction\n actorUid: string\n actorEmail?: string | null\n orgId: string\n jobId: string\n after: Record<string, unknown>\n}\n\n/**\n * One `adminAudit` row, the same writer shape the billing routes use. Never\n * throws: an audit row that fails to land is logged, not turned into a\n * failed job the customer paid for.\n */\nexport async function writeAiJobAudit(\n firestore: Firestore,\n entry: AiJobAuditEntry,\n): Promise<void> {\n try {\n await addAdminAudit(firestore, {\n actorUid: entry.actorUid,\n actorEmail: entry.actorEmail ?? null,\n action: entry.action,\n target: `orgs/${entry.orgId}/${AI_JOBS_COLLECTION}/${entry.jobId}`,\n after: entry.after,\n at: FieldValue.serverTimestamp(),\n })\n } catch (error) {\n console.error('ai job audit write failed', {\n orgId: entry.orgId,\n jobId: entry.jobId,\n action: entry.action,\n error,\n })\n }\n}\n\n// ── Running a step ────────────────────────────────────────────────────────\n\n/**\n * The refusal in customer-safe words. The org's own wall names the switch\n * (AGL-2653); a cap, a band and a budget are told apart because one is a\n * figure the org set, one is what its plan sold, and one is the operator's\n * backstop; the message cap resets on a clock the others do not.\n */\nexport function aiJobRefusalText(\n org: Partial<AglynOrgBilling> | null,\n reservation: Pick<\n AssistReservation,\n | 'refusedBy'\n | 'budgetUsd'\n | 'allotment'\n | 'capReason'\n | 'overageLimitUsd'\n | 'overageUnpaidUsd'\n >,\n): string {\n const refusedBy = reservation.refusedBy\n // AGLYN'S OWN OVERAGE GUARDS (AGL-3011). First, because a `cap` refusal\n // that carries a reason knows exactly which limit stopped the job, where\n // the `case 'cap'` below can only name the workspace's own.\n const overage = aiOverageReservationRefusal(reservation)\n if (overage) return overage.text\n // A hard allotment (AGL-2942) — the creator's, theirs on the job's site,\n // or the site's — in the sentence every door gives, naming who can raise\n // it. A member can change that, so the job parks rather than fails.\n if (refusedBy === 'allotment') {\n return aiAllotmentRefusalText(reservation.allotment?.refusal?.scope)\n }\n if (assistRefusedByHardCap(org, refusedBy)) {\n return assistHardCapRefusalText(org)\n }\n // The Free taste's own precautions (AGL-2925), in the sentences every\n // other door uses — each names a clock or an upgrade.\n const taste = assistFreeTasteRefusalText(refusedBy)\n if (taste) return taste\n switch (refusedBy) {\n case 'cap':\n return 'This workspace reached the AI spending cap it set for the month'\n case 'band':\n return 'This workspace used its AI credits for the month'\n case 'budget':\n return reservation.budgetUsd === null\n ? 'This workspace reached its AI spending limit for the month'\n : 'This workspace used its AI credits for the month'\n default:\n return 'This workspace reached its AI limit for the month'\n }\n}\n\nexport interface RunAiJobStepOptions {\n /** Who is claiming: a request id or a beat id. */\n owner: string\n /** How often the running step re-reads its job for a cancel (AGL-3616); specs shorten it. */\n cancelPollMs?: number\n now?: Date\n /**\n * A reservation the caller already holds for THIS step — the console\n * route's gate ladder takes one before the job exists. Every later step\n * reserves for itself.\n */\n reservation?: AssistReservation\n /** The org billing document when the caller has it; read otherwise. */\n org?: Partial<AglynOrgBilling> | null\n /** Ends the provider call when the caller's budget does. */\n signal?: AbortSignal\n /**\n * The person on the request when a route runs the step inline. The beat\n * has none: what a step produces is then credited to the job's creator,\n * whose brief it is, never to nobody (AGL-2929).\n */\n actor?: AiActivityActor\n /**\n * A verified staff claim on the request when a route runs the step inline.\n * The workspace's AI pause lets staff through here as the gate ladder does,\n * so staff can verify a pause with one generation (AGL-3037). The beat has\n * no caller and never passes it.\n */\n staff?: boolean\n}\n\nexport type AiJobStepRun =\n /** Nothing to do: terminal, parked, no step left, or another owner's lease. */\n | { outcome: 'not-claimable' }\n /** The reservation was refused; the job is parked with the reason. */\n | { outcome: 'needs_input'; job: AiJob }\n /** The step stopped for a person (AGL-2935): its plan waits, or its answer broke a rule twice. */\n | { outcome: 'needs_review'; job: AiJob }\n /** The step ran and was recorded; `job.status` says whether more remain. */\n | { outcome: 'done'; job: AiJob }\n | { outcome: 'failed'; job: AiJob }\n /** A retryable provider failure; the step is pending again. */\n | { outcome: 'requeued'; job: AiJob }\n /**\n * AI is paused for the job's workspace (AGL-3037): nothing was claimed,\n * reserved or run, and the job waits where it stopped, at the back of the\n * beat's queue, until staff resume AI.\n */\n | { outcome: 'paused'; job: AiJob }\n /**\n * A person canceled the job while this step held it (AGL-3616): the step\n * finished or aborted, what it spent is recorded, and the job is canceled.\n */\n | { outcome: 'canceled'; job: AiJob }\n\n/**\n * A budget that ended before the provider answered. `fetch` rejects with\n * the signal's reason, which is an `AbortError` for a manual abort and a\n * `TimeoutError` for `AbortSignal.timeout` — the one the route and the\n * sweep actually hand in.\n */\nfunction isAbort(error: unknown): boolean {\n const name = (error as { name?: string } | null)?.name\n return name === 'AbortError' || name === 'TimeoutError'\n}\n\n/** A step outcome that carries no tokens and no cost: the provider was never reached. */\nexport function aiJobStepSpentNothing(\n outcome: Pick<AiJobStepOutcome, 'usage' | 'estCostUsd'>,\n): boolean {\n const { usage } = outcome\n return (\n outcome.estCostUsd === 0 &&\n usage.inputTokens === 0 &&\n usage.outputTokens === 0 &&\n usage.cacheReadTokens === 0 &&\n usage.cacheWriteTokens === 0\n )\n}\n\n/**\n * Pause, claim, reserve, run, meter, record — one step of one job.\n *\n * The order is the invariant. The workspace's AI pause is asked BEFORE the\n * claim, so a paused workspace's job is neither claimed nor reserved for.\n * The reservation is taken AFTER the claim so a refused claim costs nothing,\n * and BEFORE the runner so the org's ceiling binds before a token is spent.\n * The cost is recorded before the step is, so a process cut off between the\n * two leaves the bill right and the step recoverable, never the other way\n * round.\n */\nexport async function runAiJobStep(\n firestore: Firestore,\n orgId: string,\n jobId: string,\n options: RunAiJobStepOptions,\n): Promise<AiJobStepRun> {\n const now = options.now ?? new Date()\n\n // A reservation the caller took before the job existed is a message\n // counted against the org; a step that fails before the provider is\n // reached spends nothing, so the message goes back rather than standing\n // as capacity the org paid for and never used.\n const releaseHeld = async () => {\n if (!options.reservation) return\n await releaseAssistMessage(firestore, orgId, options.reservation).catch(\n (releaseError) =>\n console.error('ai job release failed', { orgId, jobId, releaseError }),\n )\n }\n\n // A WORKSPACE WHOSE AI STAFF HAVE PAUSED runs none of its jobs (AGL-3037),\n // queued before the pause or not: the pause is a spend stop, and a job the\n // beat kept running would spend through it. The pause is lifted by staff\n // or by its own expiry and leaves the plan, the add-on and every\n // entitlement as they were, so a paused job is held, not failed: nothing is\n // claimed, reserved or run, and resuming AI runs it from where it stopped.\n // Read through the same verdict the jobs doors climb, before the claim.\n if (await aiJobPausedFor(orgId, jobId, options.staff === true)) {\n const held = await holdPausedAiJob(firestore, orgId, jobId, options.owner, now)\n if (!held) return { outcome: 'not-claimable' }\n await releaseHeld()\n return { outcome: 'paused', job: held }\n }\n\n const claimed = await claimNextStep(firestore, orgId, jobId, options.owner, now)\n if (!claimed) return { outcome: 'not-claimable' }\n const { job, stepIndex } = claimed\n const step = job.steps[stepIndex]\n\n const runner = aiJobRunnerForStep(job.kind, step.name)\n if (!runner) {\n await releaseHeld()\n return {\n outcome: 'failed',\n job: await failOurFailure(firestore, orgId, jobId, AI_JOB_NOT_AVAILABLE_COPY, {\n stepIndex,\n error: `no runner registered for kind ${job.kind}`,\n }, { reason: 'unavailable', stepCredits: 0 }, now),\n }\n }\n if ((step.attempts ?? 0) > AI_JOB_STEP_MAX_ATTEMPTS) {\n await releaseHeld()\n return {\n outcome: 'failed',\n job: await failOurFailure(firestore, orgId, jobId, AI_UPSTREAM_FAILURE_COPY, {\n stepIndex,\n error: `step ${step.name} exhausted ${AI_JOB_STEP_MAX_ATTEMPTS} attempts`,\n }, { reason: 'timeout', stepCredits: 0 }, now),\n }\n }\n\n const org =\n options.org ??\n (((await firestore.collection('orgs').doc(orgId).get()).data() ??\n {}) as Partial<AglynOrgBilling>)\n\n // A site that switched AI off runs none of its jobs (AGL-3028), queued\n // before the switch or not. Asked before the reservation, so the job fails\n // in words a member can act on and nothing is reserved, run or metered.\n if (await isAiOffForSite(firestore, org, job.hostId)) {\n await releaseHeld()\n return {\n outcome: 'failed',\n job: await failAiJob(firestore, orgId, jobId, AI_OFF_FOR_SITE_COPY, {\n stepIndex,\n error: `ai is switched off for site ${job.hostId}`,\n }, now),\n }\n }\n\n const entitled =\n checkEntitlement(org, 'aiAssist') || checkEntitlement(org, 'aiGenerative')\n\n let reservation: AssistReservation\n if (options.reservation) {\n reservation = options.reservation\n } else {\n try {\n // The creator's allotments on the job's site (AGL-2942): the step is\n // their spend, as the meter below attributes it.\n reservation = await reserveAssistMessage(firestore, orgId, entitled, now, org, {\n uid: job.createdBy,\n hostId: job.hostId ?? null,\n })\n } catch (error) {\n // The meter is unreachable, not refusing. Hand the step back; the\n // next beat asks again.\n console.error('ai job reservation failed', { orgId, jobId, error })\n const { job: requeued } = await recordStep(\n firestore, orgId, jobId, options.owner, stepIndex,\n { status: 'pending', creditsSpent: 0 },\n now,\n )\n return { outcome: 'requeued', job: requeued }\n }\n }\n // A refused step is the creator's refusal (AGL-2928), on their month,\n // beside the org counter the reservation moved; an admitted one records\n // nothing.\n recordUserAiRefusal(firestore, orgId, job.createdBy, reservation)\n if (!reservation.allowed) {\n const refusal = aiJobRefusalText(org, reservation)\n const parked = await markAiJobNeedsInput(firestore, orgId, jobId, refusal, now)\n // A cancel asked meanwhile ended the job instead of parking it (AGL-3616).\n if (parked.status === 'canceled') return { outcome: 'canceled', job: parked }\n // Nobody parked the job — the meter did — so the row carries no actor\n // rather than the creator's name on an act they did not perform. Only\n // when the job was not already parked for this: the reason the last\n // park stored survives the beat's re-queue, so a job the meter refuses\n // again on its hourly retry is one row, not one an hour; a different\n // ceiling is a new row. The meter names a ceiling on every refusal; the\n // guard narrows the type.\n if (reservation.refusedBy && job.error !== refusal) {\n await logAiJobNeedsInput(orgId, { uid: null }, {\n jobId,\n kind: job.kind,\n reason: reservation.refusedBy,\n })\n // The person is told once per pause (AGL-3660), not once an hour: a\n // job the beat re-queues and the meter refuses again keeps its reason.\n await announceAiJobTransition(parked, 'paused')\n }\n return { outcome: 'needs_input', job: parked }\n }\n\n // The model each step runs on (AGL-2942): the creator's pick where the\n // plan and the allotment allowlists the reservation read allow it, the\n // routing table otherwise. Handed to the runner as a function of the step\n // kind, because the runner — not the machine — knows what kind it runs.\n const bounds = {\n plan: resolveEffectivePlan(org),\n allotmentModels: reservation.allotment?.models ?? null,\n orgModels: reservation.allotment?.orgModels ?? null,\n }\n const modelFor = (kind: Parameters<typeof resolveAiModelChoice>[0]) =>\n resolveAiModelChoice(kind, job.model ?? null, bounds)?.model\n\n // A person's cancel stops the step (AGL-3616): the step's signal is the\n // caller's budget AND a watch on the job, so a cancel aborts the provider\n // call in flight, stops one about to start, and stops a write the step\n // asks `throwIfAiJobCanceled` about first.\n const stepSignal = new AbortController()\n const forwardBudget = () => stepSignal.abort(options.signal?.reason)\n if (options.signal?.aborted) forwardBudget()\n else options.signal?.addEventListener('abort', forwardBudget, { once: true })\n const stopWatch = watchAiJobCancel(\n () => getAiJob(firestore, orgId, jobId),\n stepSignal,\n options.cancelPollMs,\n )\n const canceledMidStep = () =>\n stepSignal.signal.aborted && isAiJobCanceledError(stepSignal.signal.reason)\n\n let outcome: AiJobStepOutcome\n // The runner's wall-clock time, recorded on the step beside its tokens\n // (AGL-2937): measured here, around the call, rather than asked of it.\n const runStarted = Date.now()\n try {\n outcome = await runner({\n job, stepIndex, now, signal: stepSignal.signal, firestore, org, modelFor,\n })\n } catch (error) {\n // The provider refused the request or the budget ended before it\n // answered: no usage came back, so nothing is metered and the message\n // goes back to the org. The provider's own words are already in the\n // log under the request id (AGL-2815).\n await releaseAssistMessage(firestore, orgId, reservation).catch((releaseError) =>\n console.error('ai job release failed', { orgId, jobId, releaseError }),\n )\n // Canceled mid-step (AGL-3616): the step is handed back unrun and the\n // record ends the job canceled — never failed, never retried.\n if (isAiJobCanceledError(error) || canceledMidStep()) {\n const { job: canceled } = await recordStep(\n firestore, orgId, jobId, options.owner, stepIndex,\n { status: 'pending', creditsSpent: 0 },\n now,\n )\n return { outcome: 'canceled', job: canceled }\n }\n const retryable =\n (error instanceof AiUpstreamError && error.retryable) || isAbort(error)\n if (retryable) {\n const { job: requeued } = await recordStep(\n firestore, orgId, jobId, options.owner, stepIndex,\n { status: 'pending', creditsSpent: 0 },\n now,\n )\n return { outcome: 'requeued', job: requeued }\n }\n return {\n outcome: 'failed',\n job: await failOurFailure(firestore, orgId, jobId, AI_UPSTREAM_FAILURE_COPY, {\n stepIndex,\n error,\n }, { reason: 'provider', stepCredits: 0, free: reservation.free ?? null }, now),\n }\n } finally {\n stopWatch()\n options.signal?.removeEventListener('abort', forwardBudget)\n }\n\n const latencyMs = Math.max(0, Date.now() - runStarted)\n\n // A step that failed before it reached the provider — the site it builds\n // from could not be read, say (AGL-2938) — spent nothing: its message goes\n // back and nothing is metered, exactly as for a request the provider\n // refused.\n if (outcome.failure && aiJobStepSpentNothing(outcome)) {\n await releaseAssistMessage(firestore, orgId, reservation).catch((releaseError) =>\n console.error('ai job release failed', { orgId, jobId, releaseError }),\n )\n await recordStep(\n firestore, orgId, jobId, options.owner, stepIndex,\n { status: 'failed', creditsSpent: 0, error: outcome.failure },\n now,\n )\n return {\n outcome: 'failed',\n job: await failOurFailure(firestore, orgId, jobId, outcome.failure, {\n stepIndex,\n error: `step failure before the provider: ${outcome.failure}`,\n ...(outcome.customerLimit ? { ours: false } : {}),\n }, { reason: 'step-failure', stepCredits: 0, free: reservation.free ?? null }, now),\n }\n }\n\n const tokens: AiJobStepTokenRun = {\n usage: outcome.usage,\n model: outcome.model,\n effort: outcome.effort ?? null,\n latencyMs,\n stopReason: outcome.stopReason,\n }\n\n // A step that stopped before the provider without failing — a site with no\n // room for the draft (AGL-2909), say — spent nothing too: its message goes\n // back and there is nothing to meter. What it returns is still recorded\n // below, as any step's is.\n const spentNothing = aiJobStepSpentNothing(outcome)\n if (spentNothing) {\n await releaseAssistMessage(firestore, orgId, reservation).catch((releaseError) =>\n console.error('ai job release failed', { orgId, jobId, releaseError }),\n )\n } else {\n // Tokens were spent, so the bill is written first and its failure is a\n // log line, never a failed job — the same rule the chat route keeps.\n try {\n await recordAssistCost(\n firestore,\n orgId,\n {\n route: 'ai/jobs',\n hostId: job.hostId ?? null,\n model: outcome.model,\n tier: entitled ? 'entitled' : 'free',\n usage: outcome.usage,\n docsPaths: [],\n stopReason: outcome.stopReason,\n deflected: false,\n // The account this job drew on, as the reservation decided it\n // (AGL-2925): a Free job lands on the owner's allowance and the\n // platform's day; a paid one on neither.\n free: reservation.free ?? null,\n // The step is the creator's spend, under the job's kind (AGL-2928).\n uid: job.createdBy,\n kind: job.kind,\n },\n now,\n )\n } catch (error) {\n console.error('ai job cost record failed', { orgId, jobId, error })\n }\n }\n const credits = assistCreditsFromUsd(outcome.estCostUsd)\n\n // A model that declined, or a step that got nothing usable out of the\n // model's answer (AGL-2938), fails the job with its own sentence. The\n // tokens are already on the bill above.\n if (outcome.refused || outcome.failure) {\n const message = outcome.refused ? AI_JOB_REFUSED_COPY : (outcome.failure as string)\n await recordStep(\n firestore, orgId, jobId, options.owner, stepIndex,\n { status: 'failed', creditsSpent: credits, error: message, tokens },\n now,\n )\n const detail = {\n stepIndex,\n error: outcome.refused ? 'stop_reason refusal' : `step failure: ${message}`,\n ...(outcome.customerLimit ? { ours: false } : {}),\n }\n // A model declining the brief is not our failure; a step's own is (AGL-3594).\n return {\n outcome: 'failed',\n job: outcome.refused\n ? await failAiJob(firestore, orgId, jobId, message, detail, now)\n : await failOurFailure(firestore, orgId, jobId, message, detail, {\n reason: 'step-failure', stepCredits: credits, free: reservation.free ?? null,\n }, now),\n }\n }\n\n // A guided site start whose step broke a building rule after its re-ask\n // fails, given back whole (AGL-3596): the person never saw the plan, so a\n // review asking them to look at it again is a review of nothing. The rule\n // and its findings go to the log for staff; the person reads one sentence,\n // and Try again starts a fresh job.\n if (outcome.review?.reason === 'doctrine' && aiJobAutoConfirms(job) && step.name !== AI_JOB_PLAN_STEP) {\n await recordStep(\n firestore, orgId, jobId, options.owner, stepIndex,\n {\n status: 'failed',\n creditsSpent: credits,\n error: AI_SITE_GUIDED_BUILD_FAILED_COPY,\n outputs: outcome.outputs,\n ...(spentNothing ? {} : { tokens }),\n },\n now,\n )\n return {\n outcome: 'failed',\n job: await failOurFailure(firestore, orgId, jobId, AI_SITE_GUIDED_BUILD_FAILED_COPY, {\n stepIndex,\n error: `doctrine refusal after the re-ask: ${JSON.stringify(outcome.review.findings ?? [])} ${outcome.review.message ?? ''}`,\n }, { reason: 'doctrine-refused', stepCredits: credits, free: reservation.free ?? null }, now),\n }\n }\n\n // A step that stopped for a person parks the job in the same write that\n // records it (AGL-2935): a plan review completes the step, so confirming\n // runs the next one; a doctrine or limit review hands it back, so trying\n // again runs the same one.\n //\n // A step that asks to continue (AGL-2910) is recorded as this pass — its\n // cost, its credits, its outputs, its audit rows — and handed back as\n // `pending`, so the next claim runs the same step again. A step that stopped\n // for a person never continues: the review is what it waits on. The pass\n // cap is what stops a runner that never finishes: past it the pass is\n // recorded as the step's last, and what it produced stands.\n // A build item that failed on our side gives back its own attempt's spend\n // before its row is written (AGL-3616), so the row records what was given.\n let itemRecord: AiJobItemRecord | undefined = aiJobSettlesByItem(job.kind) ? outcome.item : undefined\n // An item a person's cancel cut short is not our failure (AGL-3616): it is\n // given nothing back, and its row is the cancel's.\n if (itemRecord?.status === 'failed' && itemRecord.failure?.ours && !canceledMidStep()) {\n const slot = itemRecord.slot\n const row = (outcome.items ?? job.items ?? []).find((one) => one.slot === slot)\n const refund = await refundBuildCredits(firestore, orgId, job, {\n slot,\n attempt: row?.attempt ?? 1,\n credits: (row?.attemptCredits ?? 0) + credits,\n reason: itemRecord.failure.reason as AiJobRefundReason,\n free: reservation.free ?? null,\n now,\n })\n if (refund) itemRecord = { ...itemRecord, creditsRefunded: refund.credits, refundKey: refund.key }\n }\n\n const review = outcome.review\n const continuing =\n !review &&\n Boolean(outcome.continue) &&\n (step.passes ?? 0) + 1 < aiJobStepMaxPasses(job.kind)\n if (!review && outcome.continue && !continuing) {\n console.warn('ai job step reached its pass cap', { orgId, jobId, stepIndex })\n }\n const recorded = await recordStep(\n firestore, orgId, jobId, options.owner, stepIndex,\n {\n status: continuing || (review && review.reason !== 'plan') ? 'pending' : 'done',\n ...(continuing ? { continued: true } : {}),\n creditsSpent: credits,\n // A step that spent nothing ran no model, so it adds no run to the measure.\n ...(spentNothing ? {} : { tokens }),\n outputs: outcome.outputs,\n ...(outcome.plan ? { plan: outcome.plan } : {}),\n ...(review ? { review } : {}),\n ...(review && outcome.input ? { input: outcome.input } : {}),\n ...(outcome.sitePublish ? { sitePublish: outcome.sitePublish } : {}),\n ...(job.kind === 'import' && outcome.siteImport ? { siteImport: outcome.siteImport } : {}),\n ...(job.kind === 'site' && outcome.sitePhotos ? { sitePhotos: outcome.sitePhotos } : {}),\n ...(aiJobSettlesByItem(job.kind) && outcome.items ? { items: outcome.items } : {}),\n ...(itemRecord ? { item: itemRecord } : {}),\n // A park this call resolves at once (an auto-confirmed plan) or gives\n // back before it is told (a refusal on our side) is announced below.\n ...(review ? { quiet: true } : {}),\n },\n now,\n )\n const actor: AiActivityActor = options.actor ?? { uid: job.createdBy }\n for (const output of outcome.outputs) {\n await writeAiJobAudit(firestore, {\n action: 'ai.job.output',\n actorUid: job.createdBy,\n orgId,\n jobId,\n after: {\n resource: output.resource,\n id: output.id,\n versionId: output.versionId ?? null,\n hostId: output.hostId,\n label: output.label,\n credits,\n },\n })\n // The customer-visible row, one per output: the label names the thing,\n // never its text, which is the site's content.\n await logAiJobOutput(orgId, actor, {\n jobId,\n hostId: output.hostId,\n resource: {\n type: aiOutputTargetType(output.resource),\n id: output.id,\n name: output.label,\n versionId: output.versionId ?? null,\n },\n })\n }\n // A cancel asked while the step ran ended the job in that record (AGL-3616).\n if (recorded.job.status === 'canceled') return { outcome: 'canceled', job: recorded.job }\n if (review?.reason === 'plan' && recorded.job.status === 'needs_review' && aiJobAutoConfirms(job)) {\n // A guided site start confirms its own plan (AGL-3594): the resume\n // door's confirmation, by the job's creator, and the build queued.\n const resumed = await resumeAiJob(firestore, orgId, jobId, { uid: job.createdBy }, now)\n await writeAiJobAudit(firestore, {\n action: 'ai.job.resume',\n actorUid: job.createdBy,\n orgId,\n jobId,\n after: { status: resumed.job.status, confirmedPlan: true, autoConfirm: true },\n })\n return { outcome: 'done', job: resumed.job }\n }\n // A plan waiting for an answer only the person can give (AGL-3978): kept,\n // charged as any plan is, and asked of them; nothing about it failed.\n if (review && outcome.input && recorded.job.status === 'needs_input') {\n await logAiJobNeedsInput(orgId, { uid: null }, { jobId, kind: job.kind, reason: 'input' })\n if (recorded.parked) await announceAiJobTransition(recorded.job, 'needs-review')\n return { outcome: 'needs_input', job: recorded.job }\n }\n if (review && recorded.job.status === 'needs_review') {\n // Nobody parked the job but its own step, so the row names no actor, as\n // the meter's park does.\n await logAiJobNeedsInput(orgId, { uid: null }, {\n jobId,\n kind: job.kind,\n reason: review.reason,\n })\n // A plan the plan rules still refused, and a step that still broke a\n // building rule after its re-ask, are our failures (AGL-3594, AGL-3596):\n // given back before the person is told, so the notice can say so. Trying\n // again spends anew, and a second refusal gives back again, within the\n // day's bound.\n const ours = outcome.uncredited ? 'plan-refused' : review.reason === 'doctrine' ? 'doctrine-refused' : null\n const parked = ours\n ? await refundOurFailure(firestore, orgId, recorded.job, {\n reason: ours, stepCredits: credits, free: reservation.free ?? null, now,\n })\n : recorded.job\n if (recorded.parked) await announceAiJobTransition(parked, 'needs-review')\n return { outcome: 'needs_review', job: parked }\n }\n if (recorded.remaining > 0 || isAiJobTerminal(recorded.job.status)) {\n return { outcome: 'done', job: recorded.job }\n }\n // A build ends by its items (AGL-3616): done when anything was delivered.\n if (aiJobSettlesByItem(job.kind) && step.name !== AI_JOB_PLAN_STEP && recorded.job.items?.length) {\n // Items still open here are ones the pass cap cut off: the build stopped.\n const open = aiBuildSettlement(recorded.job.items ?? []).open\n const settled = await settleAiBuildJob(firestore, orgId, jobId, {\n now,\n free: reservation.free ?? null,\n ...(open ? { stopped: { message: AI_UPSTREAM_FAILURE_COPY, reason: 'timeout' as const } } : {}),\n })\n return { outcome: settled.status === 'failed' ? 'failed' : 'done', job: settled }\n }\n return {\n outcome: 'done',\n job: await completeAiJob(firestore, orgId, jobId, now),\n }\n}\n\n// ── The beat's sweep ──────────────────────────────────────────────────────\n\nexport interface DueAiJob {\n orgId: string\n jobId: string\n}\n\nexport interface SweepAiJobsOptions {\n firestore: Firestore\n /** The beat's identity for its leases. */\n owner: string\n now?: () => number\n budgetMs?: number\n maxJobs?: number\n /** Test seams; the defaults query Firestore and run the real step. */\n listDue?: () => Promise<DueAiJob[]>\n runStep?: (\n orgId: string,\n jobId: string,\n options: RunAiJobStepOptions,\n ) => Promise<AiJobStepRun>\n}\n\nexport interface SweepAiJobsResult {\n /** Candidates the queries returned. */\n due: number\n /** Steps this sweep actually ran to a recorded result. */\n ran: number\n /** Claims another owner held, or jobs with nothing left. */\n skipped: number\n /**\n * Jobs whose workspace's AI staff have paused (AGL-3037): held where they\n * stopped and moved to the back of the queue, to run once AI is resumed.\n */\n paused: number\n /** Candidates left untouched because the wall clock ran out. */\n remaining: number\n budgetExhausted: boolean\n}\n\n/**\n * Every job the beat should look at, oldest first: queued and running\n * across every org, then `needs_input` jobs that have rested long enough\n * for the org's standing to have changed. Two queries rather than one, so\n * a workspace with many parked jobs cannot fill the candidate list and\n * starve the queue behind it. Both use the collection-group index on\n * (status, updatedAt).\n *\n * The ordering is the cursor. A job this sweep touches has its `updatedAt`\n * moved and goes to the back of the next query; one it did not reach keeps\n * its place at the front. So there is no stored position to fall behind\n * or skip past — the same shape `media-move.ts` gives its budgeted loop,\n * where what was not done is simply what is asked for next.\n */\nexport async function listDueAiJobs(\n firestore: Firestore,\n nowMs: number,\n maxJobs = AI_JOB_SWEEP_MAX_JOBS,\n maxParked = AI_JOB_SWEEP_MAX_PARKED,\n): Promise<DueAiJob[]> {\n const group = firestore.collectionGroup(AI_JOBS_COLLECTION)\n const [active, parked] = await Promise.all([\n group\n .where('status', 'in', ['queued', 'running'])\n .orderBy('updatedAt', 'asc')\n .limit(maxJobs)\n .get(),\n group\n .where('status', '==', 'needs_input')\n .where('updatedAt', '<=', new Date(nowMs - AI_JOB_NEEDS_INPUT_RETRY_MS))\n .orderBy('updatedAt', 'asc')\n .limit(maxParked)\n .get(),\n ])\n return [...active.docs, ...parked.docs]\n .map((doc) => ({ orgId: String(doc.get('orgId') ?? ''), jobId: doc.id }))\n .filter((due) => due.orgId !== '')\n}\n\n/**\n * Run due steps until the budget is spent, and account for every candidate.\n *\n * The bound is wall clock, not a count, for the reason `media-move.ts`\n * gives: one step is one provider round trip of unknown length, so any\n * fixed number would be wrong for some brief. The budget is checked BETWEEN\n * jobs, and the step in flight gets the time that is left as its abort\n * signal, so the sweep still finishes whichever step it has started. At\n * least one candidate is always attempted, so a beat that yields having\n * done nothing cannot starve a queue behind a slow clock.\n *\n * A `needs_input` job is re-queued before its step is claimed, because the\n * claim admits only queued and running jobs; the reservation inside the\n * step is what decides whether the org's standing has changed.\n */\nexport async function sweepAiJobs(\n options: SweepAiJobsOptions,\n): Promise<SweepAiJobsResult> {\n const now = options.now ?? Date.now\n const budgetMs = options.budgetMs ?? AI_JOB_SWEEP_BUDGET_MS\n const startedAt = now()\n const listDue =\n options.listDue ??\n (() => listDueAiJobs(options.firestore, now(), options.maxJobs))\n const runStep =\n options.runStep ??\n ((orgId: string, jobId: string, stepOptions: RunAiJobStepOptions) =>\n runAiJobStep(options.firestore, orgId, jobId, stepOptions))\n\n const due = await listDue()\n const result: SweepAiJobsResult = {\n due: due.length,\n ran: 0,\n skipped: 0,\n paused: 0,\n remaining: 0,\n budgetExhausted: false,\n }\n // Jobs a run left with more of a timed step to do, in the order they ran.\n const again: DueAiJob[] = []\n const attempt = async (candidate: DueAiJob, left: number, first: boolean): Promise<void> => {\n const { orgId, jobId } = candidate\n try {\n const current = await getAiJob(options.firestore, orgId, jobId)\n // A step that needs more time than is left is not started (AGL-2907): a\n // provider call the abort cuts off is billed upstream and metered\n // nowhere. The job is left as it is, so it keeps its place at the front\n // of the next beat's queue.\n const minimumMs = current ? aiJobNextStepMinimumMs(current) : 0\n if (minimumMs > left) {\n if (first) result.remaining += 1\n return\n }\n // A job waiting for a person's answer (AGL-3978) is theirs to resume:\n // it rests again, at the back of the parked queue, so it never holds a\n // place a meter park could use.\n if (current?.status === 'needs_input' && current.input?.fields?.length) {\n await transition(options.firestore, orgId, jobId, (job) =>\n job.status === 'needs_input' && job.input?.fields?.length ? { updatedAt: new Date(now()) } : null,\n )\n if (first) result.skipped += 1\n return\n }\n if (current?.status === 'needs_input') {\n await transition(options.firestore, orgId, jobId, (job) =>\n job.status === 'needs_input' ? { status: 'queued', updatedAt: new Date(now()) } : null,\n )\n }\n const run = await runStep(orgId, jobId, {\n owner: options.owner,\n now: new Date(now()),\n signal: AbortSignal.timeout(left),\n })\n if (run.outcome === 'not-claimable') {\n if (first) result.skipped += 1\n return\n }\n if (run.outcome === 'paused') {\n if (first) result.paused += 1\n return\n }\n result.ran += 1\n // Only a step that says how long it needs is run again in this sweep:\n // one that does not could start with too little time left.\n if (\n run.outcome === 'done' &&\n run.job.status === 'queued' &&\n aiJobNextStepMinimumMs(run.job) > 0\n ) {\n again.push(candidate)\n }\n } catch (error) {\n // One job's fault is isolated, as the runner isolates one job's.\n console.error('ai job sweep step failed', { orgId, jobId, error })\n if (first) result.skipped += 1\n }\n }\n\n for (let index = 0; index < due.length; index += 1) {\n const elapsed = now() - startedAt\n if (index > 0 && elapsed >= budgetMs) {\n result.budgetExhausted = true\n result.remaining += due.length - index\n return result\n }\n await attempt(due[index], Math.max(1_000, budgetMs - elapsed), true)\n }\n // Once every due job has had its turn, a job whose timed step has more to\n // do runs again while the time it needs is left (AGL-2907): a page built a\n // section a pass otherwise waits a beat between sections. Bounded by the\n // budget, and by the sweep's candidate count so no clock is trusted alone.\n for (let round = 0; again.length && round < AI_JOB_SWEEP_MAX_JOBS; round += 1) {\n const elapsed = now() - startedAt\n if (elapsed >= budgetMs) {\n result.budgetExhausted = true\n break\n }\n await attempt(again.shift() as DueAiJob, budgetMs - elapsed, false)\n }\n return result\n}\n"],"names":["FieldValue","createResourceUid","checkEntitlement","assistCreditsFromUsd","assistFreeTasteRefusalText","assistHardCapRefusalText","assistRefusedByHardCap","AI_JOB_PLAN_STEP","AI_JOB_TERMINAL_STATUSES","aiJobInputProblems","aiPlanWithInput","resolveEffectivePlan","aiOverageReservationRefusal","aiAllotmentRefusalText","AI_OFF_FOR_SITE_COPY","isAiOffForSite","resolveAiModelChoice","aiOutputTargetType","logAiJobCanceled","logAiJobCreated","logAiJobNeedsInput","logAiJobOutput","AI_UPSTREAM_FAILURE_COPY","AiUpstreamError","recordUserAiRefusal","AI_JOB_INLINE_BUDGET_MS","AI_JOB_SWEEP_BUDGET_MS","aiMintJobDraftIds","AI_JOB_TEXT_STEP_MINIMUM_MS","runAiJobTextStep","AI_JOB_SEO_STEP_MINIMUM_MS","AI_JOB_THEME_STEP_MINIMUM_MS","assistExchangeExpiry","assistUsageDay","assistUsageMonth","recordAssistCost","releaseAssistMessage","reserveAssistMessage","aiJobItemRefundKey","aiJobRefundCredits","refundJobCredits","aiBuildSettlement","aiBuildUnits","aiBuildRetryLedger","aiBuildCreditEstimate","aiLedgerUnits","aiJobCreditEstimate","freeAssistAccount","aiJobAutoConfirms","AI_SITE_GUIDED_BUILD_FAILED_COPY","aiCustomerSafeCopy","aiCustomerSafeItem","aiCustomerSafeReview","aiSiteStartInputsOf","aiSiteImportWire","addAdminAudit","AI_JOB_CANCELED_ITEM_NOTE","aiJobCancelPending","isAiJobCanceledError","watchAiJobCancel","clearAiEmptySiteSweep","scheduleAiEmptySiteSweep","AI_JOBS_COLLECTION","AI_JOB_LEASE_MS","AI_JOB_STEP_MAX_ATTEMPTS","AI_JOB_STEP_MAX_PASSES","AI_JOB_STEP_RESERVE_CREDITS","AI_JOB_SWEEP_MAX_JOBS","AI_JOB_SWEEP_MAX_PARKED","AI_JOB_NEEDS_INPUT_RETRY_MS","AI_JOB_NOT_AVAILABLE_COPY","AI_JOB_REFUSED_COPY","toMillis","value","Date","getTime","parsed","parse","Number","isFinite","toIso","millis","toISOString","isAiJobTerminal","status","includes","AI_PLANNED_JOB_KINDS","stepRunners","Map","stepMinimums","stepRunMinimums","planStepRunner","planStepMinimumMs","stepPassCaps","registerAiJobStep","kind","runner","registration","set","minimumMs","delete","minimumMsFor","aiJobStepMinimumMs","stepName","get","aiJobStepRunMinimumMs","job","least","forRun","Math","max","aiJobNextStepMinimumMs","index","nextStepIndex","steps","name","aiJobStepRunnerFor","registerAiJobStepPasses","maxPasses","floor","aiJobStepMaxPasses","registerAiJobPlanStep","pauseReader","registerAiJobPauseReader","reader","transitionListener","registerAiJobTransitionListener","listener","aiJobTransitionListenerRegistered","noteAiJobEnded","firestore","now","announceAiJobTransition","to","failure","error","console","orgId","jobId","$id","aiJobPauseReaderRegistered","aiJobPausedFor","staff","aiJobRunnerForStep","aiJobStepNames","has","context","runAiJobThemeStep","runAiJobSeoStep","jobsCollection","collection","doc","jobFrom","snapshot","exists","data","id","createAiJob","input","brief","trim","Error","draftIds","map","startedAt","endedAt","creditsSpent","attempts","ref","hostId","inputs","model","outputs","creditsReserved","length","createdBy","createdAt","updatedAt","expiresAt","lease","creditsConfirmed","uid","email","createdByEmail","briefLength","getAiJob","listAiJobs","options","limit","min","query","where","statuses","read","from","count","orderBy","docs","filter","isFailedPrecondition","warn","recent","AI_JOBS_HOST_FALLBACK_SCAN","slice","code","aiJobSummary","leaseUntil","until","batch","step","refundedCredits","page","running","plan","labels","proposedAt","confirmedAt","confirmedBy","review","fields","applied","at","by","versions","staged","sitePublish","siteInputs","autoConfirm","items","orchestration","publishAsked","cancelRequested","siteRemoved","siteImport","leaseIsLive","owner","nowMs","anyLeaseIsLive","Boolean","aiJobCanceledPatch","request","aiCanceledItems","row","note","cancelRequestOf","aiJobCancelRequest","findIndex","claimableStepIndex","claimNextStep","ended","claimed","runTransaction","tx","patch","merge","stepIndex","heartbeatStep","holdPausedAiJob","held","aiApplyJobItemRecord","ledger","record","credits","slot","attemptCredits","creditsRefunded","refundKey","undefined","degradedBy","settledAt","AI_JOB_STEP_LAST_RUNS","AI_JOB_STEP_STOP_REASON_MAX_CHARS","addAiJobStepTokens","current","run","kept","Array","isArray","lastRuns","stopReason","usage","inputTokens","cachedRead","cacheReadTokens","cacheWrite","cacheWriteTokens","output","outputTokens","effort","latencyMs","runs","recordStep","recorded","continued","passes","tokens","remaining","item","settled","ownsLease","canceling","parksForReview","parkedAs","sitePhotos","message","reason","parked","quiet","transition","apply","changed","completeAiJob","failAiJob","detail","ours","String","markAiJobNeedsInput","cancelAiJob","actor","firstAsk","result","inFlight","refundOurFailure","delivered","some","resource","jobCredits","stepCredits","alreadyRefunded","free","ordinal","refunds","refund","month","day","refunded","refundReason","failOurFailure","aiJobSettlesByItem","settleAiBuildJob","stopped","aiBuildRetryable","AI_BUILD_NOTHING_BUILT_COPY","AI_SITE_IMPORT_NOTHING_BUILT_COPY","AI_BUILD_NOT_REACHED_COPY","refundBuildCredits","created","jobDay","attempt","key","aiBuildPlanCredits","find","given","sitePages","op","planCredits","parts","owed","size","values","reduce","total","part","nothing","retryAiBuildJob","retried","next","slots","Set","resumeAiJob","resumed","Object","keys","outstanding","pending","proposed","confirming","reducePlan","confirmed","publish","publishConfirmed","recordAiJobApplied","writeAiJobAudit","entry","actorUid","actorEmail","action","target","after","serverTimestamp","aiJobRefusalText","org","reservation","refusedBy","overage","text","allotment","refusal","scope","taste","budgetUsd","isAbort","aiJobStepSpentNothing","outcome","estCostUsd","runAiJobStep","itemRecord","releaseHeld","catch","releaseError","entitled","requeued","allowed","bounds","allotmentModels","models","orgModels","modelFor","stepSignal","AbortController","forwardBudget","abort","signal","aborted","addEventListener","once","stopWatch","cancelPollMs","canceledMidStep","runStarted","canceled","retryable","removeEventListener","customerLimit","spentNothing","route","tier","docsPaths","deflected","refused","JSON","stringify","findings","one","continuing","continue","versionId","label","type","confirmedPlan","uncredited","open","listDueAiJobs","maxJobs","maxParked","group","collectionGroup","active","Promise","all","due","sweepAiJobs","budgetMs","listDue","runStep","stepOptions","ran","skipped","paused","budgetExhausted","again","candidate","left","first","AbortSignal","timeout","push","elapsed","round","shift"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;CAKC,GACD,SAASA,UAAU,QAAQ,2BAA0B;AACrD,SAASC,iBAAiB,QAAQ,6CAA4C;AAC9E,SAASC,gBAAgB,QAAQ,sBAAqB;AACtD,SACEC,oBAAoB,EACpBC,0BAA0B,EAC1BC,wBAAwB,EACxBC,sBAAsB,QACjB,6BAAyB;AAChC,SACEC,gBAAgB,EAChBC,wBAAwB,QAenB,4BAAwB;AAC/B,SAASC,kBAAkB,EAAEC,eAAe,QAAQ,2BAAuB;AAG3E,SAASC,oBAAoB,QAAQ,2CAA0C;AAC/E,SAASC,2BAA2B,QAAQ,gCAA4B;AACxE,SAASC,sBAAsB,QAAQ,4BAAwB;AAC/D,SAASC,oBAAoB,EAAEC,cAAc,QAAQ,6BAAyB;AAC9E,SAASC,oBAAoB,QAAQ,+BAA2B;AAChE,SAASC,kBAAkB,QAAQ,qCAAiC;AACpE,SACEC,gBAAgB,EAChBC,eAAe,EACfC,kBAAkB,EAClBC,cAAc,QAET,6BAAyB;AAChC,SACEC,wBAAwB,EACxBC,eAAe,QAGV,2BAAuB;AAC9B,SAASC,mBAAmB,QAAQ,+BAA2B;AAC/D,SAASC,uBAAuB,EAAEC,sBAAsB,QAAQ,qBAAiB;AACjF,SAASC,iBAAiB,QAAQ,wBAAoB;AACtD,SACEC,2BAA2B,EAC3BC,gBAAgB,QAGX,wBAAoB;AAC3B,SAASC,0BAA0B,QAAQ,yBAAqB;AAChE,SAASC,4BAA4B,QAAQ,2BAAuB;AACpE,SACEC,oBAAoB,EACpBC,cAAc,EACdC,gBAAgB,EAChBC,gBAAgB,EAChBC,oBAAoB,EACpBC,oBAAoB,QAEf,2BAAuB;AAC9B,SACEC,kBAAkB,EAClBC,kBAAkB,EAClBC,gBAAgB,QAEX,gCAA4B;AACnC,SACEC,iBAAiB,EACjBC,YAAY,EACZC,kBAAkB,EAClBC,qBAAqB,EACrBC,aAAa,EACbC,mBAAmB,QACd,2BAAuB;AAE9B,SAASC,iBAAiB,QAAgC,gCAA4B;AACtF,SAASC,iBAAiB,QAAQ,2BAAuB;AACzD,SACEC,gCAAgC,EAChCC,kBAAkB,EAClBC,kBAAkB,EAClBC,oBAAoB,QACf,kCAA8B;AACrC,SAASC,mBAAmB,QAAQ,4BAAwB;AAC5D,SAASC,gBAAgB,QAAgC,6BAAyB;AAElF,SAASC,aAAa,QAAQ,oDAAmD;AACjF,SACEC,yBAAyB,EAEzBC,kBAAkB,EAClBC,oBAAoB,EACpBC,gBAAgB,QACX,qBAAiB;AACxB,SAASC,qBAAqB,EAAEC,wBAAwB,QAAQ,2BAAuB;AAEvF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkCC,GAED,OAAO,MAAMC,qBAAqB,SAAQ;AAE1C;;;;;;;;CAQC,GACD,OAAO,MAAMC,kBAAkB,OAAO;AAEtC,8EAA8E,GAC9E,OAAO,MAAMC,2BAA2B,EAAC;AAEzC;;;;;CAKC,GACD,OAAO,MAAMC,yBAAyB,GAAE;AAExC;;;;;;;;;;;;;CAaC,GACD,OAAO,MAAMC,8BAA8B,GAAE;AAE7C;;;;CAIC,GACD,SAASzC,uBAAuB,EAAEC,sBAAsB,GAAE;AAE1D,0EAA0E,GAC1E,OAAO,MAAMyC,wBAAwB,GAAE;AAEvC,qEAAqE,GACrE,OAAO,MAAMC,0BAA0B,EAAC;AAExC,oFAAoF,GACpF,OAAO,MAAMC,8BAA8B,KAAK,MAAM;AAEtD,OAAO,MAAMC,4BACX,4CAA2C;AAC7C,OAAO,MAAMC,sBACX,oDAAmD;AAQrD,SAASC,SAASC,KAAc;IAC9B,IAAIA,SAAS,MAAM,OAAO;IAC1B,IAAIA,iBAAiBC,MAAM,OAAOD,MAAME,OAAO;IAC/C,IAAI,OAAOF,UAAU,UAAU,OAAOA;IACtC,IAAI,OAAOA,UAAU,UAAU;QAC7B,MAAMG,SAASF,KAAKG,KAAK,CAACJ;QAC1B,OAAOK,OAAOC,QAAQ,CAACH,UAAUA,SAAS;IAC5C;IACA,OAAO,OAAOH,MAAMD,QAAQ,KAAK,aAAaC,MAAMD,QAAQ,KAAK;AACnE;AAEA,SAASQ,MAAMP,KAAc;IAC3B,MAAMQ,SAAST,SAASC;IACxB,OAAOQ,WAAW,OAAO,OAAO,IAAIP,KAAKO,QAAQC,WAAW;AAC9D;AAEA,OAAO,SAASC,gBAAgBC,MAAmB;IACjD,OAAO5E,yBAAyB6E,QAAQ,CAACD;AAC3C;AAEA,6EAA6E;AAE7E,2EAA2E;AAC3E,0EAA0E;AAC1E,SAAS7E,gBAAgB,GAAE;AAE3B;;;;CAIC,GACD,OAAO,MAAM+E,uBAA6C;IACxD;IACA;IACA;IACA;IACA;IACA;IACA;IACA;CACD,CAAA;AAED,MAAMC,cAAc,IAAIC;AACxB,MAAMC,eAAe,IAAID;AACzB,MAAME,kBAAkB,IAAIF;AAC5B,IAAIG,iBAAyC;AAC7C,wGAAwG,GACxG,IAAIC,oBAAoB;AACxB,MAAMC,eAAe,IAAIL;AA2BzB,iDAAiD,GACjD,OAAO,SAASM,kBACdC,IAAe,EACfC,MAAuB,EACvBC,eAAsC,CAAC,CAAC;IAExCV,YAAYW,GAAG,CAACH,MAAMC;IACtB,IAAIC,aAAaE,SAAS,IAAIF,aAAaE,SAAS,GAAG,GAAG;QACxDV,aAAaS,GAAG,CAACH,MAAME,aAAaE,SAAS;IAC/C,OAAO;QACLV,aAAaW,MAAM,CAACL;IACtB;IACA,IAAIE,aAAaI,YAAY,EAAE;QAC7BX,gBAAgBQ,GAAG,CAACH,MAAME,aAAaI,YAAY;IACrD,OAAO;QACLX,gBAAgBU,MAAM,CAACL;IACzB;AACF;AAEA;;;;CAIC,GACD,OAAO,SAASO,mBAAmBP,IAAe,EAAEQ,QAAgB;QACNd;IAA5D,OAAOc,aAAahG,mBAAmBqF,qBAAqBH,oBAAAA,aAAae,GAAG,CAACT,iBAAjBN,oBAA0B;AACxF;AAEA;;;;;CAKC,GACD,OAAO,SAASgB,sBAAsBC,GAAU;QAChCjB;IAAd,MAAMkB,SAAQlB,oBAAAA,aAAae,GAAG,CAACE,IAAIX,IAAI,aAAzBN,oBAA8B;IAC5C,MAAMmB,SAASlB,gBAAgBc,GAAG,CAACE,IAAIX,IAAI;IAC3C,OAAOa,SAASC,KAAKC,GAAG,CAACH,OAAOC,OAAOF,QAAQC;AACjD;AAEA,6EAA6E,GAC7E,OAAO,SAASI,uBAAuBL,GAAU;IAC/C,MAAMM,QAAQC,cAAcP;IAC5B,IAAIM,UAAU,CAAC,GAAG,OAAO;IACzB,OAAON,IAAIQ,KAAK,CAACF,MAAM,CAACG,IAAI,KAAK5G,mBAAmBqF,oBAAoBa,sBAAsBC;AAChG;AAEA,OAAO,SAASU,mBAAmBrB,IAAe;QACzCR;IAAP,QAAOA,mBAAAA,YAAYiB,GAAG,CAACT,iBAAhBR,mBAAyB;AAClC;AAEA;;;;;;;CAOC,GACD,OAAO,SAAS8B,wBAAwBtB,IAAe,EAAEuB,SAAiB;IACxEzB,aAAaK,GAAG,CAACH,MAAMc,KAAKC,GAAG,CAAC,GAAGD,KAAKU,KAAK,CAACD;AAChD;AAEA,6EAA6E,GAC7E,OAAO,SAASE,mBAAmBzB,IAAe;QACzCF;IAAP,QAAOA,oBAAAA,aAAaW,GAAG,CAACT,iBAAjBF,oBAA0B5B;AACnC;AAEA;;;;;;;;;CASC,GACD,OAAO,SAASwD,sBACdzB,MAA8B,EAC9BC,eAAsC,CAAC,CAAC;IAExCN,iBAAiBK;IACjBJ,oBACEI,UAAUC,aAAaE,SAAS,IAAIF,aAAaE,SAAS,GAAG,IAAIF,aAAaE,SAAS,GAAG;AAC9F;AASA,IAAIuB,cAAuC;AAE3C;;;;;;CAMC,GACD,OAAO,SAASC,yBAAyBC,MAA+B;IACtEF,cAAcE;AAChB;AA+BA,IAAIC,qBAAqD;AAEzD;;;;;;;;CAQC,GACD,OAAO,SAASC,gCAAgCC,QAAwC;IACtFF,qBAAqBE;AACvB;AAEA,0FAA0F,GAC1F,OAAO,SAASC;IACd,OAAOH,uBAAuB;AAChC;AAEA;;;;;;CAMC,GACD,eAAeI,eAAeC,SAAoB,EAAExB,GAAiB,EAAEyB,GAAS;IAC9E,IAAI,CAACzB,OAAQA,IAAItB,MAAM,KAAK,cAAcsB,IAAItB,MAAM,KAAK,UAAW;IACpE,MAAMvB,yBAAyBqE,WAAWxB,KAAKyB;AACjD;AAEA,iFAAiF,GACjF,eAAeC,wBACb1B,GAAU,EACV2B,EAAmB,EACnBC,OAA2B;IAE3B,IAAI,CAACT,oBAAoB;IACzB,IAAI;QACF,MAAMA,mBAAmBS,UAAU;YAAE5B;YAAK2B;YAAIC;QAAQ,IAAI;YAAE5B;YAAK2B;QAAG;IACtE,EAAE,OAAOE,OAAO;QACdC,QAAQD,KAAK,CAAC,qCAAqC;YAAEE,OAAO/B,IAAI+B,KAAK;YAAEC,OAAOhC,IAAIiC,GAAG;YAAEN;YAAIE;QAAM;IACnG;AACF;AAEA,mFAAmF,GACnF,OAAO,SAASK;IACd,OAAOlB,gBAAgB;AACzB;AAEA;;;CAGC,GACD,eAAemB,eAAeJ,KAAa,EAAEC,KAAa,EAAEI,KAAc;IACxE,IAAI,CAACpB,aAAa,OAAO;IACzB,IAAI;QACF,OAAO,MAAMA,YAAY;YAAEe;YAAOK;QAAM;IAC1C,EAAE,OAAOP,OAAO;QACdC,QAAQD,KAAK,CAAC,4BAA4B;YAAEE;YAAOC;YAAOH;QAAM;QAChE,OAAO;IACT;AACF;AAEA,wFAAwF,GACxF,OAAO,SAASQ,mBAAmBhD,IAAe,EAAEQ,QAAgB;IAClE,OAAOA,aAAahG,mBAAmBoF,iBAAiByB,mBAAmBrB;AAC7E;AAEA;;;;;CAKC,GACD,OAAO,SAASiD,eAAejD,IAAe;IAC5C,IAAIA,SAAS,QAAQ,OAAO;QAAC;KAAQ;IACrC,OAAOT,qBAAqBD,QAAQ,CAACU,SAASJ,kBAAkBJ,YAAY0D,GAAG,CAAClD,QAC5E;QAACxF;QAAkB;KAAW,GAC9B;QAAC;KAAW;AAClB;AAEA;;;;CAIC,GACDuF,kBAAkB,QAAQjE,kBAAkB;IAAEsE,WAAWvE;AAA4B;AAErF;;;;;;CAMC,GACDkE,kBACE,SACA,OAAOoD;IACL,MAAM,EAAEC,iBAAiB,EAAE,GAAG,MAAM,MAAM,CAAC;IAC3C,OAAOA,kBAAkBD;AAC3B,GACA;IAAE/C,WAAWpE;AAA6B;AAG5C;;;;;CAKC,GACD+D,kBACE,OACA,OAAOoD;IACL,MAAM,EAAEE,eAAe,EAAE,GAAG,MAAM,MAAM,CAAC;IACzC,OAAOA,gBAAgBF;AACzB,GACA;IAAE/C,WAAWrE;AAA2B;AAG1C,6EAA6E;AAE7E,SAASuH,eAAenB,SAAoB,EAAEO,KAAa;IACzD,OAAOP,UAAUoB,UAAU,CAAC,QAAQC,GAAG,CAACd,OAAOa,UAAU,CAACxF;AAC5D;AAEA,SAAS0F,QACPC,QAA4C;IAE5C,IAAI,CAACA,SAASC,MAAM,EAAE,OAAO;IAC7B,MAAMC,OAAOF,SAASE,IAAI;IAC1B,OAAO,aAAKA;QAAMhB,KAAKc,SAASG,EAAE;;AACpC;AAiBA;;;;;;CAMC,GACD,OAAO,eAAeC,YACpB3B,SAAoB,EACpB4B,KAAuB,EACvB3B,MAAM,IAAIzD,MAAM;QAqBNoF,eAIAA,eACDA,cAoBwBA,uBAKrBA;IAjDZ,MAAMC,QAAQD,MAAMC,KAAK,CAACC,IAAI;IAC9B,IAAI,CAACD,OAAO,MAAM,IAAIE,MAAM;IAC5B,2EAA2E;IAC3E,uEAAuE;IACvE,MAAMC,WAAWvI,kBAAkBmI,MAAM/D,IAAI;IAC7C,MAAMmB,QAAqB8B,eAAec,MAAM/D,IAAI,EAAEoE,GAAG,CAAC,CAAChD,OAAU;YACnEA;YACA/B,QAAQ;YACRgF,WAAW;YACXC,SAAS;YACTC,cAAc;YACd/B,OAAO;YACPgC,UAAU;WACNL,YAAY/C,SAAS5G,mBAAmB;YAAE2J;QAAS,IAAI,CAAC;IAE9D,qEAAqE;IACrE,MAAMM,MAAMnB,eAAenB,WAAW4B,MAAMrB,KAAK,EAAEc,GAAG,CAACtJ;IACvD,MAAM0J,OAAO;QACXlB,OAAOqB,MAAMrB,KAAK;QAClBgC,MAAM,GAAEX,gBAAAA,MAAMW,MAAM,YAAZX,gBAAgB;QACxB/D,MAAM+D,MAAM/D,IAAI;QAChBX,QAAQ;QACR2E;QACAW,MAAM,GAAEZ,gBAAAA,MAAMY,MAAM,YAAZZ,gBAAgB,CAAC;QACzBa,KAAK,GAAEb,eAAAA,MAAMa,KAAK,YAAXb,eAAe;QACtB5C;QACA0D,SAAS,EAAE;QACXC,iBAAiB3D,MAAM4D,MAAM,GAAG5G;QAChCoG,cAAc;QACdS,WAAWjB,MAAMiB,SAAS;QAC1BC,WAAW7C;QACX8C,WAAW9C;QACX,uEAAuE;QACvE,4DAA4D;QAC5D+C,WAAWlJ,qBAAqBmG;QAChCI,OAAO;QACP4C,OAAO;OACHrB,MAAMsB,gBAAgB,GAAG;QAAEA,kBAAkBtB,MAAMsB,gBAAgB;IAAC,IAAI,CAAC;IAE/E,MAAMZ,IAAItE,GAAG,CAACyD;IACd,4EAA4E;IAC5E,MAAM/F,sBAAsBsE,WAAW4B,MAAMW,MAAM;IACnD,MAAMtJ,gBACJ2I,MAAMrB,KAAK,EACX;QAAE4C,KAAKvB,MAAMiB,SAAS;QAAEO,KAAK,GAAExB,wBAAAA,MAAMyB,cAAc,YAApBzB,wBAAwB;IAAK,GAC5D;QACEpB,OAAO8B,IAAIZ,EAAE;QACb7D,MAAM+D,MAAM/D,IAAI;QAChByF,aAAazB,MAAMe,MAAM;QACzBL,MAAM,GAAEX,iBAAAA,MAAMW,MAAM,YAAZX,iBAAgB;IAC1B;IAEF,OAAO,aAAMH;QAAwChB,KAAK6B,IAAIZ,EAAE;;AAClE;AAEA,OAAO,eAAe6B,SACpBvD,SAAoB,EACpBO,KAAa,EACbC,KAAa;IAEb,OAAOc,QAAQ,MAAMH,eAAenB,WAAWO,OAAOc,GAAG,CAACb,OAAOlC,GAAG;AACtE;AAkBA;;;CAGC,GACD,OAAO,eAAekF,WACpBxD,SAAoB,EACpBO,KAAa,EACbkD,UAA6B,CAAC,CAAC;QAEeA;QAGrCA;IAHT,MAAMC,QAAQ/E,KAAKgF,GAAG,CAAChF,KAAKC,GAAG,CAAC,GAAGD,KAAKU,KAAK,EAACoE,iBAAAA,QAAQC,KAAK,YAAbD,iBAAiB,MAAM;IACrE,IAAIG,QAAiCzC,eAAenB,WAAWO;IAC/D,IAAIkD,QAAQvG,MAAM,EAAE0G,QAAQA,MAAMC,KAAK,CAAC,UAAU,MAAMJ,QAAQvG,MAAM;SACjE,KAAIuG,oBAAAA,QAAQK,QAAQ,qBAAhBL,kBAAkBb,MAAM,EAAEgB,QAAQA,MAAMC,KAAK,CAAC,UAAU,MAAM;WAAIJ,QAAQK,QAAQ;KAAC;SACvF,IAAIL,QAAQlB,MAAM,EAAEqB,QAAQA,MAAMC,KAAK,CAAC,UAAU,MAAMJ,QAAQlB,MAAM;IAC3E,MAAMwB,OAAO,OAAOC,MAA+BC,QACjD,AAAC,CAAA,MAAMD,KAAKE,OAAO,CAAC,aAAa,QAAQR,KAAK,CAACO,OAAO3F,GAAG,EAAC,EAAG6F,IAAI,CAC9DlC,GAAG,CAAC,CAACZ,MAAQC,QAAQD,MACrB+C,MAAM,CAAC,CAAC5F,MAAsBA,QAAQ;IAC3C,IAAI;QACF,OAAO,MAAMuF,KAAKH,OAAOF;IAC3B,EAAE,OAAOrD,OAAO;YAQ2BoD;QAPzC,sEAAsE;QACtE,yEAAyE;QACzE,yEAAyE;QACzE,sEAAsE;QACtE,sDAAsD;QACtD,yEAAyE;QACzE,8DAA8D;QAC9D,IAAI,CAACA,QAAQlB,MAAM,IAAIkB,QAAQvG,MAAM,MAAIuG,qBAAAA,QAAQK,QAAQ,qBAAhBL,mBAAkBb,MAAM,KAAI,CAACyB,qBAAqBhE,QAAQ,MAAMA;QACzGC,QAAQgE,IAAI,CAAC,uDAAuD;YAAE/D;YAAOgC,QAAQkB,QAAQlB,MAAM;QAAC;QACpG,MAAMgC,SAAS,MAAMR,KAAK5C,eAAenB,WAAWO,QAAQiE;QAC5D,OAAOD,OAAOH,MAAM,CAAC,CAAC5F,MAAQA,IAAI+D,MAAM,KAAKkB,QAAQlB,MAAM,EAAEkC,KAAK,CAAC,GAAGf;IACxE;AACF;AAEA,4FAA4F,GAC5F,OAAO,MAAMc,6BAA6B,IAAG;AAE7C,wEAAwE,GACxE,SAASH,qBAAqBhE,KAAc;IAC1C,MAAMqE,OAAQrE,yBAAD,AAACA,MAAqCqE,IAAI;IACvD,OAAOA,SAAS,KAAKA,SAAS,yBAAyBA,SAAS;AAClE;AAEA,+DAA+D,GAC/D,OAAO,SAASC,aAAanG,GAAU,EAAEyB,MAAM,IAAIzD,MAAM;QAK7CgC,aAKAA,YAQCA,cACQA,sBACHA,mBACGA,sBAEN1B,QACAA,SAEe0B,YAQZA,kBAGKA,uBASHA,uBACFA;QA9CYA,YAQZA,aAgCsBA,mBAAAA,YAYhCA,YAEwBA;IAtD9B,MAAMoG,aAAatI,UAASkC,aAAAA,IAAIyE,KAAK,qBAATzE,WAAWqG,KAAK;IAC5C,OAAO;QACLnD,IAAIlD,IAAIiC,GAAG;QACXF,OAAO/B,IAAI+B,KAAK;QAChBgC,MAAM,GAAE/D,cAAAA,IAAI+D,MAAM,YAAV/D,cAAc;QACtBX,MAAMW,IAAIX,IAAI;QACdX,QAAQsB,IAAItB,MAAM;QAClB2E,OAAOrD,IAAIqD,KAAK;QAChBiD,OAAO,SAAOtG,cAAAA,IAAIgE,MAAM,qBAAVhE,WAAY,CAAC,UAAU,MAAK,WAAWA,IAAIgE,MAAM,CAAC,UAAU,GAAG;QAC7ExD,OAAO,EAACR,aAAAA,IAAIQ,KAAK,YAATR,aAAa,EAAE,EAAEyD,GAAG,CAAC,CAAC8C;gBAKdA,oBACYA;mBANY;gBACtC9F,MAAM8F,KAAK9F,IAAI;gBACf/B,QAAQ6H,KAAK7H,MAAM;gBACnBgF,WAAWpF,MAAMiI,KAAK7C,SAAS;gBAC/BC,SAASrF,MAAMiI,KAAK5C,OAAO;gBAC3BC,YAAY,GAAE2C,qBAAAA,KAAK3C,YAAY,YAAjB2C,qBAAqB;gBACnC1E,OAAOrF,oBAAmB+J,cAAAA,KAAK1E,KAAK,YAAV0E,cAAc;YAC1C;;QACArC,OAAO,GAAElE,eAAAA,IAAIkE,OAAO,YAAXlE,eAAe,EAAE;QAC1BmE,eAAe,GAAEnE,uBAAAA,IAAImE,eAAe,YAAnBnE,uBAAuB;QACxC4D,YAAY,GAAE5D,oBAAAA,IAAI4D,YAAY,YAAhB5D,oBAAoB;QAClCwG,eAAe,GAAExG,uBAAAA,IAAIwG,eAAe,YAAnBxG,uBAAuB;QACxCqE,WAAWrE,IAAIqE,SAAS;QACxBC,SAAS,GAAEhG,SAAAA,MAAM0B,IAAIsE,SAAS,aAAnBhG,SAAmC,IAAIN,KAAK,GAAGQ,WAAW;QACrE+F,SAAS,GAAEjG,UAAAA,MAAM0B,IAAIuE,SAAS,aAAnBjG,UAAmC,IAAIN,KAAK,GAAGQ,WAAW;QACrE,8DAA8D;QAC9DqD,OAAOrF,oBAAmBwD,aAAAA,IAAI6B,KAAK,YAAT7B,aAAa,MAAM;YAAEyG,MAAMzG,IAAIX,IAAI,KAAK;QAAO;QACzEqH,SACE1G,IAAItB,MAAM,KAAK,aACf0H,eAAe,QACfA,aAAa3E,IAAIxD,OAAO;QAC1B0I,MAAM3G,IAAI2G,IAAI,GACV,aACK3G,IAAI2G,IAAI;YACXC,MAAM,GAAE5G,mBAAAA,IAAI2G,IAAI,CAACC,MAAM,YAAf5G,mBAAmB,CAAC;YAC5B6G,YAAYvI,MAAM0B,IAAI2G,IAAI,CAACE,UAAU;YACrCC,aAAaxI,MAAM0B,IAAI2G,IAAI,CAACG,WAAW;YACvCC,WAAW,GAAE/G,wBAAAA,IAAI2G,IAAI,CAACI,WAAW,YAApB/G,wBAAwB;aAEvC;QACJgH,QAAQhH,IAAIgH,MAAM,GAAGtK,qBAAqBsD,IAAIgH,MAAM,EAAE;YAAEP,MAAMzG,IAAIX,IAAI,KAAK;QAAO,KAAK;OACnFW,IAAItB,MAAM,KAAK,mBAAiBsB,aAAAA,IAAIoD,KAAK,sBAATpD,oBAAAA,WAAWiH,MAAM,qBAAjBjH,kBAAmBoE,MAAM,IAAG;QAAEhB,OAAOpD,IAAIoD,KAAK;IAAC,IAAI,CAAC;QACxF8D,SAASlH,IAAIkH,OAAO,GAChB;YACEC,IAAI7I,MAAM0B,IAAIkH,OAAO,CAACC,EAAE;YACxBC,IAAIpH,IAAIkH,OAAO,CAACE,EAAE;YAClBC,QAAQ,GAAErH,wBAAAA,IAAIkH,OAAO,CAACG,QAAQ,YAApBrH,wBAAwB,CAAC;YACnCsH,MAAM,GAAEtH,sBAAAA,IAAIkH,OAAO,CAACI,MAAM,YAAlBtH,sBAAsB,EAAE;QAClC,IACA;OACAA,IAAIuH,WAAW,GAAG;QAAEA,aAAavH,IAAIuH,WAAW;IAAC,IAAI,CAAC,GACtDvH,IAAIX,IAAI,KAAK,SAAS;QAAEmI,YAAY7K,oBAAoBqD,IAAIgE,MAAM;IAAE,IAAI,CAAC,GACzE1H,kBAAkB0D,OAAO;QAAEyH,aAAa;IAAK,IAAI,CAAC,GAClDzH,EAAAA,aAAAA,IAAI0H,KAAK,qBAAT1H,WAAWoE,MAAM,IAAG;QAAEsD,OAAO1H,IAAI0H,KAAK,CAACjE,GAAG,CAAChH;IAAoB,IAAI,CAAC,GACpEuD,IAAI2H,aAAa,GAAG;QAAEA,eAAe3H,IAAI2H,aAAa;IAAC,IAAI,CAAC,GAC5D3H,IAAIX,IAAI,KAAK,WAAWW,EAAAA,eAAAA,IAAIgE,MAAM,qBAAVhE,YAAY,CAAC,UAAU,MAAK,OAAO;QAAE4H,cAAc;IAAK,IAAI,CAAC,GACrF7K,mBAAmBiD,OAAO;QAAE6H,iBAAiB;IAAK,IAAI,CAAC,GACvD7H,IAAI8H,WAAW,GAAG;QAAEA,aAAa;IAAK,IAAI,CAAC,GAE3C9H,IAAI+H,UAAU,GAAG;QAAEA,YAAYnL,iBAAiBoD,IAAI+H,UAAU;IAAE,IAAI,CAAC;AAE7E;AASA,SAASC,YAAYhI,GAAU,EAAEiI,KAAa,EAAEC,KAAa;IAC3D,MAAMzD,QAAQzE,IAAIyE,KAAK;IACvB,IAAI,CAACA,OAAO,OAAO;IACnB,IAAIA,MAAMwD,KAAK,KAAKA,OAAO,OAAO;IAClC,MAAM5B,QAAQvI,SAAS2G,MAAM4B,KAAK;IAClC,OAAOA,UAAU,QAAQA,QAAQ6B;AACnC;AAEA,6EAA6E,GAC7E,SAASC,eAAenI,GAAU,EAAEkI,KAAa;QACxBlI;IAAvB,MAAMqG,QAAQvI,UAASkC,aAAAA,IAAIyE,KAAK,qBAATzE,WAAWqG,KAAK;IACvC,OAAO+B,QAAQpI,IAAIyE,KAAK,KAAK4B,UAAU,QAAQA,QAAQ6B;AACzD;AAEA;;;;;;CAMC,GACD,SAASG,mBAAmBrI,GAAU,EAAEyB,GAAS,EAAE6G,OAA2B;QAOlEtI;QAGJA;IATN,OAAO;QACLtB,QAAQ;QACRmJ,iBAAiBS;QACjBnE,iBAAiB;QACjBM,OAAO;QACP5C,OAAO;QACPrB,OAAO,EAACR,aAAAA,IAAIQ,KAAK,YAATR,aAAa,EAAE,EAAEyD,GAAG,CAAC,CAAC8C,OAC5BA,KAAK7H,MAAM,KAAK,YAAY,aAAK6H;gBAAM7H,QAAQ;gBAAWiF,SAAS;iBAAS4C;OAE1EvG,EAAAA,aAAAA,IAAI0H,KAAK,qBAAT1H,WAAWoE,MAAM,IAAG;QAAEsD,OAAOa,gBAAgBvI,IAAI0H,KAAK;IAAE,IAAI,CAAC;QACjEnD,WAAW9C;;AAEf;AAEA,SAAS8G,gBAAgBb,KAAiC;IACxD,OAAOA,MAAMjE,GAAG,CAAC,CAAC+E,MAChBA,IAAI9J,MAAM,KAAK,aAAa8J,IAAI9J,MAAM,KAAK,YACvC,aAAK8J;YAAK9J,QAAQ;YAAW+J,MAAM3L;aACnC0L;AAER;AAEA,sFAAsF,GACtF,SAASE,gBAAgB1I,GAAU,EAAEyB,GAAS;QACrCzB;IAAP,QAAOA,uBAAAA,IAAI6H,eAAe,YAAnB7H,uBAAuB2I,mBAAmBlH,KAAK;AACxD;AAEA,yFAAyF,GACzF,SAASkH,mBAAmBlH,GAAS,EAAE2F,EAAiB;IACtD,OAAO;QAAED,IAAI1F;QAA4C2F;IAAG;AAC9D;AAEA,SAAS7G,cAAcP,GAAU;QACvBA;IAAR,OAAO,EAACA,aAAAA,IAAIQ,KAAK,YAATR,aAAa,EAAE,EAAE4I,SAAS,CAChC,CAACrC,OAASA,KAAK7H,MAAM,KAAK,aAAa6H,KAAK7H,MAAM,KAAK;AAE3D;AAEA;;;;CAIC,GACD,SAASmK,mBAAmB7I,GAAU,EAAEiI,KAAa,EAAEC,KAAa;IAClE,IAAIlI,IAAItB,MAAM,KAAK,YAAYsB,IAAItB,MAAM,KAAK,WAAW,OAAO,CAAC;IACjE,IAAIsJ,YAAYhI,KAAKiI,OAAOC,QAAQ,OAAO,CAAC;IAC5C,OAAO3H,cAAcP;AACvB;AAEA;;;;;;;;;;CAUC,GACD,OAAO,eAAe8I,cACpBtH,SAAoB,EACpBO,KAAa,EACbC,KAAa,EACbiG,KAAa,EACbxG,MAAM,IAAIzD,MAAM;IAEhB,MAAM8F,MAAMnB,eAAenB,WAAWO,OAAOc,GAAG,CAACb;IACjD,IAAI+G,QAAsB;IAC1B,MAAMC,UAAU,MAAMxH,UAAUyH,cAAc,CAA0B,OAAOC;QAC7EH,QAAQ;QACR,MAAM/I,MAAM8C,QAAQ,MAAMoG,GAAGpJ,GAAG,CAACgE;QACjC,IAAI,CAAC9D,KAAK,OAAO;QACjB,uEAAuE;QACvE,qEAAqE;QACrE,IAAIjD,mBAAmBiD,MAAM;YAC3B,IAAI,CAACgI,YAAYhI,KAAKiI,OAAOxG,IAAIxD,OAAO,KAAK;gBAC3C,MAAMkL,QAAQd,mBAAmBrI,KAAKyB,KAAKiH,gBAAgB1I,KAAKyB;gBAChEyH,GAAG1J,GAAG,CAACsE,KAAKqF,OAAO;oBAAEC,OAAO;gBAAK;gBACjCL,QAAQ,aAAK/I,KAASmJ;YACxB;YACA,OAAO;QACT;QACA,MAAME,YAAYR,mBAAmB7I,KAAKiI,OAAOxG,IAAIxD,OAAO;QAC5D,IAAIoL,cAAc,CAAC,GAAG,OAAO;QAC7B,MAAM7I,QAAQR,IAAIQ,KAAK,CAACiD,GAAG,CAAC,CAAC8C,MAAMjG;gBAKhBiG,iBACAA;mBALjBjG,UAAU+I,YACN,aACK9C;gBACH7H,QAAQ;gBACRgF,SAAS,GAAE6C,kBAAAA,KAAK7C,SAAS,YAAd6C,kBAAkB9E;gBAC7BoC,UAAU,EAAC0C,iBAAAA,KAAK1C,QAAQ,YAAb0C,iBAAiB,KAAK;iBAEnCA;;QAEN,MAAM9B,QAAQ;YAAEwD;YAAO5B,OAAO,IAAIrI,KAAKyD,IAAIxD,OAAO,KAAKZ;QAAiB;QACxE6L,GAAG1J,GAAG,CACJsE,KACA;YAAEpF,QAAQ;YAAW8B;YAAOiE;YAAOF,WAAW9C;QAAI,GAClD;YAAE2H,OAAO;QAAK;QAEhB,OAAO;YACLpJ,KAAK,aACAA;gBACHtB,QAAQ;gBACR8B,OAAOA;gBACPiE,OAAOA;gBACPF,WAAW9C;;YAEb4H;QACF;IACF;IACA,MAAM9H,eAAeC,WAAWuH,OAAOtH;IACvC,OAAOuH;AACT;AAEA;;;;CAIC,GACD,OAAO,eAAeM,cACpB9H,SAAoB,EACpBO,KAAa,EACbC,KAAa,EACbiG,KAAa,EACbxG,MAAM,IAAIzD,MAAM;IAEhB,MAAM8F,MAAMnB,eAAenB,WAAWO,OAAOc,GAAG,CAACb;IACjD,OAAOR,UAAUyH,cAAc,CAAC,OAAOC;YAEGlJ;QADxC,MAAMA,MAAM8C,QAAQ,MAAMoG,GAAGpJ,GAAG,CAACgE;QACjC,IAAI,CAAC9D,OAAOA,IAAItB,MAAM,KAAK,aAAasB,EAAAA,aAAAA,IAAIyE,KAAK,qBAATzE,WAAWiI,KAAK,MAAKA,OAAO;YAClE,OAAO;QACT;QACAiB,GAAG1J,GAAG,CACJsE,KACA;YACEW,OAAO;gBAAEwD;gBAAO5B,OAAO,IAAIrI,KAAKyD,IAAIxD,OAAO,KAAKZ;YAAiB;YACjEkH,WAAW9C;QACb,GACA;YAAE2H,OAAO;QAAK;QAEhB,OAAO;IACT;AACF;AAEA;;;;;;;;;;CAUC,GACD,OAAO,eAAeG,gBACpB/H,SAAoB,EACpBO,KAAa,EACbC,KAAa,EACbiG,KAAa,EACbxG,MAAM,IAAIzD,MAAM;IAEhB,MAAM8F,MAAMnB,eAAenB,WAAWO,OAAOc,GAAG,CAACb;IACjD,IAAI+G,QAAsB;IAC1B,MAAMS,OAAO,MAAMhI,UAAUyH,cAAc,CAAe,OAAOC;QAC/DH,QAAQ;QACR,MAAM/I,MAAM8C,QAAQ,MAAMoG,GAAGpJ,GAAG,CAACgE;QACjC,IAAI,CAAC9D,KAAK,OAAO;QACjB,uEAAuE;QACvE,IAAIjD,mBAAmBiD,MAAM;YAC3B,IAAI,CAACgI,YAAYhI,KAAKiI,OAAOxG,IAAIxD,OAAO,KAAK;gBAC3C,MAAMkL,QAAQd,mBAAmBrI,KAAKyB,KAAKiH,gBAAgB1I,KAAKyB;gBAChEyH,GAAG1J,GAAG,CAACsE,KAAKqF,OAAO;oBAAEC,OAAO;gBAAK;gBACjCL,QAAQ,aAAK/I,KAASmJ;YACxB;YACA,OAAO;QACT;QACA,IAAIN,mBAAmB7I,KAAKiI,OAAOxG,IAAIxD,OAAO,QAAQ,CAAC,GAAG,OAAO;QACjEiL,GAAG1J,GAAG,CAACsE,KAAK;YAAES,WAAW9C;QAAI,GAAG;YAAE2H,OAAO;QAAK;QAC9C,OAAO,aAAKpJ;YAAKuE,WAAW9C;;IAC9B;IACA,MAAMF,eAAeC,WAAWuH,OAAOtH;IACvC,OAAO+H;AACT;AAyDA;;;;CAIC,GACD,OAAO,SAASC,qBACdC,MAAkC,EAClCC,MAAuB,EACvBC,OAAe,EACfnI,GAAU;IAEV,OAAOiI,OAAOjG,GAAG,CAAC,CAAC+E;YAMIA,mBACEA,qBACCA,sBAA6BmB,yBAOjCnB,cAAwBmB;YAHlCA;eAXVnB,IAAIqB,IAAI,KAAKF,OAAOE,IAAI,GACpBrB,MACA,aACKA;YACH9J,QAAQiL,OAAOjL,MAAM;YACrBkF,cAAc,EAAC4E,oBAAAA,IAAI5E,YAAY,YAAhB4E,oBAAoB,KAAKoB;YACxCE,gBAAgB,EAACtB,sBAAAA,IAAIsB,cAAc,YAAlBtB,sBAAsB,KAAKoB;YAC5CG,iBAAiB,EAACvB,uBAAAA,IAAIuB,eAAe,YAAnBvB,uBAAuB,OAAMmB,0BAAAA,OAAOI,eAAe,YAAtBJ,0BAA0B;WACrEA,OAAOK,SAAS,GAAG;YAAEA,WAAWL,OAAOK,SAAS;QAAC,IAAI,CAAC,GACtDL,OAAO/H,OAAO,KAAKqI,YAAY;YAAErI,SAAS+H,OAAO/H,OAAO;QAAC,IAAI,CAAC,GAC9D+H,OAAOlB,IAAI,KAAKwB,YAAY;YAAExB,MAAMkB,OAAOlB,IAAI;QAAC,IAAI,CAAC,GACrDkB,EAAAA,qBAAAA,OAAOO,UAAU,qBAAjBP,mBAAmBvF,MAAM,IAAG;YAAE8F,YAAYP,OAAOO,UAAU;QAAC,IAAI,CAAC,GAEjEzI,OAAOkI,OAAOjL,MAAM,KAAK,aAAaiL,OAAOjL,MAAM,KAAK,YAAY;YAAEyL,WAAW1I,IAAIjD,WAAW;QAAG,IAAI,CAAC;YAC5G0F,SAAS;oBAAKsE,eAAAA,IAAItE,OAAO,YAAXsE,eAAe,EAAE;mBAAM,EAACmB,kBAAAA,OAAOzF,OAAO,YAAdyF,kBAAkB,EAAE,EAAE/D,MAAM,CAAC,CAAC1C;wBAASsF;2BAAF,CAAC,EAACA,eAAAA,IAAItE,OAAO,YAAXsE,eAAe,EAAE,EAAE7J,QAAQ,CAACuE;;aAAK;;;AAGxH;AAYA;;;;;;;;;;;CAWC,GACD,OAAO,MAAMkH,wBAAwB,GAAE;AAEvC,8FAA8F,GAC9F,OAAO,MAAMC,oCAAoC,GAAE;AAEnD;;;;CAIC,GACD,OAAO,SAASC,mBACdC,OAA2C,EAC3CC,GAAsB;QAiBZA;IAfV,MAAM/E,QAAQ,CAAC1H;QACb,MAAMG,SAASE,OAAOL,gBAAAA,QAAS;QAC/B,OAAOK,OAAOC,QAAQ,CAACH,WAAWA,SAAS,IAAIiC,KAAKU,KAAK,CAAC3C,UAAU;IACtE;IACA,MAAMuM,OAAOC,MAAMC,OAAO,CAACJ,2BAAAA,QAASK,QAAQ,IAAIL,QAAQK,QAAQ,GAAG,EAAE;IACrE,MAAMC,aACJ,OAAOL,IAAIK,UAAU,KAAK,YAAYL,IAAIK,UAAU,GAChDL,IAAIK,UAAU,CAAC5E,KAAK,CAAC,GAAGoE,qCACxB;IACN,OAAO;QACLjH,OAAOqC,MAAM8E,2BAAAA,QAASnH,KAAK,IAAIqC,MAAM+E,IAAIM,KAAK,CAACC,WAAW;QAC1DC,YAAYvF,MAAM8E,2BAAAA,QAASS,UAAU,IAAIvF,MAAM+E,IAAIM,KAAK,CAACG,eAAe;QACxEC,YAAYzF,MAAM8E,2BAAAA,QAASW,UAAU,IAAIzF,MAAM+E,IAAIM,KAAK,CAACK,gBAAgB;QACzEC,QAAQ3F,MAAM8E,2BAAAA,QAASa,MAAM,IAAI3F,MAAM+E,IAAIM,KAAK,CAACO,YAAY;QAC7DpH,OAAOuG,IAAIvG,KAAK,KAAIsG,2BAAAA,QAAStG,KAAK,KAAI;QACtCqH,MAAM,GAAEd,cAAAA,IAAIc,MAAM,YAAVd,cAAc;QACtBe,WAAW9F,MAAM8E,2BAAAA,QAASgB,SAAS,IAAI9F,MAAM+E,IAAIe,SAAS;QAC1DC,MAAM/F,MAAM8E,2BAAAA,QAASiB,IAAI,IAAI;QAC7BZ,UAAU;eAAIH;YAAM;gBAAEI;gBAAYO,QAAQ3F,MAAM+E,IAAIM,KAAK,CAACO,YAAY;YAAE;SAAE,CAACpF,KAAK,CAAC,CAACmE;IACpF;AACF;AAUA;;;;;;;;;;;CAWC,GACD,OAAO,eAAeqB,WACpBjK,SAAoB,EACpBO,KAAa,EACbC,KAAa,EACbiG,KAAa,EACboB,SAAiB,EACjBjG,KAAsB,EACtB3B,MAAM,IAAIzD,MAAM;IAEhB,MAAM8F,MAAMnB,eAAenB,WAAWO,OAAOc,GAAG,CAACb;IACjD,MAAM0J,WAAW,MAAMlK,UAAUyH,cAAc,CAAC,OAAOC;YAsBtC9F,MAAAA,cA2BCpD,cAAwBoD,gBACvBpD,mBAMIA,sBACZA,uBAWiBA,sBAgBXoD,cASqDsE;YAnElD1H,YAyCZoD,aA0BOsE;QA5Fb,MAAM1H,MAAM8C,QAAQ,MAAMoG,GAAGpJ,GAAG,CAACgE;QACjC,IAAI,CAAC9D,KAAK,MAAM,IAAIuD,MAAM,CAAC,OAAO,EAAExB,MAAM,CAAC,EAAEC,MAAM,SAAS,CAAC;QAC7D,MAAMxB,QAAQR,IAAIQ,KAAK,CAACiD,GAAG,CAAC,CAAC8C,MAAMjG;gBAMZiG,oBACRnD,cAEqBmD;mBARlCjG,UAAU+I,YACN,aACK9C;gBACH7H,QAAQ0E,MAAM1E,MAAM;gBACpBiF,SAASP,MAAM1E,MAAM,KAAK,YAAY,OAAO+C;gBAC7CmC,cAAc,EAAC2C,qBAAAA,KAAK3C,YAAY,YAAjB2C,qBAAqB,KAAKnD,MAAMQ,YAAY;gBAC3D/B,KAAK,GAAEuB,eAAAA,MAAMvB,KAAK,YAAXuB,eAAe;eAClBA,MAAMuI,SAAS,IAAIvI,MAAM1E,MAAM,KAAK,YACpC;gBAAEmF,UAAU;gBAAG+H,QAAQ,EAACrF,eAAAA,KAAKqF,MAAM,YAAXrF,eAAe,KAAK;YAAE,IAC9C,CAAC,GACDnD,MAAMyI,MAAM,GAAG;gBAAEA,QAAQvB,mBAAmB/D,KAAKsF,MAAM,EAAEzI,MAAMyI,MAAM;YAAE,IAAI,CAAC,KAElFtF;;QAEN,MAAMuF,YAAYtL,MAAMoF,MAAM,CAC5B,CAACW,OAASA,KAAK7H,MAAM,KAAK,aAAa6H,KAAK7H,MAAM,KAAK,WACvD0F,MAAM;QACR,4EAA4E;QAC5E,MAAMsF,UAAStG,QAAAA,eAAAA,MAAMsE,KAAK,YAAXtE,eAAepD,IAAI0H,KAAK,YAAxBtE,OAA4B;QAC3C,MAAMsE,QACJgC,UAAUtG,MAAM2I,IAAI,GAAGtC,qBAAqBC,QAAQtG,MAAM2I,IAAI,EAAE3I,MAAMQ,YAAY,EAAEnC,OAAO2B,MAAMsE,KAAK,GAAGgC,SAAS;QACpH,MAAMsC,UAAU5I,MAAM1E,MAAM,KAAK;QACjC,MAAMuN,YAAYjM,EAAAA,aAAAA,IAAIyE,KAAK,qBAATzE,WAAWiI,KAAK,MAAKA;QACvC,0EAA0E;QAC1E,0EAA0E;QAC1E,MAAMiE,YAAYnP,mBAAmBiD;QACrC,MAAMmM,iBAAiB/D,QAAQhF,MAAM4D,MAAM,KAAK,CAACvI,gBAAgBuB,IAAItB,MAAM,KAAK,CAACwN;QACjF,yEAAyE;QACzE,MAAME,WAAwBhJ,MAAMA,KAAK,GAAG,gBAAgB;QAC5D,MAAM1E,SAAsBD,gBAAgBuB,IAAItB,MAAM,IAClDsB,IAAItB,MAAM,GACVwN,YACE,aACAlM,IAAItB,MAAM,KAAK,gBACbsB,IAAItB,MAAM,GACVyN,iBACEC,WACAhJ,MAAM1E,MAAM,KAAK,WACf,YACAoN,YAAY,IACV,WACA;QACd,MAAM3C,QAAQ;YACZzK;YACA8B;YACA0D,SAAS;oBAAKlE,eAAAA,IAAIkE,OAAO,YAAXlE,eAAe,EAAE;oBAAOoD,iBAAAA,MAAMc,OAAO,YAAbd,iBAAiB,EAAE;aAAE;YAC3DQ,cAAc,EAAC5D,oBAAAA,IAAI4D,YAAY,YAAhB5D,oBAAoB,KAAKoD,MAAMQ,YAAY;YAC1D,uEAAuE;YACvE,wEAAwE;YACxEO,iBAAiBgI,iBACb,IACAH,UACE7L,KAAKC,GAAG,CAAC,GAAG,EAACJ,uBAAAA,IAAImE,eAAe,YAAnBnE,uBAAuB,KAAKxC,gCACxCwC,wBAAAA,IAAImE,eAAe,YAAnBnE,wBAAuB;YAC9BuE,WAAW9C;WACPwK,YAAY;YAAExH,OAAO;QAAK,IAAI,CAAC,GAG/BrB,MAAMuD,IAAI,GAAG;YAAEA,MAAMvD,MAAMuD,IAAI;QAAC,IAAI,CAAC,GACrCvD,MAAMmE,WAAW,GAAG;YAAEA,aAAanE,MAAMmE,WAAW;QAAC,IAAI,CAAC,GAC1DnE,MAAM2E,UAAU,GAAG;YAAEA,YAAY3E,MAAM2E,UAAU;QAAC,IAAI,CAAC,GACvD3E,MAAMiJ,UAAU,GAAG;YAAEA,YAAYjJ,MAAMiJ,UAAU;QAAC,IAAI,CAAC,GACvD3E,QAAQ;YAAEA;QAAM,IAAI,CAAC,GACrBtE,EAAAA,cAAAA,MAAM2I,IAAI,qBAAV3I,YAAY2G,eAAe,IAC3B;YAAEvD,iBAAiB,EAACxG,uBAAAA,IAAIwG,eAAe,YAAnBxG,uBAAuB,KAAKoD,MAAM2I,IAAI,CAAChC,eAAe;QAAC,IAC3E,CAAC,GAKDoC,kBAAkB/I,MAAM4D,MAAM,GAC9B;YACEA,QAAQ5D,MAAM4D,MAAM;YACpB,kEAAkE;YAClE,qCAAqC;YACrCnF,OAAOuB,MAAMA,KAAK,GACdA,MAAMA,KAAK,CAACkJ,OAAO,GACnBlJ,MAAM4D,MAAM,CAACuF,MAAM,KAAK,UAAUnJ,MAAM4D,MAAM,CAACuF,MAAM,KAAK,SACxD,OACAnJ,MAAM4D,MAAM,CAACsF,OAAO;YAC1BlJ,KAAK,GAAEA,eAAAA,MAAMA,KAAK,YAAXA,eAAe;QACxB,IACA,CAAC,GACD8I,YACA;YACE/H,iBAAiB;YACjBM,OAAO;YACP5C,OAAO;YACPrB,OAAOA,MAAMiD,GAAG,CAAC,CAAC8C,OAAUA,KAAK7H,MAAM,KAAK,YAAY,aAAK6H;oBAAM7H,QAAQ;qBAAuB6H;WAC9F,EAACmB,QAAAA,gBAAAA,QAAS1H,IAAI0H,KAAK,qBAAnB,AAACA,MAAqBtD,MAAM,IAAG;YAAEsD,OAAOa,iBAAgBb,QAAAA,gBAAAA,QAAS1H,IAAI0H,KAAK,YAAlBA,QAAsB,EAAE;QAAE,IAAI,CAAC,KAE7F,CAAC;QAEPwB,GAAG1J,GAAG,CAACsE,KAAKqF,OAAO;YAAEC,OAAO;QAAK;QACjC,OAAO;YACLpJ,KAAK,aAAKA,KAASmJ;YACnB2C;YACA,mGAAmG;YACnGU,QAAQL,kBAAkBnM,IAAItB,MAAM,KAAK0N;YACzC,6CAA6C;YAC7CrD,OAAOmD,aAAa,CAACzN,gBAAgBuB,IAAItB,MAAM;QACjD;IACF;IACA,IAAIgN,SAAS3C,KAAK,EAAE,MAAMxH,eAAeC,WAAWkK,SAAS1L,GAAG,EAAEyB;IAClE,IAAIiK,SAASc,MAAM,IAAI,CAACpJ,MAAMqJ,KAAK,EAAE,MAAM/K,wBAAwBgK,SAAS1L,GAAG,EAAE;IACjF,OAAO;QAAEA,KAAK0L,SAAS1L,GAAG;QAAE8L,WAAWJ,SAASI,SAAS;QAAEU,QAAQd,SAASc,MAAM;IAAC;AACrF;AAEA,eAAeE,WACblL,SAAoB,EACpBO,KAAa,EACbC,KAAa,EACb2K,KAAqD;IAErD,MAAM7I,MAAMnB,eAAenB,WAAWO,OAAOc,GAAG,CAACb;IACjD,OAAOR,UAAUyH,cAAc,CAAC,OAAOC;QACrC,MAAMlJ,MAAM8C,QAAQ,MAAMoG,GAAGpJ,GAAG,CAACgE;QACjC,IAAI,CAAC9D,KAAK,MAAM,IAAIuD,MAAM,CAAC,OAAO,EAAExB,MAAM,CAAC,EAAEC,MAAM,SAAS,CAAC;QAC7D,MAAMmH,QAAQwD,MAAM3M;QACpB,IAAI,CAACmJ,OAAO,OAAO;YAAEnJ;YAAK4M,SAAS;QAAM;QACzC1D,GAAG1J,GAAG,CAACsE,KAAKqF,OAAO;YAAEC,OAAO;QAAK;QACjC,OAAO;YACLpJ,KAAK,aAAKA,KAASmJ;YACnByD,SAAS;QACX;IACF;AACF;AAEA,0DAA0D,GAC1D,OAAO,eAAeC,cACpBrL,SAAoB,EACpBO,KAAa,EACbC,KAAa,EACbP,MAAM,IAAIzD,MAAM;IAEhB,MAAM,EAAEgC,GAAG,EAAE4M,OAAO,EAAE,GAAG,MAAMF,WAAWlL,WAAWO,OAAOC,OAAO,CAACuI,UAClE9L,gBAAgB8L,QAAQ7L,MAAM,IAC1B,OAEA3B,mBAAmBwN,WACjBlC,mBAAmBkC,SAAS9I,KAAKiH,gBAAgB6B,SAAS9I,QAC1D;YACE/C,QAAQ;YACRyF,iBAAiB;YACjBM,OAAO;YACP5C,OAAO;YACP0C,WAAW9C;QACb;IAER,IAAImL,WAAW5M,IAAItB,MAAM,KAAK,QAAQ,MAAMgD,wBAAwB1B,KAAK;IACzE,IAAI4M,SAAS,MAAMrL,eAAeC,WAAWxB,KAAKyB;IAClD,OAAOzB;AACT;AAEA;;;;CAIC,GACD,OAAO,eAAe8M,UACpBtL,SAAoB,EACpBO,KAAa,EACbC,KAAa,EACbsK,OAAe,EACfS,MAAgE,EAChEtL,MAAM,IAAIzD,MAAM;;IAEhB8D,QAAQD,KAAK,CAAC,iBAAiB;QAC7BE;QACAC;QACAqH,SAAS,UAAE0D,0BAAAA,OAAQ1D,SAAS,mBAAI;QAChCxH,OAAOkL,CAAAA,0BAAAA,OAAQlL,KAAK,aAAY0B,QAAQwJ,OAAOlL,KAAK,CAACyK,OAAO,YAAGS,0BAAAA,OAAQlL,KAAK,oBAAI;IAClF;IACA,MAAM,EAAE7B,GAAG,EAAE4M,OAAO,EAAE,GAAG,MAAMF,WAAWlL,WAAWO,OAAOC,OAAO,CAACuI,UAClE9L,gBAAgB8L,QAAQ7L,MAAM,IAC1B,OAEA3B,mBAAmBwN,WACjBlC,mBAAmBkC,SAAS9I,KAAKiH,gBAAgB6B,SAAS9I,QAC1D;YACE/C,QAAQ;YACRmD,OAAOyK;YACPnI,iBAAiB;YACjBM,OAAO;YACPjE,OAAO+J,QAAQ/J,KAAK,CAACiD,GAAG,CAAC,CAAC8C,OACxBA,KAAK7H,MAAM,KAAK,YACZ,aAAK6H;oBAAM7H,QAAQ;oBAAUiF,SAASlC;oBAAKI,OAAOyK;qBAClD/F;YAENhC,WAAW9C;QACb;IAER,IAAImL,WAAW5M,IAAItB,MAAM,KAAK,UAAU;;QACtC,MAAMgD,wBAAwB1B,KAAK,UAAU;YAC3CgN,IAAI,WAAED,0BAAAA,OAAQC,IAAI,oBAAI;YACtB3D,SAAS,WAAE0D,0BAAAA,OAAQ1D,SAAS,oBAAI;YAChCxH,OAAOkL,CAAAA,0BAAAA,OAAQlL,KAAK,aAAY0B,QAAQwJ,OAAOlL,KAAK,CAACyK,OAAO,GAAGS,CAAAA,0BAAAA,OAAQlL,KAAK,KAAI,OAAO,OAAOoL,OAAOF,OAAOlL,KAAK;QACnH;IACF;IACA,IAAI+K,SAAS,MAAMrL,eAAeC,WAAWxB,KAAKyB;IAClD,OAAOzB;AACT;AAEA;;;;CAIC,GACD,OAAO,eAAekN,oBACpB1L,SAAoB,EACpBO,KAAa,EACbC,KAAa,EACbuK,MAAc,EACd9K,MAAM,IAAIzD,MAAM;IAEhB,MAAM,EAAEgC,GAAG,EAAE4M,OAAO,EAAE,GAAG,MAAMF,WAAWlL,WAAWO,OAAOC,OAAO,CAACuI,UAClE9L,gBAAgB8L,QAAQ7L,MAAM,IAC1B,OACA3B,mBAAmBwN,WACjBlC,mBAAmBkC,SAAS9I,KAAKiH,gBAAgB6B,SAAS9I,QAC1D;YACE/C,QAAQ;YACRmD,OAAO0K;YACP9H,OAAO;YACPjE,OAAO+J,QAAQ/J,KAAK,CAACiD,GAAG,CAAC,CAAC8C;oBAKKA;uBAJ7BA,KAAK7H,MAAM,KAAK,YACZ,aACK6H;oBACH7H,QAAQ;oBACRmF,UAAU1D,KAAKC,GAAG,CAAC,GAAG,EAACmG,iBAAAA,KAAK1C,QAAQ,YAAb0C,iBAAiB,KAAK;qBAE/CA;;YAENhC,WAAW9C;QACb;IAER,IAAImL,SAAS,MAAMrL,eAAeC,WAAWxB,KAAKyB;IAClD,OAAOzB;AACT;AASA;;;;;;;;;;;;;;;;;;;;;;CAsBC,GACD,OAAO,eAAemN,YACpB3L,SAAoB,EACpBO,KAAa,EACbC,KAAa,EACbP,MAAM,IAAIzD,MAAM,EAChBoP,QAAgC,IAAI;;IAEpC,MAAM9E,UAAUK,mBAAmBlH,aAAK2L,yBAAAA,MAAOzI,GAAG,mBAAI;IACtD,IAAI0I,WAAW;IACf,MAAMC,SAAS,MAAMZ,WAAWlL,WAAWO,OAAOC,OAAO,CAACuI;QACxD,IAAI9L,gBAAgB8L,QAAQ7L,MAAM,GAAG,OAAO;QAC5C,MAAM6O,WAAWpF,eAAeoC,SAAS9I,IAAIxD,OAAO;QACpD,IAAIsM,QAAQ1C,eAAe,EAAE;YAC3B,kEAAkE;YAClE,kDAAkD;YAClD,OAAO0F,WAAW,OAAOlF,mBAAmBkC,SAAS9I,KAAK8I,QAAQ1C,eAAe;QACnF;QACAwF,WAAW;QACX,OAAOE,WACH;YAAE1F,iBAAiBS;YAAS/D,WAAW9C;QAAI,IAC3C4G,mBAAmBkC,SAAS9I,KAAK6G;IACvC;IACA,IAAIgF,OAAOV,OAAO,IAAIS,UAAU;QAC9B,MAAM7S,iBAAiBuH,OAAOqL,gBAAAA,QAAS;YAAEzI,KAAK;QAAK,GAAG;YACpD3C;YACA3C,MAAMiO,OAAOtN,GAAG,CAACX,IAAI;QACvB;IACF;IACA,IAAIiO,OAAOV,OAAO,EAAE,MAAMrL,eAAeC,WAAW8L,OAAOtN,GAAG,EAAEyB;IAChE,OAAO6L;AACT;AAEA;;;;;;;;CAQC,GACD,eAAeE,iBACbhM,SAAoB,EACpBO,KAAa,EACb/B,GAAU,EACVoD,KAOC;IAED,IAAI;YACiBpD,cAGLA,mBAEKA,sBAOV,OAEOA;QAdhB,MAAMyN,YAAY,EAACzN,eAAAA,IAAIkE,OAAO,YAAXlE,eAAe,EAAE,EAAE0N,IAAI,CAAC,CAACtC,SAAWA,OAAOuC,QAAQ,KAAK;QAC3E,MAAM/D,UAAU/N,mBAAmB;YACjC4R;YACAG,UAAU,GAAE5N,oBAAAA,IAAI4D,YAAY,YAAhB5D,oBAAoB;YAChC6N,aAAazK,MAAMyK,WAAW;YAC9BC,eAAe,GAAE9N,uBAAAA,IAAIwG,eAAe,YAAnBxG,uBAAuB;QAC1C;QACA,IAAI4J,WAAW,GAAG,OAAO5J;QACzB,MAAM+N,OACJ3K,MAAM2K,IAAI,KAAK9D,YACX7G,MAAM2K,IAAI,GACV1R,mBACG,QAAA,AAAC,CAAA,MAAMmF,UAAUoB,UAAU,CAAC,QAAQC,GAAG,CAACd,OAAOjC,GAAG,EAAC,EAAGmD,IAAI,cAA1D,QAAgE,CAAC;QAE1E,MAAM+K,WAAUhO,eAAAA,IAAIiO,OAAO,YAAXjO,eAAe;QAC/B,MAAMkO,SAAS,MAAMpS,iBAAiB0F,WAAW;YAC/CO;YACAgM;YACA/L,OAAOhC,IAAIiC,GAAG;YACd+L;YACApE;YACAuE,OAAO3S,iBAAiB4H,MAAM3B,GAAG;YACjC2M,KAAK7S,eAAe6H,MAAM3B,GAAG;YAC7B8K,QAAQnJ,MAAMmJ,MAAM;QACtB;QACA,IAAI2B,OAAOxP,MAAM,KAAK,YAAY,OAAOsB;QACzC,MAAM,EAAEA,KAAKqO,QAAQ,EAAE,GAAG,MAAM3B,WAAWlL,WAAWO,OAAO/B,IAAIiC,GAAG,EAAE,CAACsI;gBACnDA,0BAERA;mBAHwE;gBAClF/D,iBAAiB,EAAC+D,2BAAAA,QAAQ/D,eAAe,YAAvB+D,2BAA2B,KAAKX;gBAClD0E,cAAclL,MAAMmJ,MAAM;gBAC1B0B,SAAS,EAAC1D,mBAAAA,QAAQ0D,OAAO,YAAf1D,mBAAmB,KAAK;gBAClChG,WAAWnB,MAAM3B,GAAG;YACtB;;QACA,OAAO4M;IACT,EAAE,OAAOxM,OAAO;QACdC,QAAQD,KAAK,CAAC,wBAAwB;YAAEE;YAAOC,OAAOhC,IAAIiC,GAAG;YAAEJ;QAAM;QACrE,OAAO7B;IACT;AACF;AAEA;;;;;;CAMC,GACD,eAAeuO,eACb/M,SAAoB,EACpBO,KAAa,EACbC,KAAa,EACbsK,OAAe,EACfS,MAA+D,EAC/DmB,MAA2F,EAC3FzM,GAAS;QAuB6DsL;QAbnBxC;IARnD,MAAMA,UAAU,MAAMxF,SAASvD,WAAWO,OAAOC;IACjD,0EAA0E;IAC1E,gEAAgE;IAChE,IAAIuI,WAAWxN,mBAAmBwN,UAAU;QAC1C,OAAOuC,UAAUtL,WAAWO,OAAOC,OAAOsK,SAAS,aAAKS;YAAQC,MAAM;YAASvL;IACjF;IACA,wEAAwE;IACxE,wEAAwE;IACxE,IAAI8I,WAAWiE,mBAAmBjE,QAAQlL,IAAI,OAAKkL,iBAAAA,QAAQ7C,KAAK,qBAAb6C,eAAenG,MAAM,KAAI,CAAC3F,gBAAgB8L,QAAQ7L,MAAM,GAAG;YAC/CqO;QAA7DjL,QAAQD,KAAK,CAAC,oBAAoB;YAAEE;YAAOC;YAAOqH,SAAS,GAAE0D,oBAAAA,OAAO1D,SAAS,YAAhB0D,oBAAoB;YAAMlL,OAAOkL,OAAOlL,KAAK;QAAC;QAC3G,OAAO4M,iBAAiBjN,WAAWO,OAAOC,OAAO;YAC/CP;YACAsM,MAAMG,OAAOH,IAAI;YACjBW,SAAS;gBAAEpC;gBAASC,QAAQ2B,OAAO3B,MAAM;YAAC;QAC5C;IACF;IACA,IAAIhC,WAAW,CAAC9L,gBAAgB8L,QAAQ7L,MAAM,GAAG;QAC/C,MAAM8O,iBAAiBhM,WAAWO,OAAOwI,SAAS,aAAK2D;YAAQzM;;IACjE;IACA,4EAA4E;IAC5E,6EAA6E;IAC7E,OAAOqL,UAAUtL,WAAWO,OAAOC,OAAOsK,SAAS,aAAKS;QAAQC,IAAI,GAAED,eAAAA,OAAOC,IAAI,YAAXD,eAAe;QAAQtL;AAC/F;AAEA,4EAA4E;AAE5E;;;;CAIC,GACD,OAAO,SAAS+M,mBAAmBnP,IAAe;IAChD,wEAAwE;IACxE,2DAA2D;IAC3D,OAAOA,SAAS,WAAWA,SAAS,UAAUA,SAAS;AACzD;AAEA;;;;CAIC,GACD,OAAO,SAASsP,iBAAiB3O,GAAmC;IAClE,IAAIA,IAAIX,IAAI,KAAK,SAAS,OAAOW,IAAItB,MAAM,KAAK,UAAUsB,IAAItB,MAAM,KAAK;IACzE,OAAOsB,IAAIX,IAAI,KAAK,UAAUW,IAAItB,MAAM,KAAK;AAC/C;AAEA,+EAA+E,GAC/E,OAAO,MAAMkQ,8BACX,8EAA6E;AAE/E,mGAAmG,GACnG,OAAO,MAAMC,oCACX,4GAA2G;AAE7G,4EAA4E,GAC5E,OAAO,MAAMC,4BAA4B,uDAAsD;AAE/F;;;;;CAKC,GACD,eAAeC,mBACbvN,SAAoB,EACpBO,KAAa,EACb/B,GAAU,EACVoD,KAOC;IAED,MAAMwG,UAAUzJ,KAAKC,GAAG,CAAC,GAAGD,KAAKU,KAAK,CAACuC,MAAMwG,OAAO;IACpD,IAAIA,WAAW,GAAG,OAAO;IACzB,IAAI;YAKO;QAJT,MAAMmE,OACJ3K,MAAM2K,IAAI,KAAK9D,YACX7G,MAAM2K,IAAI,GACV1R,mBACG,QAAA,AAAC,CAAA,MAAMmF,UAAUoB,UAAU,CAAC,QAAQC,GAAG,CAACd,OAAOjC,GAAG,EAAC,EAAGmD,IAAI,cAA1D,QAAgE,CAAC;QAE1E,MAAM+L,UAAUlR,SAASkC,IAAIsE,SAAS;QACtC,MAAM2K,SAAS1T,eAAeyT,YAAY,OAAO5L,MAAM3B,GAAG,GAAG,IAAIzD,KAAKgR;QACtE,MAAMd,SAAS,MAAMpS,iBAAiB0F,WAAW;YAC/CO;YACAgM;YACA/L,OAAOhC,IAAIiC,GAAG;YACd+L,SAAS;YACTpE;YACAuE,OAAO3S,iBAAiB4H,MAAM3B,GAAG;YACjC2M,KAAK7S,eAAe6H,MAAM3B,GAAG;YAC7B8K,QAAQnJ,MAAMmJ,MAAM;YACpBR,MAAM;gBAAElC,MAAMzG,MAAMyG,IAAI;gBAAEqF,SAAS9L,MAAM8L,OAAO;gBAAED;YAAO;QAC3D;QACA,+EAA+E;QAC/E,IAAIf,OAAOxP,MAAM,KAAK,cAAcwP,OAAOxP,MAAM,KAAK,aAAa,OAAO;QAC1E,OAAO;YACLkL;YACAuF,KAAKvT,mBAAmB;gBAAEwS,KAAKa;gBAAQjN,OAAOhC,IAAIiC,GAAG;gBAAE4H,MAAMzG,MAAMyG,IAAI;gBAAEqF,SAAS9L,MAAM8L,OAAO;YAAC;QAClG;IACF,EAAE,OAAOrN,OAAO;QACdC,QAAQD,KAAK,CAAC,0BAA0B;YAAEE;YAAOC,OAAOhC,IAAIiC,GAAG;YAAE4H,MAAMzG,MAAMyG,IAAI;YAAEhI;QAAM;QACzF,OAAO;IACT;AACF;AAEA,yDAAyD,GACzD,SAASuN,mBAAmBpP,GAAyB;;QAC5CA;IAAP,gBAAOA,kBAAAA,IAAIQ,KAAK,CAAC6O,IAAI,CAAC,CAAC9I,OAASA,KAAK9F,IAAI,KAAK5G,sCAAvCmG,gBAA0D4D,YAAY,mBAAI;AACnF;AAEA;;;;;;;CAOC,GACD,OAAO,eAAe6K,iBACpBjN,SAAoB,EACpBO,KAAa,EACbC,KAAa,EACboB,KAIC;QAQepD,YA2CyBA,aAGnBA,0BA4EOA;QA/C3BA;IAjFF,MAAM,EAAEyB,GAAG,EAAE,GAAG2B;IAChB,IAAIpD,MAAM,MAAM+E,SAASvD,WAAWO,OAAOC;IAC3C,IAAI,CAAChC,OAAOvB,gBAAgBuB,IAAItB,MAAM,GAAG,OAAOsB;IAChD,qEAAqE;IACrE,IAAIjD,mBAAmBiD,MAAM,OAAO6M,cAAcrL,WAAWO,OAAOC,OAAOP;IAC3E,MAAMiN,UAAUtL,MAAMsL,OAAO;IAC7B,IAAIA,WAAW,EAAC1O,aAAAA,IAAI0H,KAAK,YAAT1H,aAAa,EAAE,EAAE0N,IAAI,CAAC,CAAClF,MAAQA,IAAI9J,MAAM,KAAK,aAAa8J,IAAI9J,MAAM,KAAK,YAAY;YAElFsB;QADlB,MAAMiO,UAAU,IAAInP;QACpB,KAAK,MAAM0J,QAAOxI,cAAAA,IAAI0H,KAAK,YAAT1H,cAAa,EAAE,CAAE;gBAKtBwI;YAJX,IAAIA,IAAI9J,MAAM,KAAK,WAAW;YAC9B,MAAMwP,SAAS,MAAMa,mBAAmBvN,WAAWO,OAAO/B,KAAK;gBAC7D6J,MAAMrB,IAAIqB,IAAI;gBACdqF,SAAS1G,IAAI0G,OAAO;gBACpBtF,OAAO,GAAEpB,sBAAAA,IAAIsB,cAAc,YAAlBtB,sBAAsB;gBAC/B+D,QAAQmC,QAAQnC,MAAM;gBACtBwB,MAAM3K,MAAM2K,IAAI;gBAChBtM;YACF;YACA,IAAIyM,QAAQD,QAAQzO,GAAG,CAACgJ,IAAIqB,IAAI,EAAEqE;QACpC;QACAlO,MAAM,AACJ,CAAA,MAAM0M,WAAWlL,WAAWO,OAAOC,OAAO,CAACuI;gBAG1BA,gBAgBKA;YAlBpB,IAAI9L,gBAAgB8L,QAAQ7L,MAAM,GAAG,OAAO;YAC5C,IAAI4Q,QAAQ;YACZ,MAAM5H,QAAQ,EAAC6C,iBAAAA,QAAQ7C,KAAK,YAAb6C,iBAAiB,EAAE,EAAE9G,GAAG,CAAC,CAAC+E;gBACvC,IAAIA,IAAI9J,MAAM,KAAK,WAAW;8BAOR8J;oBANpB,MAAM0F,SAASD,QAAQnO,GAAG,CAAC0I,IAAIqB,IAAI;oBACnCyF,iBAASpB,0BAAAA,OAAQtE,OAAO,mBAAI;oBAC5B,OAAO,aACFpB;wBACH9J,QAAQ;wBACRkD,SAAS;4BAAEoL,MAAM;4BAAMT,QAAQmC,QAAQnC,MAAM;4BAAED,SAASoC,QAAQpC,OAAO;wBAAC;wBACxEvC,iBAAiB,EAACvB,uBAAAA,IAAIuB,eAAe,YAAnBvB,uBAAuB,eAAM0F,0BAAAA,OAAQtE,OAAO,oBAAI;uBAC9DsE,SAAS;wBAAElE,WAAWkE,OAAOiB,GAAG;oBAAC,IAAI,CAAC;gBAE9C;gBACA,OAAO3G,IAAI9J,MAAM,KAAK,YAAY,aAAK8J;oBAAK9J,QAAQ;oBAAW+J,MAAMqG;qBAA8BtG;YACrG;YACA,OAAO;gBACLd;gBACAlB,iBAAiB,EAAC+D,2BAAAA,QAAQ/D,eAAe,YAAvB+D,2BAA2B,KAAK+E;gBAClD/K,WAAW9C;YACb;QACF,EAAC,EACDzB,GAAG;IACP;IACA,uEAAuE;IACvE,0EAA0E;IAC1E,yCAAyC;IACzC,MAAMuP,YAAYvP,IAAIX,IAAI,KAAK,SAAS,EAACW,cAAAA,IAAI0H,KAAK,YAAT1H,cAAa,EAAE,EAAE4F,MAAM,CAAC,CAAC4C,MAAQA,IAAIgH,EAAE,KAAK,UAAU,EAAE;IACjG,MAAM/B,YAAY8B,UAAUnL,MAAM,GAC9BmL,UAAU7B,IAAI,CAAC,CAAClF,MAAQA,IAAI9J,MAAM,KAAK,eAAe8J,IAAI9J,MAAM,KAAK,cACrE3C,mBAAkBiE,cAAAA,IAAI0H,KAAK,YAAT1H,cAAa,EAAE,EAAEyN,SAAS;IAChD,MAAMgC,cAAcL,mBAAmBpP;IACvC,IAAIyN,WAAW;QACb,MAAMf,WAAWlL,WAAWO,OAAOC,OAAO,CAACuI,UACzC9L,gBAAgB8L,QAAQ7L,MAAM,IAC1B,OACA;gBAAEiJ,eAAe;oBAAE/D,cAAc6L;oBAAazD,SAAS;gBAAU;gBAAGzH,WAAW9C;YAAI;QAEzF,OAAOoL,cAAcrL,WAAWO,OAAOC,OAAOP;IAChD;IACA,yEAAyE;IACzE,iEAAiE;IACjE,MAAMiO,QAAQ,IAAI5Q;IAClB,IAAIkB,IAAIX,IAAI,KAAK,QAAQ;YACLW;QAAlB,KAAK,MAAMwI,QAAOxI,cAAAA,IAAI0H,KAAK,YAAT1H,cAAa,EAAE,CAAE;gBACPwI,mBAA0BA;YAApD,MAAMmH,OAAOxP,KAAKC,GAAG,CAAC,GAAG,EAACoI,oBAAAA,IAAI5E,YAAY,YAAhB4E,oBAAoB,OAAMA,uBAAAA,IAAIuB,eAAe,YAAnBvB,uBAAuB;YAC3E,IAAImH,QAAQ,KAAKnH,IAAI9J,MAAM,KAAK,UAAU;YAC1C,MAAM4Q,QAAQ,MAAMP,mBAAmBvN,WAAWO,OAAO/B,KAAK;gBAC5D6J,MAAMrB,IAAIqB,IAAI;gBACdqF,SAAS1G,IAAI0G,OAAO;gBACpBtF,SAAS+F;gBACTpD,MAAM,WAAEmC,2BAAAA,QAASnC,MAAM,oBAAI;gBAC3BwB,MAAM3K,MAAM2K,IAAI;gBAChBtM;YACF;YACA,IAAI6N,OAAOI,MAAMlQ,GAAG,CAACgJ,IAAIqB,IAAI,EAAEyF;QACjC;IACF;IACA,MAAMpB,SACJlO,EAAAA,qBAAAA,IAAI2H,aAAa,qBAAjB3H,mBAAmBgM,OAAO,MAAK,aAC3B,OACA,MAAM+C,mBAAmBvN,WAAWO,OAAO/B,KAAK;QAC9C6J,MAAM;QACNqF,SAAS;QACTtF,SAAS6F;QACTlD,MAAM,UAAEmC,2BAAAA,QAASnC,MAAM,mBAAI;QAC3BwB,MAAM3K,MAAM2K,IAAI;QAChBtM;IACF;IACN,MAAMiL,WAAWlL,WAAWO,OAAOC,OAAO,CAACuI;YAW1BA,uCAQKA;YAbOA;eAL3B9L,gBAAgB8L,QAAQ7L,MAAM,IAC1B,OACA;YACEiJ,eAAe;gBACb/D,cAAc6L;gBACdzD,SAASkC,UAAU3D,EAAAA,yBAAAA,QAAQ5C,aAAa,qBAArB4C,uBAAuByB,OAAO,MAAK,aAAa,aAAa;YAClF;WACIkC,UAAUwB,MAAME,IAAI,GACpB;YACEpJ,iBACE,EAAC+D,2BAAAA,QAAQ/D,eAAe,YAAvB+D,2BAA2B,cAC3B2D,0BAAAA,OAAQtE,OAAO,mBAAI,KACpB;mBAAI8F,MAAMG,MAAM;aAAG,CAACC,MAAM,CAAC,CAACC,OAAOC,OAASD,QAAQC,KAAKpG,OAAO,EAAE;YACpE0E,YAAY,WAAEI,2BAAAA,QAASnC,MAAM,oBAAI;QACnC,IACA,CAAC,GACDmD,MAAME,IAAI,GACV;YACElI,OAAO,EAAC6C,iBAAAA,QAAQ7C,KAAK,YAAb6C,iBAAiB,EAAE,EAAE9G,GAAG,CAAC,CAAC+E;oBAEUA;gBAD1C,MAAMwH,OAAON,MAAM5P,GAAG,CAAC0I,IAAIqB,IAAI;gBAC/B,OAAOmG,OAAO,aAAKxH;oBAAKuB,iBAAiB,EAACvB,uBAAAA,IAAIuB,eAAe,YAAnBvB,uBAAuB,KAAKwH,KAAKpG,OAAO;oBAAEI,WAAWgG,KAAKb,GAAG;qBAAK3G;YAC9G;QACF,IACA,CAAC;YACLjE,WAAW9C;;;IAGnB,MAAMwO,UACJjQ,IAAIX,IAAI,KAAK,SACT9C,mCACAyD,IAAIX,IAAI,KAAK,WACXwP,oCACAD;IACR,OAAO9B,UAAUtL,WAAWO,OAAOC,gBAAO0M,2BAAAA,QAASpC,OAAO,oBAAI2D,SAAS;QACrEpO,OAAO;QACP,yEAAyE;QACzEmL,MAAM5E,QAAQsG,YAAY,EAAC1O,cAAAA,IAAI0H,KAAK,YAAT1H,cAAa,EAAE,EAAE0N,IAAI,CAAC,CAAClF;gBAAmCA;mBAA3BA,IAAI9J,MAAM,KAAK,cAAY8J,eAAAA,IAAI5G,OAAO,qBAAX4G,aAAawE,IAAI;;IACxG,GAAGvL;AACL;AAEA;;;;;;;CAOC,GACD,OAAO,eAAeyO,gBACpB1O,SAAoB,EACpBO,KAAa,EACbC,KAAa,EACbP,MAAM,IAAIzD,MAAM,EAChBiH,UAGI,CAAC,CAAC;IAEN,IAAIkL,UAAoB,EAAE;IAC1B,MAAM7C,SAAS,MAAMZ,WAAWlL,WAAWO,OAAOC,OAAO,CAACuI;YAE3CA,eACCA;QAFd,IAAI,CAACoE,iBAAiBpE,UAAU,OAAO;QACvC,MAAM5D,OAAO4D,EAAAA,gBAAAA,QAAQ5D,IAAI,qBAAZ4D,cAAc7L,MAAM,MAAK,cAAc6L,QAAQ5D,IAAI,GAAG;QACnE,IAAI,CAACA,QAAQ,GAAC4D,iBAAAA,QAAQ7C,KAAK,qBAAb6C,eAAenG,MAAM,GAAE,OAAO;QAC5C,MAAMgM,OAAOnU,mBAAmBsO,QAAQ7C,KAAK,EAAE6C,QAAQlL,IAAI,KAAK,SAASlD,cAAcoO,QAAQ7C,KAAK,IAAI1L,aAAa2K;QACrH,IAAI,CAACyJ,KAAK1G,MAAM,CAACgE,IAAI,CAAC,CAAClF,MAAQA,IAAI9J,MAAM,KAAK,YAAY,OAAO;QACjEyR,UAAUC,KAAKD,OAAO;QACtB,OAAO;YACLzR,QAAQ;YACRgJ,OAAO0I,KAAK1G,MAAM;YAClBlJ,OAAO+J,QAAQ/J,KAAK,CAACiD,GAAG,CAAC,CAAC8C,OACxBA,KAAK9F,IAAI,KAAK5G,mBACV0M,OACA,aAAKA;oBAAM7H,QAAQ;oBAAWmF,UAAU;oBAAG+H,QAAQ;oBAAGjI,SAAS;oBAAM9B,OAAO;;YAElFmF,QAAQ;YACRnF,OAAO;YACP4C,OAAO;YACPN,iBAAiBhE,KAAKC,GAAG,CACvB5C,6BACA+M,QAAQlL,IAAI,KAAK,SACb+Q,KAAKD,OAAO,CAAC/L,MAAM,GAAG5G,8BACtBtB,sBAAsByK,MAAM;gBAAE0J,OAAO,IAAIC,IAAIF,KAAKD,OAAO;YAAE;WAE7DlL,QAAQP,gBAAgB,GAAG;YAAEA,kBAAkBO,QAAQP,gBAAgB;QAAC,IAAI,CAAC;YACjFH,WAAW9C;;IAEf;IACA,sDAAsD;IACtD,IAAI6L,OAAOV,OAAO,EAAE,MAAM1P,sBAAsBsE,WAAW8L,OAAOtN,GAAG,CAAC+D,MAAM;IAC5E,OAAO,aAAKuJ;QAAQ6C,SAAS7C,OAAOV,OAAO,GAAGuD,UAAU,EAAE;;AAC5D;AAEA;;;;;;;;;;;CAWC,GACD,OAAO,eAAeI,YACpB/O,SAAoB,EACpBO,KAAa,EACbC,KAAa,EACboL,KAAsB,EACtB3L,MAAM,IAAIzD,MAAM,EAChBiH,UAoBI,CAAC,CAAC;IAEN,MAAMuL,UAAU,MAAM9D,WAAWlL,WAAWO,OAAOC,OAAO,CAACuI;YA+BJtF;YAtBbsF,uBAAAA,gBAqBvBA,iBAauDA;QA1CxE,uEAAuE;QACvE,uEAAuE;QACvE,uEAAuE;QACvE,6CAA6C;QAC7C,0EAA0E;QAC1E,qEAAqE;QACrE,uEAAuE;QACvE,6CAA6C;QAC7C,IAAIA,QAAQ7L,MAAM,KAAK,mBAAiB6L,iBAAAA,QAAQnH,KAAK,sBAAbmH,wBAAAA,eAAetD,MAAM,qBAArBsD,sBAAuBnG,MAAM,GAAE;YACrE,MAAMyL,SAAS5K,QAAQ7B,KAAK;YAC5B,IAAI,CAACyM,UAAU,CAACtF,QAAQ5D,IAAI,IAAI8J,OAAOC,IAAI,CAAC3W,mBAAmBwQ,QAAQnH,KAAK,EAAEyM,SAASzL,MAAM,EAAE,OAAO;YACtG,OAAO;gBACL1F,QAAQ;gBACRiI,MAAM3M,gBAAgBuQ,QAAQ5D,IAAI,EAAE4D,QAAQnH,KAAK,EAAEyM;gBACnDzM,OAAO;gBACPvB,OAAO;gBACP4C,OAAO;gBACPF,WAAW9C;YACb;QACF;QACA,IAAI8I,QAAQ7L,MAAM,KAAK,eAAe;YACpC,OAAO;gBAAEA,QAAQ;gBAAUmD,OAAO;gBAAM4C,OAAO;gBAAMF,WAAW9C;YAAI;QACtE;QACA,IAAI8I,QAAQ7L,MAAM,KAAK,gBAAgB,OAAO;QAC9C,MAAM8B,QAAQ+J,QAAQ/J,KAAK,CAACiD,GAAG,CAAC,CAAC8C,OAC/BA,KAAK7H,MAAM,KAAK,YAAY,aAAK6H;gBAAM1C,UAAU;iBAAM0C;QAEzD,MAAMoK,cAAcnQ,MAAMoF,MAAM,CAAC,CAACW,OAASA,KAAK7H,MAAM,KAAK,WAAW0F,MAAM;QAC5E,MAAMwM,UAAUD,cAAc;QAC9B,MAAME,WAAWtG,EAAAA,kBAAAA,QAAQvD,MAAM,qBAAduD,gBAAgBgC,MAAM,MAAK,UAAUhC,QAAQ5D,IAAI,GAAG4D,QAAQ5D,IAAI,GAAG;QACpF,MAAMmK,aAAaD,YAAY5L,QAAQ8L,UAAU,IAAI9L,sBAAAA,QAAQ8L,UAAU,CAACF,qBAAnB5L,sBAAgC4L,WAAYA;QACjG,MAAMG,YAAYF,aACd;YACEnK,MAAM,aACDmK;gBACHpS,QAAQ;gBACRoI,aAAarF;gBACbsF,aAAaqG,MAAMzI,GAAG;;QAE1B,IACA,CAAC;QACL,MAAMsM,UACJH,cAAcvG,QAAQlL,IAAI,KAAK,WAAW4F,QAAQiM,gBAAgB,IAAI3G,EAAAA,kBAAAA,QAAQvG,MAAM,qBAAduG,eAAgB,CAAC,UAAU,MAAK,OAClG;YAAEvG,QAAQ,aAAKuG,QAAQvG,MAAM;gBAAEkN,kBAAkB;;QAAO,IACxD,CAAC;QACP,OAAO;YACLxS,QAAQkS,UAAU,WAAW;YAC7BpQ;YACAwG,QAAQ;YACRnF,OAAO;WACJmP,WACAC,SACChM,QAAQP,gBAAgB,GAAG;YAAEA,kBAAkBO,QAAQP,gBAAgB;QAAC,IAAI,CAAC;YACjFP,iBAAiByM,UACbzQ,KAAKC,GAAG,CACNuQ,cAAcnT,6BACdsT,aAAa1U,oBAAoBmO,QAAQlL,IAAI,EAAEyR,cAAc,KAE/D;YACJvM,WAAW9C;;IAEf;IACA,kDAAkD;IAClD,IAAI+O,QAAQ5D,OAAO,EAAE,MAAM1P,sBAAsBsE,WAAWgP,QAAQxQ,GAAG,CAAC+D,MAAM;IAC9E,OAAOyM;AACT;AAEA;;;;;CAKC,GACD,OAAO,eAAeW,mBACpB3P,SAAoB,EACpBO,KAAa,EACbC,KAAa,EACbkF,OAAqB,EACrBzF,MAAM,IAAIzD,MAAM;IAEhB,MAAM,EAAEgC,GAAG,EAAE,GAAG,MAAM0M,WAAWlL,WAAWO,OAAOC,OAAO,IAAO,CAAA;YAC/DkF;YACA3C,WAAW9C;QACb,CAAA;IACA,OAAOzB;AACT;AAeA;;;;CAIC,GACD,OAAO,eAAeoR,gBACpB5P,SAAoB,EACpB6P,KAAsB;IAEtB,IAAI;YAGYA;QAFd,MAAMxU,cAAc2E,WAAW;YAC7B8P,UAAUD,MAAMC,QAAQ;YACxBC,UAAU,GAAEF,oBAAAA,MAAME,UAAU,YAAhBF,oBAAoB;YAChCG,QAAQH,MAAMG,MAAM;YACpBC,QAAQ,CAAC,KAAK,EAAEJ,MAAMtP,KAAK,CAAC,CAAC,EAAE3E,mBAAmB,CAAC,EAAEiU,MAAMrP,KAAK,EAAE;YAClE0P,OAAOL,MAAMK,KAAK;YAClBvK,IAAI7N,WAAWqY,eAAe;QAChC;IACF,EAAE,OAAO9P,OAAO;QACdC,QAAQD,KAAK,CAAC,6BAA6B;YACzCE,OAAOsP,MAAMtP,KAAK;YAClBC,OAAOqP,MAAMrP,KAAK;YAClBwP,QAAQH,MAAMG,MAAM;YACpB3P;QACF;IACF;AACF;AAEA,6EAA6E;AAE7E;;;;;CAKC,GACD,OAAO,SAAS+P,iBACdC,GAAoC,EACpCC,WAQC;IAED,MAAMC,YAAYD,YAAYC,SAAS;IACvC,wEAAwE;IACxE,yEAAyE;IACzE,4DAA4D;IAC5D,MAAMC,UAAU9X,4BAA4B4X;IAC5C,IAAIE,SAAS,OAAOA,QAAQC,IAAI;IAChC,yEAAyE;IACzE,yEAAyE;IACzE,oEAAoE;IACpE,IAAIF,cAAc,aAAa;YACCD,gCAAAA;QAA9B,OAAO3X,wBAAuB2X,yBAAAA,YAAYI,SAAS,sBAArBJ,iCAAAA,uBAAuBK,OAAO,qBAA9BL,+BAAgCM,KAAK;IACrE;IACA,IAAIxY,uBAAuBiY,KAAKE,YAAY;QAC1C,OAAOpY,yBAAyBkY;IAClC;IACA,sEAAsE;IACtE,sDAAsD;IACtD,MAAMQ,QAAQ3Y,2BAA2BqY;IACzC,IAAIM,OAAO,OAAOA;IAClB,OAAQN;QACN,KAAK;YACH,OAAO;QACT,KAAK;YACH,OAAO;QACT,KAAK;YACH,OAAOD,YAAYQ,SAAS,KAAK,OAC7B,+DACA;QACN;YACE,OAAO;IACX;AACF;AAyDA;;;;;CAKC,GACD,SAASC,QAAQ1Q,KAAc;IAC7B,MAAMpB,OAAQoB,yBAAD,AAACA,MAAoCpB,IAAI;IACtD,OAAOA,SAAS,gBAAgBA,SAAS;AAC3C;AAEA,uFAAuF,GACvF,OAAO,SAAS+R,sBACdC,OAAuD;IAEvD,MAAM,EAAE3H,KAAK,EAAE,GAAG2H;IAClB,OACEA,QAAQC,UAAU,KAAK,KACvB5H,MAAMC,WAAW,KAAK,KACtBD,MAAMO,YAAY,KAAK,KACvBP,MAAMG,eAAe,KAAK,KAC1BH,MAAMK,gBAAgB,KAAK;AAE/B;AAEA;;;;;;;;;;CAUC,GACD,OAAO,eAAewH,aACpBnR,SAAoB,EACpBO,KAAa,EACbC,KAAa,EACbiD,OAA4B;QAEhBA,cA4CPsB,gBAYHtB,cACE,oBAgLMwN,iBAoIPlM,cA2B4BtB;QAjQZ6M,wBACNA,yBAWT7M,iBACCA,kBAkKDwN,iBAqCmCG,qBA0H+BlH;IArdtE,MAAMjK,OAAMwD,eAAAA,QAAQxD,GAAG,YAAXwD,eAAe,IAAIjH;IAE/B,oEAAoE;IACpE,oEAAoE;IACpE,wEAAwE;IACxE,+CAA+C;IAC/C,MAAM6U,cAAc;QAClB,IAAI,CAAC5N,QAAQ6M,WAAW,EAAE;QAC1B,MAAMpW,qBAAqB8F,WAAWO,OAAOkD,QAAQ6M,WAAW,EAAEgB,KAAK,CACrE,CAACC,eACCjR,QAAQD,KAAK,CAAC,yBAAyB;gBAAEE;gBAAOC;gBAAO+Q;YAAa;IAE1E;IAEA,2EAA2E;IAC3E,2EAA2E;IAC3E,yEAAyE;IACzE,iEAAiE;IACjE,4EAA4E;IAC5E,2EAA2E;IAC3E,wEAAwE;IACxE,IAAI,MAAM5Q,eAAeJ,OAAOC,OAAOiD,QAAQ7C,KAAK,KAAK,OAAO;QAC9D,MAAMoH,OAAO,MAAMD,gBAAgB/H,WAAWO,OAAOC,OAAOiD,QAAQgD,KAAK,EAAExG;QAC3E,IAAI,CAAC+H,MAAM,OAAO;YAAEiJ,SAAS;QAAgB;QAC7C,MAAMI;QACN,OAAO;YAAEJ,SAAS;YAAUzS,KAAKwJ;QAAK;IACxC;IAEA,MAAMR,UAAU,MAAMF,cAActH,WAAWO,OAAOC,OAAOiD,QAAQgD,KAAK,EAAExG;IAC5E,IAAI,CAACuH,SAAS,OAAO;QAAEyJ,SAAS;IAAgB;IAChD,MAAM,EAAEzS,GAAG,EAAEqJ,SAAS,EAAE,GAAGL;IAC3B,MAAMzC,OAAOvG,IAAIQ,KAAK,CAAC6I,UAAU;IAEjC,MAAM/J,SAAS+C,mBAAmBrC,IAAIX,IAAI,EAAEkH,KAAK9F,IAAI;IACrD,IAAI,CAACnB,QAAQ;QACX,MAAMuT;QACN,OAAO;YACLJ,SAAS;YACTzS,KAAK,MAAMuO,eAAe/M,WAAWO,OAAOC,OAAOpE,2BAA2B;gBAC5EyL;gBACAxH,OAAO,CAAC,8BAA8B,EAAE7B,IAAIX,IAAI,EAAE;YACpD,GAAG;gBAAEkN,QAAQ;gBAAesB,aAAa;YAAE,GAAGpM;QAChD;IACF;IACA,IAAI,EAAC8E,iBAAAA,KAAK1C,QAAQ,YAAb0C,iBAAiB,KAAKjJ,0BAA0B;QACnD,MAAMuV;QACN,OAAO;YACLJ,SAAS;YACTzS,KAAK,MAAMuO,eAAe/M,WAAWO,OAAOC,OAAOpH,0BAA0B;gBAC3EyO;gBACAxH,OAAO,CAAC,KAAK,EAAE0E,KAAK9F,IAAI,CAAC,WAAW,EAAEnD,yBAAyB,SAAS,CAAC;YAC3E,GAAG;gBAAEiP,QAAQ;gBAAWsB,aAAa;YAAE,GAAGpM;QAC5C;IACF;IAEA,MAAMoQ,OACJ5M,eAAAA,QAAQ4M,GAAG,YAAX5M,gBACE,QAAA,AAAC,CAAA,MAAMzD,UAAUoB,UAAU,CAAC,QAAQC,GAAG,CAACd,OAAOjC,GAAG,EAAC,EAAGmD,IAAI,cAA1D,QACA,CAAC;IAEL,uEAAuE;IACvE,2EAA2E;IAC3E,wEAAwE;IACxE,IAAI,MAAM5I,eAAemH,WAAWqQ,KAAK7R,IAAI+D,MAAM,GAAG;QACpD,MAAM8O;QACN,OAAO;YACLJ,SAAS;YACTzS,KAAK,MAAM8M,UAAUtL,WAAWO,OAAOC,OAAO5H,sBAAsB;gBAClEiP;gBACAxH,OAAO,CAAC,4BAA4B,EAAE7B,IAAI+D,MAAM,EAAE;YACpD,GAAGtC;QACL;IACF;IAEA,MAAMuR,WACJxZ,iBAAiBqY,KAAK,eAAerY,iBAAiBqY,KAAK;IAE7D,IAAIC;IACJ,IAAI7M,QAAQ6M,WAAW,EAAE;QACvBA,cAAc7M,QAAQ6M,WAAW;IACnC,OAAO;QACL,IAAI;gBAKQ9R;YAJV,qEAAqE;YACrE,iDAAiD;YACjD8R,cAAc,MAAMnW,qBAAqB6F,WAAWO,OAAOiR,UAAUvR,KAAKoQ,KAAK;gBAC7ElN,KAAK3E,IAAIqE,SAAS;gBAClBN,MAAM,GAAE/D,cAAAA,IAAI+D,MAAM,YAAV/D,cAAc;YACxB;QACF,EAAE,OAAO6B,OAAO;YACd,kEAAkE;YAClE,wBAAwB;YACxBC,QAAQD,KAAK,CAAC,6BAA6B;gBAAEE;gBAAOC;gBAAOH;YAAM;YACjE,MAAM,EAAE7B,KAAKiT,QAAQ,EAAE,GAAG,MAAMxH,WAC9BjK,WAAWO,OAAOC,OAAOiD,QAAQgD,KAAK,EAAEoB,WACxC;gBAAE3K,QAAQ;gBAAWkF,cAAc;YAAE,GACrCnC;YAEF,OAAO;gBAAEgR,SAAS;gBAAYzS,KAAKiT;YAAS;QAC9C;IACF;IACA,sEAAsE;IACtE,wEAAwE;IACxE,WAAW;IACXnY,oBAAoB0G,WAAWO,OAAO/B,IAAIqE,SAAS,EAAEyN;IACrD,IAAI,CAACA,YAAYoB,OAAO,EAAE;QACxB,MAAMf,UAAUP,iBAAiBC,KAAKC;QACtC,MAAMtF,SAAS,MAAMU,oBAAoB1L,WAAWO,OAAOC,OAAOmQ,SAAS1Q;QAC3E,2EAA2E;QAC3E,IAAI+K,OAAO9N,MAAM,KAAK,YAAY,OAAO;YAAE+T,SAAS;YAAYzS,KAAKwM;QAAO;QAC5E,sEAAsE;QACtE,sEAAsE;QACtE,oEAAoE;QACpE,uEAAuE;QACvE,qEAAqE;QACrE,wEAAwE;QACxE,0BAA0B;QAC1B,IAAIsF,YAAYC,SAAS,IAAI/R,IAAI6B,KAAK,KAAKsQ,SAAS;YAClD,MAAMzX,mBAAmBqH,OAAO;gBAAE4C,KAAK;YAAK,GAAG;gBAC7C3C;gBACA3C,MAAMW,IAAIX,IAAI;gBACdkN,QAAQuF,YAAYC,SAAS;YAC/B;YACA,oEAAoE;YACpE,uEAAuE;YACvE,MAAMrQ,wBAAwB8K,QAAQ;QACxC;QACA,OAAO;YAAEiG,SAAS;YAAezS,KAAKwM;QAAO;IAC/C;IAEA,uEAAuE;IACvE,uEAAuE;IACvE,0EAA0E;IAC1E,wEAAwE;IACxE,MAAM2G,SAAS;QACbxM,MAAM1M,qBAAqB4X;QAC3BuB,eAAe,WAAEtB,yBAAAA,YAAYI,SAAS,qBAArBJ,uBAAuBuB,MAAM,mBAAI;QAClDC,SAAS,YAAExB,0BAAAA,YAAYI,SAAS,qBAArBJ,wBAAuBwB,SAAS,oBAAI;IACjD;IACA,MAAMC,WAAW,CAAClU;YACWW;YAA3B1F;gBAAAA,wBAAAA,qBAAqB+E,OAAMW,aAAAA,IAAIiE,KAAK,YAATjE,aAAa,MAAMmT,4BAA9C7Y,sBAAuD2J,KAAK;;IAE9D,wEAAwE;IACxE,0EAA0E;IAC1E,uEAAuE;IACvE,2CAA2C;IAC3C,MAAMuP,aAAa,IAAIC;IACvB,MAAMC,gBAAgB;YAAuBzO;eAAjBuO,WAAWG,KAAK,EAAC1O,kBAAAA,QAAQ2O,MAAM,qBAAd3O,gBAAgBsH,MAAM;;IACnE,KAAItH,kBAAAA,QAAQ2O,MAAM,qBAAd3O,gBAAgB4O,OAAO,EAAEH;UACxBzO,mBAAAA,QAAQ2O,MAAM,qBAAd3O,iBAAgB6O,gBAAgB,CAAC,SAASJ,eAAe;QAAEK,MAAM;IAAK;IAC3E,MAAMC,YAAY/W,iBAChB,IAAM8H,SAASvD,WAAWO,OAAOC,QACjCwR,YACAvO,QAAQgP,YAAY;IAEtB,MAAMC,kBAAkB,IACtBV,WAAWI,MAAM,CAACC,OAAO,IAAI7W,qBAAqBwW,WAAWI,MAAM,CAACrH,MAAM;IAE5E,IAAIkG;IACJ,uEAAuE;IACvE,uEAAuE;IACvE,MAAM0B,aAAanW,KAAKyD,GAAG;IAC3B,IAAI;QACFgR,UAAU,MAAMnT,OAAO;YACrBU;YAAKqJ;YAAW5H;YAAKmS,QAAQJ,WAAWI,MAAM;YAAEpS;YAAWqQ;YAAK0B;QAClE;IACF,EAAE,OAAO1R,OAAO;YAiCmCiQ;QAhCjD,iEAAiE;QACjE,sEAAsE;QACtE,oEAAoE;QACpE,uCAAuC;QACvC,MAAMpW,qBAAqB8F,WAAWO,OAAO+P,aAAagB,KAAK,CAAC,CAACC,eAC/DjR,QAAQD,KAAK,CAAC,yBAAyB;gBAAEE;gBAAOC;gBAAO+Q;YAAa;QAEtE,sEAAsE;QACtE,8DAA8D;QAC9D,IAAI/V,qBAAqB6E,UAAUqS,mBAAmB;YACpD,MAAM,EAAElU,KAAKoU,QAAQ,EAAE,GAAG,MAAM3I,WAC9BjK,WAAWO,OAAOC,OAAOiD,QAAQgD,KAAK,EAAEoB,WACxC;gBAAE3K,QAAQ;gBAAWkF,cAAc;YAAE,GACrCnC;YAEF,OAAO;gBAAEgR,SAAS;gBAAYzS,KAAKoU;YAAS;QAC9C;QACA,MAAMC,YACJ,AAACxS,iBAAiBhH,mBAAmBgH,MAAMwS,SAAS,IAAK9B,QAAQ1Q;QACnE,IAAIwS,WAAW;YACb,MAAM,EAAErU,KAAKiT,QAAQ,EAAE,GAAG,MAAMxH,WAC9BjK,WAAWO,OAAOC,OAAOiD,QAAQgD,KAAK,EAAEoB,WACxC;gBAAE3K,QAAQ;gBAAWkF,cAAc;YAAE,GACrCnC;YAEF,OAAO;gBAAEgR,SAAS;gBAAYzS,KAAKiT;YAAS;QAC9C;QACA,OAAO;YACLR,SAAS;YACTzS,KAAK,MAAMuO,eAAe/M,WAAWO,OAAOC,OAAOpH,0BAA0B;gBAC3EyO;gBACAxH;YACF,GAAG;gBAAE0K,QAAQ;gBAAYsB,aAAa;gBAAGE,IAAI,GAAE+D,oBAAAA,YAAY/D,IAAI,YAAhB+D,oBAAoB;YAAK,GAAGrQ;QAC7E;IACF,SAAU;YAERwD;QADA+O;SACA/O,mBAAAA,QAAQ2O,MAAM,qBAAd3O,iBAAgBqP,mBAAmB,CAAC,SAASZ;IAC/C;IAEA,MAAMnI,YAAYpL,KAAKC,GAAG,CAAC,GAAGpC,KAAKyD,GAAG,KAAK0S;IAE3C,yEAAyE;IACzE,2EAA2E;IAC3E,qEAAqE;IACrE,WAAW;IACX,IAAI1B,QAAQ7Q,OAAO,IAAI4Q,sBAAsBC,UAAU;YAeAX;QAdrD,MAAMpW,qBAAqB8F,WAAWO,OAAO+P,aAAagB,KAAK,CAAC,CAACC,eAC/DjR,QAAQD,KAAK,CAAC,yBAAyB;gBAAEE;gBAAOC;gBAAO+Q;YAAa;QAEtE,MAAMtH,WACJjK,WAAWO,OAAOC,OAAOiD,QAAQgD,KAAK,EAAEoB,WACxC;YAAE3K,QAAQ;YAAUkF,cAAc;YAAG/B,OAAO4Q,QAAQ7Q,OAAO;QAAC,GAC5DH;QAEF,OAAO;YACLgR,SAAS;YACTzS,KAAK,MAAMuO,eAAe/M,WAAWO,OAAOC,OAAOyQ,QAAQ7Q,OAAO,EAAE;gBAClEyH;gBACAxH,OAAO,CAAC,kCAAkC,EAAE4Q,QAAQ7Q,OAAO,EAAE;eACzD6Q,QAAQ8B,aAAa,GAAG;gBAAEvH,MAAM;YAAM,IAAI,CAAC,IAC9C;gBAAET,QAAQ;gBAAgBsB,aAAa;gBAAGE,IAAI,GAAE+D,qBAAAA,YAAY/D,IAAI,YAAhB+D,qBAAoB;YAAK,GAAGrQ;QACjF;IACF;IAEA,MAAMoK,SAA4B;QAChCf,OAAO2H,QAAQ3H,KAAK;QACpB7G,OAAOwO,QAAQxO,KAAK;QACpBqH,MAAM,GAAEmH,kBAAAA,QAAQnH,MAAM,YAAdmH,kBAAkB;QAC1BlH;QACAV,YAAY4H,QAAQ5H,UAAU;IAChC;IAEA,2EAA2E;IAC3E,2EAA2E;IAC3E,wEAAwE;IACxE,2BAA2B;IAC3B,MAAM2J,eAAehC,sBAAsBC;IAC3C,IAAI+B,cAAc;QAChB,MAAM9Y,qBAAqB8F,WAAWO,OAAO+P,aAAagB,KAAK,CAAC,CAACC,eAC/DjR,QAAQD,KAAK,CAAC,yBAAyB;gBAAEE;gBAAOC;gBAAO+Q;YAAa;IAExE,OAAO;QACL,uEAAuE;QACvE,qEAAqE;QACrE,IAAI;gBAMU/S,cAUF8R;YAfV,MAAMrW,iBACJ+F,WACAO,OACA;gBACE0S,OAAO;gBACP1Q,MAAM,GAAE/D,eAAAA,IAAI+D,MAAM,YAAV/D,eAAc;gBACtBiE,OAAOwO,QAAQxO,KAAK;gBACpByQ,MAAM1B,WAAW,aAAa;gBAC9BlI,OAAO2H,QAAQ3H,KAAK;gBACpB6J,WAAW,EAAE;gBACb9J,YAAY4H,QAAQ5H,UAAU;gBAC9B+J,WAAW;gBACX,8DAA8D;gBAC9D,gEAAgE;gBAChE,yCAAyC;gBACzC7G,IAAI,GAAE+D,qBAAAA,YAAY/D,IAAI,YAAhB+D,qBAAoB;gBAC1B,oEAAoE;gBACpEnN,KAAK3E,IAAIqE,SAAS;gBAClBhF,MAAMW,IAAIX,IAAI;YAChB,GACAoC;QAEJ,EAAE,OAAOI,OAAO;YACdC,QAAQD,KAAK,CAAC,6BAA6B;gBAAEE;gBAAOC;gBAAOH;YAAM;QACnE;IACF;IACA,MAAM+H,UAAUnQ,qBAAqBgZ,QAAQC,UAAU;IAEvD,sEAAsE;IACtE,sEAAsE;IACtE,wCAAwC;IACxC,IAAID,QAAQoC,OAAO,IAAIpC,QAAQ7Q,OAAO,EAAE;YAkBsBkQ;QAjB5D,MAAMxF,UAAUmG,QAAQoC,OAAO,GAAGhX,sBAAuB4U,QAAQ7Q,OAAO;QACxE,MAAM6J,WACJjK,WAAWO,OAAOC,OAAOiD,QAAQgD,KAAK,EAAEoB,WACxC;YAAE3K,QAAQ;YAAUkF,cAAcgG;YAAS/H,OAAOyK;YAAST;QAAO,GAClEpK;QAEF,MAAMsL,SAAS;YACb1D;YACAxH,OAAO4Q,QAAQoC,OAAO,GAAG,wBAAwB,CAAC,cAAc,EAAEvI,SAAS;WACvEmG,QAAQ8B,aAAa,GAAG;YAAEvH,MAAM;QAAM,IAAI,CAAC;QAEjD,8EAA8E;QAC9E,OAAO;YACLyF,SAAS;YACTzS,KAAKyS,QAAQoC,OAAO,GAChB,MAAM/H,UAAUtL,WAAWO,OAAOC,OAAOsK,SAASS,QAAQtL,OAC1D,MAAM8M,eAAe/M,WAAWO,OAAOC,OAAOsK,SAASS,QAAQ;gBAC7DR,QAAQ;gBAAgBsB,aAAajE;gBAASmE,IAAI,GAAE+D,qBAAAA,YAAY/D,IAAI,YAAhB+D,qBAAoB;YAC1E,GAAGrQ;QACT;IACF;IAEA,wEAAwE;IACxE,0EAA0E;IAC1E,0EAA0E;IAC1E,2EAA2E;IAC3E,oCAAoC;IACpC,IAAIgR,EAAAA,kBAAAA,QAAQzL,MAAM,qBAAdyL,gBAAgBlG,MAAM,MAAK,cAAcjQ,kBAAkB0D,QAAQuG,KAAK9F,IAAI,KAAK5G,kBAAkB;YAgBrC4Y,0BAAkCA,yBACnCX;QAhB/D,MAAMrG,WACJjK,WAAWO,OAAOC,OAAOiD,QAAQgD,KAAK,EAAEoB,WACxC;YACE3K,QAAQ;YACRkF,cAAcgG;YACd/H,OAAOtF;YACP2H,SAASuO,QAAQvO,OAAO;WACpBsQ,eAAe,CAAC,IAAI;YAAE3I;QAAO,IAEnCpK;QAEF,OAAO;YACLgR,SAAS;YACTzS,KAAK,MAAMuO,eAAe/M,WAAWO,OAAOC,OAAOzF,kCAAkC;gBACnF8M;gBACAxH,OAAO,CAAC,mCAAmC,EAAEiT,KAAKC,SAAS,EAACtC,2BAAAA,QAAQzL,MAAM,CAACgO,QAAQ,YAAvBvC,2BAA2B,EAAE,EAAE,CAAC,GAAEA,0BAAAA,QAAQzL,MAAM,CAACsF,OAAO,YAAtBmG,0BAA0B,IAAI;YAC9H,GAAG;gBAAElG,QAAQ;gBAAoBsB,aAAajE;gBAASmE,IAAI,GAAE+D,qBAAAA,YAAY/D,IAAI,YAAhB+D,qBAAoB;YAAK,GAAGrQ;QAC3F;IACF;IAEA,wEAAwE;IACxE,yEAAyE;IACzE,yEAAyE;IACzE,2BAA2B;IAC3B,EAAE;IACF,yEAAyE;IACzE,sEAAsE;IACtE,6EAA6E;IAC7E,yEAAyE;IACzE,sEAAsE;IACtE,4DAA4D;IAC5D,0EAA0E;IAC1E,2EAA2E;IAC3E,IAAImR,aAA0CpE,mBAAmBxO,IAAIX,IAAI,IAAIoT,QAAQ1G,IAAI,GAAG9B;IAC5F,2EAA2E;IAC3E,mDAAmD;IACnD,IAAI2I,CAAAA,8BAAAA,WAAYlU,MAAM,MAAK,cAAYkU,sBAAAA,WAAWhR,OAAO,qBAAlBgR,oBAAoB5F,IAAI,KAAI,CAACkH,mBAAmB;YAExEzB,OAAAA,8BAMLX;QAPR,MAAMjI,OAAO+I,WAAW/I,IAAI;QAC5B,MAAMrB,MAAM,EAACiK,SAAAA,iBAAAA,QAAQ/K,KAAK,YAAb+K,iBAAiBzS,IAAI0H,KAAK,YAA1B+K,QAA8B,EAAE,EAAEpD,IAAI,CAAC,CAAC4F,MAAQA,IAAIpL,IAAI,KAAKA;QAC1E,MAAMqE,SAAS,MAAMa,mBAAmBvN,WAAWO,OAAO/B,KAAK;YAC7D6J;YACAqF,OAAO,WAAE1G,uBAAAA,IAAK0G,OAAO,oBAAI;YACzBtF,SAAS,UAACpB,uBAAAA,IAAKsB,cAAc,oBAAI,KAAKF;YACtC2C,QAAQqG,WAAWhR,OAAO,CAAC2K,MAAM;YACjCwB,IAAI,GAAE+D,qBAAAA,YAAY/D,IAAI,YAAhB+D,qBAAoB;YAC1BrQ;QACF;QACA,IAAIyM,QAAQ0E,aAAa,aAAKA;YAAY7I,iBAAiBmE,OAAOtE,OAAO;YAAEI,WAAWkE,OAAOiB,GAAG;;IAClG;IAEA,MAAMnI,SAASyL,QAAQzL,MAAM;IAC7B,MAAMkO,aACJ,CAAClO,UACDoB,QAAQqK,QAAQ0C,QAAQ,KACxB,EAAC5O,eAAAA,KAAKqF,MAAM,YAAXrF,eAAe,KAAK,IAAIzF,mBAAmBd,IAAIX,IAAI;IACtD,IAAI,CAAC2H,UAAUyL,QAAQ0C,QAAQ,IAAI,CAACD,YAAY;QAC9CpT,QAAQgE,IAAI,CAAC,oCAAoC;YAAE/D;YAAOC;YAAOqH;QAAU;IAC7E;IACA,MAAMqC,WAAW,MAAMD,WACrBjK,WAAWO,OAAOC,OAAOiD,QAAQgD,KAAK,EAAEoB,WACxC;QACE3K,QAAQwW,cAAelO,UAAUA,OAAOuF,MAAM,KAAK,SAAU,YAAY;OACrE2I,aAAa;QAAEvJ,WAAW;IAAK,IAAI,CAAC;QACxC/H,cAAcgG;OAEV4K,eAAe,CAAC,IAAI;QAAE3I;IAAO;QACjC3H,SAASuO,QAAQvO,OAAO;OACpBuO,QAAQ9L,IAAI,GAAG;QAAEA,MAAM8L,QAAQ9L,IAAI;IAAC,IAAI,CAAC,GACzCK,SAAS;QAAEA;IAAO,IAAI,CAAC,GACvBA,UAAUyL,QAAQrP,KAAK,GAAG;QAAEA,OAAOqP,QAAQrP,KAAK;IAAC,IAAI,CAAC,GACtDqP,QAAQlL,WAAW,GAAG;QAAEA,aAAakL,QAAQlL,WAAW;IAAC,IAAI,CAAC,GAC9DvH,IAAIX,IAAI,KAAK,YAAYoT,QAAQ1K,UAAU,GAAG;QAAEA,YAAY0K,QAAQ1K,UAAU;IAAC,IAAI,CAAC,GACpF/H,IAAIX,IAAI,KAAK,UAAUoT,QAAQpG,UAAU,GAAG;QAAEA,YAAYoG,QAAQpG,UAAU;IAAC,IAAI,CAAC,GAClFmC,mBAAmBxO,IAAIX,IAAI,KAAKoT,QAAQ/K,KAAK,GAAG;QAAEA,OAAO+K,QAAQ/K,KAAK;IAAC,IAAI,CAAC,GAC5EkL,aAAa;QAAE7G,MAAM6G;IAAW,IAAI,CAAC,GAGrC5L,SAAS;QAAEyF,OAAO;IAAK,IAAI,CAAC,IAElChL;IAEF,MAAM2L,SAAyBnI,iBAAAA,QAAQmI,KAAK,YAAbnI,iBAAiB;QAAEN,KAAK3E,IAAIqE,SAAS;IAAC;IACrE,KAAK,MAAM+G,UAAUqH,QAAQvO,OAAO,CAAE;YASrBkH,mBAeAA;QAvBf,MAAMgG,gBAAgB5P,WAAW;YAC/BgQ,QAAQ;YACRF,UAAUtR,IAAIqE,SAAS;YACvBtC;YACAC;YACA0P,OAAO;gBACL/D,UAAUvC,OAAOuC,QAAQ;gBACzBzK,IAAIkI,OAAOlI,EAAE;gBACbkS,SAAS,GAAEhK,oBAAAA,OAAOgK,SAAS,YAAhBhK,oBAAoB;gBAC/BrH,QAAQqH,OAAOrH,MAAM;gBACrBsR,OAAOjK,OAAOiK,KAAK;gBACnBzL;YACF;QACF;QACA,uEAAuE;QACvE,+CAA+C;QAC/C,MAAMjP,eAAeoH,OAAOqL,OAAO;YACjCpL;YACA+B,QAAQqH,OAAOrH,MAAM;YACrB4J,UAAU;gBACR2H,MAAM/a,mBAAmB6Q,OAAOuC,QAAQ;gBACxCzK,IAAIkI,OAAOlI,EAAE;gBACbzC,MAAM2K,OAAOiK,KAAK;gBAClBD,SAAS,GAAEhK,qBAAAA,OAAOgK,SAAS,YAAhBhK,qBAAoB;YACjC;QACF;IACF;IACA,6EAA6E;IAC7E,IAAIM,SAAS1L,GAAG,CAACtB,MAAM,KAAK,YAAY,OAAO;QAAE+T,SAAS;QAAYzS,KAAK0L,SAAS1L,GAAG;IAAC;IACxF,IAAIgH,CAAAA,0BAAAA,OAAQuF,MAAM,MAAK,UAAUb,SAAS1L,GAAG,CAACtB,MAAM,KAAK,kBAAkBpC,kBAAkB0D,MAAM;QACjG,mEAAmE;QACnE,mEAAmE;QACnE,MAAMwQ,UAAU,MAAMD,YAAY/O,WAAWO,OAAOC,OAAO;YAAE2C,KAAK3E,IAAIqE,SAAS;QAAC,GAAG5C;QACnF,MAAM2P,gBAAgB5P,WAAW;YAC/BgQ,QAAQ;YACRF,UAAUtR,IAAIqE,SAAS;YACvBtC;YACAC;YACA0P,OAAO;gBAAEhT,QAAQ8R,QAAQxQ,GAAG,CAACtB,MAAM;gBAAE6W,eAAe;gBAAM9N,aAAa;YAAK;QAC9E;QACA,OAAO;YAAEgL,SAAS;YAAQzS,KAAKwQ,QAAQxQ,GAAG;QAAC;IAC7C;IACA,0EAA0E;IAC1E,sEAAsE;IACtE,IAAIgH,UAAUyL,QAAQrP,KAAK,IAAIsI,SAAS1L,GAAG,CAACtB,MAAM,KAAK,eAAe;QACpE,MAAMhE,mBAAmBqH,OAAO;YAAE4C,KAAK;QAAK,GAAG;YAAE3C;YAAO3C,MAAMW,IAAIX,IAAI;YAAEkN,QAAQ;QAAQ;QACxF,IAAIb,SAASc,MAAM,EAAE,MAAM9K,wBAAwBgK,SAAS1L,GAAG,EAAE;QACjE,OAAO;YAAEyS,SAAS;YAAezS,KAAK0L,SAAS1L,GAAG;QAAC;IACrD;IACA,IAAIgH,UAAU0E,SAAS1L,GAAG,CAACtB,MAAM,KAAK,gBAAgB;YAgBJoT;QAfhD,wEAAwE;QACxE,yBAAyB;QACzB,MAAMpX,mBAAmBqH,OAAO;YAAE4C,KAAK;QAAK,GAAG;YAC7C3C;YACA3C,MAAMW,IAAIX,IAAI;YACdkN,QAAQvF,OAAOuF,MAAM;QACvB;QACA,qEAAqE;QACrE,yEAAyE;QACzE,yEAAyE;QACzE,uEAAuE;QACvE,eAAe;QACf,MAAMS,OAAOyF,QAAQ+C,UAAU,GAAG,iBAAiBxO,OAAOuF,MAAM,KAAK,aAAa,qBAAqB;QACvG,MAAMC,SAASQ,OACX,MAAMQ,iBAAiBhM,WAAWO,OAAO2J,SAAS1L,GAAG,EAAE;YACrDuM,QAAQS;YAAMa,aAAajE;YAASmE,IAAI,GAAE+D,qBAAAA,YAAY/D,IAAI,YAAhB+D,qBAAoB;YAAMrQ;QACtE,KACAiK,SAAS1L,GAAG;QAChB,IAAI0L,SAASc,MAAM,EAAE,MAAM9K,wBAAwB8K,QAAQ;QAC3D,OAAO;YAAEiG,SAAS;YAAgBzS,KAAKwM;QAAO;IAChD;IACA,IAAId,SAASI,SAAS,GAAG,KAAKrN,gBAAgBiN,SAAS1L,GAAG,CAACtB,MAAM,GAAG;QAClE,OAAO;YAAE+T,SAAS;YAAQzS,KAAK0L,SAAS1L,GAAG;QAAC;IAC9C;IACA,0EAA0E;IAC1E,IAAIwO,mBAAmBxO,IAAIX,IAAI,KAAKkH,KAAK9F,IAAI,KAAK5G,sBAAoB6R,sBAAAA,SAAS1L,GAAG,CAAC0H,KAAK,qBAAlBgE,oBAAoBtH,MAAM,GAAE;YAEjEsH,sBAGvBoG;QAJR,0EAA0E;QAC1E,MAAM2D,OAAO1Z,mBAAkB2P,uBAAAA,SAAS1L,GAAG,CAAC0H,KAAK,YAAlBgE,uBAAsB,EAAE,EAAE+J,IAAI;QAC7D,MAAMzJ,UAAU,MAAMyC,iBAAiBjN,WAAWO,OAAOC,OAAO;YAC9DP;YACAsM,IAAI,GAAE+D,qBAAAA,YAAY/D,IAAI,YAAhB+D,qBAAoB;WACtB2D,OAAO;YAAE/G,SAAS;gBAAEpC,SAAS1R;gBAA0B2R,QAAQ;YAAmB;QAAE,IAAI,CAAC;QAE/F,OAAO;YAAEkG,SAASzG,QAAQtN,MAAM,KAAK,WAAW,WAAW;YAAQsB,KAAKgM;QAAQ;IAClF;IACA,OAAO;QACLyG,SAAS;QACTzS,KAAK,MAAM6M,cAAcrL,WAAWO,OAAOC,OAAOP;IACpD;AACF;AA0CA;;;;;;;;;;;;;CAaC,GACD,OAAO,eAAeiU,cACpBlU,SAAoB,EACpB0G,KAAa,EACbyN,UAAUlY,qBAAqB,EAC/BmY,YAAYlY,uBAAuB;IAEnC,MAAMmY,QAAQrU,UAAUsU,eAAe,CAAC1Y;IACxC,MAAM,CAAC2Y,QAAQvJ,OAAO,GAAG,MAAMwJ,QAAQC,GAAG,CAAC;QACzCJ,MACGxQ,KAAK,CAAC,UAAU,MAAM;YAAC;YAAU;SAAU,EAC3CK,OAAO,CAAC,aAAa,OACrBR,KAAK,CAACyQ,SACN7V,GAAG;QACN+V,MACGxQ,KAAK,CAAC,UAAU,MAAM,eACtBA,KAAK,CAAC,aAAa,MAAM,IAAIrH,KAAKkK,QAAQvK,8BAC1C+H,OAAO,CAAC,aAAa,OACrBR,KAAK,CAAC0Q,WACN9V,GAAG;KACP;IACD,OAAO;WAAIiW,OAAOpQ,IAAI;WAAK6G,OAAO7G,IAAI;KAAC,CACpClC,GAAG,CAAC,CAACZ;YAAyBA;eAAhB;YAAEd,OAAOkL,QAAOpK,WAAAA,IAAI/C,GAAG,CAAC,oBAAR+C,WAAoB;YAAKb,OAAOa,IAAIK,EAAE;QAAC;OACrE0C,MAAM,CAAC,CAACsQ,MAAQA,IAAInU,KAAK,KAAK;AACnC;AAEA;;;;;;;;;;;;;;CAcC,GACD,OAAO,eAAeoU,YACpBlR,OAA2B;QAEfA,cACKA,mBAGfA,kBAGAA;IAPF,MAAMxD,OAAMwD,eAAAA,QAAQxD,GAAG,YAAXwD,eAAejH,KAAKyD,GAAG;IACnC,MAAM2U,YAAWnR,oBAAAA,QAAQmR,QAAQ,YAAhBnR,oBAAoBjK;IACrC,MAAM0I,YAAYjC;IAClB,MAAM4U,WACJpR,mBAAAA,QAAQoR,OAAO,YAAfpR,mBACC,IAAMyQ,cAAczQ,QAAQzD,SAAS,EAAEC,OAAOwD,QAAQ0Q,OAAO;IAChE,MAAMW,WACJrR,mBAAAA,QAAQqR,OAAO,YAAfrR,mBACC,CAAClD,OAAeC,OAAeuU,cAC9B5D,aAAa1N,QAAQzD,SAAS,EAAEO,OAAOC,OAAOuU;IAElD,MAAML,MAAM,MAAMG;IAClB,MAAM/I,SAA4B;QAChC4I,KAAKA,IAAI9R,MAAM;QACfoS,KAAK;QACLC,SAAS;QACTC,QAAQ;QACR5K,WAAW;QACX6K,iBAAiB;IACnB;IACA,0EAA0E;IAC1E,MAAMC,QAAoB,EAAE;IAC5B,MAAM1H,UAAU,OAAO2H,WAAqBC,MAAcC;QACxD,MAAM,EAAEhV,KAAK,EAAEC,KAAK,EAAE,GAAG6U;QACzB,IAAI;gBAcuCtM,uBAAAA;YAbzC,MAAMA,UAAU,MAAMxF,SAASE,QAAQzD,SAAS,EAAEO,OAAOC;YACzD,wEAAwE;YACxE,kEAAkE;YAClE,wEAAwE;YACxE,4BAA4B;YAC5B,MAAMvC,YAAY8K,UAAUlK,uBAAuBkK,WAAW;YAC9D,IAAI9K,YAAYqX,MAAM;gBACpB,IAAIC,OAAOzJ,OAAOxB,SAAS,IAAI;gBAC/B;YACF;YACA,sEAAsE;YACtE,uEAAuE;YACvE,gCAAgC;YAChC,IAAIvB,CAAAA,2BAAAA,QAAS7L,MAAM,MAAK,mBAAiB6L,iBAAAA,QAAQnH,KAAK,sBAAbmH,wBAAAA,eAAetD,MAAM,qBAArBsD,sBAAuBnG,MAAM,GAAE;gBACtE,MAAMsI,WAAWzH,QAAQzD,SAAS,EAAEO,OAAOC,OAAO,CAAChC;wBACjBA,mBAAAA;2BAAhCA,IAAItB,MAAM,KAAK,mBAAiBsB,aAAAA,IAAIoD,KAAK,sBAATpD,oBAAAA,WAAWiH,MAAM,qBAAjBjH,kBAAmBoE,MAAM,IAAG;wBAAEG,WAAW,IAAIvG,KAAKyD;oBAAO,IAAI;;gBAE/F,IAAIsV,OAAOzJ,OAAOmJ,OAAO,IAAI;gBAC7B;YACF;YACA,IAAIlM,CAAAA,2BAAAA,QAAS7L,MAAM,MAAK,eAAe;gBACrC,MAAMgO,WAAWzH,QAAQzD,SAAS,EAAEO,OAAOC,OAAO,CAAChC,MACjDA,IAAItB,MAAM,KAAK,gBAAgB;wBAAEA,QAAQ;wBAAU6F,WAAW,IAAIvG,KAAKyD;oBAAO,IAAI;YAEtF;YACA,MAAM+I,MAAM,MAAM8L,QAAQvU,OAAOC,OAAO;gBACtCiG,OAAOhD,QAAQgD,KAAK;gBACpBxG,KAAK,IAAIzD,KAAKyD;gBACdmS,QAAQoD,YAAYC,OAAO,CAACH;YAC9B;YACA,IAAItM,IAAIiI,OAAO,KAAK,iBAAiB;gBACnC,IAAIsE,OAAOzJ,OAAOmJ,OAAO,IAAI;gBAC7B;YACF;YACA,IAAIjM,IAAIiI,OAAO,KAAK,UAAU;gBAC5B,IAAIsE,OAAOzJ,OAAOoJ,MAAM,IAAI;gBAC5B;YACF;YACApJ,OAAOkJ,GAAG,IAAI;YACd,sEAAsE;YACtE,2DAA2D;YAC3D,IACEhM,IAAIiI,OAAO,KAAK,UAChBjI,IAAIxK,GAAG,CAACtB,MAAM,KAAK,YACnB2B,uBAAuBmK,IAAIxK,GAAG,IAAI,GAClC;gBACA4W,MAAMM,IAAI,CAACL;YACb;QACF,EAAE,OAAOhV,OAAO;YACd,iEAAiE;YACjEC,QAAQD,KAAK,CAAC,4BAA4B;gBAAEE;gBAAOC;gBAAOH;YAAM;YAChE,IAAIkV,OAAOzJ,OAAOmJ,OAAO,IAAI;QAC/B;IACF;IAEA,IAAK,IAAInW,QAAQ,GAAGA,QAAQ4V,IAAI9R,MAAM,EAAE9D,SAAS,EAAG;QAClD,MAAM6W,UAAU1V,QAAQiC;QACxB,IAAIpD,QAAQ,KAAK6W,WAAWf,UAAU;YACpC9I,OAAOqJ,eAAe,GAAG;YACzBrJ,OAAOxB,SAAS,IAAIoK,IAAI9R,MAAM,GAAG9D;YACjC,OAAOgN;QACT;QACA,MAAM4B,QAAQgH,GAAG,CAAC5V,MAAM,EAAEH,KAAKC,GAAG,CAAC,MAAOgW,WAAWe,UAAU;IACjE;IACA,0EAA0E;IAC1E,2EAA2E;IAC3E,yEAAyE;IACzE,2EAA2E;IAC3E,IAAK,IAAIC,QAAQ,GAAGR,MAAMxS,MAAM,IAAIgT,QAAQ3Z,uBAAuB2Z,SAAS,EAAG;QAC7E,MAAMD,UAAU1V,QAAQiC;QACxB,IAAIyT,WAAWf,UAAU;YACvB9I,OAAOqJ,eAAe,GAAG;YACzB;QACF;QACA,MAAMzH,QAAQ0H,MAAMS,KAAK,IAAgBjB,WAAWe,SAAS;IAC/D;IACA,OAAO7J;AACT"}
|