@semiont/jobs 0.5.38 → 0.5.39
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/dist/index.d.ts +22 -2
- package/dist/index.js +13 -4
- package/dist/index.js.map +1 -1
- package/dist/worker-main.js +10 -3
- package/dist/worker-main.js.map +1 -1
- package/package.json +8 -8
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/job-queue-interface.ts","../src/will-retry.ts","../src/checkpoint-merge.ts","../src/fs-job-queue.ts","../src/jetstream-job-queue.ts","../src/types.ts","../src/workers/inference-call.ts","../src/failure-class.ts","../src/workers/detection/chunk-size-controller.ts","../src/workers/detection/detection-chunking.ts","../src/workers/detection/motivation-prompts.ts","../src/workers/detection/anchor-audit.ts","../src/workers/detection/motivation-parsers.ts","../src/workers/annotation-detection.ts","../src/workers/detection/entity-extractor.ts","../src/workers/generation/resource-generation.ts","../src/workers/generation/typst-compiler.ts","../src/workers/generation/citation-resolver.ts","../src/workers/detection/bounded-concurrency.ts","../src/processors.ts","../src/worker-runtime.ts"],"names":["fs","toJobId","PROGRESS_WRITE_MIN_INTERVAL_MS","StructuredReadError","isObject","isString","getLocaleEnglishName","estimateTokens","response","join","reconcileSelector","generated","citations"],"mappings":"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","file":"index.js","sourcesContent":["import type { AnyJob } from './types';\nimport type { EventMap, JobId, UnitCursor } from '@semiont/core';\n\nexport interface JobQueue {\n initialize(): Promise<void>;\n destroy(): void;\n createJob(job: AnyJob): Promise<void>;\n getJob(jobId: JobId): Promise<AnyJob | null>;\n /**\n * Atomically claim the NEXT pending job matching one of `types` — the ONE\n * transition that makes this a queue rather than a state store\n * (JOB-QUEUE-DRIVER P0; reshaped claim-by-TYPE in P2, while every worker\n * is still ours). pending → running, `startedAt` stamped, progress empty.\n * An announcement is a WAKE-UP, not a reservation: the claimed job may\n * differ from any announced one, no ordering among matching pending jobs\n * is promised, and an empty `types` accepts any type. Simultaneous claims\n * admit one winner PER pending job; a claim that finds nothing is\n * DECLINED (`none-available`), never an error.\n */\n claimNextJob(types: string[]): Promise<{ job: AnyJob } | { declined: 'none-available' }>;\n /** Move a running job to `complete`. Returns false if the job isn't running. */\n completeJob(jobId: JobId, result: Record<string, unknown>): Promise<boolean>;\n /**\n * Move a running job back to `pending` (retry, re-announced) while\n * `retryCount < maxRetries`, else to `failed`. Returns what happened,\n * or null if the job isn't running. `completedUnits` — the units the\n * failing attempt fully emitted — are unioned into the record's\n * checkpoint (ABANDONED-INFERENCE P2) so a retry skips them. A\n * `failureClass` of 'deterministic' goes straight to `failed` with any\n * budget remaining — a second identical attempt cannot succeed (P3).\n */\n failJob(jobId: JobId, error: string, completedUnits?: string[], failureClass?: 'transient' | 'deterministic', unitCursors?: Record<string, UnitCursor>): Promise<'retried' | 'failed' | null>;\n /**\n * Persist a running job's completed-unit checkpoint AT unit completion —\n * not only when a job fails (JOB-RESTART-SAFETY P2). `failJob` carries the\n * checkpoint on a clean failure, but a worker that DIES (crash/OOM/kill)\n * never emits `job:fail`, so its finished units would be lost and the\n * janitor's recovery would redo them. This writes them into the running\n * file's metadata as each unit lands, unioned with any existing\n * checkpoint, so recovery resumes rather than restarts. Unthrottled (a\n * unit completion must never be dropped); a no-op for non-running jobs.\n *\n * `unitCursors` is the finer grain `completedUnits` cannot express\n * (CHUNK-GRAIN-RESUME P2): how far an UNFINISHED unit got, so a job that\n * dies mid-unit resumes there instead of at the top. It is written per\n * committed chunk, not per unit.\n *\n * **The two merge differently, and the difference is the contract.**\n * `completedUnits` is a set, so a union converges under concurrent snapshots\n * — a set only grows. A cursor converges only if the merge is **monotone per\n * unit**: a stale snapshot must never move one backward. `next` and `size`\n * move TOGETHER as one observation; taking `size` from one snapshot and\n * `next` from another would describe a chunk that never existed. A unit that\n * reaches `completedUnits` drops its cursor, so \"in progress with a cursor\"\n * and \"complete\" stay structurally exclusive rather than by convention.\n *\n * The rule belongs here rather than to any one driver because it is what\n * makes a cursor safe to merge at all.\n */\n checkpointUnits(jobId: JobId, completedUnits: string[], unitCursors?: Record<string, UnitCursor>): Promise<void>;\n /** Write progress into a running job's file (throttled, best-effort). */\n recordProgress(jobId: JobId, progress: Record<string, unknown>): Promise<void>;\n /**\n * Cancel all pending jobs in a category — 'generation' is the\n * `generation` type; 'annotation' is every `*-annotation` type.\n * Running jobs are left to finish. Returns the number cancelled.\n */\n cancelPendingJobs(category: 'annotation' | 'generation'): Promise<number>;\n cancelJob(jobId: JobId): Promise<boolean>;\n getStats(): Promise<{ pending: number; running: number; complete: number; failed: number; cancelled: number }>;\n}\n\n/**\n * Every bus channel a queue DRIVER emits, maintained beside the interface\n * both drivers implement and censused against their sources\n * (queue-emits-census.test.ts). The gateway's signal bridge forwards these\n * from its local bus onto the plane — a queue announcement that stays on\n * the raw bus reaches no worker under a remote driver (the job:queued\n * starvation, 2026-09-15).\n */\nexport const JOB_QUEUE_EMITS = ['job:queued'] as const satisfies readonly (keyof EventMap)[];\n","import type { JobMetadata } from './types';\n\n/**\n * Will this failure be re-queued for another attempt?\n *\n * ONE decision site, deliberately (JOB-RESTART-SAFETY P5). Two places need\n * the answer and they must never disagree:\n *\n * - `FsJobQueue.failJob` acts on it — re-queue or write the terminal record;\n * - the worker reports it on `job:fail` as `willRetry`, so a client's\n * job-watch stream knows whether the failure it just saw is the end.\n *\n * A second copy of this predicate would be the classic N-places-decide-one-\n * thing bug: the client would end a stream the queue then revived, or hold a\n * stream open on a job nobody will run again. The worker reads the budget off\n * the record it claimed, and `retryCount` only changes inside `failJob`, so\n * the two evaluations see the same numbers.\n *\n * A known-deterministic failure skips the budget outright — the same request\n * cannot succeed on a second attempt, so a retry is guaranteed waste at full\n * price. Unclassified failures are treated as transient.\n */\nexport function willRetryAfter(\n metadata: Pick<JobMetadata, 'retryCount' | 'maxRetries'>,\n failureClass?: 'transient' | 'deterministic',\n): boolean {\n return failureClass !== 'deterministic' && metadata.retryCount < metadata.maxRetries;\n}\n","import type { UnitCursor } from '@semiont/core';\n\n/**\n * Merge per-unit cursors monotonically (CHUNK-GRAIN-RESUME P2).\n *\n * `completedUnits` is a set, so unioning it converges under concurrent\n * snapshots for free — a set only grows. A cursor has no such property: two\n * checkpoints can be in flight at once and the OLDER one can land last, so a\n * last-writer-wins would drag the resume position backward and the retry would\n * re-pay for chunks it already committed. Keeping the furthest `next` per unit\n * is what makes the merge order-independent.\n *\n * `next` and `size` move together because they are ONE observation of one\n * chunk. Taking the furthest `next` from one snapshot and the `size` from\n * another would describe a chunk that was never cut.\n *\n * A unit in `completed` has no cursor at all: \"in progress, here\" and\n * \"finished\" are then structurally exclusive rather than a rule each reader has\n * to remember, and a stale snapshot cannot resurrect a finished unit's cursor.\n *\n * ONE module, shared by every driver (JOB-QUEUE-DRIVER P0): the interface\n * states this rule, and two copies of it would be two chances to disagree\n * about the one property that makes a cursor safe to merge at all.\n */\nexport function mergeUnitCursors(\n existing: Record<string, UnitCursor> | undefined,\n incoming: Record<string, UnitCursor> | undefined,\n completed: string[],\n): Record<string, UnitCursor> {\n const done = new Set(completed);\n const merged: Record<string, UnitCursor> = {};\n for (const [unit, cursor] of Object.entries({ ...existing })) {\n if (!done.has(unit)) merged[unit] = cursor;\n }\n for (const [unit, cursor] of Object.entries({ ...incoming })) {\n if (done.has(unit)) continue;\n const held = merged[unit];\n if (!held || cursor.next > held.next) merged[unit] = cursor;\n }\n return merged;\n}\n","/**\n * Job Queue Manager\n *\n * Filesystem-based job queue with atomic operations.\n * Jobs are stored in directories by status; status transitions are\n * atomic delete + write across directories.\n */\n\nimport { promises as fs } from 'fs';\nimport * as path from 'path';\nimport type { AnyJob, JobStatus, JobQueryFilters, CancelledJob, CompleteJob, FailedJob, PendingJob, RunningJob } from './types';\nimport type { SemiontState } from '@semiont/core/node';\nimport { jobId as toJobId, type JobId, type Logger, type EventBus, type UnitCursor } from '@semiont/core';\nimport type { JobQueue } from './job-queue-interface';\nimport { willRetryAfter } from './will-retry';\nimport { mergeUnitCursors } from './checkpoint-merge';\n\n/**\n * How often pending jobs are re-announced on `job:queued` and stale\n * running jobs are checked for recovery.\n *\n * The announcement in `createJob` only reaches workers that are\n * connected and idle at that moment. Re-announcing every pending job\n * on an interval restores catch-up for everything that announcement\n * misses: all eligible workers busy, a worker offline or mid-SSE-\n * reconnect, or a gateway restart with a pending backlog. Claim\n * arbitration (the `job:claim` handler refuses non-pending jobs)\n * makes duplicate announcements harmless.\n */\nconst REANNOUNCE_INTERVAL_MS = 30_000;\n\n/**\n * A running job whose file hasn't been touched for this long is\n * presumed orphaned by a dead worker and fed through the retry-or-fail\n * path. Progress writes (`recordProgress`) refresh the file's mtime,\n * so this is a heartbeat timeout, not a job-duration limit — but a\n * worker that emits no progress for the whole window will be falsely\n * recovered, so it stays deliberately generous.\n */\nconst STALE_RUNNING_MS = 30 * 60_000;\n\n/** Minimum spacing between progress writes per job — workers can be chatty. */\nconst PROGRESS_WRITE_MIN_INTERVAL_MS = 5_000;\n\n/** Terminal jobs (complete/failed/cancelled) are pruned after this long. */\nconst RETENTION_HOURS = 24;\n\n/** How often the retention pruning runs. */\nconst CLEANUP_INTERVAL_MS = 3_600_000;\n\nexport class FsJobQueue implements JobQueue {\n private jobsDir: string;\n private logger: Logger;\n private reannounceTimer: ReturnType<typeof setInterval> | null = null;\n private cleanupTimer: ReturnType<typeof setInterval> | null = null;\n /** Per-job timestamp of the last progress write, for throttling. */\n private lastProgressWrite = new Map<string, number>();\n\n constructor(\n /** `SemiontState` and not `SemiontProject`: the queue reads ONE path,\n * and the gateway that owns it mounts no KB tree (SINGLE-KB-MOUNT P5). */\n state: SemiontState,\n logger: Logger,\n private eventBus?: EventBus\n ) {\n this.jobsDir = state.jobsDir;\n this.logger = logger;\n }\n\n /**\n * Initialize job queue directories, announce any pending backlog,\n * and start the re-announce interval. Idempotent.\n */\n async initialize(): Promise<void> {\n const statuses: JobStatus[] = ['pending', 'running', 'complete', 'failed', 'cancelled'];\n\n for (const status of statuses) {\n const dir = path.join(this.jobsDir, status);\n await fs.mkdir(dir, { recursive: true });\n }\n\n if (this.eventBus && !this.reannounceTimer) {\n // Jobs left pending across a restart are announced right away…\n await this.announcePendingJobs();\n // …and anything that misses an announcement is retried here, along\n // with recovery of jobs orphaned by a dead worker.\n this.reannounceTimer = setInterval(() => {\n this.announcePendingJobs().catch((error) => {\n this.logger.warn('Pending-job re-announce failed', {\n error: error instanceof Error ? error.message : String(error),\n });\n });\n this.recoverStaleRunningJobs().catch((error) => {\n this.logger.warn('Stale-running recovery failed', {\n error: error instanceof Error ? error.message : String(error),\n });\n });\n }, REANNOUNCE_INTERVAL_MS);\n // Don't let the interval keep the process alive on shutdown.\n this.reannounceTimer.unref?.();\n }\n\n if (!this.cleanupTimer) {\n this.cleanupTimer = setInterval(() => {\n this.cleanupOldJobs(RETENTION_HOURS).catch((error) => {\n this.logger.warn('Job retention cleanup failed', {\n error: error instanceof Error ? error.message : String(error),\n });\n });\n }, CLEANUP_INTERVAL_MS);\n this.cleanupTimer.unref?.();\n }\n\n this.logger.info('Job queue initialized');\n }\n\n /**\n * Stop the re-announce and retention intervals\n */\n destroy(): void {\n if (this.reannounceTimer) {\n clearInterval(this.reannounceTimer);\n this.reannounceTimer = null;\n }\n if (this.cleanupTimer) {\n clearInterval(this.cleanupTimer);\n this.cleanupTimer = null;\n }\n }\n\n /**\n * Emit `job:queued` for a pending job, if an EventBus is wired and\n * the job carries a `resourceId` (every current job type does).\n */\n private announce(job: AnyJob): void {\n if (this.eventBus && 'params' in job && 'resourceId' in job.params) {\n this.eventBus.get('job:queued').next({\n jobId: job.metadata.id,\n jobType: job.metadata.type,\n resourceId: job.params.resourceId,\n userId: job.metadata.userId,\n });\n }\n }\n\n /**\n * Announce every job currently in `pending/`. Files that vanish or\n * fail to parse mid-scan (claimed, cancelled, partially written)\n * are skipped — they're either gone for a good reason or picked up\n * on the next tick.\n */\n private async announcePendingJobs(): Promise<void> {\n const pendingDir = path.join(this.jobsDir, 'pending');\n let files: string[];\n try {\n files = await fs.readdir(pendingDir);\n } catch {\n return;\n }\n files.sort();\n for (const file of files) {\n try {\n const content = await fs.readFile(path.join(pendingDir, file), 'utf-8');\n this.announce(JSON.parse(content) as AnyJob);\n } catch {\n // Skip unreadable files\n }\n }\n }\n\n /**\n * Create a new job\n */\n async createJob(job: AnyJob): Promise<void> {\n const jobPath = this.getJobPath(job.metadata.id, job.status);\n await fs.writeFile(jobPath, JSON.stringify(job, null, 2), 'utf-8');\n this.logger.info('Job created', { jobId: job.metadata.id, status: job.status });\n\n if (job.status === 'pending') {\n this.announce(job);\n }\n }\n\n /**\n * Get a job by ID (searches all status directories)\n */\n async getJob(jobId: JobId): Promise<AnyJob | null> {\n const statuses: JobStatus[] = ['pending', 'running', 'complete', 'failed', 'cancelled'];\n\n for (const status of statuses) {\n const jobPath = this.getJobPath(jobId, status);\n try {\n const content = await fs.readFile(jobPath, 'utf-8');\n return JSON.parse(content) as AnyJob;\n } catch (error) {\n // File doesn't exist in this status directory, try next\n continue;\n }\n }\n\n return null;\n }\n\n /**\n * Serializes claims. This driver's atomicity is single-process by design,\n * but `claimJob`'s contract must hold even against interleaved awaits in\n * that one process — a read-check-write with await points in it can admit\n * two winners without this chain (the TOCTOU the operation exists to kill).\n */\n private claimChain: Promise<unknown> = Promise.resolve();\n\n async claimNextJob(types: string[]): Promise<{ job: AnyJob } | { declined: 'none-available' }> {\n const claim = this.claimChain.then(() => this.doClaimNext(types));\n // A failed claim must not wedge every later one.\n this.claimChain = claim.catch(() => undefined);\n return claim;\n }\n\n private async doClaimNext(types: string[]): Promise<{ job: AnyJob } | { declined: 'none-available' }> {\n const pending = await this.listJobs({ status: 'pending', limit: Number.MAX_SAFE_INTEGER });\n const match = pending.find((j) => types.length === 0 || types.includes(j.metadata.type));\n if (!match) return { declined: 'none-available' };\n\n // Progress starts empty: a just-claimed job has reported nothing yet.\n // The typed progress shapes describe REPORTS, and the first report\n // arrives from the worker via recordProgress.\n const running: RunningJob<any, any> = {\n status: 'running',\n metadata: match.metadata,\n params: match.params,\n startedAt: new Date().toISOString(),\n progress: {},\n };\n await this.transition(running, 'pending');\n return { job: running };\n }\n\n /**\n * Update a job (atomic: delete old, write new)\n */\n /**\n * Cross-status move (or in-place rewrite): delete old slot, write new.\n * Private — the interface exposes named transitions only (JOB-QUEUE-DRIVER\n * P0); a generic public patch is trivial on files and impossible on an\n * in-flight message, so no driver may offer one. Re-entering `pending`\n * announces, which is how failJob's retry reaches a listening worker.\n */\n private async transition(job: AnyJob, oldStatus?: JobStatus): Promise<void> {\n // If oldStatus provided, delete from old location\n if (oldStatus && oldStatus !== job.status) {\n const oldPath = this.getJobPath(job.metadata.id, oldStatus);\n try {\n await fs.unlink(oldPath);\n } catch (error) {\n // Ignore if file doesn't exist\n }\n }\n\n // Write to new location\n const newPath = this.getJobPath(job.metadata.id, job.status);\n await fs.writeFile(newPath, JSON.stringify(job, null, 2), 'utf-8');\n\n if (oldStatus && oldStatus !== job.status) {\n this.logger.info('Job moved', { jobId: job.metadata.id, oldStatus, newStatus: job.status });\n // Re-entering pending (e.g. a retry) is a fresh announcement.\n if (job.status === 'pending') {\n this.announce(job);\n }\n } else {\n this.logger.info('Job updated', { jobId: job.metadata.id, status: job.status });\n }\n }\n\n /**\n * Move a running job to `complete`. Returns false (and changes\n * nothing) if the job is missing or not running — which also makes\n * duplicate `job:complete` events harmless.\n */\n async completeJob(jobId: JobId, result: Record<string, unknown>): Promise<boolean> {\n const job = await this.getJob(jobId);\n if (!job || job.status !== 'running') {\n return false;\n }\n\n this.lastProgressWrite.delete(jobId);\n const completed: CompleteJob<any, any> = {\n status: 'complete',\n metadata: job.metadata,\n params: job.params,\n startedAt: job.startedAt,\n completedAt: new Date().toISOString(),\n result,\n };\n await this.transition(completed, 'running');\n return true;\n }\n\n /**\n * Retry-or-fail a running job. While `retryCount < maxRetries` the\n * job goes back to `pending` with the count bumped (and is\n * re-announced); after that it lands in `failed` with the error.\n * Returns null (and changes nothing) if the job isn't running.\n */\n async failJob(jobId: JobId, error: string, completedUnits?: string[], failureClass?: 'transient' | 'deterministic', unitCursors?: Record<string, UnitCursor>): Promise<'retried' | 'failed' | null> {\n const job = await this.getJob(jobId);\n if (!job || job.status !== 'running') {\n return null;\n }\n\n this.lastProgressWrite.delete(jobId);\n\n // Checkpointed resume (ABANDONED-INFERENCE P2): union this attempt's\n // completed units with earlier attempts' — attempt 3 must keep what\n // attempt 1 finished. Stored in metadata so the spread below (and in\n // every later rebuild) carries it forward; the terminal failed record\n // keeps it too, so the waste of a discarded run stays visible (D3).\n const checkpoint = [...new Set([...(job.metadata.completedUnits ?? []), ...(completedUnits ?? [])])];\n // The mid-unit half of the same checkpoint (CHUNK-GRAIN-RESUME P2), merged\n // by the same monotone rule the durable per-chunk writes use — `failJob`\n // and `checkpointUnits` race on the same record, and whichever lands last\n // must not drag a cursor back.\n const cursors = mergeUnitCursors(job.metadata.unitCursors, unitCursors, checkpoint);\n const { unitCursors: superseded, ...base } = job.metadata;\n void superseded;\n const metadata = {\n ...base,\n ...(checkpoint.length > 0 ? { completedUnits: checkpoint } : {}),\n ...(Object.keys(cursors).length > 0 ? { unitCursors: cursors } : {}),\n };\n\n // A known-deterministic failure skips the budget outright: the same\n // request cannot succeed on a second attempt, so a retry is guaranteed\n // waste at full price (ABANDONED-INFERENCE P3, A4). Unclassified and\n // transient failures retry as before.\n if (willRetryAfter(job.metadata, failureClass)) {\n const retried: PendingJob<any> = {\n status: 'pending',\n metadata: { ...metadata, retryCount: job.metadata.retryCount + 1 },\n params: job.params,\n };\n await this.transition(retried, 'running');\n return 'retried';\n }\n\n if (failureClass === 'deterministic' && job.metadata.retryCount < job.metadata.maxRetries) {\n this.logger.info('Job failed without retry — deterministic failure', { jobId, error });\n }\n\n const failed: FailedJob<any> = {\n status: 'failed',\n metadata,\n params: job.params,\n startedAt: job.startedAt,\n completedAt: new Date().toISOString(),\n error,\n };\n await this.transition(failed, 'running');\n return 'failed';\n }\n\n /**\n * Persist a running job's completed-unit checkpoint at unit completion\n * (JOB-RESTART-SAFETY P2). `failJob` writes this checkpoint on a clean\n * failure, but a worker that dies without emitting `job:fail` would lose\n * its finished units — so the janitor's recovery (which re-queues with\n * whatever `metadata.completedUnits` the running file holds) would redo\n * them. Writing it HERE, as each unit lands, makes a crash lose at most\n * the in-flight unit. Unioned with any existing checkpoint, unthrottled\n * (a unit completion is rare and must never be dropped), a no-op for\n * non-running jobs. Written directly, like `recordProgress`, so it also\n * refreshes the mtime heartbeat.\n */\n async checkpointUnits(jobId: JobId, completedUnits: string[], unitCursors?: Record<string, UnitCursor>): Promise<void> {\n const job = await this.getJob(jobId);\n if (!job || job.status !== 'running') {\n return;\n }\n const merged = [...new Set([...(job.metadata.completedUnits ?? []), ...completedUnits])];\n const cursors = mergeUnitCursors(job.metadata.unitCursors, unitCursors, merged);\n // The prior value is destructured OUT rather than spread over: omitting a\n // key from a spread leaves the old one in place, so the last unit to finish\n // would keep a cursor pointing into work that is done.\n const { unitCursors: superseded, ...metadata } = job.metadata;\n void superseded;\n const updated: RunningJob<any, any> = {\n ...job,\n metadata: {\n ...metadata,\n completedUnits: merged,\n // Absent rather than `{}`: an empty object would assert that units were\n // tracked and none had progress, a different claim from a job that\n // never reported a cursor at all.\n ...(Object.keys(cursors).length > 0 ? { unitCursors: cursors } : {}),\n },\n };\n await fs.writeFile(this.getJobPath(jobId, 'running'), JSON.stringify(updated, null, 2), 'utf-8');\n }\n\n /**\n * Write progress into a running job's file. Throttled per job, and\n * a no-op for jobs that aren't running. Beyond surfacing live\n * progress to `job:status-requested`, each write refreshes the\n * file's mtime — the heartbeat `recoverStaleRunningJobs` watches.\n */\n async recordProgress(jobId: JobId, progress: Record<string, unknown>): Promise<void> {\n const now = Date.now();\n const lastWrite = this.lastProgressWrite.get(jobId) ?? 0;\n if (now - lastWrite < PROGRESS_WRITE_MIN_INTERVAL_MS) {\n return;\n }\n this.lastProgressWrite.set(jobId, now);\n\n const job = await this.getJob(jobId);\n if (!job || job.status !== 'running') {\n this.lastProgressWrite.delete(jobId);\n return;\n }\n\n // Written directly (not via updateJob) so chatty progress doesn't\n // flood the info log.\n const updated: RunningJob<any, any> = { ...job, progress };\n await fs.writeFile(this.getJobPath(jobId, 'running'), JSON.stringify(updated, null, 2), 'utf-8');\n }\n\n /**\n * List jobs with filters\n */\n async listJobs(filters: JobQueryFilters = {}): Promise<AnyJob[]> {\n const jobs: AnyJob[] = [];\n\n // Determine which status directories to scan\n const statuses: JobStatus[] = filters.status\n ? [filters.status]\n : ['pending', 'running', 'complete', 'failed', 'cancelled'];\n\n for (const status of statuses) {\n const statusDir = path.join(this.jobsDir, status);\n\n try {\n const files = await fs.readdir(statusDir);\n\n for (const file of files) {\n const jobPath = path.join(statusDir, file);\n const content = await fs.readFile(jobPath, 'utf-8');\n const job = JSON.parse(content) as AnyJob;\n\n // Apply filters\n if (filters.type && job.metadata.type !== filters.type) continue;\n if (filters.userId && job.metadata.userId !== filters.userId) continue;\n\n jobs.push(job);\n }\n } catch (error) {\n // Directory might not exist yet\n continue;\n }\n }\n\n // Sort by created descending (newest first)\n jobs.sort((a, b) => new Date(b.metadata.created).getTime() - new Date(a.metadata.created).getTime());\n\n // Apply pagination\n const offset = filters.offset || 0;\n const limit = filters.limit || 100;\n\n return jobs.slice(offset, offset + limit);\n }\n\n /**\n * Cancel a job\n */\n async cancelJob(jobId: JobId): Promise<boolean> {\n const job = await this.getJob(jobId);\n\n if (!job) {\n return false;\n }\n\n // Can only cancel pending or running jobs\n if (job.status !== 'pending' && job.status !== 'running') {\n return false;\n }\n\n const oldStatus = job.status;\n\n // Create cancelled job with proper structure\n const cancelledJob: CancelledJob<any> = {\n status: 'cancelled',\n metadata: job.metadata,\n params: job.status === 'pending' ? job.params : job.params,\n startedAt: job.status === 'running' ? job.startedAt : undefined,\n completedAt: new Date().toISOString(),\n };\n\n await this.transition(cancelledJob, oldStatus);\n return true;\n }\n\n /**\n * Cancel all pending jobs in a category — the granularity of the\n * `job:cancel-requested` UI signal. Running jobs are left to finish:\n * interrupting a worker mid-inference would need a worker-side kill\n * channel that doesn't exist.\n */\n async cancelPendingJobs(category: 'annotation' | 'generation'): Promise<number> {\n const matches = category === 'generation'\n ? (type: string) => type === 'generation'\n : (type: string) => type.endsWith('-annotation');\n\n const pending = await this.listJobs({ status: 'pending', limit: Number.MAX_SAFE_INTEGER });\n let cancelled = 0;\n for (const job of pending) {\n if (!matches(job.metadata.type)) continue;\n if (await this.cancelJob(job.metadata.id)) {\n cancelled++;\n }\n }\n\n if (cancelled > 0) {\n this.logger.info('Cancelled pending jobs', { category, cancelled });\n }\n return cancelled;\n }\n\n /**\n * Recover running jobs orphaned by a dead worker: any `running/`\n * file whose mtime is older than the stale window is fed through\n * the same retry-or-fail path as `job:fail`. Progress writes\n * refresh the mtime, so a live worker is never recovered out from\n * under itself as long as it reports within the window.\n */\n async recoverStaleRunningJobs(): Promise<number> {\n const runningDir = path.join(this.jobsDir, 'running');\n let files: string[];\n try {\n files = await fs.readdir(runningDir);\n } catch {\n return 0;\n }\n\n const now = Date.now();\n let recovered = 0;\n for (const file of files) {\n if (!file.endsWith('.json')) continue;\n try {\n const stat = await fs.stat(path.join(runningDir, file));\n if (now - stat.mtimeMs < STALE_RUNNING_MS) continue;\n\n const staleId = toJobId(file.slice(0, -'.json'.length));\n const outcome = await this.failJob(\n staleId,\n `worker presumed dead — no progress within ${STALE_RUNNING_MS / 60_000} minutes`,\n );\n if (outcome) {\n this.logger.warn('Recovered stale running job', { jobId: staleId, outcome });\n recovered++;\n }\n } catch {\n // File vanished mid-scan (job finished) — nothing to recover\n }\n }\n return recovered;\n }\n\n /**\n * Clean up old completed/failed jobs (older than retention period)\n */\n async cleanupOldJobs(retentionHours: number = 24): Promise<number> {\n const cutoffTime = Date.now() - (retentionHours * 60 * 60 * 1000);\n let deletedCount = 0;\n\n const cleanupStatuses: JobStatus[] = ['complete', 'failed', 'cancelled'];\n\n for (const status of cleanupStatuses) {\n const statusDir = path.join(this.jobsDir, status);\n\n try {\n const files = await fs.readdir(statusDir);\n\n for (const file of files) {\n const jobPath = path.join(statusDir, file);\n const content = await fs.readFile(jobPath, 'utf-8');\n const job = JSON.parse(content) as AnyJob;\n\n if (job.status === 'complete' || job.status === 'failed' || job.status === 'cancelled') {\n const completedTime = new Date(job.completedAt).getTime();\n\n if (completedTime < cutoffTime) {\n await fs.unlink(jobPath);\n deletedCount++;\n }\n }\n }\n } catch (error) {\n this.logger.error('Error cleaning up jobs', { status, error: error instanceof Error ? error.message : String(error) });\n }\n }\n\n if (deletedCount > 0) {\n this.logger.info('Jobs cleaned up', { deletedCount });\n }\n\n return deletedCount;\n }\n\n /**\n * Get job file path\n */\n private getJobPath(jobId: JobId, status: JobStatus): string {\n return path.join(this.jobsDir, status, `${jobId}.json`);\n }\n\n /**\n * Get statistics about the queue\n */\n async getStats(): Promise<{\n pending: number;\n running: number;\n complete: number;\n failed: number;\n cancelled: number;\n }> {\n const stats = {\n pending: 0,\n running: 0,\n complete: 0,\n failed: 0,\n cancelled: 0\n };\n\n const statuses: JobStatus[] = ['pending', 'running', 'complete', 'failed', 'cancelled'];\n\n for (const status of statuses) {\n const statusDir = path.join(this.jobsDir, status);\n\n try {\n const files = await fs.readdir(statusDir);\n stats[status] = files.length;\n } catch (error) {\n // Directory might not exist yet\n stats[status] = 0;\n }\n }\n\n return stats;\n }\n}\n","/**\n * JetStreamJobQueue — the JetStream driver behind the `JobQueue` interface\n * (JOB-QUEUE-DRIVER P1, topology ruling M: gateway-mediated).\n *\n * Two primitives, one authority each:\n *\n * - **KV bucket `jobs`** is the AUTHORITATIVE operational state: one entry\n * per job (`{ job, lastProgressAt }`), every transition a revision-CAS —\n * which is what makes `claimJob` atomic: simultaneous claims race on one\n * revision and exactly one update wins.\n * - **Stream `JOBS`** (subjects `jobs.<category>.<type>`, work-queue\n * retention) is the DELIVERY vehicle and redelivery timer. A delivered\n * message is the lease this process holds for a job; `working()`\n * heartbeats extend it, and a gateway that dies stops heartbeating, so\n * `AckWait` redelivers the job to a live instance — gateway-death\n * recovery, protocol-native.\n *\n * Worker-death recovery is NOT AckWait's job under the mediated topology\n * (the gateway holding the lease is alive; the worker vanished): it is the\n * `lastProgressAt` sweep (`recoverStaleRunningJobs`), the same contract the\n * fs driver's mtime janitor implemented. The redelivery handler checks KV\n * state, so the two recovery paths can never double-apply.\n *\n * Battery ownership (JOB-QUEUE-DRIVER P0): the retry decision belongs to\n * `will-retry.ts` — `max_deliver` is unlimited and a deterministic failure\n * is `term()`ed, so the backend's retry engine never becomes a second\n * authority. The cursor merge belongs to `checkpoint-merge.ts`, shared with\n * every driver.\n *\n * The boundary is the point: no NATS type, subject string, or delivery\n * handle escapes this file.\n */\n\nimport { connect, RetentionPolicy, AckPolicy, DeliverPolicy, nanos } from 'nats';\nimport type { NatsConnection, JetStreamClient, JetStreamManager, JsMsg, KV, ConsumerMessages } from 'nats';\nimport type { AnyJob, PendingJob, RunningJob, FailedJob, CompleteJob, CancelledJob } from './types';\nimport { jobId as toJobId, type JobId, type Logger, type EventBus, type UnitCursor } from '@semiont/core';\nimport type { JobQueue } from './job-queue-interface';\nimport { willRetryAfter } from './will-retry';\nimport { mergeUnitCursors } from './checkpoint-merge';\n\nconst STREAM = 'JOBS';\nconst CONSUMER = 'gateway-claims';\nconst BUCKET = 'jobs';\n\n/** Minimum spacing between progress writes per job — workers can be chatty. */\nconst PROGRESS_WRITE_MIN_INTERVAL_MS = 5_000;\n\n/** Bounded CAS retries: past this, something is systemically wrong. */\nconst MAX_CAS_ATTEMPTS = 20;\n\nconst enc = new TextEncoder();\nconst dec = new TextDecoder();\n\n/** The KV record: the job plus the liveness fact the sweep reads. */\ninterface JobEnvelope {\n job: AnyJob;\n lastProgressAt: string;\n}\n\n/**\n * The JOBS stream's capture filter — exported as the ONE home of the fact.\n * A JetStream stream is a server-side subscription: anything published under\n * these subjects is persisted regardless of which client API produced it.\n * The signal plane's disjointness gate derives from this export instead of\n * restating it (SIGNAL-PLANE D3 gate 3).\n */\nexport const JOBS_STREAM_SUBJECTS = ['jobs.>'] as const;\n\nexport interface JetStreamJobQueueOptions {\n /** NATS server address(es), e.g. \"192.168.64.42:4222\". */\n servers: string | string[];\n /** Worker presumed dead after this long without progress (default 30 min). */\n staleRunningMs?: number;\n /** Lease redelivery window when THIS process stops heartbeating (default 30 s). */\n ackWaitMs?: number;\n /** Re-announce + worker-death-sweep cadence (default 30 s; tests shrink it). */\n tickMs?: number;\n /** Reconnect on connection loss (default true; tests turn it off). */\n reconnect?: boolean;\n}\n\nfunction categoryOf(type: string): 'annotation' | 'generation' {\n return type === 'generation' ? 'generation' : 'annotation';\n}\n\nfunction subjectFor(job: AnyJob): string {\n return `jobs.${categoryOf(job.metadata.type)}.${job.metadata.type}`;\n}\n\nexport class JetStreamJobQueue implements JobQueue {\n private nc!: NatsConnection;\n private js!: JetStreamClient;\n private jsm!: JetStreamManager;\n private kv!: KV;\n private iter: ConsumerMessages | null = null;\n /** Delivered messages this process holds — the lease, keyed by jobId,\n * with the job's type so a by-type claim can walk them without a read. */\n private readonly held = new Map<string, { m: JsMsg; type: string }>();\n private heartbeat: ReturnType<typeof setInterval> | null = null;\n private reconciling = false;\n private tick: ReturnType<typeof setInterval> | null = null;\n private ticking = false;\n private readonly lastProgressWrite = new Map<string, number>();\n private readonly staleRunningMs: number;\n private readonly ackWaitMs: number;\n private readonly tickMs: number;\n\n constructor(\n private readonly options: JetStreamJobQueueOptions,\n private readonly logger: Logger,\n private readonly eventBus?: EventBus,\n ) {\n this.staleRunningMs = options.staleRunningMs ?? 30 * 60_000;\n this.ackWaitMs = options.ackWaitMs ?? 30_000;\n this.tickMs = options.tickMs ?? 30_000;\n }\n\n async initialize(): Promise<void> {\n this.nc = await connect({\n servers: this.options.servers,\n reconnect: this.options.reconnect ?? true,\n timeout: 10_000,\n });\n this.js = this.nc.jetstream();\n this.jsm = await this.nc.jetstreamManager();\n this.kv = await this.js.views.kv(BUCKET);\n\n try {\n await this.jsm.streams.info(STREAM);\n } catch {\n await this.jsm.streams.add({\n name: STREAM,\n subjects: [...JOBS_STREAM_SUBJECTS],\n retention: RetentionPolicy.Workqueue,\n });\n }\n try {\n await this.jsm.consumers.info(STREAM, CONSUMER);\n } catch {\n await this.jsm.consumers.add(STREAM, {\n durable_name: CONSUMER,\n ack_policy: AckPolicy.Explicit,\n deliver_policy: DeliverPolicy.All,\n ack_wait: nanos(this.ackWaitMs),\n // ONE retry authority: will-retry.ts decides; the stream never\n // gives up a message on its own count.\n max_deliver: -1,\n });\n }\n\n const consumer = await this.js.consumers.get(STREAM, CONSUMER);\n this.iter = await consumer.consume({\n callback: (m) => {\n void this.onDelivery(m).catch((error) => {\n this.logger.warn('Job delivery handling failed', {\n error: error instanceof Error ? error.message : String(error),\n });\n });\n },\n });\n\n // The lease heartbeat doubles as RECONCILIATION (the multi-instance\n // lease-settle pin): a held delivery whose job concluded on ANOTHER\n // instance — its claim lost the CAS race, or a redelivery landed here\n // after a gateway death — can only be settled by the holder, because\n // nobody else has the delivery to ack. Terminal in KV → ack and drop;\n // otherwise the lease extends. While this process is alive its leases\n // never expire; when it dies they redeliver after ackWait — that IS the\n // gateway-death recovery path.\n this.heartbeat = setInterval(() => {\n if (this.reconciling) return;\n this.reconciling = true;\n void this.reconcileHeld()\n .catch((error) => {\n this.logger.warn('Held-lease reconciliation failed', {\n error: error instanceof Error ? error.message : String(error),\n });\n })\n .finally(() => {\n this.reconciling = false;\n });\n }, Math.max(250, Math.floor(this.ackWaitMs / 4)));\n this.heartbeat.unref?.();\n\n // The queue's periodic tick, mirroring the fs driver's internal janitor:\n // (a) RE-ANNOUNCE held pending deliveries — an announcement is a wake-up\n // with no memory, so a worker that was busy (or not yet connected)\n // when one fired must hear about still-unclaimed work again;\n // (b) SWEEP for worker death (`recoverStaleRunningJobs`) — `AckWait`\n // covers a dead GATEWAY; only this sweep covers a dead WORKER, and\n // rows do not sweep themselves (JOB-RESTART-SAFETY P7).\n this.tick = setInterval(() => {\n if (this.ticking) return;\n this.ticking = true;\n void (async () => {\n for (const [id, held] of [...this.held]) {\n const envelope = await this.read(toJobId(id));\n if (envelope?.job.status === 'pending') this.announce(envelope.job);\n void held;\n }\n await this.recoverStaleRunningJobs();\n })()\n .catch((error) => {\n this.logger.warn('Job-queue tick failed', {\n error: error instanceof Error ? error.message : String(error),\n });\n })\n .finally(() => {\n this.ticking = false;\n });\n }, this.tickMs);\n this.tick.unref?.();\n }\n\n destroy(): void {\n this.iter?.stop();\n this.iter = null;\n if (this.heartbeat) clearInterval(this.heartbeat);\n this.heartbeat = null;\n if (this.tick) clearInterval(this.tick);\n this.tick = null;\n this.held.clear();\n void this.nc?.close();\n }\n\n /**\n * A delivery is a job arriving at this gateway. What it means depends on\n * the job's authoritative (KV) state:\n * - pending → hold the lease, announce for a worker to claim;\n * - running → hold silently (a claim raced ahead of the delivery, or a\n * redelivery reached a fresh instance after a gateway death);\n * - terminal → the work already concluded elsewhere; consume the message;\n * - unknown → not ours to run; terminate it.\n */\n private async onDelivery(m: JsMsg): Promise<void> {\n let id: string;\n try {\n id = (JSON.parse(dec.decode(m.data)) as { jobId: string }).jobId;\n } catch {\n m.term();\n return;\n }\n const envelope = await this.read(toJobId(id));\n if (!envelope) {\n m.term();\n return;\n }\n switch (envelope.job.status) {\n case 'pending':\n this.held.set(id, { m, type: envelope.job.metadata.type });\n this.announce(envelope.job);\n break;\n case 'running':\n this.held.set(id, { m, type: envelope.job.metadata.type });\n break;\n default:\n m.ack();\n this.held.delete(id);\n }\n }\n\n /** Same wire shape as the fs driver: only jobs with a resourceId announce. */\n private announce(job: AnyJob): void {\n if (this.eventBus && 'params' in job && 'resourceId' in (job.params as Record<string, unknown>)) {\n this.eventBus.get('job:queued').next({\n jobId: job.metadata.id,\n jobType: job.metadata.type,\n resourceId: (job.params as { resourceId: unknown }).resourceId as never,\n userId: job.metadata.userId,\n });\n }\n }\n\n private async read(jobIdArg: JobId): Promise<{ job: AnyJob; lastProgressAt: string; revision: number } | null> {\n const entry = await this.kv.get(jobIdArg as string);\n if (!entry || entry.operation !== 'PUT') return null;\n const envelope = JSON.parse(dec.decode(entry.value)) as JobEnvelope;\n return { ...envelope, revision: entry.revision };\n }\n\n private async write(jobIdArg: JobId, envelope: JobEnvelope, revision?: number): Promise<number> {\n const payload = enc.encode(JSON.stringify(envelope));\n if (revision === undefined) return this.kv.put(jobIdArg as string, payload);\n return this.kv.update(jobIdArg as string, payload, revision);\n }\n\n /**\n * Read-transform-CAS with bounded retries: the transform sees the current\n * job and returns the replacement (or null to stop). Every state\n * transition goes through here, which is what makes each one atomic.\n */\n private async cas<T>(\n jobIdArg: JobId,\n transform: (job: AnyJob, lastProgressAt: string) => { envelope: JobEnvelope; result: T } | { result: T } | null,\n onMissing: T,\n ): Promise<T> {\n for (let attempt = 0; attempt < MAX_CAS_ATTEMPTS; attempt++) {\n const current = await this.read(jobIdArg);\n if (!current) return onMissing;\n const out = transform(current.job, current.lastProgressAt);\n if (out === null) return onMissing;\n if (!('envelope' in out)) return out.result;\n try {\n await this.write(jobIdArg, out.envelope, current.revision);\n return out.result;\n } catch {\n // Revision conflict — someone else transitioned first; re-read.\n }\n }\n throw new Error(`Job ${jobIdArg} transition failed after ${MAX_CAS_ATTEMPTS} CAS attempts`);\n }\n\n async createJob(job: AnyJob): Promise<void> {\n const now = new Date().toISOString();\n await this.kv.create(job.metadata.id as string, enc.encode(JSON.stringify({ job, lastProgressAt: now } satisfies JobEnvelope)));\n // Only pending jobs need delivery — the stream is the claim vehicle,\n // not the record (KV is).\n if (job.status === 'pending') {\n await this.js.publish(subjectFor(job), enc.encode(JSON.stringify({ jobId: job.metadata.id })));\n }\n }\n\n async getJob(jobIdArg: JobId): Promise<AnyJob | null> {\n const envelope = await this.read(jobIdArg);\n return envelope?.job ?? null;\n }\n\n async claimNextJob(types: string[]): Promise<{ job: AnyJob } | { declined: 'none-available' }> {\n const matches = (t: string) => types.length === 0 || types.includes(t);\n // Lease-aligned fast path (topology memo, mechanic 1): deliveries this\n // gateway already holds — the claim lands where the worker is connected.\n for (const [id, held] of [...this.held]) {\n if (!matches(held.type)) continue;\n const won = await this.tryClaim(toJobId(id));\n if (won) return won;\n }\n // Fallback: pending state whose delivery has not reached us — yet (a\n // claim racing ahead of its delivery) or ever (another gateway holds\n // it; KV is the authority, and that holder's reconcile settles the\n // stale lease once this claim concludes the job).\n for (const key of await this.allKeys()) {\n const envelope = await this.read(toJobId(key));\n if (!envelope || envelope.job.status !== 'pending' || !matches(envelope.job.metadata.type)) continue;\n const won = await this.tryClaim(toJobId(key));\n if (won) return won;\n }\n return { declined: 'none-available' };\n }\n\n /** CAS one pending job to running; null when someone else won it. */\n private async tryClaim(jobIdArg: JobId): Promise<{ job: AnyJob } | null> {\n return this.cas<{ job: AnyJob } | null>(\n jobIdArg,\n (job) => {\n if (job.status !== 'pending') return { result: null };\n const running: RunningJob<any, any> = {\n status: 'running',\n metadata: job.metadata,\n params: job.params,\n startedAt: new Date().toISOString(),\n progress: {},\n };\n return {\n envelope: { job: running, lastProgressAt: new Date().toISOString() },\n result: { job: running as AnyJob },\n };\n },\n null,\n );\n }\n\n async completeJob(jobIdArg: JobId, result: Record<string, unknown>): Promise<boolean> {\n const moved = await this.cas<boolean>(\n jobIdArg,\n (job) => {\n if (job.status !== 'running') return { result: false };\n const completed: CompleteJob<any, any> = {\n status: 'complete',\n metadata: job.metadata,\n params: job.params,\n startedAt: job.startedAt,\n completedAt: new Date().toISOString(),\n result,\n };\n return { envelope: { job: completed, lastProgressAt: new Date().toISOString() }, result: true };\n },\n false,\n );\n if (moved) this.settleLease(jobIdArg, 'ack');\n return moved;\n }\n\n async failJob(\n jobIdArg: JobId,\n error: string,\n completedUnits?: string[],\n failureClass?: 'transient' | 'deterministic',\n unitCursors?: Record<string, UnitCursor>,\n ): Promise<'retried' | 'failed' | null> {\n const outcome = await this.cas<'retried' | 'failed' | null>(\n jobIdArg,\n (job) => {\n if (job.status !== 'running') return { result: null };\n const units = [...new Set([...(job.metadata.completedUnits ?? []), ...(completedUnits ?? [])])];\n const cursors = mergeUnitCursors(job.metadata.unitCursors, unitCursors, units);\n const metadata = {\n ...job.metadata,\n ...(units.length > 0 ? { completedUnits: units } : {}),\n ...(Object.keys(cursors).length > 0 ? { unitCursors: cursors } : {}),\n };\n if ('unitCursors' in metadata && Object.keys(cursors).length === 0) delete (metadata as { unitCursors?: unknown }).unitCursors;\n\n if (willRetryAfter(job.metadata, failureClass)) {\n const retried: PendingJob<any> = {\n status: 'pending',\n metadata: { ...metadata, retryCount: job.metadata.retryCount + 1 },\n params: job.params,\n };\n return { envelope: { job: retried, lastProgressAt: new Date().toISOString() }, result: 'retried' as const };\n }\n const failed: FailedJob<any> = {\n status: 'failed',\n metadata,\n params: job.params,\n startedAt: job.startedAt,\n completedAt: new Date().toISOString(),\n error,\n };\n return { envelope: { job: failed, lastProgressAt: new Date().toISOString() }, result: 'failed' as const };\n },\n null,\n );\n\n if (outcome === 'retried') {\n // The nak'd (or republished) delivery arrives as a fresh pending job\n // and re-announces — retry IS redelivery under this driver.\n const held = this.held.get(jobIdArg as string);\n if (held) {\n this.held.delete(jobIdArg as string);\n held.m.nak();\n } else {\n const envelope = await this.read(jobIdArg);\n if (envelope) await this.js.publish(subjectFor(envelope.job), enc.encode(JSON.stringify({ jobId: jobIdArg })));\n }\n } else if (outcome === 'failed') {\n // Deterministic or exhausted: the message must never redeliver.\n this.settleLease(jobIdArg, 'term');\n }\n return outcome;\n }\n\n async checkpointUnits(jobIdArg: JobId, completedUnits: string[], unitCursors?: Record<string, UnitCursor>): Promise<void> {\n await this.cas<void>(\n jobIdArg,\n (job) => {\n if (job.status !== 'running') return { result: undefined };\n const units = [...new Set([...(job.metadata.completedUnits ?? []), ...completedUnits])];\n const cursors = mergeUnitCursors(job.metadata.unitCursors, unitCursors, units);\n const metadata = { ...job.metadata, completedUnits: units } as typeof job.metadata;\n if (Object.keys(cursors).length > 0) metadata.unitCursors = cursors;\n else delete (metadata as { unitCursors?: unknown }).unitCursors;\n const updated = { ...job, metadata } as AnyJob;\n // A checkpoint is progress — it refreshes the liveness clock.\n return { envelope: { job: updated, lastProgressAt: new Date().toISOString() }, result: undefined };\n },\n undefined,\n );\n }\n\n async recordProgress(jobIdArg: JobId, progress: Record<string, unknown>): Promise<void> {\n const last = this.lastProgressWrite.get(jobIdArg as string) ?? 0;\n if (Date.now() - last < PROGRESS_WRITE_MIN_INTERVAL_MS) return;\n this.lastProgressWrite.set(jobIdArg as string, Date.now());\n\n this.held.get(jobIdArg as string)?.m.working();\n await this.cas<void>(\n jobIdArg,\n (job) => {\n if (job.status !== 'running') return { result: undefined };\n const updated: RunningJob<any, any> = {\n status: 'running',\n metadata: job.metadata,\n params: job.params,\n startedAt: job.startedAt,\n progress,\n };\n return { envelope: { job: updated, lastProgressAt: new Date().toISOString() }, result: undefined };\n },\n undefined,\n );\n }\n\n async cancelPendingJobs(category: 'annotation' | 'generation'): Promise<number> {\n const matches = category === 'generation'\n ? (type: string) => type === 'generation'\n : (type: string) => type.endsWith('-annotation');\n\n let cancelled = 0;\n for (const key of await this.allKeys()) {\n const envelope = await this.read(toJobId(key));\n if (!envelope || envelope.job.status !== 'pending' || !matches(envelope.job.metadata.type)) continue;\n const done = await this.cas<boolean>(\n toJobId(key),\n (job) => {\n if (job.status !== 'pending' || !matches(job.metadata.type)) return { result: false };\n const record: CancelledJob<any> = {\n status: 'cancelled',\n metadata: job.metadata,\n params: job.params,\n startedAt: undefined,\n completedAt: new Date().toISOString(),\n };\n return { envelope: { job: record, lastProgressAt: new Date().toISOString() }, result: true };\n },\n false,\n );\n if (done) {\n cancelled++;\n this.settleLease(toJobId(key), 'term');\n }\n }\n // Undelivered messages for the category die with the state change.\n await this.jsm.streams.purge(STREAM, { filter: `jobs.${category}.>` });\n return cancelled;\n }\n\n async cancelJob(jobIdArg: JobId): Promise<boolean> {\n const done = await this.cas<boolean>(\n jobIdArg,\n (job) => {\n if (job.status !== 'pending' && job.status !== 'running') return { result: false };\n const record: CancelledJob<any> = {\n status: 'cancelled',\n metadata: job.metadata,\n params: job.params,\n startedAt: job.status === 'running' ? job.startedAt : undefined,\n completedAt: new Date().toISOString(),\n };\n return { envelope: { job: record, lastProgressAt: new Date().toISOString() }, result: true };\n },\n false,\n );\n if (done) this.settleLease(jobIdArg, 'term');\n return done;\n }\n\n async getStats(): Promise<{ pending: number; running: number; complete: number; failed: number; cancelled: number }> {\n const stats = { pending: 0, running: 0, complete: 0, failed: 0, cancelled: 0 };\n for (const key of await this.allKeys()) {\n const envelope = await this.read(toJobId(key));\n if (envelope) stats[envelope.job.status]++;\n }\n return stats;\n }\n\n /**\n * Worker-death recovery: the same contract the fs driver's mtime janitor\n * implemented — a running job with no progress inside the stale window is\n * retried or failed, checkpoint intact. Gateway-death recovery never comes\n * through here; that is AckWait redelivery.\n */\n async recoverStaleRunningJobs(): Promise<number> {\n const now = Date.now();\n let recovered = 0;\n for (const key of await this.allKeys()) {\n const envelope = await this.read(toJobId(key));\n if (!envelope || envelope.job.status !== 'running') continue;\n if (now - Date.parse(envelope.lastProgressAt) < this.staleRunningMs) continue;\n const outcome = await this.failJob(\n toJobId(key),\n `worker presumed dead — no progress within ${this.staleRunningMs / 60_000} minutes`,\n );\n if (outcome) {\n this.logger.warn('Recovered stale running job', { jobId: key, outcome });\n recovered++;\n }\n }\n return recovered;\n }\n\n /** The heartbeat body — extend live leases, settle concluded ones. */\n private async reconcileHeld(): Promise<void> {\n for (const [id, held] of [...this.held]) {\n const envelope = await this.read(toJobId(id));\n const status = envelope?.job.status;\n if (!envelope || status === 'complete' || status === 'failed' || status === 'cancelled') {\n this.held.delete(id);\n held.m.ack();\n } else {\n held.m.working();\n }\n }\n }\n\n /**\n * Materialize the KV key list BEFORE touching any entry: interleaving\n * `get`/`update` with an open `keys()` iterator on the same connection\n * makes the iterator drop entries (measured: the second pending job\n * vanished from every sweep until this was split into two passes).\n */\n private async allKeys(): Promise<string[]> {\n const keys: string[] = [];\n for await (const key of await this.kv.keys()) keys.push(key);\n return keys;\n }\n\n /** Conclude this process's lease on a job, if it holds one. */\n private settleLease(jobIdArg: JobId, how: 'ack' | 'term'): void {\n const held = this.held.get(jobIdArg as string);\n if (!held) return;\n this.held.delete(jobIdArg as string);\n if (how === 'ack') held.m.ack();\n else held.m.term();\n }\n}\n","/**\n * Job Queue Type Definitions - Discriminated Union Design\n *\n * Jobs represent async work that can be queued, processed, and monitored.\n * Uses TypeScript discriminated unions to enforce valid state transitions.\n *\n * Design principles:\n * - Each job status has specific valid fields\n * - Type narrowing works automatically via status discriminant\n * - No optional fields that may or may not exist\n * - State machine is explicit and type-safe\n */\n\nimport type { JobId, EntityType, ResourceId, UserId, GenerationJobParams, TagSchema, UnitCursor } from '@semiont/core';\nimport type { JobReferenceAnnotationResult, JobHighlightAnnotationResult, JobCommentAnnotationResult, JobAssessmentAnnotationResult, JobTagAnnotationResult } from '@semiont/core';\n\nexport type JobType = 'reference-annotation' | 'generation' | 'highlight-annotation' | 'assessment-annotation' | 'comment-annotation' | 'tag-annotation';\nexport type JobStatus = 'pending' | 'running' | 'complete' | 'failed' | 'cancelled';\n\n// ============================================================================\n// Core Metadata and Parameters\n// ============================================================================\n\n/**\n * Job metadata - common to all states\n */\nexport interface JobMetadata {\n id: JobId;\n type: JobType;\n userId: UserId;\n /**\n * Audit-only snapshot of the requesting user (with `userEmail` and\n * `userDomain` below), stamped at job creation and persisted in the\n * on-disk job file. No code path reads these back — annotation\n * `creator` attribution is derived from `userId` via `didToAgent()`.\n * Kept intentionally so job files are self-describing to a human.\n */\n userName: string;\n userEmail: string;\n userDomain: string;\n created: string;\n retryCount: number;\n maxRetries: number;\n /**\n * Checkpointed resume (ABANDONED-INFERENCE P2, HD1): the entity-type\n * units whose annotations were fully emitted by earlier failed\n * attempts. Written only by `failJob`, unioned across attempts — and\n * because `failJob` rebuilds the retried record by spreading metadata,\n * the checkpoint survives every subsequent rebuild for free. A retried\n * claim skips these units, so completed work is neither redone nor\n * duplicated.\n */\n completedUnits?: string[];\n /**\n * The finer grain `completedUnits` cannot express (CHUNK-GRAIN-RESUME P2):\n * how far each UNFINISHED unit got, keyed by unit. Written per committed\n * chunk, so a job that dies mid-unit resumes there rather than at the top —\n * which for a one-unit job (every motivation job, and the 1958 document's\n * single `Person` type) is the difference between resuming and restarting.\n *\n * A unit here is in progress, never complete; the two sets are disjoint by\n * construction in `checkpointUnits`. Merged monotonically per unit, never\n * unioned — see `mergeUnitCursors`.\n */\n unitCursors?: Record<string, UnitCursor>;\n}\n\n/**\n * Locale conventions for detection/generation params.\n *\n * Two independent locales flow through these jobs:\n *\n * - `language` — *annotation body* locale. The BCP-47 tag the LLM should\n * write generated body text in (comment text, assessment text, generated\n * resource content, tag category label). Sourced from the user's UI\n * locale. Stamped onto the W3C `TextualBody.language` field.\n *\n * - `sourceLanguage` — *source resource* locale. The BCP-47 tag of the\n * content being analyzed. Sourced from `ResourceDescriptor` (carried as\n * `Representation.language` on the primary representation). Used in\n * prompts so the LLM analyzes non-English source correctly even when\n * the user's UI locale differs.\n *\n * Examples: a German user analyzing an English document → `language='de'`,\n * `sourceLanguage='en'`. An English user detecting entities in a French\n * document → `language='en'` (unused for entity references), `sourceLanguage='fr'`.\n */\n\n/**\n * Detection job parameters\n */\nexport interface DetectionParams {\n resourceId: ResourceId;\n entityTypes: EntityType[];\n includeDescriptiveReferences?: boolean;\n /** Annotation body locale — see locale conventions above. */\n language?: string;\n /** Source-resource locale — see locale conventions above. */\n sourceLanguage?: string;\n}\n\n\n/**\n * Highlight detection job parameters\n */\nexport interface HighlightDetectionParams {\n resourceId: ResourceId;\n instructions?: string;\n density?: number;\n /** Source-resource locale — see locale conventions above. */\n sourceLanguage?: string;\n}\n\n/**\n * Assessment detection job parameters\n */\nexport interface AssessmentDetectionParams {\n resourceId: ResourceId;\n instructions?: string;\n tone?: 'analytical' | 'critical' | 'balanced' | 'constructive';\n density?: number;\n /** Annotation body locale — see locale conventions above. */\n language?: string;\n /** Source-resource locale — see locale conventions above. */\n sourceLanguage?: string;\n}\n\n/**\n * Comment detection job parameters\n */\nexport interface CommentDetectionParams {\n resourceId: ResourceId;\n instructions?: string;\n tone?: 'scholarly' | 'explanatory' | 'conversational' | 'technical';\n density?: number;\n /** Annotation body locale — see locale conventions above. */\n language?: string;\n /** Source-resource locale — see locale conventions above. */\n sourceLanguage?: string;\n}\n\n/**\n * Tag detection job parameters.\n *\n * Carries the *full* `TagSchema` (not just an id). The dispatcher resolves\n * the caller-supplied `schemaId` against the per-KB tag-schema projection\n * at job-creation time and embeds the resolved schema here, keeping the\n * worker independent of the registry.\n */\nexport interface TagDetectionParams {\n resourceId: ResourceId;\n schema: TagSchema;\n categories: string[];\n /** Annotation body locale — see locale conventions above. */\n language?: string;\n /** Source-resource locale — see locale conventions above. */\n sourceLanguage?: string;\n}\n\n// ============================================================================\n// Progress and Result Types\n// ============================================================================\n\n/**\n * Detection job progress\n */\nexport interface DetectionProgress {\n totalEntityTypes: number;\n processedEntityTypes: number;\n entitiesFound: number;\n entitiesEmitted: number;\n}\n\n\n/**\n * Generation job progress\n */\nexport interface YieldProgress {\n /** The two real generation transitions — LLM call running, then persisting. */\n stage: 'generating' | 'creating';\n percentage: number;\n message?: string;\n}\n\n/**\n * Generation job result\n */\nexport interface GenerationResult {\n kind: 'generation';\n resourceId: ResourceId;\n resourceName: string;\n /** True when the model stopped at the maxTokens ceiling — the artifact is cut off, not complete (GENERATE-FROM-RESOURCE D6). */\n truncated: boolean;\n}\n\n/**\n * Highlight detection job progress\n */\nexport interface HighlightDetectionProgress {\n stage: 'analyzing' | 'creating';\n percentage: number;\n message?: string;\n}\n\n\n/**\n * Assessment detection job progress\n */\nexport interface AssessmentDetectionProgress {\n stage: 'analyzing' | 'creating';\n percentage: number;\n message?: string;\n}\n\n\n/**\n * Comment detection job progress\n */\nexport interface CommentDetectionProgress {\n stage: 'analyzing' | 'creating';\n percentage: number;\n message?: string;\n}\n\n\n/**\n * Tag detection job progress\n */\nexport interface TagDetectionProgress {\n stage: 'analyzing' | 'creating';\n percentage: number;\n currentCategory?: string;\n processedCategories: number;\n totalCategories: number;\n message?: string;\n}\n\n\n// ============================================================================\n// Generic Job State Types\n// ============================================================================\n\n/**\n * Pending job - just created, waiting to be picked up\n */\nexport interface PendingJob<P> {\n status: 'pending';\n metadata: JobMetadata;\n params: P;\n}\n\n/**\n * Running job - actively being processed\n */\nexport interface RunningJob<P, PG> {\n status: 'running';\n metadata: JobMetadata;\n params: P;\n startedAt: string;\n progress: PG;\n}\n\n/**\n * Complete job - successfully finished\n */\nexport interface CompleteJob<P, R> {\n status: 'complete';\n metadata: JobMetadata;\n params: P;\n startedAt: string;\n completedAt: string;\n result: R;\n}\n\n/**\n * Failed job - encountered an error\n */\nexport interface FailedJob<P> {\n status: 'failed';\n metadata: JobMetadata;\n params: P;\n startedAt?: string;\n completedAt: string;\n error: string;\n}\n\n/**\n * Cancelled job - stopped by user\n */\nexport interface CancelledJob<P> {\n status: 'cancelled';\n metadata: JobMetadata;\n params: P;\n startedAt?: string;\n completedAt: string;\n}\n\n/**\n * Generic job - discriminated union of all states\n */\nexport type Job<P, PG, R> =\n | PendingJob<P>\n | RunningJob<P, PG>\n | CompleteJob<P, R>\n | FailedJob<P>\n | CancelledJob<P>;\n\n// ============================================================================\n// Concrete Job Types\n// ============================================================================\n\nexport type DetectionJob = Job<DetectionParams, DetectionProgress, JobReferenceAnnotationResult>;\nexport type GenerationJob = Job<GenerationJobParams, YieldProgress, GenerationResult>;\nexport type HighlightDetectionJob = Job<HighlightDetectionParams, HighlightDetectionProgress, JobHighlightAnnotationResult>;\nexport type AssessmentDetectionJob = Job<AssessmentDetectionParams, AssessmentDetectionProgress, JobAssessmentAnnotationResult>;\nexport type CommentDetectionJob = Job<CommentDetectionParams, CommentDetectionProgress, JobCommentAnnotationResult>;\nexport type TagDetectionJob = Job<TagDetectionParams, TagDetectionProgress, JobTagAnnotationResult>;\n\n/**\n * Discriminated union of all job types\n */\nexport type AnyJob = DetectionJob | GenerationJob | HighlightDetectionJob | AssessmentDetectionJob | CommentDetectionJob | TagDetectionJob;\n\n// ============================================================================\n// Type Guards\n// ============================================================================\n\nconst JOB_TYPES: ReadonlySet<string> = new Set<JobType>([\n 'reference-annotation',\n 'generation',\n 'highlight-annotation',\n 'assessment-annotation',\n 'comment-annotation',\n 'tag-annotation',\n]);\n\n/**\n * Narrow a job type arriving off the bus, where it is only `string`.\n *\n * Worth checking rather than asserting: `jobType` is a required, enumerated\n * field on every job lifecycle command, so an unrecognized value produces a\n * payload the gateway will reject — better caught at the worker with a clear\n * error than as an opaque emit failure.\n */\nexport function isJobType(value: string): value is JobType {\n return JOB_TYPES.has(value);\n}\n\n/**\n * Narrow bus-delivered job params to the shape a processor expects.\n *\n * Job params cross the bus as `Record<string, unknown>`. Every params type in\n * this file requires exactly one field — `resourceId` — with the rest\n * optional, so that is what gets verified; absent optionals are legitimately\n * absent, not a validation failure. Callers pass the params type for the job\n * they have already branched on.\n */\nexport function asJobParams<T extends { resourceId: ResourceId }>(\n params: Record<string, unknown>,\n): T {\n if (typeof params.resourceId !== 'string') {\n throw new Error('Job params are missing a resourceId');\n }\n return params as unknown as T;\n}\n\n// Generation params are deliberately NOT covered: generation is the one job\n// that does not read a source resource (it mints one), so it requires no\n// `resourceId` and there is nothing to verify.\n\nexport function isPendingJob(job: AnyJob): job is PendingJob<any> {\n return job.status === 'pending';\n}\n\nexport function isRunningJob(job: AnyJob): job is RunningJob<any, any> {\n return job.status === 'running';\n}\n\nexport function isCompleteJob(job: AnyJob): job is CompleteJob<any, any> {\n return job.status === 'complete';\n}\n\nexport function isFailedJob(job: AnyJob): job is FailedJob<any> {\n return job.status === 'failed';\n}\n\nexport function isCancelledJob(job: AnyJob): job is CancelledJob<any> {\n return job.status === 'cancelled';\n}\n\n// ============================================================================\n// Job Query Types\n// ============================================================================\n\nexport interface JobQueryFilters {\n status?: JobStatus;\n type?: JobType;\n userId?: UserId;\n limit?: number;\n offset?: number;\n}\n\n","/**\n * Bounded inference calls — WORKER-LIVENESS.md P2 (G1: prevention).\n *\n * The claim loop's only unbounded await is the model call: bus\n * operations gained transport timeouts in 0.5.6, the inference HTTP\n * request did not. One request that never settles used to wedge the\n * worker forever — the adapter ignores announcements while\n * `isProcessing`, so a single stuck call silenced the whole agent.\n * Bounding the call converts that silent hang into an ordinary job\n * failure that flows through the existing `job:fail` path (and the\n * gateway's retry budget — a timeout is transient-shaped, so retrying\n * is correct) and frees the claim loop.\n *\n * This is a timeout AND a cancellation (ABANDONED-INFERENCE P1): on expiry\n * the bound aborts the underlying request through the `InferenceClient`\n * signal, so the transport — and, on the Anthropic path, the SDK's internal\n * retry loop — is torn down rather than left running as a billed zombie\n * (P0 caught one completing 24–34 minutes after its job was gone). The\n * timeout stays as the last line either way, exactly as before; the abort is\n * the addition, not the replacement (D1). The eventual settlement of the\n * aborted promise is still swallowed so it cannot surface as an unhandled\n * rejection.\n */\n\nimport type { ElementSchema, InferenceClient, InferenceResponse, StructuredResponse } from '@semiont/inference';\nimport type { Logger } from '@semiont/core';\nimport { withSpan } from '@semiont/observability';\n\n/**\n * Generous single-call bound. Slow local models on large prompts run\n * minutes, not tens of minutes; the stall watchdog (P3) sits above\n * this at 15 minutes, and the gateway's dead-worker janitor above\n * that at 30. Fixed by design — no env knob.\n */\nexport const INFERENCE_TIMEOUT_MS = 10 * 60_000;\n\n/**\n * The bound's own rejection, typed so classification (ABANDONED-INFERENCE\n * P3) never string-matches our own error message. A timeout says nothing\n * about the request — it classifies transient.\n */\nexport class InferenceTimeoutError extends Error {\n override readonly name = 'InferenceTimeoutError';\n}\n\n/**\n * How often an in-flight call reports that it is still alive\n * (DETECTION-HEARTBEAT D2).\n *\n * Detection's other liveness signal — the chunk-boundary heartbeat — emits\n * `N − 1` events for `N` chunks, which is ZERO for the single-chunk case that\n * every realistic document falls into (the derived input budget is ~935 K\n * tokens). A 7-minute call then emits nothing at all, and the client's\n * *inter-emission* timeout (`mark-state-unit`, 180 s) kills a perfectly\n * healthy job.\n *\n * Sized against that consumer: 15 s gives ~12 beats of margin inside the\n * 180 s window. Fixed by design, like the bound above — no env knob.\n */\nexport const INFERENCE_HEARTBEAT_MS = 15_000;\n\n/**\n * Called while a provider call is still in flight. Liveness only — the\n * caller repeats its current stage rather than inventing an advancing\n * percentage (D3): nothing here knows how far a single model call has got.\n */\nexport type InferenceHeartbeat = () => void;\n\n/**\n * One span per provider call. Before this, a 411-second detection job was a\n * SINGLE span with no children on a fully instrumented stack — it was not\n * possible to tell extraction from inference from telemetry, which is what\n * made the sibling silent-empty bug expensive to find. Attributes stay to\n * what is known before the answer arrives; token counts are recorded by\n * `recordInferenceUsage` in the client.\n */\nfunction spanned<T>(client: InferenceClient, kind: string, maxTokens: number, work: () => Promise<T>): Promise<T> {\n return withSpan(`inference:${kind}`, work, {\n attrs: {\n 'inference.provider': client.type,\n 'inference.model': client.modelId,\n 'inference.max_tokens': maxTokens,\n },\n });\n}\n\nasync function withTimeout<T>(\n work: (signal: AbortSignal) => Promise<T>,\n meta: { provider: string; model: string; label: string },\n onHeartbeat?: InferenceHeartbeat,\n logger?: Logger,\n): Promise<T> {\n const controller = new AbortController();\n let timer!: ReturnType<typeof setTimeout>;\n const timedOut = new Promise<never>((_, reject) => {\n timer = setTimeout(() => {\n // True cancellation: tear the request down at the transport so it\n // cannot keep running (and billing) against a job that no longer\n // exists — and name the abort in the log, because an invisible\n // abandonment is what let a zombie burn 24+ minutes unrecorded (D3).\n logger?.warn('Aborting in-flight inference call at the timeout bound', {\n provider: meta.provider,\n model: meta.model,\n label: meta.label,\n boundMs: INFERENCE_TIMEOUT_MS,\n });\n controller.abort();\n reject(new InferenceTimeoutError(\n `Inference call timed out after ${INFERENCE_TIMEOUT_MS / 60_000} minutes (${meta.label}) — failing the job to keep the claim loop live`,\n ));\n }, INFERENCE_TIMEOUT_MS);\n timer.unref?.();\n });\n\n // One timer at one site covers every provider call, present and future —\n // putting it in the detection loops instead would re-couple liveness to\n // detection's own structure, which is the coupling this exists to undo.\n let heartbeat: ReturnType<typeof setInterval> | undefined;\n if (onHeartbeat) {\n heartbeat = setInterval(() => {\n try {\n onHeartbeat();\n } catch {\n // A failing progress emit must never take down the inference call\n // it is merely reporting on.\n }\n }, INFERENCE_HEARTBEAT_MS);\n heartbeat.unref?.();\n }\n\n const pending = work(controller.signal);\n try {\n return await Promise.race([pending, timedOut]);\n } catch (err) {\n // The aborted call settles promptly now (AbortError from the transport)\n // rather than minutes later — but its rejection still lands after the\n // race is lost, so it is still swallowed here to keep it from surfacing\n // as an unhandled one and killing the process.\n pending.catch(() => {});\n throw err;\n } finally {\n clearTimeout(timer);\n // Cleared with the timeout, in the same finally: a leaked interval would\n // beat forever on a completed job.\n if (heartbeat) clearInterval(heartbeat);\n }\n}\n\nexport function boundedGenerateWithMetadata(\n client: InferenceClient,\n prompt: string,\n maxTokens: number,\n temperature: number,\n onHeartbeat?: InferenceHeartbeat,\n logger?: Logger,\n): Promise<InferenceResponse> {\n return spanned(client, 'text', maxTokens, () => withTimeout(\n (signal) => client.generateTextWithMetadata(prompt, maxTokens, temperature, signal),\n { provider: client.type, model: client.modelId, label: `${client.type}:${client.modelId}` },\n onHeartbeat,\n logger,\n ));\n}\n\nexport function boundedGenerateStructured<T>(\n client: InferenceClient,\n prompt: string,\n maxTokens: number,\n temperature: number,\n elementSchema: ElementSchema,\n onHeartbeat?: InferenceHeartbeat,\n logger?: Logger,\n): Promise<StructuredResponse<T>> {\n return spanned(client, 'structured', maxTokens, () => withTimeout(\n (signal) => client.generateStructured<T>(prompt, maxTokens, temperature, elementSchema, signal),\n { provider: client.type, model: client.modelId, label: `${client.type}:${client.modelId}` },\n onHeartbeat,\n logger,\n ));\n}\n","/**\n * Failure classification — ABANDONED-INFERENCE P3 (A4, HD2).\n *\n * A retried deterministic failure always costs exactly double: the second\n * attempt of the same request cannot succeed. HD2's ruling is one-sided —\n * only KNOWN-deterministic failures skip the retry budget; everything\n * unrecognized stays retryable (`undefined`), because mis-classifying a\n * transient failure as deterministic silently halves reliability, while the\n * reverse merely costs one wasted attempt (today's behavior).\n *\n * Classification happens HERE, in the worker, where errors are still typed —\n * at the gateway's `job:fail` handler the failure is already a flattened\n * string, and message-regex classification is the drift this module exists\n * to avoid. The class rides the `job:fail` payload; `failJob` consumes it.\n */\n\nimport { isNumber, isObject, isString, RETRY_RULES, type components } from '@semiont/core';\nimport { StructuredReadError } from '@semiont/inference';\nimport { InferenceTimeoutError } from './workers/inference-call';\n\n/** Derived from the spec, not restated: the wire owns this vocabulary\n * (`FailureClass.json`, referenced by job:fail and job:failed alike). */\nexport type FailureClass = components['schemas']['FailureClass'];\n\n/**\n * Marker for failures WE know cannot succeed on a second identical attempt —\n * thrown at the sites that judge the work itself rather than the transport:\n * response truncation despite the derived budget, a media type with nothing\n * to extract. Throw it instead of `Error` wherever that knowledge exists.\n */\nexport class DeterministicJobError extends Error {\n // Typed string, not the literal: subclasses (YieldCollapseError) carry\n // their own name — classification is instanceof, never name-matching.\n override readonly name: string = 'DeterministicJobError';\n}\n\n/**\n * The taxonomy, and its provider coupling — THE part that will drift:\n *\n * - `@anthropic-ai/sdk` errors carry a numeric `status`, and WHICH statuses are\n * worth another attempt is not decided here: it is `RETRY_RULES.job`\n * (RETRY-CLASSIFICATION P2). This file used to restate the same three\n * conditions, which made it a second opinion on a question core already\n * answered — and the census that found it showed that where a bare list and a\n * reasoned rule disagree, the list wins silently. The rule carries the\n * reasoning; this file carries the ONE thing the rule cannot know: that\n * anything else at or above 400 is a rejected request, and re-sending it\n * unchanged is guaranteed waste → deterministic.\n * - Aborts (`APIUserAbortError` from the SDK, `AbortError` from fetch/mock)\n * are our own bound or shutdown tearing the transport down — nothing was\n * judged → transient.\n * - Ollama surfaces plain `Error`s (no status) and network failures as\n * `TypeError: fetch failed`; the SDK's `MessageStream terminated` is a\n * dropped connection. All land `undefined` → retryable, the safe default.\n * - `@semiont/inference`'s `StructuredReadError` carries the stop reason\n * because the cause classifies differently: `max_tokens` is truncation of\n * an over-demanded answer — the adapter throws before the caller's\n * `assertNotTruncated` can see the stop reason, so the classification\n * must happen on the typed error itself (measured live 2026-09-02) —\n * while any other reason is model misbehavior a retry may fix.\n */\nexport function classifyFailure(error: unknown): FailureClass | undefined {\n if (error instanceof DeterministicJobError) return 'deterministic';\n if (error instanceof InferenceTimeoutError) return 'transient';\n if (error instanceof StructuredReadError && error.stopReason === 'max_tokens') return 'deterministic';\n // A `StructuredReadError` with any OTHER stop reason falls through to\n // `undefined` — retryable, and DECIDED rather than defaulted\n // (RETRY-CLASSIFICATION P2, 2026-09-12).\n //\n // It only reaches here having exhausted `MAX_SUBDIVISION_DEPTH`, so\n // `subdividable()` has already argued the opposite: at\n // `DETECTION_TEMPERATURE` 0 the identical call returns the identical failure.\n // That argument was decisive while chunk boundaries were fixed up front. It is\n // not any more — CHUNK-GRAIN-RESUME HD2 (option C) seeds a resumed unit's size\n // from the checkpoint and then takes one shrink step, so the retry re-cuts the\n // poison piece at a DIFFERENT size and can genuinely come out differently. The\n // price of being wrong fell with it: one chunk, not the whole prefix.\n //\n // It stays `undefined` rather than becoming `'transient'` on purpose. The wire\n // vocabulary has two values, and this is neither: it is not weather, it is\n // \"retryable because the next attempt reads different input\". Claiming\n // `transient` would assert an environmental cause nobody established, and\n // absent already means exactly what is true — unrecognised, so retryable.\n if (!isObject(error)) return undefined;\n\n const name = isString(error.name) ? error.name : '';\n if (name === 'DeterministicJobError') return 'deterministic';\n if (name === 'APIUserAbortError' || name === 'AbortError') return 'transient';\n\n const status = isNumber(error.status) ? error.status : undefined;\n if (status !== undefined) {\n if (RETRY_RULES.job.retryable({ status })) return 'transient';\n if (status >= 400) return 'deterministic';\n }\n\n return undefined;\n}\n","/**\n * Adaptive chunk sizing (DETECTION-QUALITY-THROUGHPUT P2) — the sizing RULE.\n *\n * `deriveDetectionBudget` picks ONE input size for every document from provider\n * limits alone. It cannot see the document, so it is sized for a worst-case\n * dense one (input = half the output budget) — and a sparse document then pays\n * that pessimism as many tiny, overhead-dominated calls. This adjusts the chunk\n * size AS THE UNIT RUNS, from what each call actually produced, so a document\n * that turns out sparse grows into bigger chunks and a dense stretch pulls back.\n *\n * It steers ONE number: how full of the output budget each chunk came in. That\n * is the whole model, and it is enough because both hard failures are already\n * caught downstream by `callChunkSubdividing` — a chunk that truncates is\n * halved-and-retried, a chunk that outruns the guillotine times out and is\n * halved-and-retried. So the sizer does not model truncation or duration; it\n * only keeps chunks in a band that uses the budget well, and the net below it\n * forgives an overshoot. `bounds.ceiling` is therefore the WINDOW fit, not either\n * derived input size: on a shared window both of those come back at exactly the\n * density guess this function exists to replace, leaving nothing to grow into. That safety net is why this is a step rule and not a\n * control loop (user direction, 2026-09-04: keep it simple).\n *\n * \"Maximize, don't predict\": the only thing that grows a chunk is a MEASURED\n * under-use of the budget. No document statistic, no a-priori density model\n * (#1121's ban). Re-evaluated every chunk, so it tracks a gradient (sparse\n * intro → dense index) rather than betting the run on the first sample.\n *\n * Pure and feedback-only — unit-testable in isolation. The loop that applies it is\n * `runAdaptiveChunks` in `detection-chunking.ts`, which both detection paths drive:\n * `extractEntities` (reference) and `detectInChunks` (highlight, comment, assessment,\n * tag). It cuts chunk N+1 only after chunk N has reported, which is the only ordering\n * in which this function's output can reach anything.\n */\n\n/** What one chunk's call produced — the minimum the sizer needs. Duration and\n * input are NOT here: the guillotine and truncation are the hard bounds, both\n * backstopped by subdivision, so utilization is the only signal that sizing\n * acts on. (P1 telemetry records the fuller picture separately.) */\nexport interface CallOutcome {\n /** Provider-reported output tokens for the chunk, ABSENT when the provider\n * reported none.\n *\n * Absent is not zero. `usage` is optional on the inference interface and both\n * real clients emit it conditionally, so a run against a provider that stays\n * silent would read as \"this chunk produced nothing\", i.e. 0%% of the budget,\n * i.e. grow — every chunk, to the ceiling, on no evidence whatsoever. That is\n * the precise inverse of \"maximize, don't predict\". Unmeasured holds. */\n outputTokens?: number;\n /** The chunk hit a size-shaped bound (truncation or the guillotine), even if\n * subdivision then recovered it. Forces a shrink regardless of the count —\n * the signal that the last size was too big for this stretch. */\n truncated: boolean;\n}\n\n/** Provider-DERIVED sizing bounds for one job — not tuning, not policy. From\n * `deriveDetectionBudget` and the model's window; recomputed per job, never\n * user-facing. */\nexport interface SizingBounds {\n /** Smallest useful input chunk (tokens) — the overlap-derived floor. */\n floor: number;\n /** Largest input chunk (tokens) — the window-fit safety cap; never exceeded. */\n ceiling: number;\n /** The output budget one call is given. Utilization is measured against it. */\n outputBudget: number;\n}\n\n/**\n * The tuning knobs — the numbers that decide how aggressively chunk size chases\n * measured output usage. Grouped and named deliberately, because these are\n * POLICY, not derivation, and the likely future is that they become\n * user/admin-tunable, perhaps per inference provider (a fast, cheap model\n * tolerates more aggressive growth than a slow, dear one).\n *\n * That future changes where a `ChunkSizingPolicy` comes FROM — provider config,\n * a settings surface — not the sizing function, which already takes it as a\n * value, nor the caller's shape. There is exactly ONE today, the default below.\n * No config system, no env var, no per-provider map yet: just a single named\n * home, so when the need is real there is one obvious thing to make configurable\n * and one obvious place to select it (the caller holds the `InferenceClient`,\n * hence the provider).\n */\nexport interface ChunkSizingPolicy {\n /** Below this fraction of the output budget, the chunk left room — grow. */\n growBelow: number;\n /** Above this fraction, the chunk is near the edge — ease off before it\n * truncates and costs a subdivision retry. */\n shrinkAbove: number;\n /** Geometric step when growing (>1). Geometric so a genuinely sparse document\n * reaches its ceiling in a few steps and a bad step costs one correction. */\n growFactor: number;\n /** Geometric step when easing off (<1). Also the response to a truncation. */\n shrinkFactor: number;\n}\n\n/** The one policy in effect today. Coarse on purpose — subdivision forgives a\n * wrong guess — and every value is a candidate for the tunable surface above. */\nexport const DEFAULT_CHUNK_SIZING_POLICY: ChunkSizingPolicy = {\n growBelow: 0.5,\n shrinkAbove: 0.8,\n growFactor: 1.5,\n shrinkFactor: 0.7,\n};\n\nfunction clamp(value: number, lo: number, hi: number): number {\n return Math.max(lo, Math.min(hi, value));\n}\n\n/**\n * Propose the next chunk's input size from what the last chunk produced.\n *\n * A truncated chunk, or one whose output filled more than `shrinkAbove` of the\n * budget, eases the next chunk down; one that used less than `growBelow` grows\n * it; in between — or with nothing measured at all — hold. Always clamped to `[floor, ceiling]`. That is the whole\n * rule — the hard bounds are subdivision's job, not this function's.\n */\nexport function nextChunkSize(\n outcome: CallOutcome,\n current: number,\n bounds: SizingBounds,\n policy: ChunkSizingPolicy = DEFAULT_CHUNK_SIZING_POLICY,\n): number {\n const grow = () => clamp(Math.floor(current * policy.growFactor), bounds.floor, bounds.ceiling);\n const shrink = () => clamp(Math.floor(current * policy.shrinkFactor), bounds.floor, bounds.ceiling);\n const hold = () => clamp(current, bounds.floor, bounds.ceiling);\n\n if (outcome.truncated) return shrink();\n // No measurement, no move. A truncation above is evidence even without a\n // count; an absent count on its own is not.\n if (outcome.outputTokens === undefined) return hold();\n // A zero/absent budget cannot inform utilization; hold rather than divide by\n // zero or grow blindly.\n if (bounds.outputBudget <= 0) return hold();\n\n const utilization = outcome.outputTokens / bounds.outputBudget;\n if (utilization < policy.growBelow) return grow();\n if (utilization > policy.shrinkAbove) return shrink();\n return hold();\n}\n","/**\n * Detection budget derivation — pure window arithmetic over the inference\n * provider's published limits. No hand-tuned chunk constants, no density or\n * yield modeling: document content never enters this arithmetic. The guard\n * for the pathological tail (a chunk whose annotation JSON still overflows\n * the output budget) is `assertNotTruncated` below — invoked per chunk by\n * the callers on every response, not a prediction here.\n *\n * Provider shapes (see `@semiont/inference` interface.ts):\n * - Shared window (Ollama): the provider publishes\n * `maxOutputTokens === contextTokens` — prompt and response share one\n * `num_ctx`. What remains after the prompt scaffold is split\n * input:output = 1:2 (annotation JSON echoes each span plus a fixed\n * key/context envelope, so output needs the larger share). The 1:2 ratio\n * is the plan's one allocation policy — doc-independent, tuned only on\n * live evidence.\n * - Separate ceilings (Anthropic): output takes its full ceiling (duration-\n * capped below), and input follows the same 1:2 allocation — never more\n * than half the output budget. Input does NOT get \"the rest of the\n * window\": measured 2026-09-02, a window-sized chunk of entity-dense text\n * demands more output than any budget holds, so the model grinds toward\n * max_tokens for minutes (killed at the call bound as a \"stall\") or\n * collapses to the degenerate []. Large documents chunk; that is the fix,\n * not a cost.\n */\n\nimport { chunkText, cutChunk, type ChunkingConfig, type Logger, type UnitCursor } from '@semiont/core';\nimport { StructuredReadError, type InferenceLimits, type TokenUsage } from '@semiont/inference';\nimport { recordDetectionCall } from '@semiont/observability';\nimport { DeterministicJobError } from '../../failure-class';\nimport { INFERENCE_TIMEOUT_MS, InferenceTimeoutError } from '../inference-call';\nimport { nextChunkSize, type CallOutcome, type SizingBounds } from './chunk-size-controller';\n\n/**\n * A `max_tokens` stop reason means the model's JSON was cut off mid-stream.\n * Structured output serializes whatever was generated, so that still yields\n * a syntactically-valid but incomplete array — it would parse cleanly and\n * silently under-report. Fail the job loudly instead. With derived budgets\n * this fires only on pathological annotation density.\n *\n * ONE decider for every detection path (entity extraction and the four\n * motivations) — the classification must not diverge between them: same\n * input truncates the same way, so a retry is guaranteed waste and the\n * throw carries the deterministic class (ABANDONED-INFERENCE P3, A4).\n */\nexport function assertNotTruncated(response: { stopReason: string }, label: string, at: number, totalChars: number, outputBudget: number): void {\n if (response.stopReason === 'max_tokens') {\n throw new DeterministicJobError(`${label} response truncated (max_tokens) at character ${at} of ${totalChars} despite the derived output budget of ${outputBudget} tokens — failing the job rather than under-reporting annotations.`);\n }\n}\n\n/**\n * `reconcileSelector` disambiguates a span with up to 64 chars of prefix and\n * 64 of suffix (the annotation-selector schema). Overlap must let a span\n * sitting at a chunk boundary carry that context — plus a span allowance of\n * the same order — into the adjacent chunk. Schema-derived, not tuned.\n */\nconst SELECTOR_CONTEXT_CHARS = 64;\nconst OVERLAP_CHARS =\n SELECTOR_CONTEXT_CHARS + // prefix\n SELECTOR_CONTEXT_CHARS + // suffix\n 2 * SELECTOR_CONTEXT_CHARS; // span allowance\n/** ~4 chars/token — the same heuristic `estimateTokens`/`chunkText` use. */\nconst OVERLAP_TOKENS = Math.ceil(OVERLAP_CHARS / 4);\n\n/**\n * One temperature for every detection call. Detection copies spans verbatim\n * against a closed type vocabulary — a fidelity task, so determinism is the\n * right default (reproducible re-runs). Measured equal to hotter settings\n * on yield and consistency at production call sizes.\n */\nexport const DETECTION_TEMPERATURE = 0;\n\n/**\n * Assumed worst-case output rate for providers that publish none\n * (OLLAMA-DETECTION-TESTING P3b, from F9). Rate-silent providers (Ollama —\n * local hardware, rate unknowable a priori) previously got NO duration bound:\n * capacity-sizing handed a 262K-window model a ~174K-token output budget, and\n * a repetition loop then burned the full 10-minute guillotine as TRANSIENT —\n * retried identically. Under a duration-shaped cap the same loop dies in\n * minutes as max_tokens → deterministic → subdividable: the useful failure.\n *\n * 30 tok/s. P2 measured 36–90 tok/s across three local model families\n * (qwen3.5:9b ~36, gemma4:26b ~50, gemma4:e2b ~90), and the half-bound rule\n * in the cap below means real rates down to 15 tok/s still finish inside the\n * guillotine — anything slower is wedged, not working, which is exactly what\n * the guillotine is for. A conservative floor, deliberately NOT per-model:\n * F8 showed per-model thresholds chase noise. Owned policy constant, same\n * status as the 1:2 split and DETECTION_TEMPERATURE.\n */\nexport const ASSUMED_OUTPUT_TOKENS_PER_HOUR = 108_000;\n\n/**\n * The count-verifier band (OLLAMA-DETECTION-TESTING P3c, user-ratified\n * 2026-09-05): an extraction that found fewer than 1/BAND of the mentions a\n * cheap count call reports is flagged as silent yield collapse (F7). The band\n * is LOAD-BEARING at 2: F12 measured the same model flipping between\n * entity-like and mention-like enumeration on the same corpus, and that\n * legitimate judgment spread must fit INSIDE the band — probe evidence (n=6)\n * put every healthy ratio at 0.68–0.88 and every collapse at 0.18–0.41.\n */\nexport const YIELD_COLLAPSE_BAND = 2;\n\n/**\n * The silent-yield-collapse verdict (F7): a schema-clean, done-reason-clean\n * extraction that found a fraction of what a cheap count call says is present.\n * Extends DeterministicJobError because the collapse is MEASURED deterministic\n * — bit-identical across retries and across budget regimes — so a retry is\n * guaranteed waste; the classification and the size-floored subdivision\n * descent both follow from the base class. It diverges from truncation at the\n * size floor: NO re-roll (a same-size re-roll provably returns the identical\n * collapse), and — ruled 2026-09-05 after P4 attempt 1, amending the original\n * fail-the-job invariant — the floor ACCEPTS the flagged piece's `salvage`\n * loudly rather than failing the unit: one hostile ~530-char stretch had\n * discarded ~20 chunks of good extraction, and at floor sizes the count's\n * evidence sits far below anything the probe validated. The warning and the\n * 'collapsed' telemetry rows are the durable record; re-detection heals.\n */\n/** A floor-accepted piece's evidence: what extraction found against what a\n * count call reported, over a piece of this size. Facts only — never a\n * judgment against an expected yield. */\nexport interface UnderReportedPiece {\n found: number;\n counted: number;\n pieceChars: number;\n}\n\nexport class YieldCollapseError extends DeterministicJobError {\n override readonly name = 'YieldCollapseError';\n /** What the flagged extraction DID find — every span write-time-verified,\n * so discarding it at the floor would add loss on top of the under-report.\n * Carried on the error because the flag site cannot know whether descent\n * remains possible; the floor is the subdivider's knowledge. The verdict\n * rides beside it so an acceptance can report evidence, not a message\n * string. */\n constructor(message: string, readonly salvage: unknown[], readonly verdict: UnderReportedPiece) {\n super(message);\n }\n}\n\nexport interface DetectionBudget {\n /** The chunk size a run OPENS at (tokens), plus the overlap every cut keeps.\n * Opening, not fixed: `runAdaptiveChunks` moves the size within `bounds` as\n * the document reports what it actually costs. */\n chunking: ChunkingConfig;\n /** Pass as `maxTokens` on every per-chunk inference call. */\n outputBudget: number;\n /** How far the sizer may move the chunk size, in either direction. Derived\n * from the same provider arithmetic — never tuning. */\n bounds: SizingBounds;\n}\n\n/**\n * Derive the per-call input/output token budgets for a detection job from the\n * provider's actual limits and the measured prompt scaffold.\n *\n * @param limits - the provider's published ceilings (`client.limits()`)\n * @param scaffoldTokens - tokens of the prompt template around the content,\n * measured from the actually-built template (e.g. `estimateTokens(build(''))`)\n * @param typesPerCall - how many entity types (or equivalent span families)\n * ONE call asks for: output demand scales with it, so the allocation\n * divides by it. The per-type loop passes 1; a future multi-type batch\n * passes its batch size.\n * @throws when the window is too small to hold the scaffold plus a useful\n * chunk — fail-loud, same family as the truncation and window guards.\n */\nexport function deriveDetectionBudget(\n limits: InferenceLimits,\n scaffoldTokens: number,\n typesPerCall: number,\n): DetectionBudget {\n const { contextTokens, maxOutputTokens } = limits;\n const available = contextTokens - scaffoldTokens;\n\n let inputBudget: number;\n let outputBudget: number;\n\n if (maxOutputTokens >= contextTokens) {\n // Shared window: 1:2 input:output split of what the scaffold leaves.\n inputBudget = Math.floor(available / 3);\n outputBudget = available - inputBudget;\n } else {\n outputBudget = maxOutputTokens;\n inputBudget = contextTokens - outputBudget - scaffoldTokens;\n if (inputBudget <= 0) {\n // Degenerate separate-ceilings shape (output ceiling nearly fills the\n // window): fall back to the shared split rather than starving input.\n inputBudget = Math.floor(available / 3);\n outputBudget = available - inputBudget;\n }\n }\n\n // Duration floor (ABANDONED-INFERENCE P4 HD3; universalized by\n // OLLAMA-DETECTION-TESTING P3b): cap each call's output at what the\n // provider's worst-case rate finishes inside our own inference bound — a\n // call projected past the 10-minute guillotine is planned-to-fail — and\n // scale input by the same factor, so the input:output ratio (and with it\n // the per-chunk truncation risk profile) is exactly the capacity\n // solution's. Capacity says what CAN fit in one call; this says what\n // SHOULD. EVERY provider gets the bound: the published rate when there is\n // one, the conservative assumed floor when there is not — a rate-silent\n // provider with no bound turned repetition loops into hour-long transient\n // burns (F9; see ASSUMED_OUTPUT_TOKENS_PER_HOUR). It is a floor on chunk\n // count, never a raise (a tighter capacity budget is left alone). Side\n // effect worth knowing: on Anthropic this lands every detection call at\n // or under the SDK's non-streaming threshold — off the MessageStream path\n // the original `terminated` failure arrived on.\n const outputTokensPerHour = limits.outputTokensPerHour ?? ASSUMED_OUTPUT_TOKENS_PER_HOUR;\n {\n // Half the bound, not all of it: at the full bound the slowest\n // legitimate full-budget generation lands exactly on the guillotine.\n // At half, the guillotine fires only on calls at least 2× slower than\n // the provider's own worst-case model — wedged, not working.\n const durationSafeOutput = Math.floor(outputTokensPerHour * (INFERENCE_TIMEOUT_MS / 2) / 3_600_000);\n if (outputBudget > durationSafeOutput) {\n inputBudget = Math.floor(inputBudget * (durationSafeOutput / outputBudget));\n outputBudget = durationSafeOutput;\n }\n }\n\n // The 1:2 allocation policy, applied to every provider shape (the shared\n // split satisfies it by construction; separate ceilings did not). Output\n // must hold an annotation echo of every span in the chunk plus its\n // key/context envelope, so a chunk larger than half the output budget is\n // a call whose honest answer cannot fit — the failure measured live on\n // 2026-09-02 (silent multi-minute grinds to max_tokens, degenerate []).\n // The demand is PER TYPE ASKED FOR, so a K-type call divides the input\n // share by K. Still no density modeling: same one policy, content never\n // enters.\n // The window fit: the largest input that still leaves the WHOLE output budget\n // room beside it. This is the sizer's ceiling, and it is the only hard bound\n // on input there is — the two derivations above are not.\n //\n // The 1:2 rule below is a GUESS about how much output a chunk will demand,\n // and on a shared window it is baked into the 1:3 split as well, which is why\n // the ceiling cannot be read off either of them (both come back at exactly\n // the guess, leaving no room to grow into on precisely the self-hosted\n // providers the live gate runs). The duration bound is real but bounds\n // GENERATION, so it belongs to `outputBudget`; it reaches input only through\n // the same ratio guess. `nextChunkSize` exists to replace that guess with\n // measurement, so the guess opens the run and the window caps it.\n const capacityInput = contextTokens - scaffoldTokens - outputBudget;\n\n inputBudget = Math.min(inputBudget, Math.floor(outputBudget / (2 * typesPerCall)));\n\n if (inputBudget <= OVERLAP_TOKENS) {\n throw new Error(\n `Inference window too small for detection: context ${contextTokens} tokens minus scaffold ${scaffoldTokens} leaves an input budget of ${inputBudget} (need > ${OVERLAP_TOKENS}). Use a model with a larger context window or reduce the prompt scaffold.`,\n );\n }\n\n return {\n chunking: { chunkSize: inputBudget, overlap: OVERLAP_TOKENS },\n outputBudget,\n bounds: {\n // Two overlaps: at the floor a chunk is still half text it has not seen\n // before, so the cursor keeps making progress rather than re-reading its\n // own tail. Clamped under the opening size, because a cramped window can\n // derive an opening below even that — and a floor above the opening would\n // clamp every proposal upward, silently reversing the sizer.\n floor: Math.min(inputBudget, 2 * OVERLAP_TOKENS),\n // Never below the opening: a degenerate window can make the fit\n // arithmetic smaller than the size already chosen, and a ceiling under\n // the opening would clamp every proposal DOWN on the first call.\n ceiling: Math.max(inputBudget, capacityInput),\n outputBudget,\n },\n };\n}\n\n/** One chunk handed out by `runAdaptiveChunks`, with the cursor either side of\n * it. `at`/`next` over `totalChars` is exact progress — and, once\n * CHUNK-GRAIN-RESUME lands, the checkpoint identity a variable boundary forces\n * (an ordinal cannot name a chunk whose size is decided while the job runs). */\n/**\n * The half of a `UnitCursor` the DETECTION layer can honestly report: where the\n * walk stands and how it is cutting. The tallies belong to the processor, which\n * owns unit identity and unit counts — this loop counts chunks, not annotations,\n * and a zero it invented would be indistinguishable from a real one.\n */\nexport type ChunkCursor = Pick<UnitCursor, 'next' | 'size'>;\n\nexport interface AdaptiveChunk {\n piece: string;\n /** The token size this piece was cut at. Hand it to `callChunkSubdividing`:\n * a descent halves from the size that actually failed, not from the size the\n * run opened at, which adaptivity has long since left behind. */\n size: number;\n /** Characters consumed BEFORE this chunk — the in-flight liveness position. */\n at: number;\n /** Characters consumed once this chunk completes — the boundary position. */\n next: number;\n /** The document's length. */\n totalChars: number;\n}\n\n/**\n * Walk a document in chunks whose size is decided by the chunks before them.\n *\n * `chunkText` fixes every boundary up front from provider limits alone, which\n * is why the sizing rule could exist for a week and change nothing: a\n * measurement taken on chunk N had nowhere to land. Here chunk N+1 is cut only\n * after chunk N has reported, so the run opens at the static density guess and\n * then moves — a sparse document climbing toward the window's real capacity\n * (fewer, bigger calls), a dense one easing off before it pays a subdivision.\n *\n * `onChunk` returns what the chunk cost. It is awaited, so the caller's own\n * durability (committing the chunk's annotations) still gates the next cut, and\n * a throw stops the walk where it stands rather than advancing past unprocessed\n * text.\n *\n * `resume` restarts a unit an earlier attempt left partway (CHUNK-GRAIN-RESUME\n * P3) — the checkpoint P2 made durable, spent. Both halves of it matter and they\n * are spent differently: the position is taken as given, while the size is\n * seeded and then stepped ONCE, as if the last outcome had been a failure. It\n * was: the job died. Opening at the budget instead would throw away the\n * calibration the dead attempt paid for over its earlier chunks, and seeding\n * unchanged would re-cut the identical piece — which at `DETECTION_TEMPERATURE`\n * 0 returns the identical answer, failure included (HD2, option C).\n */\nexport async function runAdaptiveChunks(\n text: string,\n budget: DetectionBudget,\n onChunk: (chunk: AdaptiveChunk) => Promise<CallOutcome>,\n resume?: UnitCursor,\n): Promise<void> {\n let at = resume?.next ?? 0;\n let size = resume\n ? nextChunkSize({ truncated: true }, resume.size, budget.bounds)\n : budget.chunking.chunkSize;\n\n while (at < text.length) {\n const { piece, next } = cutChunk(text, at, { chunkSize: size, overlap: budget.chunking.overlap });\n const outcome = await onChunk({ piece, size, at, next, totalChars: text.length });\n at = next;\n size = nextChunkSize(outcome, size, budget.bounds);\n }\n}\n\n/**\n * Depth cap for TIMEOUTS only: a call still timing out on a quarter-sized\n * chunk is not a size problem, and timeouts classify transient — the\n * job-level retry is their second chance. Truncations descend by size\n * instead (see `callChunkSubdividing`).\n */\nexport const MAX_SUBDIVISION_DEPTH = 2;\n\n/** The failures a smaller chunk can plausibly fix. An unreadable response\n * that stopped naturally is model misbehavior, not size. */\n/** The F3 shape: unreadable output whose stop reason is UNKNOWN (done_reason\n * absent). Held not-subdividable through P3a for lack of evidence; the live\n * gate then reproduced it twice on real text (2026-09-03 at ~21K chars, P4\n * attempt 2 at ~4.5K, mid-descent, adjacent to max_tokens truncations that\n * healed by subdividing) and measured the retry-at-same-size alternative as a\n * deterministic 34 s burn. Size-shaped on the evidence — descends like\n * truncation, but with no floor re-roll (near-deterministic, nothing salvaged)\n * and unchanged retryable classification (a genuinely broken server still\n * deserves its budget). */\nfunction unknownUnreadable(error: unknown): boolean {\n return error instanceof StructuredReadError && error.stopReason === 'unknown';\n}\n\n/**\n * The model FINISHED and still emitted unparseable JSON — not cut off, not\n * stopped for an unknown reason. Subdividable, but DEPTH-capped rather than\n * size-floored, and the two halves of that are separately earned:\n *\n * Subdividable, because one of these killed a 26-minute attempt at chunk 25 of\n * 49 while the preceding 24 chunks parsed cleanly on the same prompt and model\n * — so the malformation was content-triggered drift, which changing the input\n * can fix. A same-size retry cannot: DETECTION_TEMPERATURE is 0, so the\n * identical call returns the identical response (the same reason a collapse\n * verdict gets no re-roll).\n *\n * Depth-capped rather than size-floored, because if the drift is instead\n * systematic — the model answering this prompt shape wrongly everywhere — every\n * extra level re-reads the same content for the same verdict. Measured, the two\n * settings often coincide (the descent aborts at the first failing sub-piece\n * instead of exploring siblings, so cost is linear in depth, not geometric),\n * and where they differ the cap is the cheaper wrong answer. Timeouts are\n * capped on the same reasoning.\n */\nfunction unreadableDespiteFinishing(error: unknown): boolean {\n return error instanceof StructuredReadError && error.stopReason === 'end_turn';\n}\n\nfunction subdividable(error: unknown): boolean {\n return error instanceof InferenceTimeoutError || truncation(error)\n || unknownUnreadable(error) || unreadableDespiteFinishing(error);\n}\n\n/** Truncation in either surface: parsed-but-flagged (`assertNotTruncated`'s\n * `DeterministicJobError`) or cut off mid-JSON (`StructuredReadError` with\n * `max_tokens`). */\nfunction truncation(error: unknown): boolean {\n return (\n error instanceof DeterministicJobError ||\n (error instanceof StructuredReadError && error.stopReason === 'max_tokens')\n );\n}\n\n/**\n * Run one chunk's inference call, subdividing IN PLACE when it fails in a\n * way a smaller chunk can fix — instead of burning the whole attempt to\n * come back at the same size. Sub-pieces re-use the caller's overlap, so\n * spans straddling a split are caught twice and fall to the downstream\n * span-keyed dedupe. On a failure subdivision cannot fix, the ORIGINAL\n * error propagates so classification sees what actually happened.\n */\n/** What `callChunkSubdividing` produced AND what it cost — the second half is\n * what `runAdaptiveChunks` sizes the next chunk from. Accumulated across the\n * whole descent, because the caller's own `call` closure sees one piece at a\n * time and cannot know a subdivision happened at all. */\nexport interface SubdividedCall<T> {\n items: T[];\n outcome: CallOutcome;\n}\n\nexport interface ChunkCallResult<T> {\n items: T[];\n /** The provider's own token counts, when it reported any. Never estimated. */\n usage?: TokenUsage;\n /** The count-verifier's expectation for this piece, when one was priced. */\n counted?: number;\n}\n\n/** Which shape of failure this was — they demand opposite responses, so the\n * history must tell them apart: truncation descends by size, a timeout fails\n * fast, anything else is not size-shaped at all. */\nfunction outcomeOf(error: unknown): 'truncated' | 'timeout' | 'collapsed' | 'error' {\n // Collapse before truncation: it IS a DeterministicJobError (so the\n // truncation check would swallow it), but in the history the two are\n // different facts — one is output overflow, the other silent under-report.\n if (error instanceof YieldCollapseError) return 'collapsed';\n if (truncation(error)) return 'truncated';\n if (error instanceof InferenceTimeoutError) return 'timeout';\n return 'error';\n}\n\nexport async function callChunkSubdividing<T>(\n label: string,\n chunk: string,\n chunking: ChunkingConfig,\n call: (piece: string) => Promise<ChunkCallResult<T>>,\n logger?: Logger,\n /** A floor-accepted piece's evidence, as it is accepted. Only the floor\n * reports: a collapse healed by descent is telemetry, not result. */\n onUnderReport?: (verdict: UnderReportedPiece) => void,\n /** Each ACCEPTED piece's count-verifier expectation — successes, floor\n * re-rolls, floor-accepted salvage. A flagged piece that descends reports\n * nothing: its children's counts replace it, or the same text is priced\n * twice. */\n onCounted?: (counted: number) => void,\n): Promise<SubdividedCall<T>> {\n // The chunk's cost, summed over however many calls its descent takes. Both\n // are recorded where EVERY call passes, so a subdivision cannot hide from\n // them.\n let outputTokens = 0;\n let sizeShaped = false;\n // A descent's calls must ALL report usage for the sum to mean anything: a\n // partial sum under-counts, and under-counting biases toward growth — the\n // one direction that costs a truncation to discover was wrong.\n let callsMade = 0;\n let callsMeasured = 0;\n // One telemetry record per model call, successes AND failures\n // (DETECTION-QUALITY-THROUGHPUT P1). This is the only place `depth` and\n // `reroll` exist, so it is the only place a complete record can be written.\n async function recorded(piece: string, depth: number, reroll: boolean): Promise<ChunkCallResult<T>> {\n const start = performance.now();\n try {\n const result = await call(piece);\n callsMade += 1;\n if (result.usage) {\n callsMeasured += 1;\n outputTokens += result.usage.outputTokens;\n }\n recordDetectionCall({\n label, pieceChars: piece.length, durationMs: performance.now() - start,\n items: result.items.length, depth, reroll, outcome: 'success',\n ...(result.usage ? { inputTokens: result.usage.inputTokens, outputTokens: result.usage.outputTokens } : {}),\n });\n // A success is always accepted — only failures subdivide.\n if (result.counted !== undefined) onCounted?.(result.counted);\n return result;\n } catch (error) {\n // A failed call still cost its input and its wall time — the descent's\n // price is invisible without it.\n recordDetectionCall({\n label, pieceChars: piece.length, durationMs: performance.now() - start,\n items: 0, depth, reroll, outcome: outcomeOf(error),\n });\n throw error;\n }\n }\n\n async function attempt(piece: string, chunkSize: number, depth: number): Promise<T[]> {\n try {\n return (await recorded(piece, depth, false)).items;\n } catch (error) {\n if (!subdividable(error)) throw error;\n // Size-shaped, so the size was wrong — true whether or not the descent\n // below rescues it. The next chunk must not be cut at a size this one\n // already paid to discover was too big.\n sizeShaped = true;\n const half = Math.floor(chunkSize / 2);\n // Truncation descends by SIZE: demand halves with each subdivision,\n // so descent terminates — and list-dense text (registers, indexes)\n // honestly yields several times its input in annotation output, so\n // the descent must go as deep as the content demands. The floor\n // derives from the overlap constant; below it even solid names fit\n // the budget. Timeouts stay depth-capped and fail fast.\n //\n // A descent must also actually CHANGE the input: once a piece fits\n // inside the smaller chunk size, re-chunking returns it unchanged, and\n // at temperature 0 the identical call returns the identical failure —\n // measured live (P4 attempt 1: one 572-char piece \"descended\" through\n // three depths, same verdict each time). A piece that cannot shrink is\n // AT its floor, whatever the arithmetic floor says.\n const pieces = chunkText(piece, { chunkSize: half, overlap: chunking.overlap });\n const shrinks = pieces.length > 1 || pieces[0] !== piece;\n // Size-shaped failures (truncation, collapse, the F3 unknown-unreadable)\n // descend to the SIZE floor; only timeouts are depth-capped — the F3 fix\n // must reach failures that first appear mid-descent (measured at depth 3).\n const canDescend = shrinks && (truncation(error) || unknownUnreadable(error)\n ? half > 2 * OVERLAP_TOKENS\n : depth < MAX_SUBDIVISION_DEPTH);\n if (!canDescend) {\n // A collapse verdict at the floor is ACCEPTED, loudly (ruled\n // 2026-09-05): its salvage flows through with a warning instead of\n // one hostile piece discarding the whole unit's work. No re-roll —\n // the collapse is deterministic, a same-size retry changes nothing.\n if (error instanceof YieldCollapseError) {\n logger?.warn('Floor-size piece still flagged as collapsed — accepting its under-reported salvage and continuing', {\n pieceChars: piece.length,\n salvaged: error.salvage.length,\n error: error.message,\n });\n onUnderReport?.(error.verdict);\n onCounted?.(error.verdict.counted);\n return error.salvage as T[];\n }\n // At the size floor honest overflow is impossible, so truncation\n // here is a degeneration loop — a sampling accident. One same-size\n // re-roll; a second truncation propagates. Timeouts get no re-roll.\n if (!truncation(error)) throw error;\n logger?.warn('Floor-size piece truncated — re-rolling once before giving up', {\n pieceChars: piece.length,\n error: error instanceof Error ? error.message : String(error),\n });\n return (await recorded(piece, depth, true)).items;\n }\n logger?.warn('Chunk call failed at a size-shaped bound — subdividing and retrying smaller', {\n depth: depth + 1,\n pieceChars: piece.length,\n nextChunkSizeTokens: half,\n error: error instanceof Error ? error.message : String(error),\n });\n const collected: T[] = [];\n for (const p of pieces) {\n collected.push(...(await attempt(p, half, depth + 1)));\n }\n return collected;\n }\n }\n const items = await attempt(chunk, chunking.chunkSize, 0);\n const measured = callsMade > 0 && callsMeasured === callsMade;\n return {\n items,\n outcome: { truncated: sizeShaped, ...(measured ? { outputTokens } : {}) },\n };\n}\n","/**\n * Prompt builders for annotation detection motivations\n *\n * Provides static methods to build AI prompts for each Web Annotation motivation type.\n * Extracted from worker implementations to centralize prompt logic.\n *\n * Locale handling: builders take two optional BCP-47 tags. `language` is the\n * locale the *generated body text* should be written in (sourced from the user's\n * UI locale); `sourceLanguage` is the locale of the *text being analyzed*\n * (sourced from `ResourceDescriptor` / its primary representation). They are\n * independent — see `types.ts` \"Locale conventions\" for the full discussion.\n */\n\nimport { getLocaleEnglishName } from '@semiont/core';\n\nfunction languageName(tag: string): string {\n return getLocaleEnglishName(tag) || tag;\n}\n\n/** Returns \"Source text language: <Name>.\" or empty string. */\nfunction sourceLanguageGuidance(sourceLanguage?: string): string {\n if (!sourceLanguage) return '';\n return `\\n\\nSource text language: ${languageName(sourceLanguage)}.`;\n}\n\n/** Returns \"Write your <kind> in <Name>.\" or empty string. */\nfunction bodyLanguageGuidance(language: string | undefined, kind: string): string {\n if (!language || language === 'en') return '';\n return `\\n\\nIMPORTANT: Write your ${kind} in ${languageName(language)}.`;\n}\n\nexport class MotivationPrompts {\n /**\n * Build a prompt for detecting comment-worthy passages\n *\n * @param content - The text content to analyze — a chunk sized by the\n * caller from derived provider limits; NEVER re-truncated here (a\n * builder-level clip is silent input loss — see #738)\n * @param instructions - Optional user-provided instructions\n * @param tone - Optional tone guidance (e.g., \"academic\", \"conversational\")\n * @param density - Optional target number of comments per 2000 words\n * @returns Formatted prompt string\n */\n static buildCommentPrompt(\n content: string,\n instructions?: string,\n tone?: string,\n density?: number,\n language?: string,\n sourceLanguage?: string\n ): string {\n let prompt: string;\n const sourceLang = sourceLanguageGuidance(sourceLanguage);\n const bodyLang = bodyLanguageGuidance(language, 'comments');\n\n if (instructions) {\n // User provided specific instructions - minimal prompt, let instructions drive behavior\n const toneGuidance = tone ? ` Use a ${tone} tone.` : '';\n const densityGuidance = density\n ? `\\n\\nAim for approximately ${density} comments per 2000 words of text.`\n : ''; // Let user instructions determine density\n\n prompt = `Add comments to passages in this text following these instructions:\n\n${instructions}${toneGuidance}${densityGuidance}${sourceLang}${bodyLang}\n\nText to analyze:\n---\n${content}\n---\n\nReturn a JSON array of comments. Each comment must have:\n- \"exact\": the exact text passage being commented on (quoted verbatim from source)\n- \"prefix\": up to 64 characters of text immediately before the passage\n- \"suffix\": up to 64 characters of text immediately after the passage\n- \"comment\": your comment following the instructions above\n\nRespond with a valid JSON array.\n\nExample:\n[\n {\"exact\": \"the quarterly review meeting\", \"prefix\": \"We need to schedule \", \"suffix\": \" for next month.\", \"comment\": \"Who will lead this? Should we invite the external auditors?\"}\n]`;\n } else {\n // No specific instructions - fall back to explanatory/educational mode\n const toneGuidance = tone\n ? `\\n\\nTone: Use a ${tone} style in your comments.`\n : '';\n const densityGuidance = density\n ? `\\n- Aim for approximately ${density} comments per 2000 words`\n : `\\n- Aim for 3-8 comments per 2000 words (not too sparse or dense)`;\n\n prompt = `Identify passages in this text that would benefit from explanatory comments.\nFor each passage, provide contextual information, clarification, or background.${toneGuidance}\n\nGuidelines:\n- Select passages that reference technical terms, historical figures, complex concepts, or unclear references\n- Provide comments that ADD VALUE beyond restating the text\n- Focus on explanation, background, or connections to other ideas\n- Avoid obvious or trivial comments\n- Keep comments concise (1-3 sentences typically)${densityGuidance}${sourceLang}${bodyLang}\n\nText to analyze:\n---\n${content}\n---\n\nReturn a JSON array of comments. Each comment should have:\n- \"exact\": the exact text passage being commented on (quoted verbatim from source)\n- \"prefix\": up to 64 characters of text immediately before the passage\n- \"suffix\": up to 64 characters of text immediately after the passage\n- \"comment\": your explanatory comment (1-3 sentences, provide context/background/clarification)\n\nRespond with a valid JSON array.\n\nExample format:\n[\n {\"exact\": \"Ouranos\", \"prefix\": \"In the beginning, \", \"suffix\": \" ruled the universe\", \"comment\": \"Ouranos (also spelled Uranus) is the primordial Greek deity personifying the sky. In Hesiod's Theogony, he is the son and husband of Gaia (Earth) and father of the Titans.\"}\n]`;\n }\n\n return prompt;\n }\n\n /**\n * Build a prompt for detecting highlight-worthy passages\n *\n * @param content - The text content to analyze — a chunk sized by the\n * caller from derived provider limits; NEVER re-truncated here (a\n * builder-level clip is silent input loss — see #738)\n * @param instructions - Optional user-provided instructions\n * @param density - Optional target number of highlights per 2000 words\n * @returns Formatted prompt string\n */\n static buildHighlightPrompt(\n content: string,\n instructions?: string,\n density?: number,\n sourceLanguage?: string\n ): string {\n let prompt: string;\n const sourceLang = sourceLanguageGuidance(sourceLanguage);\n\n if (instructions) {\n // User provided specific instructions - minimal prompt, let instructions drive behavior\n const densityGuidance = density\n ? `\\n\\nAim for approximately ${density} highlights per 2000 words of text.`\n : ''; // Let user instructions determine density\n\n prompt = `Identify passages in this text to highlight following these instructions:\n\n${instructions}${densityGuidance}${sourceLang}\n\nText to analyze:\n---\n${content}\n---\n\nReturn a JSON array of highlights. Each highlight must have:\n- \"exact\": the exact text passage to highlight (quoted verbatim from source)\n- \"prefix\": up to 64 characters of text immediately before the passage\n- \"suffix\": up to 64 characters of text immediately after the passage\n\nRespond with a valid JSON array.\n\nExample:\n[\n {\"exact\": \"revenue grew 45% year-over-year\", \"prefix\": \"In Q3 2024, \", \"suffix\": \", exceeding all forecasts.\"}\n]`;\n } else {\n // No specific instructions - fall back to importance/salience mode\n const densityGuidance = density\n ? `\\n- Aim for approximately ${density} highlights per 2000 words`\n : `\\n- Aim for 3-8 highlights per 2000 words (be selective)`;\n\n prompt = `Identify passages in this text that merit highlighting for their importance or salience.\nFocus on content that readers should notice and remember.\n\nGuidelines:\n- Highlight key claims, findings, or conclusions\n- Highlight important definitions, terminology, or concepts\n- Highlight notable quotes or particularly striking statements\n- Highlight critical decisions, action items, or turning points\n- Select passages that are SIGNIFICANT, not just interesting\n- Avoid trivial or obvious content${densityGuidance}${sourceLang}\n\nText to analyze:\n---\n${content}\n---\n\nReturn a JSON array of highlights. Each highlight should have:\n- \"exact\": the exact text passage to highlight (quoted verbatim from source)\n- \"prefix\": up to 64 characters of text immediately before the passage\n- \"suffix\": up to 64 characters of text immediately after the passage\n\nRespond with a valid JSON array.\n\nExample format:\n[\n {\"exact\": \"we will discontinue support for legacy systems by March 2025\", \"prefix\": \"After careful consideration, \", \"suffix\": \". This decision affects\"}\n]`;\n }\n\n return prompt;\n }\n\n /**\n * Build a prompt for detecting assessment-worthy passages\n *\n * @param content - The text content to analyze — a chunk sized by the\n * caller from derived provider limits; NEVER re-truncated here (a\n * builder-level clip is silent input loss — see #738)\n * @param instructions - Optional user-provided instructions\n * @param tone - Optional tone guidance (e.g., \"critical\", \"supportive\")\n * @param density - Optional target number of assessments per 2000 words\n * @returns Formatted prompt string\n */\n static buildAssessmentPrompt(\n content: string,\n instructions?: string,\n tone?: string,\n density?: number,\n language?: string,\n sourceLanguage?: string\n ): string {\n let prompt: string;\n const sourceLang = sourceLanguageGuidance(sourceLanguage);\n const bodyLang = bodyLanguageGuidance(language, 'assessments');\n\n if (instructions) {\n // User provided specific instructions - minimal prompt, let instructions drive behavior\n const toneGuidance = tone ? ` Use a ${tone} tone.` : '';\n const densityGuidance = density\n ? `\\n\\nAim for approximately ${density} assessments per 2000 words of text.`\n : ''; // Let user instructions determine density\n\n prompt = `Assess passages in this text following these instructions:\n\n${instructions}${toneGuidance}${densityGuidance}${sourceLang}${bodyLang}\n\nText to analyze:\n---\n${content}\n---\n\nReturn a JSON array of assessments. Each assessment must have:\n- \"exact\": the exact text passage being assessed (quoted verbatim from source)\n- \"prefix\": up to 64 characters of text immediately before the passage\n- \"suffix\": up to 64 characters of text immediately after the passage\n- \"assessment\": your assessment following the instructions above\n\nRespond with a valid JSON array.\n\nExample:\n[\n {\"exact\": \"the quarterly revenue target\", \"prefix\": \"We established \", \"suffix\": \" for Q4 2024.\", \"assessment\": \"This target seems ambitious given market conditions. Consider revising based on recent trends.\"}\n]`;\n } else {\n // No specific instructions - fall back to analytical/evaluation mode\n const toneGuidance = tone\n ? `\\n\\nTone: Use a ${tone} style in your assessments.`\n : '';\n const densityGuidance = density\n ? `\\n- Aim for approximately ${density} assessments per 2000 words`\n : `\\n- Aim for 2-6 assessments per 2000 words (focus on key passages)`;\n\n prompt = `Identify passages in this text that merit critical assessment or evaluation.\nFor each passage, provide analysis of its validity, strength, or implications.${toneGuidance}\n\nGuidelines:\n- Select passages containing claims, arguments, conclusions, or assertions\n- Assess evidence quality, logical soundness, or practical implications\n- Provide assessments that ADD INSIGHT beyond restating the text\n- Focus on passages where evaluation would help readers form judgments\n- Keep assessments concise yet substantive (1-3 sentences typically)${densityGuidance}${sourceLang}${bodyLang}\n\nText to analyze:\n---\n${content}\n---\n\nReturn a JSON array of assessments. Each assessment should have:\n- \"exact\": the exact text passage being assessed (quoted verbatim from source)\n- \"prefix\": up to 64 characters of text immediately before the passage\n- \"suffix\": up to 64 characters of text immediately after the passage\n- \"assessment\": your analytical assessment (1-3 sentences, evaluate validity/strength/implications)\n\nRespond with a valid JSON array.\n\nExample format:\n[\n {\"exact\": \"AI will replace most jobs by 2030\", \"prefix\": \"Many experts predict that \", \"suffix\": \", fundamentally reshaping\", \"assessment\": \"This claim lacks nuance and supporting evidence. Employment patterns historically show job transformation rather than wholesale replacement. The timeline appears speculative without specific sector analysis.\"}\n]`;\n }\n\n return prompt;\n }\n\n /**\n * Build a prompt for detecting structural tags\n *\n * @param content - The full text content to analyze (NOT truncated for structural analysis)\n * @param category - The specific category to detect\n * @param schemaName - Human-readable schema name\n * @param schemaDescription - Schema description\n * @param schemaDomain - Schema domain\n * @param categoryDescription - Category description\n * @param categoryExamples - Example questions/guidance for this category\n * @returns Formatted prompt string\n */\n static buildTagPrompt(\n content: string,\n category: string,\n schemaName: string,\n schemaDescription: string,\n schemaDomain: string,\n categoryDescription: string,\n categoryExamples: string[],\n sourceLanguage?: string\n ): string {\n // Tags are schema-defined identifiers (\"Issue\", \"Rule\") — the LLM doesn't\n // generate the category label, so there's no body-language guidance here.\n // Only `sourceLanguage` is wired into the prompt; `language` (body locale)\n // is consumed at the body-stamp site in `processors.ts`.\n const sourceLang = sourceLanguageGuidance(sourceLanguage);\n\n const prompt = `You are analyzing a text using the ${schemaName} framework.\n\nSchema: ${schemaDescription}\nDomain: ${schemaDomain}\n\nYour task: Identify passages that serve the structural role of \"${category}\".\n\nCategory: ${category}\nDescription: ${categoryDescription}\nKey questions:\n${categoryExamples.map(ex => `- ${ex}`).join('\\n')}\n\nGuidelines:\n- Focus on STRUCTURAL FUNCTION, not semantic content\n- A passage serves the \"${category}\" role if it performs this function in the document's structure\n- Look for passages that explicitly fulfill this role\n- Passages can be sentences, paragraphs, or sections\n- Aim for precision - only tag passages that clearly serve this structural role\n- Typical documents have 1-5 instances of each category (some may have 0)${sourceLang}\n\nText to analyze:\n---\n${content}\n---\n\nReturn a JSON array of tags. Each tag should have:\n- \"exact\": the exact text passage (quoted verbatim from source)\n- \"prefix\": up to 64 characters of text immediately before the passage\n- \"suffix\": up to 64 characters of text immediately after the passage\n\nRespond with a valid JSON array.\n\nExample format:\n[\n {\"exact\": \"What duty did the defendant owe?\", \"prefix\": \"The central question is: \", \"suffix\": \" This question must be\"},\n {\"exact\": \"In tort law, a duty of care is established when...\", \"prefix\": \"Legal framework:\\\\n\", \"suffix\": \"\\\\n\\\\nApplying this standard\"}\n]`;\n\n return prompt;\n }\n}\n","import type { AnchorMethod, Logger } from '@semiont/core';\nimport { recordAnchorOutcome } from '@semiont/observability';\n\n/**\n * The anchoring methods that picked a plausible occurrence rather than a\n * certain one: `first-of-many` had several candidates and no usable context,\n * `fuzzy-match` recovered through case/whitespace/Levenshtein. Neither is an\n * error — the write-time selector invariant still holds — but both are places\n * the span could be the wrong instance.\n *\n * ONE decider, deliberately. This classification lived in two places (the\n * reference path's inline warn and the motivation parsers' logger) that\n * happened to agree; two copies of a risk judgement is one copy too many.\n */\nconst DEGRADED: ReadonlySet<string> = new Set<AnchorMethod>(['first-of-many', 'fuzzy-match']);\n\n/**\n * Audit one anchoring outcome: counted always, warned only when degraded.\n *\n * Counting EVERY outcome is the point (P5). A warning tells an operator that\n * one anchor was uncertain; only a rate over all anchors says whether the\n * detection run was precise, and the rate is what the plan asks to put beside\n * the yield numbers.\n */\nexport function noteAnchor(label: string, exact: string, method: AnchorMethod, logger?: Logger): void {\n recordAnchorOutcome(label, method);\n if (!DEGRADED.has(method)) return;\n const detail = { text: exact, anchorMethod: method };\n if (logger) logger.warn('Annotation anchored via degraded method', { label, ...detail });\n else console.warn(`[${label}] anchored via ${method}: \"${exact}\"`);\n}\n","/**\n * Response parsers for annotation detection motivations\n *\n * Provides static methods to parse and validate AI responses for each motivation type.\n * Includes offset validation and correction logic.\n * Extracted from worker implementations to centralize parsing logic.\n *\n * NOTE: These are static utility methods without logger access.\n * Console statements kept for debugging - consider adding logger parameter in future.\n */\n\nimport { reconcileSelector, isObject, isString, type AnchorMethod } from '@semiont/core';\nimport type { ElementSchema } from '@semiont/inference';\nimport { noteAnchor } from './anchor-audit';\n\n// Parsers receive ALREADY-PARSED elements (`unknown[]`) from the structured\n// inference surface — `generateStructured` returns `T[]` or throws, so\n// \"could not read the model\" never reaches this layer and there is no string\n// to parse here (the former strict `parseJsonArray` moved into that contract,\n// as the earlier tolerant walker moved into it before). What remains here is\n// per-element structural validation (STRUCTURED-INFERENCE D5 — the last line\n// on the Ollama path and the schema/type drift guard) plus reconciliation\n// against the full document.\n//\n// Each element schema is declared ADJACENT to the Match interface it mirrors:\n// the schema constrains the wire, the interface is what the code consumes,\n// and nothing verifies they agree — adjacency is the drift guard.\n// `prefix`/`suffix` stay OUT of `required` deliberately: requiring them turns\n// \"sometimes absent\" into \"always present, sometimes empty\" (measured\n// 2026-08-06), an anchoring-path change Phase 3 examines before anyone\n// relies on it.\n\n/**\n * Represents a detected comment with validated position\n */\nexport interface CommentMatch {\n exact: string;\n start: number;\n end: number;\n prefix?: string;\n suffix?: string;\n comment: string;\n}\n\n/** Wire schema for one comment element — keep in lockstep with `CommentMatch`. */\nexport const COMMENT_ELEMENT_SCHEMA: ElementSchema = {\n type: 'object',\n properties: {\n exact: { type: 'string' },\n prefix: { type: 'string' },\n suffix: { type: 'string' },\n comment: { type: 'string' },\n },\n required: ['exact', 'comment'],\n additionalProperties: false,\n};\n\n/**\n * Represents a detected highlight with validated position\n */\nexport interface HighlightMatch {\n exact: string;\n start: number;\n end: number;\n prefix?: string;\n suffix?: string;\n}\n\n/** Wire schema for one highlight element — keep in lockstep with `HighlightMatch`. */\nexport const HIGHLIGHT_ELEMENT_SCHEMA: ElementSchema = {\n type: 'object',\n properties: {\n exact: { type: 'string' },\n prefix: { type: 'string' },\n suffix: { type: 'string' },\n },\n required: ['exact'],\n additionalProperties: false,\n};\n\n/**\n * Represents a detected assessment with validated position\n */\nexport interface AssessmentMatch {\n exact: string;\n start: number;\n end: number;\n prefix?: string;\n suffix?: string;\n assessment: string;\n}\n\n/** Wire schema for one assessment element — keep in lockstep with `AssessmentMatch`. */\nexport const ASSESSMENT_ELEMENT_SCHEMA: ElementSchema = {\n type: 'object',\n properties: {\n exact: { type: 'string' },\n prefix: { type: 'string' },\n suffix: { type: 'string' },\n assessment: { type: 'string' },\n },\n required: ['exact', 'assessment'],\n additionalProperties: false,\n};\n\n/**\n * Represents a detected tag with validated position\n */\nexport interface TagMatch {\n exact: string;\n start: number;\n end: number;\n prefix?: string;\n suffix?: string;\n category: string;\n}\n\n/**\n * Wire schema for one tag element — keep in lockstep with `RawTagInput`\n * (the category is stamped by the caller, not emitted by the model).\n */\nexport const TAG_ELEMENT_SCHEMA: ElementSchema = {\n type: 'object',\n properties: {\n exact: { type: 'string' },\n prefix: { type: 'string' },\n suffix: { type: 'string' },\n },\n required: ['exact'],\n additionalProperties: false,\n};\n\nexport class MotivationParsers {\n /**\n * Validate and reconcile structured comment elements.\n *\n * @param parsed - Already-parsed elements from the structured surface\n * @param content - Original content to validate offsets against\n * @returns Array of validated comment matches\n */\n static parseComments(parsed: unknown[], content: string): CommentMatch[] {\n\n const valid = parsed.filter((c): c is { exact: string; prefix?: string; suffix?: string; comment: string } =>\n isObject(c) &&\n isString(c.exact) &&\n isString(c.comment) &&\n c.comment.trim().length > 0\n );\n\n console.log(`[MotivationParsers] Parsed ${valid.length} valid comments from ${parsed.length} total`);\n\n const validatedComments: CommentMatch[] = [];\n for (const comment of valid) {\n const reconciled = reconcileSelector(content, {\n exact: comment.exact,\n ...(typeof comment.prefix === 'string' ? { prefix: comment.prefix } : {}),\n ...(typeof comment.suffix === 'string' ? { suffix: comment.suffix } : {}),\n });\n if (!reconciled) {\n console.warn(`[MotivationParsers] Dropped hallucinated comment \"${comment.exact}\"`);\n continue;\n }\n logAnchorMethod('comment', comment.exact, reconciled.anchorMethod);\n validatedComments.push({\n comment: comment.comment,\n exact: reconciled.exact,\n start: reconciled.start,\n end: reconciled.end,\n ...(reconciled.prefix !== undefined ? { prefix: reconciled.prefix } : {}),\n ...(reconciled.suffix !== undefined ? { suffix: reconciled.suffix } : {}),\n });\n }\n\n return validatedComments;\n }\n\n /**\n * Validate and reconcile structured highlight elements.\n *\n * @param parsed - Already-parsed elements from the structured surface\n * @param content - Original content to validate offsets against\n * @returns Array of validated highlight matches\n */\n static parseHighlights(parsed: unknown[], content: string): HighlightMatch[] {\n\n const highlights = parsed.filter((h): h is { exact: string; prefix?: string; suffix?: string } =>\n isObject(h) && isString(h.exact)\n );\n\n const validatedHighlights: HighlightMatch[] = [];\n for (const highlight of highlights) {\n const reconciled = reconcileSelector(content, {\n exact: highlight.exact,\n ...(typeof highlight.prefix === 'string' ? { prefix: highlight.prefix } : {}),\n ...(typeof highlight.suffix === 'string' ? { suffix: highlight.suffix } : {}),\n });\n if (!reconciled) {\n console.warn(`[MotivationParsers] Dropped hallucinated highlight \"${highlight.exact}\"`);\n continue;\n }\n logAnchorMethod('highlight', highlight.exact, reconciled.anchorMethod);\n validatedHighlights.push({\n exact: reconciled.exact,\n start: reconciled.start,\n end: reconciled.end,\n ...(reconciled.prefix !== undefined ? { prefix: reconciled.prefix } : {}),\n ...(reconciled.suffix !== undefined ? { suffix: reconciled.suffix } : {}),\n });\n }\n\n return validatedHighlights;\n }\n\n /**\n * Validate and reconcile structured assessment elements.\n *\n * @param parsed - Already-parsed elements from the structured surface\n * @param content - Original content to validate offsets against\n * @returns Array of validated assessment matches\n */\n static parseAssessments(parsed: unknown[], content: string): AssessmentMatch[] {\n\n const assessments = parsed.filter((a): a is { exact: string; prefix?: string; suffix?: string; assessment: string } =>\n isObject(a) && isString(a.exact) && isString(a.assessment)\n );\n\n const validatedAssessments: AssessmentMatch[] = [];\n for (const assessment of assessments) {\n const reconciled = reconcileSelector(content, {\n exact: assessment.exact,\n ...(typeof assessment.prefix === 'string' ? { prefix: assessment.prefix } : {}),\n ...(typeof assessment.suffix === 'string' ? { suffix: assessment.suffix } : {}),\n });\n if (!reconciled) {\n console.warn(`[MotivationParsers] Dropped hallucinated assessment \"${assessment.exact}\"`);\n continue;\n }\n logAnchorMethod('assessment', assessment.exact, reconciled.anchorMethod);\n validatedAssessments.push({\n assessment: assessment.assessment,\n exact: reconciled.exact,\n start: reconciled.start,\n end: reconciled.end,\n ...(reconciled.prefix !== undefined ? { prefix: reconciled.prefix } : {}),\n ...(reconciled.suffix !== undefined ? { suffix: reconciled.suffix } : {}),\n });\n }\n\n return validatedAssessments;\n }\n\n /**\n * Validate structured tag elements into raw, pre-reconciliation tag inputs.\n * Reconciliation happens in `validateTagOffsets`, which adds `start`/`end`\n * by anchoring `exact` against the source content.\n *\n * @param parsed - Already-parsed elements from the structured surface\n */\n static parseTags(parsed: unknown[]): RawTagInput[] {\n\n const valid = parsed.filter((t): t is RawTagInput =>\n isObject(t) && isString(t.exact) && t.exact.trim().length > 0\n );\n\n console.log(`[MotivationParsers] Parsed ${valid.length} valid tags from ${parsed.length} total`);\n\n return valid;\n }\n\n /**\n * Anchor raw tag inputs against source content and add category.\n */\n static validateTagOffsets(\n tags: RawTagInput[],\n content: string,\n category: string\n ): TagMatch[] {\n const validatedTags: TagMatch[] = [];\n for (const tag of tags) {\n const reconciled = reconcileSelector(content, {\n exact: tag.exact,\n ...(typeof tag.prefix === 'string' ? { prefix: tag.prefix } : {}),\n ...(typeof tag.suffix === 'string' ? { suffix: tag.suffix } : {}),\n });\n if (!reconciled) {\n console.warn(`[MotivationParsers] Dropped hallucinated tag \"${tag.exact}\" for category \"${category}\"`);\n continue;\n }\n logAnchorMethod('tag', tag.exact, reconciled.anchorMethod);\n validatedTags.push({\n category,\n exact: reconciled.exact,\n start: reconciled.start,\n end: reconciled.end,\n ...(reconciled.prefix !== undefined ? { prefix: reconciled.prefix } : {}),\n ...(reconciled.suffix !== undefined ? { suffix: reconciled.suffix } : {}),\n });\n }\n return validatedTags;\n }\n}\n\n/** Raw LLM-emitted tag, pre-reconciliation. */\nexport interface RawTagInput {\n exact: string;\n prefix?: string;\n suffix?: string;\n}\n\n/**\n * Audit one anchor-method classification. Forwards to the single decider\n * shared with the reference path (`anchor-audit.ts`) — which methods count as\n * risky is one judgement, and this file used to hold a second copy of it.\n */\nfunction logAnchorMethod(motivation: string, exact: string, anchorMethod: AnchorMethod): void {\n noteAnchor(motivation, exact, anchorMethod);\n}\n","/**\n * Annotation Detection\n *\n * Orchestrates the full annotation detection pipeline:\n * 1. Build AI prompts using MotivationPrompts\n * 2. Call AI inference\n * 3. Parse and validate results using MotivationParsers\n *\n * All methods take content as a string parameter — the worker process\n * fetches it and hands it in.\n */\n\nimport type { ElementSchema, InferenceClient } from '@semiont/inference';\nimport { estimateTokens, type UnitCursor } from '@semiont/core';\nimport { boundedGenerateStructured } from './inference-call';\nimport { assertNotTruncated, callChunkSubdividing, deriveDetectionBudget, runAdaptiveChunks, type ChunkCursor, DETECTION_TEMPERATURE } from './detection/detection-chunking';\nimport { MotivationPrompts } from './detection/motivation-prompts';\nimport {\n MotivationParsers,\n COMMENT_ELEMENT_SCHEMA,\n HIGHLIGHT_ELEMENT_SCHEMA,\n ASSESSMENT_ELEMENT_SCHEMA,\n TAG_ELEMENT_SCHEMA,\n type CommentMatch,\n type HighlightMatch,\n type AssessmentMatch,\n type TagMatch,\n} from './detection/motivation-parsers';\nimport type { TagSchema } from '@semiont/core';\n\n/**\n * Per-chunk detection loop shared by the four motivations.\n *\n * Budgets derive from the provider's actual limits plus the measured prompt\n * scaffold (`buildPrompt('')`) — no literals. The prompt receives one chunk;\n * `parse` reconciles against the FULL document (the callers close over it),\n * so offsets index into the whole resource with no re-anchoring arithmetic.\n * Overlap duplicates pass through — the processor's span-keyed seen-set is\n * the single dedupe point.\n *\n * `onActivity` fires whenever the detection is demonstrably alive: at each\n * chunk boundary (the count advances) AND periodically while one inference\n * call is in flight (the count repeats — liveness, not progress). Progress is\n * the worker's liveness heartbeat AND the client's inter-emission timeout\n * signal, so a silent single-chunk run kills a healthy job\n * (DETECTION-HEARTBEAT).\n */\nasync function detectInChunks<T>(\n client: InferenceClient,\n content: string,\n buildPrompt: (chunk: string) => string,\n motivation: string,\n elementSchema: ElementSchema,\n parse: (items: unknown[]) => T[],\n onActivity?: (consumedChars: number, totalChars: number) => void,\n /**\n * This chunk's parsed matches, awaited before the loop continues: the\n * caller commits them, and the loop must not run ahead of durability.\n * Unlike `onActivity` (a liveness heartbeat, which may repeat), this fires\n * exactly once per chunk, including the last.\n *\n * `cursor` is where the run stands once this chunk is committed — handed over\n * WITH the results so a caller cannot record a position it has not made\n * durable (CHUNK-GRAIN-RESUME P2).\n */\n /** Where an earlier attempt left this unit (CHUNK-GRAIN-RESUME P3).\n * Before the callback below, which stays last. */\n resume?: UnitCursor,\n onChunkResults?: (parsed: T[], cursor: ChunkCursor) => Promise<void>,\n): Promise<T[]> {\n const limits = await client.limits();\n const scaffoldTokens = estimateTokens(buildPrompt(''));\n // One motivation's spans per call — the single span family this prompt\n // asks for, the motivation-path analogue of one entity type.\n const budget = deriveDetectionBudget(limits, scaffoldTokens, 1);\n const { outputBudget } = budget;\n\n const collected: T[] = [];\n await runAdaptiveChunks(content, budget, async ({ piece: chunk, size, at, next, totalChars }) => {\n // Structured surface: parsed elements or a throw — an unreadable model\n // response fails the job rather than reading as an empty detection. A\n // size-shaped failure (duration bound, truncation) subdivides in place\n // and retries smaller before it is allowed to fail the job.\n const { items, outcome } = await callChunkSubdividing<unknown>(\n motivation, chunk, { chunkSize: size, overlap: budget.chunking.overlap },\n async (piece) => {\n const response = await boundedGenerateStructured<unknown>(\n client, buildPrompt(piece), outputBudget, DETECTION_TEMPERATURE, elementSchema,\n // Still alive, same position (a long single call is otherwise silent).\n () => onActivity?.(at, totalChars),\n );\n assertNotTruncated(response, `${motivation} detection`, at, totalChars, outputBudget);\n // Usage rides back so the telemetry record carries what the call COST\n // beside what it yielded — the provider's own counts, not an estimate.\n return { items: response.items, ...(response.usage ? { usage: response.usage } : {}) };\n },\n );\n const fromChunk = parse(items);\n collected.push(...fromChunk);\n await onChunkResults?.(fromChunk, { next, size });\n // Chunk boundary: the cursor advances (real progress). Only when text\n // remains — the final cut has no boundary after it, and the caller reports\n // the unit's completion itself.\n if (next < totalChars) onActivity?.(next, totalChars);\n return outcome;\n }, resume);\n return collected;\n}\n\nexport class AnnotationDetection {\n\n /**\n * Detect comments in content.\n *\n * `language` is the locale the LLM should write comment text in (annotation\n * body locale). `sourceLanguage` is the locale of the content being analyzed\n * (source-resource locale). See `types.ts` \"Locale conventions\" for the\n * full discussion.\n */\n static async detectComments(\n content: string,\n client: InferenceClient,\n instructions?: string,\n tone?: string,\n density?: number,\n language?: string,\n sourceLanguage?: string,\n onActivity?: (consumedChars: number, totalChars: number) => void,\n /** This chunk's matches, as the chunk completes. */\n /** Where an earlier attempt left this unit (CHUNK-GRAIN-RESUME P3). */\n resume?: UnitCursor,\n /** This chunk's matches, as the chunk completes. Kept LAST. */\n onChunkResults?: (matches: CommentMatch[], cursor: ChunkCursor) => Promise<void>,\n ): Promise<CommentMatch[]> {\n return detectInChunks(\n client, content,\n (chunk) => MotivationPrompts.buildCommentPrompt(chunk, instructions, tone, density, language, sourceLanguage),\n 'comment', COMMENT_ELEMENT_SCHEMA,\n (items) => MotivationParsers.parseComments(items, content),\n onActivity,\n resume,\n onChunkResults,\n );\n }\n\n /**\n * Detect highlights in content.\n *\n * Highlights have no body — only `sourceLanguage` (source-resource locale)\n * applies, used in the prompt so the LLM analyzes non-English source\n * correctly.\n */\n static async detectHighlights(\n content: string,\n client: InferenceClient,\n instructions?: string,\n density?: number,\n sourceLanguage?: string,\n onActivity?: (consumedChars: number, totalChars: number) => void,\n /** This chunk's matches, as the chunk completes. */\n /** Where an earlier attempt left this unit (CHUNK-GRAIN-RESUME P3). */\n resume?: UnitCursor,\n /** This chunk's matches, as the chunk completes. Kept LAST. */\n onChunkResults?: (matches: HighlightMatch[], cursor: ChunkCursor) => Promise<void>,\n ): Promise<HighlightMatch[]> {\n return detectInChunks(\n client, content,\n (chunk) => MotivationPrompts.buildHighlightPrompt(chunk, instructions, density, sourceLanguage),\n 'highlight', HIGHLIGHT_ELEMENT_SCHEMA,\n (items) => MotivationParsers.parseHighlights(items, content),\n onActivity,\n resume,\n onChunkResults,\n );\n }\n\n /**\n * Detect assessments in content.\n *\n * `language` is the locale the LLM should write assessment text in\n * (annotation body locale). `sourceLanguage` is the locale of the content\n * being analyzed (source-resource locale).\n */\n static async detectAssessments(\n content: string,\n client: InferenceClient,\n instructions?: string,\n tone?: string,\n density?: number,\n language?: string,\n sourceLanguage?: string,\n onActivity?: (consumedChars: number, totalChars: number) => void,\n /** This chunk's matches, as the chunk completes. */\n /** Where an earlier attempt left this unit (CHUNK-GRAIN-RESUME P3). */\n resume?: UnitCursor,\n /** This chunk's matches, as the chunk completes. Kept LAST. */\n onChunkResults?: (matches: AssessmentMatch[], cursor: ChunkCursor) => Promise<void>,\n ): Promise<AssessmentMatch[]> {\n return detectInChunks(\n client, content,\n (chunk) => MotivationPrompts.buildAssessmentPrompt(chunk, instructions, tone, density, language, sourceLanguage),\n 'assessment', ASSESSMENT_ELEMENT_SCHEMA,\n (items) => MotivationParsers.parseAssessments(items, content),\n onActivity,\n resume,\n onChunkResults,\n );\n }\n\n /**\n * Detect tags in content for a specific category.\n *\n * The full `TagSchema` is supplied by the dispatcher (resolved against\n * the per-KB tag-schema projection at job-creation time) so the worker\n * is independent of the registry.\n *\n * `sourceLanguage` is the locale of the content being analyzed. Body-locale\n * (`language`) doesn't influence the tag prompt — categories are schema\n * identifiers, not LLM-generated text — so it's consumed at the body-stamp\n * site, not here.\n */\n static async detectTags(\n content: string,\n client: InferenceClient,\n schema: TagSchema,\n category: string,\n sourceLanguage?: string,\n onActivity?: (consumedChars: number, totalChars: number) => void,\n /**\n * This chunk's matches, ANCHORED before they leave: `parse` here yields\n * raw tags, so this path runs `validateTagOffsets` per chunk — a per-item\n * anchor against the full document, so partitioning changes nothing.\n */\n /** Where an earlier attempt left this unit (CHUNK-GRAIN-RESUME P3). */\n resume?: UnitCursor,\n /** This chunk's matches, as the chunk completes. Kept LAST. */\n onChunkResults?: (matches: TagMatch[], cursor: ChunkCursor) => Promise<void>,\n ): Promise<TagMatch[]> {\n const categoryInfo = schema.tags.find((t) => t.name === category);\n if (!categoryInfo) {\n throw new Error(`Invalid category \"${category}\" for schema ${schema.id}`);\n }\n\n // Parse per chunk; anchor once against the full document afterward.\n const parsedTags = await detectInChunks(\n client, content,\n (chunk) => MotivationPrompts.buildTagPrompt(\n chunk,\n category,\n schema.name,\n schema.description,\n schema.domain,\n categoryInfo.description,\n categoryInfo.examples,\n sourceLanguage\n ),\n 'tag', TAG_ELEMENT_SCHEMA,\n (items) => MotivationParsers.parseTags(items),\n onActivity,\n resume,\n onChunkResults\n ? async (raw, cursor) => onChunkResults(MotivationParsers.validateTagOffsets(raw, content, category), cursor)\n : undefined,\n );\n return MotivationParsers.validateTagOffsets(parsedTags, content, category);\n }\n}\n","import type { ElementSchema, InferenceClient } from '@semiont/inference';\nimport { estimateTokens, getLocaleEnglishName, isObject, isString, type Logger, type UnitCursor } from '@semiont/core';\nimport { boundedGenerateStructured, boundedGenerateWithMetadata } from '../inference-call';\nimport { assertNotTruncated, callChunkSubdividing, deriveDetectionBudget, runAdaptiveChunks, type ChunkCursor, DETECTION_TEMPERATURE, YIELD_COLLAPSE_BAND, YieldCollapseError, type UnderReportedPiece } from './detection-chunking';\n\n/**\n * Entity reference extracted from text — pre-reconciliation.\n *\n * The LLM emits `exact` (verbatim text span), `entityType`, and optional\n * `prefix` / `suffix` context for disambiguation. Offsets are not asked\n * for — `reconcileSelector` computes them by anchoring `exact` against\n * the source content in the calling processor.\n */\nexport interface ExtractedEntity {\n exact: string;\n entityType: string;\n prefix?: string;\n suffix?: string;\n}\n\n/**\n * JSON Schema for one extracted entity — the provider-enforced shape.\n *\n * Declared adjacent to `ExtractedEntity` deliberately: the schema is what\n * constrains the wire and the interface is what the code consumes, and\n * nothing verifies they agree — adjacency is the drift guard. The\n * per-element `isObject`/`isString` checks below stay as the structural\n * backstop (STRUCTURED-INFERENCE D5).\n *\n * `prefix`/`suffix` are deliberately NOT in `required`: with all four\n * required, models return `\"prefix\": \"\"` instead of omitting the key\n * (measured 2026-08-06), turning \"sometimes absent\" into \"always present,\n * sometimes empty\" — an anchoring-path change Phase 3 re-examines before\n * anyone relies on it.\n */\nconst ENTITY_ELEMENT_SCHEMA: ElementSchema = {\n type: 'object',\n properties: {\n exact: { type: 'string' },\n entityType: { type: 'string' },\n prefix: { type: 'string' },\n suffix: { type: 'string' },\n },\n required: ['exact', 'entityType'],\n additionalProperties: false,\n};\n\n/**\n * Extract entity references from text using AI.\n *\n * Locale: entity references' bodies are entity-type identifiers (not\n * LLM-generated natural-language text), so only `sourceLanguage` (source-\n * resource locale) is meaningful here — it's used in the prompt so the LLM\n * analyzes non-English source correctly. There's no body-locale parameter.\n *\n * @param text - The text to analyze\n * @param entityTypes - Array of entity types to detect (optionally with examples)\n * @param client - Inference client for AI operations\n * @param includeDescriptiveReferences - Include anaphoric/cataphoric references (default: false)\n * @param logger - Logger for entity-extraction diagnostics (parse failures,\n * anchor decisions, drops). Required so dropped/filtered entities never\n * disappear silently.\n * @param sourceLanguage - BCP-47 tag for the source content's language\n * @param onActivity - Invoked with (consumedChars, totalChars) whenever the\n * extraction is demonstrably alive: at each chunk boundary (the cursor\n * advances) AND periodically while a single inference call is in flight\n * (the position repeats — liveness, not progress). Characters, not chunk\n * ordinals: chunk sizing is decided as the run goes, so there is no chunk\n * total to divide by — and the cursor was always the more honest numerator,\n * since boundary-seeking already made chunks unequal. The caller MUST forward\n * this to its progress channel: progress is the worker's liveness\n * heartbeat for the stall watchdog, and the client's timeout is an\n * INTER-EMISSION one, so a silent single-chunk run kills a healthy job\n * (DETECTION-HEARTBEAT).\n * @returns Array of extracted entities with their character offsets\n */\n/** Output cap for the count call: the answer is one number (≤7 digits), and a\n * tiny cap is itself the safety — a ~5-token output structurally cannot loop\n * or truncate the way the extraction's array can. */\nconst COUNT_MAX_TOKENS = 16;\n\n/** The count answer, read as its leading integer. \"Respond with only the\n * number\" is the prompt's contract, but a model that pads it (\"There are 50\")\n * still yields its verdict; anything with no integer yields none. */\nfunction parseCount(text: string): number | undefined {\n const m = text.trim().match(/\\d+/);\n return m ? Number(m[0]) : undefined;\n}\n\n/**\n * The count-verifier (OLLAMA-DETECTION-TESTING P3c): guard a SUCCESSFUL\n * extraction against silent yield collapse (F7) — a schema-clean response\n * carrying a fraction of the mentions present, measured deterministic and\n * classification-invisible, so nothing else can catch it.\n *\n * A cheap count call (\"respond with only the number\") sets the expectation\n * FROM THE SAME TEXT — corpus-free, per the no-input-assumptions principle.\n * An extraction under 1/BAND of the count throws the collapse verdict, which\n * subdivision treats like truncation (descend by size) except at the floor,\n * where it fails the job loudly (user-ratified).\n *\n * The verifier's own failure — count call errors, or answers with no number —\n * disables it for the chunk (warn, pass through): a safety net's outage must\n * not take down a healthy extraction. Known limit, documented in the bug\n * report: the count saturates past ~8K chars, so margins are cleanest at\n * post-descent sizes; it still flagged every measured collapse at 16K/32K.\n */\nasync function assertYieldNotCollapsed(\n client: InferenceClient,\n piece: string,\n items: readonly unknown[],\n entityTypesDescription: string,\n logger: Logger,\n): Promise<number | undefined> {\n const prompt = `Count every mention of: ${entityTypesDescription} in the following text. Repeated mentions of the same entity count separately. Respond with only the number.\n\nText:\n\"\"\"\n${piece}\n\"\"\"`;\n\n let counted: number | undefined;\n try {\n const response = await boundedGenerateWithMetadata(client, prompt, COUNT_MAX_TOKENS, DETECTION_TEMPERATURE, undefined, logger);\n counted = parseCount(response.text);\n } catch (err) {\n logger.warn('Count-verifier call failed — yield check skipped for this chunk', {\n pieceChars: piece.length,\n error: err instanceof Error ? err.message : String(err),\n });\n return undefined;\n }\n if (counted === undefined) {\n logger.warn('Count-verifier answer carried no number — yield check skipped for this chunk', { pieceChars: piece.length });\n return undefined;\n }\n if (items.length * YIELD_COLLAPSE_BAND < counted) {\n // The salvage rides the error: descent discards it (a smaller re-run does\n // better), the floor accepts it (better than nothing, and every span is\n // write-time-verified).\n throw new YieldCollapseError(\n `Extraction found ${items.length} entities where a count call reports ~${counted} mentions (band ×${YIELD_COLLAPSE_BAND}) on a ${piece.length}-char chunk — silent yield collapse (F7): deterministic — a same-size retry returns the identical under-report.`,\n [...items],\n { found: items.length, counted, pieceChars: piece.length },\n );\n }\n return counted;\n}\n\nexport async function extractEntities(\n exact: string,\n entityTypes: string[] | { type: string; examples?: string[] }[],\n client: InferenceClient,\n includeDescriptiveReferences: boolean,\n logger: Logger,\n sourceLanguage?: string,\n onActivity?: (consumedChars: number, totalChars: number) => void,\n /** A floor-accepted piece's evidence, as it is accepted. */\n onUnderReport?: (verdict: UnderReportedPiece) => void,\n /** Each accepted piece's count-verifier expectation — the denominator. */\n onCounted?: (counted: number) => void,\n /** Where an earlier attempt left this unit (CHUNK-GRAIN-RESUME P3). Absent on\n * a first attempt, and then the run opens at the top exactly as before.\n * Deliberately BEFORE the callback below, which stays last. */\n resume?: UnitCursor,\n /**\n * This chunk's entities, awaited before the loop continues: the caller\n * commits them, and the loop must not run ahead of durability. Unlike\n * `onActivity` (a liveness heartbeat, which may repeat), this fires exactly\n * once per chunk, including the last. Kept LAST on every detection seam —\n * test harnesses read it as the final positional argument, so a parameter\n * appended after it silently starves them of results.\n *\n * `cursor` is where the run stands ONCE THIS CHUNK IS COMMITTED — the pair\n * CHUNK-GRAIN-RESUME checkpoints. It is handed over with the results rather\n * than reported separately so the two cannot drift: a caller that records the\n * position without durably committing the annotations would checkpoint ahead\n * of the log.\n */\n onChunkResults?: (items: ExtractedEntity[], cursor: ChunkCursor) => Promise<void>,\n): Promise<ExtractedEntity[]> {\n\n // Format entity types for the prompt\n const entityTypesDescription = entityTypes.map(et => {\n if (typeof et === 'string') {\n return et;\n }\n return et.examples && et.examples.length > 0\n ? `${et.type} (examples: ${et.examples.slice(0, 3).join(', ')})`\n : et.type;\n }).join(', ');\n\n // Build prompt with optional support for anaphoric/cataphoric references\n // Anaphora: references that point backward (e.g., \"John arrived. He was tired.\")\n // Cataphora: references that point forward (e.g., \"When she arrived, Mary was surprised.\")\n // When enabled, include substantive descriptive references beyond simple pronouns\n const descriptiveReferenceGuidance = includeDescriptiveReferences\n ? `\nInclude both:\n- Direct mentions (names, proper nouns)\n- Descriptive references (substantive phrases that refer to entities)\n\nFor descriptive references, include:\n- Definite descriptions: \"the Nobel laureate\", \"the tech giant\", \"the former president\"\n- Role-based references: \"the CEO\", \"the physicist\", \"the author\", \"the owner\", \"the contractor\"\n- Epithets with context: \"the Cupertino-based company\", \"the iPhone maker\"\n- References to entities even when identity is unknown or unspecified\n\nDo NOT include:\n- Simple pronouns alone: he, she, it, they, him, her, them\n- Generic determiners alone: this, that, these, those\n- Possessives without substance: his, her, their, its\n\nExamples:\n- For \"Marie Curie\", include \"the Nobel laureate\" and \"the physicist\" but NOT \"she\"\n- For an unknown person, include \"the owner\" or \"the contractor\" (role-based references count even when identity is unspecified)\n`\n : `\nFind direct mentions only (names, proper nouns). Do not include pronouns or descriptive references.\n`;\n\n const sourceLangGuidance = sourceLanguage\n ? `\\nSource text language: ${getLocaleEnglishName(sourceLanguage) || sourceLanguage}.\\n`\n : '';\n\n // The LLM is asked for `exact`, `prefix`, and `suffix` — no character\n // offsets. Offsets get computed by `reconcileSelector` against the\n // source content. Asking the model for offsets wastes tokens and\n // encourages it to fabricate where it shouldn't.\n const buildPrompt = (text: string): string => `Identify entity references in the following text. Look for mentions of: ${entityTypesDescription}.\n${descriptiveReferenceGuidance}${sourceLangGuidance}\nText to analyze:\n\"\"\"\n${text}\n\"\"\"\n\nRespond with a JSON array of entities found. Each entity should have:\n- exact: the exact text span from the input (quoted verbatim — character-for-character)\n- entityType: one of the provided entity types\n- prefix: up to 64 characters of text immediately before the entity (used to disambiguate when the same text appears more than once)\n- suffix: up to 64 characters of text immediately after the entity (same purpose)\n\nIf no entities are found, respond with an empty array [].\n\nExample output:\n[{\"exact\":\"Alice\",\"entityType\":\"Person\",\"prefix\":\"\",\"suffix\":\" went to\"},{\"exact\":\"Paris\",\"entityType\":\"Location\",\"prefix\":\"went to \",\"suffix\":\" yesterday\"}]`;\n\n // Budgets derive from the provider's actual limits + the measured scaffold\n // (the template around the content) — no literals. Input is chunked only\n // when the derived budget forces it; small documents make one call, as\n // before.\n const limits = await client.limits();\n // The provider DECLARES whether its extractions get count-verified\n // (universal for real providers since the 2026-09-05 ruling — unverified\n // completeness is not a savings). Jobs does no provider-specific switching:\n // whatever varies by provider is a capability on the InferenceClient\n // contract, read here like `maxConcurrency`.\n const verifyYield = client.verifyDetectionYield;\n const scaffoldTokens = estimateTokens(buildPrompt(''));\n // One call asks for every type in `entityTypes` — the processor's per-type\n // loop passes one, so this is 1 in production today.\n const budget = deriveDetectionBudget(limits, scaffoldTokens, entityTypes.length);\n const { chunking, outputBudget } = budget;\n\n logger.debug('Sending entity extraction request', {\n entityTypes: entityTypesDescription,\n chars: exact.length,\n // The size the run OPENS at, and how far measured yield may move it. The\n // chunk COUNT is deliberately absent: with sizing decided as the run goes,\n // there is no honest total until the cursor reaches the end.\n openingChunkSizeTokens: chunking.chunkSize,\n ceilingChunkSizeTokens: budget.bounds.ceiling,\n outputBudget,\n });\n\n const collected: ExtractedEntity[] = [];\n await runAdaptiveChunks(exact, budget, async ({ piece: chunk, size, at, next, totalChars }) => {\n // The structured surface returns parsed elements or THROWS — an\n // unreadable model response is a job failure, never a silent []. A\n // size-shaped failure (duration bound, truncation) subdivides in place\n // and retries smaller before it is allowed to fail the job.\n const { items, outcome } = await callChunkSubdividing<unknown>(\n 'reference', chunk, { chunkSize: size, overlap: chunking.overlap },\n async (piece) => {\n const response = await boundedGenerateStructured<unknown>(\n client,\n buildPrompt(piece),\n outputBudget,\n DETECTION_TEMPERATURE,\n ENTITY_ELEMENT_SCHEMA,\n // Still alive, same position: a long single call would otherwise emit\n // nothing at all between start and finish.\n () => onActivity?.(at, totalChars),\n logger,\n );\n logger.debug('Got entity extraction response', {\n at,\n totalChars,\n chunkSizeTokens: size,\n pieceChars: piece.length,\n items: response.items.length,\n });\n\n // Truncation is data loss, not \"no entities\" — check it BEFORE\n // consuming: a truncated structured response can still carry a valid\n // partial array, so the items themselves cannot signal the loss.\n assertNotTruncated(response, 'Entity extraction', at, totalChars, outputBudget);\n // And a CLEAN response can still be a silent under-report (F7) — the\n // count-verifier is the only signal for that, and a flag throws the\n // collapse verdict so subdivision changes the input.\n const counted = verifyYield\n ? await assertYieldNotCollapsed(client, piece, response.items, entityTypesDescription, logger)\n : undefined;\n // Usage rides back so the telemetry record carries what the call COST\n // beside what it yielded — the provider's own counts, not an estimate.\n return {\n items: response.items,\n ...(response.usage ? { usage: response.usage } : {}),\n ...(counted !== undefined ? { counted } : {}),\n };\n },\n logger, onUnderReport, onCounted,\n );\n\n const fromChunk: ExtractedEntity[] = [];\n for (const e of items) {\n // No dedupe here: overlap duplicates from adjacent chunks pass through\n // to the caller's decider — the single dedupe point.\n if (isObject(e) && isString(e.exact) && isString(e.entityType)) {\n fromChunk.push({\n exact: e.exact,\n entityType: e.entityType,\n ...(isString(e.prefix) ? { prefix: e.prefix } : {}),\n ...(isString(e.suffix) ? { suffix: e.suffix } : {}),\n });\n } else {\n logger.debug('Dropped malformed LLM entity', { entity: e });\n }\n }\n collected.push(...fromChunk);\n await onChunkResults?.(fromChunk, { next, size });\n\n // Chunk boundary: the cursor advances (real progress). Only when text\n // remains — the final cut has no boundary after it, and the caller reports\n // the unit's completion itself.\n if (next < totalChars) onActivity?.(next, totalChars);\n return outcome;\n }, resume);\n\n return collected;\n}\n","/**\n * Resource Generation\n *\n * Generates markdown resources from topics using AI inference.\n */\n\nimport { getLocaleEnglishName, deriveViews } from '@semiont/core';\nimport type { GatheredContext, Logger, SupportedMediaType } from '@semiont/core';\nimport type { InferenceClient } from '@semiont/inference';\nimport { boundedGenerateWithMetadata } from '../inference-call';\n\n\nfunction getLanguageName(locale: string): string {\n return getLocaleEnglishName(locale) || locale;\n}\n\n// Prompt-embedding caps — bound the context fed to the model (CONTEXT-IDENTIFIERS\n// D4: named constants; promote to caller-tunable options only if a consumer\n// actually hits the wall). A fact past these bounds never reaches the model.\nconst RESOURCE_CONTENT_CAP = 4000;\nconst SEMANTIC_MATCH_LIMIT = 3;\nconst SEMANTIC_MATCH_CHARS = 240;\n\n/**\n * Model-visible identifier handle for an embedded excerpt — the ONE bracket\n * convention across all context sections (CONTEXT-IDENTIFIERS D1/D2): resource id\n * always, annotation id as a suffix when the excerpt is annotation-derived.\n */\nfunction idLabel(resourceId: string, annotationId?: string): string {\n return `[${resourceId}${annotationId ? `/${annotationId}` : ''}]`;\n}\n\n/**\n * The canonical gateway default for maxTokens — a short-definition size. The\n * UI also initialises its field to 500 as a UX convenience, but the\n * authoritative fallback lives here so direct API callers get a sensible\n * limit when they omit the parameter. Exported so error messages elsewhere\n * can name the ceiling that actually applied (one constant, no drift).\n */\nexport const DEFAULT_MAX_TOKENS = 500;\n\n/**\n * Generate resource content using inference.\n *\n * Locale parameters: `locale` is the *body* locale — the language the\n * generated resource should be written in (sourced from the user's UI\n * locale). `sourceLanguage` is the *source* locale — the language of the\n * referenced resource whose context (selected passage, surrounding text)\n * is embedded into the prompt. They're independent: a German user can\n * generate German content from an English source resource. See\n * `types.ts` \"Locale conventions\" for the full discussion.\n */\nexport async function generateResourceFromTopic(\n topic: string,\n entityTypes: string[],\n client: InferenceClient,\n logger: Logger,\n userPrompt?: string,\n locale?: string,\n context?: GatheredContext,\n temperature?: number,\n maxTokens?: number,\n sourceLanguage?: string,\n outputMediaType: SupportedMediaType = 'text/markdown',\n task: string = 'resource',\n structure?: string,\n cite: boolean = false,\n repair?: { source: string; error: string }\n): Promise<{ title: string; content: string; truncated: boolean }> {\n logger.debug('Generating resource from topic', {\n topicPreview: topic.substring(0, 100),\n entityTypes,\n hasUserPrompt: !!userPrompt,\n locale,\n sourceLanguage,\n hasContext: !!context,\n temperature,\n maxTokens,\n outputMediaType,\n task,\n structure\n });\n\n // Use provided values or defaults.\n const finalTemperature = temperature ?? 0.7;\n const finalMaxTokens = maxTokens ?? DEFAULT_MAX_TOKENS;\n\n // Determine language instructions. Body locale (\"write the resource in X\")\n // and source locale (\"the embedded source text is in Y\") are independent —\n // a German user can ask for a German write-up of an English source.\n const languageInstruction = locale && locale !== 'en'\n ? `\\n\\nIMPORTANT: Write the entire resource in ${getLanguageName(locale)}.`\n : '';\n const sourceLanguageInstruction = sourceLanguage\n ? `\\n\\nThe source resource and embedded context are in ${getLanguageName(sourceLanguage)}.`\n : '';\n\n // ── Context sections — switch on the unified GatheredContext focus ─────────\n // Annotation focus drives the annotation + selected-passage sections; resource\n // focus drives a minimal resource anchor (the full grounding — summary,\n // suggestedReferences, content — is YIELD-FROM-RESOURCE Gap B). The graph and\n // semantic sections are shared base, rendered for either focus.\n let annotationSection = '';\n let contextSection = '';\n let resourceSection = '';\n let graphSection = '';\n\n if (context) {\n const { focus } = context;\n // The focal resource's id — equal to the id `buildKnowledgeGraph` (P3) anchored\n // the graph's main node on, so `deriveViews` resolves edges. Read directly off\n // the descriptor (a plain string on the generated type); there is no event id to\n // thread here as the matcher had.\n const mainResourceId =\n focus.kind === 'annotation'\n ? focus.sourceResource['@id']\n : focus.resource['@id'];\n const focalAnnotationId = focus.kind === 'annotation' ? focus.annotation.id : undefined;\n\n if (focus.kind === 'annotation') {\n const parts: string[] = [];\n parts.push(`- Annotation motivation: ${focus.annotation.motivation}`);\n parts.push(`- Source resource: ${focus.sourceResource.name} ${idLabel(mainResourceId)}`);\n // Include body text for commenting/assessing annotations\n const { motivation, body } = focus.annotation;\n if (motivation === 'commenting' || motivation === 'assessing') {\n const bodyItem = Array.isArray(body) ? body[0] : body;\n if (bodyItem && 'value' in bodyItem && bodyItem.value) {\n const label = motivation === 'commenting' ? 'Comment' : 'Assessment';\n parts.push(`- ${label}: ${bodyItem.value}`);\n }\n }\n // The wizard's Hint: supplements or replaces the selected text as the\n // subject signal (GatheredContext.json focus.userHint). The matcher folds\n // it into its search term the same way — the two consumers of one field.\n if (focus.userHint) {\n parts.push(`- User hint (steers what to generate): ${focus.userHint}`);\n }\n annotationSection = `\\n\\nAnnotation context:\\n${parts.join('\\n')}`;\n\n if (focus.selected) {\n const { before, text, after } = focus.selected;\n contextSection = `\\n\\nSource document context:\n---\n${before ? `...${before}` : ''}\n**[${text}]**\n${after ? `${after}...` : ''}\n---\n`;\n }\n } else {\n // Resource focus — ground in the focal resource's summary, suggested\n // references, and content (omit-empty). Content is capped per resource to\n // bound prompt size; the caller already chose breadth via gather.resource.\n const parts = [`- Resource: ${focus.resource.name} ${idLabel(mainResourceId)}`];\n if (focus.summary) parts.push(`- Summary: ${focus.summary}`);\n if (focus.suggestedReferences && focus.suggestedReferences.length > 0) {\n parts.push(`- Suggested references: ${focus.suggestedReferences.join(', ')}`);\n }\n resourceSection = `\\n\\nResource context:\\n${parts.join('\\n')}`;\n\n if (focus.content?.main) {\n resourceSection += `\\n\\nResource content:\\n---\\n${focus.content.main.slice(0, RESOURCE_CONTENT_CAP)}\\n---`;\n }\n const related = Object.entries(focus.content?.related ?? {});\n if (related.length > 0) {\n const blocks = related\n .map(([id, text]) => `[${id}]\\n${text.slice(0, RESOURCE_CONTENT_CAP)}`)\n .join('\\n\\n');\n resourceSection += `\\n\\nRelated resource content:\\n---\\n${blocks}\\n---`;\n }\n }\n\n // Shared base: graph-derived neighborhood (connections / citedBy / siblings)\n // plus the LLM relationship summary, recomputed from the unified graph.\n if (mainResourceId) {\n const views = deriveViews(context.graph, mainResourceId, focalAnnotationId);\n const parts: string[] = [];\n\n if (views.connections.length > 0) {\n const connList = views.connections\n .map(c => `${c.resourceName}${c.entityTypes.length ? ` (${c.entityTypes.join(', ')})` : ''} ${idLabel(c.resourceId)}`)\n .join(', ');\n parts.push(`- Connected resources: ${connList}`);\n }\n\n if (views.citedByCount > 0) {\n const citedNames = views.citedBy.map(c => `${c.resourceName} ${idLabel(c.resourceId)}`).join(', ');\n parts.push(`- This resource is cited by ${views.citedByCount} other resource${views.citedByCount > 1 ? 's' : ''}${citedNames ? `: ${citedNames}` : ''}`);\n }\n\n if (views.siblingEntityTypes.length > 0) {\n parts.push(`- Related entity types in this document: ${views.siblingEntityTypes.join(', ')}`);\n }\n\n if (context.inferredRelationshipSummary) {\n parts.push(`- Relationship summary: ${context.inferredRelationshipSummary}`);\n }\n\n if (parts.length > 0) {\n graphSection = `\\n\\nKnowledge graph context:\\n${parts.join('\\n')}`;\n }\n }\n }\n\n // Build semantic context section if available — the vector matches the gather\n // flow already retrieved for the focal passage, used to ground generation (RAG).\n // Capped and truncated to bound prompt cost (see the named caps above); the\n // gather step pre-filters to ≤10 matches above a 0.5 cosine threshold. Each\n // passage carries its source id so the model can attribute what it uses.\n // See .plans/SEMANTIC-CONTEXT-RAG.md + .plans/CONTEXT-IDENTIFIERS.md.\n let semanticContextSection = '';\n const similar = context?.semanticContext?.similar ?? [];\n if (similar.length > 0) {\n const lines = [...similar]\n .sort((a, b) => b.score - a.score)\n .slice(0, SEMANTIC_MATCH_LIMIT)\n .map(m => `- ${idLabel(m.resourceId, m.annotationId)} (${m.score.toFixed(2)})${m.machineRead ? ' [OCR]' : ''} ${m.text.slice(0, SEMANTIC_MATCH_CHARS)}`);\n // A passage marked [OCR] was recognized from a scanned image, so its\n // wording — and especially its digits — may be misread. Saying so is the\n // whole point of carrying the flag: the model is the only reader of these\n // passages, and it would otherwise quote a scanned figure as confidently\n // as a typed one.\n const ocrNote = similar.some(m => m.machineRead)\n ? '\\nPassages marked [OCR] were read from scanned images by character recognition; treat their exact wording and numbers as uncertain, and say so if you rely on one.'\n : '';\n semanticContextSection = `\\n\\nRelated passages from the knowledge base:\\n${lines.join('\\n')}${ocrNote}`;\n }\n\n // ── Task framing (YIELD-STRUCTURE D1): canonical tasks map to tested framings;\n // an unknown task string is used VERBATIM as the framing instruction (loud\n // degrade — warn, never a silent fallback to 'resource').\n let leadLine: string;\n if (task === 'resource') {\n leadLine = `Generate a concise, informative resource about \"${topic}\".`;\n } else if (task === 'answer') {\n leadLine = `Answer the following question directly and concisely, grounded in the provided context: \"${topic}\"`;\n } else if (task === 'summary') {\n leadLine = `Write a concise summary of \"${topic}\".`;\n } else {\n logger.warn('Unknown task — using it verbatim as the framing instruction', { task });\n leadLine = `${task}\\nTopic: \"${topic}\"`;\n }\n\n // ── Structure directive (YIELD-STRUCTURE D2/D4): emitted ONLY when the caller\n // sets one — unset means no directive at all (the task framing and the model\n // determine shape). Never derived from the token budget: maxTokens is length\n // only. The forced `# Title` heading exists only under canonical 'sections'.\n const isPlainText = outputMediaType === 'text/plain';\n // PDF output means the model authors Typst source (Q1, direct Typst); the\n // worker compiles it. The prompt therefore teaches Typst syntax, and the\n // markdown scaffolding never applies.\n const isPdf = outputMediaType === 'application/pdf';\n let structureRequirement = '';\n let titleRequirement = '';\n if (structure === 'sections') {\n structureRequirement = isPdf\n ? '\\n- Organize the content into titled sections (= Heading) with well-structured paragraphs'\n : isPlainText\n ? '\\n- Organize the content into titled sections with well-structured paragraphs'\n : '\\n- Organize the content into titled sections (## Section) with well-structured paragraphs';\n if (!isPlainText && !isPdf) {\n titleRequirement = '\\n- Start with a clear heading (# Title)';\n }\n } else if (structure === 'prose') {\n structureRequirement = '\\n- Write flowing, well-structured paragraphs with no section headings';\n } else if (structure === 'chat') {\n structureRequirement = '\\n- Structure the content as a conversational chat transcript — a sequence of alternating, speaker-labeled turns (no section headings)';\n } else if (structure) {\n logger.warn('Unknown structure — passing it through as freeform organization guidance', { structure });\n structureRequirement = `\\n- Organize the output as: ${structure}`;\n }\n\n // Citation instruction (INLINE-CITATIONS): ask the model to emit [[<id>]]\n // transport tokens next to each claim, citing only ids the context embedding\n // shows. The worker strips the tokens and reconciles them into linking\n // annotations — they never reach the stored content.\n const citeRequirement = cite\n ? '\\n- Ground every claim in the provided context. Immediately after each claim, cite its source by emitting [[<id>]], where <id> is an id shown in square brackets in the context above (for a passage labeled [abc], emit [[abc]]). Cite only ids that appear in the context.'\n : '';\n\n const formatRequirements = isPdf\n ? `- Write the response as Typst markup (the Typst typesetting language — not markdown, not LaTeX)\n- Headings are written as = Heading (deeper levels == Subheading); everything else is plain prose paragraphs\n- Do not emit markdown syntax or code fences`\n : isPlainText\n ? `- Write the response as plain text — no formatting markup (no #, *, backticks, headings, or links)\n- Begin with the title on its own first line`\n : `- Use markdown formatting\n- Write the response as markdown`;\n\n // Compile-repair context (PDF-GENERATION P3): the previous Typst source\n // failed to compile; the legible compiler diagnostics go back to the model\n // as an authoritative instruction to fix and re-emit the full document.\n const repairSection = repair\n ? `\\n\\nYour previous attempt failed to compile. Fix the error and return the complete corrected document — full source, not a diff.\nCompile error:\n${repair.error}\nPrevious source:\n${repair.source}`\n : '';\n\n // The caller's prompt is an authoritative leading Instruction (YIELD-STRUCTURE D3),\n // not background \"additional context\" — task = what to produce, prompt = how.\n const prompt = `${leadLine}\n${userPrompt ? `Instruction: ${userPrompt}` : ''}${repairSection}\n${entityTypes.length > 0 ? `Focus on these entity types: ${entityTypes.join(', ')}.` : ''}${annotationSection}${contextSection}${resourceSection}${graphSection}${semanticContextSection}${sourceLanguageInstruction}${languageInstruction}\n\nRequirements:\n- Aim for approximately ${finalMaxTokens} tokens of content\n- Be factual and informative${structureRequirement}${titleRequirement}${citeRequirement}\n${formatRequirements}`;\n\n // Simple parser - just use the response directly as markdown\n const parseResponse = (response: string): { title: string; content: string } => {\n // Clean up any markdown code fences if present\n let content = response.trim();\n if (content.startsWith('```markdown') || content.startsWith('```md') || content.startsWith('```typst')) {\n content = content.slice(content.indexOf('\\n') + 1);\n const endIndex = content.lastIndexOf('```');\n if (endIndex !== -1) {\n content = content.slice(0, endIndex);\n }\n } else if (content.startsWith('```')) {\n content = content.slice(3);\n const endIndex = content.lastIndexOf('```');\n if (endIndex !== -1) {\n content = content.slice(0, endIndex);\n }\n }\n\n content = content.trim();\n\n // Title is provided by the caller (topic), not extracted from generated content\n return {\n title: topic,\n content: content\n };\n };\n\n logger.debug('Sending prompt to inference', {\n promptLength: prompt.length,\n temperature: finalTemperature,\n maxTokens: finalMaxTokens\n });\n const response = await boundedGenerateWithMetadata(client, prompt, finalMaxTokens, finalTemperature, undefined, logger);\n logger.debug('Got response from inference', { responseLength: response.text.length, stopReason: response.stopReason });\n\n const result = parseResponse(response.text);\n logger.debug('Parsed response', {\n hasTitle: !!result.title,\n titleLength: result.title?.length,\n hasContent: !!result.content,\n contentLength: result.content?.length\n });\n\n // The provider's stopReason collapses to one protocol-owned bit HERE and\n // nowhere else (GENERATE-FROM-RESOURCE D6): 'max_tokens' means the artifact\n // is cut off, not complete; every other reason is a natural stop.\n return { ...result, truncated: response.stopReason === 'max_tokens' };\n}\n","/**\n * Typst compiler invocation (PDF-GENERATION P3).\n *\n * The model writes Typst source (Q1, settled 2026-08-04: direct Typst); this\n * module turns it into PDF bytes via the pinned binary the worker image ships\n * (P2 — `/usr/local/bin/typst`, v0.15.1, resolved through PATH so tests can\n * substitute a fake). Compile failures return the legible error text — Typst's\n * `file:line:col` + caret diagnostics are the load-bearing input to the bounded\n * repair loop in `processGenerationJob`.\n */\nimport { execFileSync } from 'node:child_process';\nimport { mkdtempSync, writeFileSync, readFileSync, rmSync } from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport { join } from 'node:path';\n\n/**\n * Every compile pins the PDF creation timestamp. Unpinned compiles of\n * identical source produce different bytes every time (CreationDate/ModDate),\n * which churns the content checksum and makes the Smelter re-embed a document\n * that did not change — the spurious-re-embed failure S11/S12 exist to\n * prevent. The value is arbitrary; its fixity is the point.\n */\nexport const PINNED_CREATION_TIMESTAMP = 1700000000;\n\n/**\n * Model-authored source can fail to compile; the error is fed back for repair\n * a bounded number of times, then the job fails loudly with the compiler's\n * diagnostics. Bounded because generation is non-idempotent and paid per\n * attempt — this is a repair loop, not a retry loop.\n */\nexport const MAX_COMPILE_REPAIRS = 2;\n\nexport function compileTypst(source: string): { pdf: Uint8Array } | { error: string } {\n const dir = mkdtempSync(join(tmpdir(), 'typst-'));\n try {\n const inFile = join(dir, 'doc.typ');\n const outFile = join(dir, 'doc.pdf');\n writeFileSync(inFile, source);\n try {\n execFileSync(\n 'typst',\n ['compile', '--creation-timestamp', String(PINNED_CREATION_TIMESTAMP), inFile, outFile],\n { stdio: ['ignore', 'pipe', 'pipe'] },\n );\n } catch (err) {\n const stderr = (err as { stderr?: Buffer }).stderr;\n return { error: stderr?.length ? stderr.toString('utf8') : String(err) };\n }\n return { pdf: new Uint8Array(readFileSync(outFile)) };\n } finally {\n rmSync(dir, { recursive: true, force: true });\n }\n}\n","/**\n * Citation-token resolver (INLINE-CITATIONS P1).\n *\n * Under `cite`, the generation prompt asks the model to emit `[[<resourceId>]]` /\n * `[[<resourceId>/<annotationId>]]` transport tokens after each claim, citing ids\n * the context embedding put in front of it (CONTEXT-IDENTIFIERS). Tokens are\n * TRANSPORT, not content: this resolver parses them, validates each id against the\n * ids actually present in the embedded context (the hallucination guard — an\n * unknown id is dropped loudly, never silently linked), STRIPS them from the\n * content before upload, and returns claim-span citations the worker mints as W3C\n * linking annotations on the derived resource. See .plans/INLINE-CITATIONS.md.\n */\nimport type { GatheredContext, Logger } from '@semiont/core';\n\nexport interface GenerationCitation {\n /** The cited source resource (validated present in the embedded context). */\n resourceId: string;\n /** The contributing annotation, when the cited excerpt was annotation-derived. */\n annotationId?: string;\n /** Claim span in the FINAL (token-stripped) content; substring(start, end) === exact. */\n start: number;\n end: number;\n exact: string;\n}\n\n/** `[[rid]]` or `[[rid/annId]]` — ids are bare (no whitespace, brackets, or slashes). */\nconst CITATION_TOKEN = /\\[\\[([^\\s[\\]/]+)(?:\\/([^\\s[\\]/]+))?\\]\\]/g;\n\n/**\n * The ids the context embedding actually put in front of the model — the\n * hallucination guard's ground truth. Mirrors what the prompt renderers label:\n * the focal resource, graph resource nodes, semantic matches, related content.\n */\nexport function collectContextResourceIds(context: GatheredContext | undefined): Set<string> {\n const ids = new Set<string>();\n if (!context) return ids;\n\n const { focus } = context;\n ids.add(focus.kind === 'annotation' ? focus.sourceResource['@id'] : focus.resource['@id']);\n for (const node of context.graph.nodes) {\n if (node.type === 'resource') ids.add(node.id);\n }\n for (const m of context.semanticContext?.similar ?? []) ids.add(m.resourceId);\n if (focus.kind === 'resource') {\n for (const id of Object.keys(focus.content?.related ?? {})) ids.add(id);\n }\n return ids;\n}\n\nexport function resolveCitationTokens(\n content: string,\n validResourceIds: ReadonlySet<string>,\n logger: Logger,\n): { content: string; citations: GenerationCitation[] } {\n const citations: GenerationCitation[] = [];\n let clean = '';\n let last = 0;\n\n for (const match of content.matchAll(CITATION_TOKEN)) {\n const token = match[0];\n const citedResourceId = match[1]!;\n const annotationId = match[2];\n\n // Append the text before the token, dropping the whitespace run immediately\n // preceding it so stripping never leaves a dangling space.\n clean += content.slice(last, match.index).replace(/[ \\t]+$/, '');\n last = match.index! + token.length;\n\n if (!validResourceIds.has(citedResourceId)) {\n logger.warn('Citation token references an id absent from the provided context — dropped', {\n resourceId: citedResourceId,\n });\n continue;\n }\n\n // Claim span: the sentence preceding the token, computed against the clean\n // content. Offsets are final — later appends never shift earlier positions.\n const end = clean.length;\n let start = 0;\n for (let i = end - 2; i >= 0; i--) {\n const ch = clean[i];\n if (ch === '.' || ch === '!' || ch === '?' || ch === '\\n') {\n start = i + 1;\n break;\n }\n }\n while (start < end && /\\s/.test(clean[start]!)) start++;\n const exact = clean.slice(start, end);\n if (exact.length === 0) {\n logger.warn('Citation token has no preceding claim text — dropped', {\n resourceId: citedResourceId,\n });\n continue;\n }\n\n citations.push({\n resourceId: citedResourceId,\n ...(annotationId ? { annotationId } : {}),\n start,\n end,\n exact,\n });\n }\n\n clean += content.slice(last);\n return { content: clean, citations };\n}\n","/**\n * Run `worker` over `items` with at most `limit` of them in flight at once\n * (DETECTION-QUALITY-THROUGHPUT P6). Hand-rolled rather than a dependency: the\n * need is exactly one bounded map, and the BOUND is the whole point — detection\n * parallelizes independent entity types, but unbounded fan-out just trades\n * sequential waiting for provider 429 thrash, so the cap is the feature.\n *\n * A fixed pool of `min(limit, items.length)` pumps, each pulling the next index\n * until the work is exhausted — so at most `limit` calls are ever awaiting at\n * once, whatever the item count. Results are returned in INPUT order (a caller\n * can zip them back to items); COMPLETION order is not input order — that is the\n * point of concurrency — so a caller that must act \"as each finishes\" does so\n * inside `worker`, not on the returned array.\n *\n * Failure matches the sequential loop it replaces: the first `worker` rejection\n * rejects the whole run. Pumps already awaiting finish (they cannot be\n * cancelled mid-await), but no new item is pulled once a rejection propagates.\n */\nexport async function runBounded<T, R>(\n items: readonly T[],\n limit: number,\n worker: (item: T, index: number) => Promise<R>,\n): Promise<R[]> {\n const results = new Array<R>(items.length);\n let next = 0;\n\n async function pump(): Promise<void> {\n while (true) {\n const i = next++;\n if (i >= items.length) return;\n results[i] = await worker(items[i]!, i);\n }\n }\n\n const poolSize = Math.max(1, Math.min(limit, items.length));\n await Promise.all(Array.from({ length: poolSize }, () => pump()));\n return results;\n}\n","/**\n * Job Processors\n *\n * Pure functions that take content + inference client + params,\n * report progress via callback, and return annotations + results.\n *\n * No EventBus, no JobQueue, no side effects except calling inference.\n * Driven by the remote worker process (worker-process.ts), which claims\n * jobs over SSE and dispatches by jobType to these functions.\n */\n\nimport { AnnotationDetection } from './workers/annotation-detection';\nimport { extractEntities } from './workers/detection/entity-extractor';\nimport { DEFAULT_MAX_TOKENS, generateResourceFromTopic } from './workers/generation/resource-generation';\nimport { compileTypst, MAX_COMPILE_REPAIRS } from './workers/generation/typst-compiler';\nimport { withinByteBudget, MAX_PDF_BYTES } from '@semiont/content';\nimport { resolveCitationTokens, collectContextResourceIds, type GenerationCitation } from './workers/generation/citation-resolver';\nimport { annotationIdFor } from '@semiont/event-sourcing';\nimport { didToAgent, GENERATABLE_MEDIA_TYPES, type Annotation, type GenerationJobParams, type Logger, type ResourceId, type SupportedMediaType, type components, type JobReferenceAnnotationResult, type JobHighlightAnnotationResult, type JobCommentAnnotationResult, type JobAssessmentAnnotationResult, type JobTagAnnotationResult, type UnitCursor } from '@semiont/core';\nimport { reconcileSelector, createFragmentSelector, locate, type ReconciledSelector, type AnchoredText } from '@semiont/core';\nimport type { InferenceClient } from '@semiont/inference';\nimport type {\n HighlightDetectionParams,\n CommentDetectionParams,\n AssessmentDetectionParams,\n DetectionParams,\n TagDetectionParams,\n GenerationResult,\n} from './types';\nimport { noteAnchor } from './workers/detection/anchor-audit';\nimport { runBounded } from './workers/detection/bounded-concurrency';\n\ntype Agent = components['schemas']['Agent'];\n\n/** A detected span — offsets into the extracted `.text`, plus optional context. */\nexport type SpanMatch = { exact: string; start: number; end: number; prefix?: string; suffix?: string };\n\n/**\n * The span half of an annotation's identity (JOB-RESTART-SAFETY P3).\n *\n * Shared by both builders because the span IS the same fact in both — the PDF\n * path additionally persists geometry for it, but that geometry is derived\n * from these offsets, so hashing it too would add no distinguishing power and\n * would make an id depend on a layout the text path cannot reproduce.\n *\n * `exact` is included, not just the offsets: after a content update the same\n * offsets cover different text, and that is a different annotation.\n */\nfunction spanAnchor(match: Pick<SpanMatch, 'start' | 'end' | 'exact'>): string {\n return `${match.start}:${match.end}:${match.exact}`;\n}\n\n/**\n * Turn a detected span into a stored annotation. The media type, resource, and\n * attribution context are closed over by the caller (see `prepareDetection`);\n * the detection processor supplies only the motivation, the span, and any\n * motivation-specific body. This is the single axis that varies by media type,\n * so the detection processors themselves stay media-agnostic.\n */\nexport type BuildAnnotation = (\n motivation: Motivation,\n match: SpanMatch,\n body?: Annotation['body'],\n) => Annotation;\n\n/**\n * Progress callback. The two positional args are the required `JobProgress`\n * fields (`percentage`, `message`). The third optional arg carries the\n * job-type-specific fields (`completedEntityTypes`, `requestParams`, etc.)\n * that the progress UI renders.\n *\n * Anything in `extra` describing the RUN rather than the moment must be passed\n * on EVERY call: the client's `progress$` replaces its value per event, so a\n * field sent once disappears on the next tick.\n *\n * `message` is a CODE plus typed params, never a prose sentence\n * (ASSIST-PROGRESS-CONSOLIDATION A6). The producer reports what happened;\n * each client renders it in the user's language — react-ui from its 29\n * locales, the Go launcher from its English map. The vocabulary is frozen\n * by the census of these call sites: adding a shape means adding a variant\n * to `JobProgressMessage.json` and copy in every client, not composing a\n * new sentence here.\n */\nexport type OnProgress = (\n percentage: number,\n message: JobProgressMessage,\n extra?: Partial<JobProgress>,\n) => void;\n\ntype JobProgress = components['schemas']['JobProgress'];\n/** Derived from the wire, never restated: the per-unit entry's shape is the\n * spec's, so a field added there reaches these accumulators by regeneration\n * rather than by someone remembering two places. */\ntype CompletedItem = NonNullable<JobProgress['completedItems']>[number];\ntype JobProgressMessage = components['schemas']['JobProgressMessage'];\n\n/** The five W3C motivations this system mints — a closed vocabulary, so it is\n * typed as one rather than as `string`. */\nexport type Motivation = Annotation['motivation'];\n\n/**\n * A detection processor returns only its result. Annotations leave through\n * `onChunkComplete`, per chunk — a return that also carried them would be a\n * second path to the same write.\n */\nexport interface ProcessorResult<R> {\n result: R;\n}\n\n/**\n * Strip the audit-only fields (`anchorMethod`, `llmOffsets`, `matchQuality`)\n * off a `ReconciledSelector` so the rest is shaped like a match input for\n * `buildTextAnnotation`. The audit info belongs in logs, not in storage.\n */\nfunction toMatch(r: ReconciledSelector): { exact: string; start: number; end: number; prefix?: string; suffix?: string } {\n return {\n exact: r.exact,\n start: r.start,\n end: r.end,\n ...(r.prefix !== undefined ? { prefix: r.prefix } : {}),\n ...(r.suffix !== undefined ? { suffix: r.suffix } : {}),\n };\n}\n\n/**\n * Identity key for a built annotation: motivation + anchored span + body.\n * Two annotations with the same key are the same event written twice.\n */\nfunction annotationDedupeKey(ann: Record<string, unknown>): string {\n const target = ann.target as\n | { selector?: Array<{ type: string; start?: number; end?: number; value?: string; exact?: string; prefix?: string; suffix?: string }> }\n | undefined;\n const selectors = Array.isArray(target?.selector) ? target.selector : [];\n const pos = selectors.find((s) => s.type === 'TextPositionSelector');\n // Anchor identity is media-specific. Text annotations carry a\n // TextPositionSelector (durable char offsets). PDF annotations have none —\n // their anchor is the per-line FragmentSelector viewrect geometry plus the\n // TextQuoteSelector text. Keying only on TextPositionSelector would collapse\n // every PDF annotation sharing a motivation+body onto one (its offsets fall\n // back to '?'), so e.g. multiple PDF highlights emit as a single annotation.\n let anchor: string;\n if (pos) {\n anchor = `pos:${pos.start ?? '?'}:${pos.end ?? '?'}`;\n } else {\n const frags = selectors.filter((s) => s.type === 'FragmentSelector').map((s) => s.value ?? '').join(',');\n const quote = selectors.find((s) => s.type === 'TextQuoteSelector');\n anchor = `frag:${frags}|quote:${quote?.exact ?? ''}:${quote?.prefix ?? ''}:${quote?.suffix ?? ''}`;\n }\n return [ann.motivation as string, anchor, JSON.stringify(ann.body ?? null)].join('|');\n}\n\n/**\n * Drop annotations that are identical in the fields that define an\n * annotation's meaning: motivation, anchored span, and body.\n *\n * Why this is needed: each LLM-emitted span is reconciled independently\n * (no cross-entry coordination), and `reconcileSelector`'s `first-of-many`\n * fallback anchors every undisambiguated entry at the *same* first\n * occurrence. So a phrase repeated in non-distinctive context can produce\n * several entries that all collapse onto one span — identical events. This\n * collapses them back to one.\n *\n * What it does NOT drop: same span, *different* body (e.g. the same text\n * tagged as two entity types, or two distinct comments on one passage).\n * Those are legitimately distinct annotations.\n *\n * Applied identically by every processor below.\n */\n/**\n * THE dedupe decider — one mechanism for all five detection types, held\n * across a stream of chunk batches: adjacent chunks overlap, so the same\n * span arrives twice, and there is no post-pass to collapse it in. Scope it\n * to one emission stream — per unit for reference detection, per job for the\n * four motivations. Never add a batch post-pass beside it (gated).\n */\nfunction makeSpanDeduper(): (annotations: Annotation[]) => Annotation[] {\n const seen = new Set<string>();\n return (annotations) => {\n const out: Annotation[] = [];\n for (const ann of annotations) {\n const key = annotationDedupeKey(ann);\n if (seen.has(key)) continue;\n seen.add(key);\n out.push(ann);\n }\n return out;\n };\n}\n\nexport function buildTextAnnotation(\n content: string,\n resourceId: ResourceId,\n userId: string,\n generator: Agent,\n motivation: Motivation,\n match: { exact: string; start: number; end: number; prefix?: string; suffix?: string },\n // Body may be a single AnnotationBody object or a non-empty array of\n // them, OR omitted entirely. W3C treats body as optional; annotations\n // whose motivation alone conveys meaning (highlighting) legitimately\n // skip it. Every other motivation currently passes something; the\n // processor that calls this makes the choice per-motivation.\n body?: Annotation['body'],\n) {\n // Write-time invariant. Every selector that reaches storage must be\n // internally consistent with the source content. If a worker bypasses\n // `reconcileSelector` or a future change re-introduces overlap, the\n // throw fires loudly here instead of corrupting the KB.\n if (content.substring(match.start, match.end) !== match.exact) {\n throw new Error(\n `buildTextAnnotation invariant: content.substring(${match.start}, ${match.end}) !== exact ` +\n `for resource ${resourceId}, motivation ${motivation}`,\n );\n }\n if (match.prefix !== undefined) {\n const actualPrefix = content.substring(Math.max(0, match.start - match.prefix.length), match.start);\n if (actualPrefix !== match.prefix) {\n throw new Error(\n `buildTextAnnotation invariant: content prefix-slice !== prefix ` +\n `for resource ${resourceId}, motivation ${motivation}`,\n );\n }\n }\n if (match.suffix !== undefined) {\n const actualSuffix = content.substring(match.end, Math.min(content.length, match.end + match.suffix.length));\n if (actualSuffix !== match.suffix) {\n throw new Error(\n `buildTextAnnotation invariant: content suffix-slice !== suffix ` +\n `for resource ${resourceId}, motivation ${motivation}`,\n );\n }\n }\n\n // `userId` here is the DID of the human who initiated the work. The\n // worker process is acting on their behalf using `generator` to\n // produce content. Per the protocol attribution model:\n // creator = who initiated (the human)\n // generator = what produced (the software peer)\n // wasAttributedTo = both parties (PROV-O)\n // For autonomous-agent work creator and generator collapse to the\n // same Software Agent; the same field assignments still hold.\n const creator = didToAgent(userId);\n const wasAttributedTo: Agent[] =\n creator['@id'] === generator['@id'] ? [generator] : [creator, generator];\n return {\n '@context': 'http://www.w3.org/ns/anno.jsonld' as const,\n 'type': 'Annotation' as const,\n 'id': annotationIdFor({ resourceId: resourceId as string, motivation, anchor: spanAnchor(match), body }),\n motivation,\n creator,\n generator,\n wasAttributedTo,\n created: new Date().toISOString(),\n target: {\n type: 'SpecificResource' as const,\n source: resourceId as string,\n selector: [\n { type: 'TextPositionSelector' as const, start: match.start, end: match.end },\n {\n type: 'TextQuoteSelector' as const,\n exact: match.exact,\n ...(match.prefix && { prefix: match.prefix }),\n ...(match.suffix && { suffix: match.suffix }),\n },\n ],\n },\n ...(body !== undefined ? { body } : {}),\n };\n}\n\n/**\n * PDF sibling of `buildTextAnnotation`. The model returns the same\n * `{ exact, start, end, prefix?, suffix? }` match over the extracted text\n * layer's `text`; geometry comes from the layer, never the model.\n *\n * `target.selector` = one `FragmentSelector` per line (`locate` unions the\n * overlapping text-layer items into per-line viewrects) plus a\n * `TextQuoteSelector` anchor. No `TextPositionSelector`: the extracted text\n * layer is a derived artifact, not the stored content, so its char offsets are\n * not a durable anchor.\n *\n * **And the `id` is hashed over exactly those offsets** — `annotationIdFor` gets\n * `spanAnchor(match)`, i.e. `${start}:${end}:${exact}`. That is worth stating\n * plainly, because it sits in tension with the paragraph above: identity here\n * depends on something the annotation refuses to store, on the grounds that it is\n * not durable. The consequence is not a corrupt write but an invisible one — an\n * extraction that moved would re-identify the same visual span, and the two\n * annotations would agree in every stored field (same rects, same quoted text) and\n * differ only in `id`, so every dedupe layer would correctly let both through.\n *\n * It is left as it is on evidence, not by oversight. The offsets come from a\n * derivation cached per content checksum and gated by a stamp that a release of\n * `@semiont/content`, the PDF engine or its traineddata busts by design; measured\n * across the caret-reachable engine move (pdfjs 6.2.108 → 6.3.289) over 1,192 pages\n * of real documents, they did not move, and `pdf-offset-stability.test.ts` in\n * `@semiont/content` now fails if they ever do. The OCR path is unmeasured, and a\n * scanned document takes its whole text from there. If that gate ever fires, this\n * is the line to revisit: hashing the DURABLE anchor instead (page geometry plus\n * `exact`) makes identity depend only on what the annotation carries — at the cost\n * of one round of new ids for every PDF annotation minted afterwards.\n *\n * Write-time invariant (geometry <-> text): geometry is item-level (word runs),\n * so the covered items' text must *contain* `exact` (whitespace-normalized) —\n * containment, not reconstruction. An empty cover (no overlapping items -> no\n * rects) also fails. Throws loudly, naming the resource + motivation, rather\n * than persisting geometry that doesn't back the quoted text.\n */\nexport function buildPdfAnnotation(\n anchored: AnchoredText,\n resourceId: ResourceId,\n userId: string,\n generator: Agent,\n motivation: Motivation,\n match: { exact: string; start: number; end: number; prefix?: string; suffix?: string },\n body?: Annotation['body'],\n) {\n // `locate` returns both the per-line rects and the overlap items it found;\n // reuse `overlap` for the containment check rather than re-scanning layer.items.\n const { rects, overlap } = locate(anchored, match.start, match.end);\n\n const coveredText = overlap.length\n ? anchored.text.substring(\n Math.min(...overlap.map((i) => i.start)),\n Math.max(...overlap.map((i) => i.end)),\n )\n : '';\n const normalize = (s: string) => s.replace(/\\s+/g, ' ').trim();\n // Two distinct failures, reported distinctly. Merged, both printed \"covered\n // text does not contain exact\" — which sends anyone debugging an empty cover\n // to inspect text matching that never ran. Same class deliberately: both stay\n // plain `Error`, so `classifyFailure` leaves them unrecognized and therefore\n // retryable (ABANDONED-INFERENCE HD2 is one-sided — only KNOWN-deterministic\n // failures skip the budget). No rects LOOKS deterministic, but the stored map\n // is keyed by content checksum, so a retry after the bytes change reads a\n // different map and can legitimately succeed.\n if (rects.length === 0) {\n throw new Error(\n `buildPdfAnnotation invariant: no rects located for offsets ${match.start}-${match.end} ` +\n `for resource ${resourceId}, motivation ${motivation}`,\n );\n }\n if (!normalize(coveredText).includes(normalize(match.exact))) {\n throw new Error(\n `buildPdfAnnotation invariant: covered text does not contain exact ` +\n `for resource ${resourceId}, motivation ${motivation}`,\n );\n }\n\n const creator = didToAgent(userId);\n const wasAttributedTo: Agent[] =\n creator['@id'] === generator['@id'] ? [generator] : [creator, generator];\n\n return {\n '@context': 'http://www.w3.org/ns/anno.jsonld' as const,\n 'type': 'Annotation' as const,\n 'id': annotationIdFor({ resourceId: resourceId as string, motivation, anchor: spanAnchor(match), body }),\n motivation,\n creator,\n generator,\n wasAttributedTo,\n created: new Date().toISOString(),\n target: {\n type: 'SpecificResource' as const,\n source: resourceId as string,\n selector: [\n ...rects.map((coord) => ({\n type: 'FragmentSelector' as const,\n conformsTo: 'http://tools.ietf.org/rfc/rfc3778' as const,\n value: createFragmentSelector(coord),\n })),\n {\n type: 'TextQuoteSelector' as const,\n exact: match.exact,\n ...(match.prefix && { prefix: match.prefix }),\n ...(match.suffix && { suffix: match.suffix }),\n },\n ],\n },\n ...(body !== undefined ? { body } : {}),\n };\n}\n\n/**\n * Where one unit stands once the chunk just handed over is durable\n * (CHUNK-GRAIN-RESUME P2).\n *\n * The unit is named HERE rather than in the detection layer, which knows about\n * chunks and nothing about jobs: for `reference-annotation` a unit is an entity\n * type, for `tag-annotation` a category — both loop over several — and for the\n * other three the job runs exactly one unit, its own motivation.\n */\nexport interface UnitCheckpoint {\n unit: string;\n cursor: UnitCursor;\n}\n\nexport async function processHighlightJob(\n content: string,\n inferenceClient: InferenceClient,\n params: HighlightDetectionParams,\n buildAnnotation: BuildAnnotation,\n onProgress: OnProgress,\n /** This chunk's novel annotations, awaited: the durability write. */\n onChunkComplete: (annotations: Annotation[], checkpoint: UnitCheckpoint) => Promise<void>,\n /** Where earlier attempts left each unit (CHUNK-GRAIN-RESUME P3), keyed the\n * same way the checkpoint is. A unit absent here starts at the top, which is\n * every unit of a first attempt. */\n resumeCursors?: Record<string, UnitCursor>,\n): Promise<ProcessorResult<JobHighlightAnnotationResult>> {\n const echo = detectionEcho(params);\n\n onProgress(10, { code: 'loading' }, echo);\n onProgress(30, { code: 'analyzing' }, echo);\n\n const dedupe = makeSpanDeduper();\n // Seeded from what an earlier attempt already counted for this unit, so a\n // resumed job's terminal record describes the document rather than the\n // remainder it happened to run (CHUNK-GRAIN-RESUME HD3).\n const prior = resumeCursors?.['highlighting'];\n let found = prior?.found ?? 0;\n let created = prior?.emitted ?? 0;\n await AnnotationDetection.detectHighlights(\n content, inferenceClient, params.instructions, params.density, params.sourceLanguage,\n // Liveness (chunk boundaries + in-flight heartbeat): 30–60 band.\n (consumedChars, totalChars) => onProgress(30 + Math.round((consumedChars / totalChars) * 30), { code: 'analyzing' }, echo),\n resumeCursors?.['highlighting'],\n async (matches, cursor) => {\n found += matches.length;\n // Highlights carry no body — motivation:'highlighting' on a target\n // is a complete annotation per the W3C Web Annotation Model.\n const fresh = dedupe(matches.map((h) => buildAnnotation('highlighting', h)));\n created += fresh.length;\n onProgress(60, { code: 'creating-annotations', count: created }, echo);\n // One motivation per job, so exactly one unit — and with only one, a\n // unit-grain checkpoint could record nothing until the whole document\n // was done. The cursor is the entire resume story for these three types.\n await onChunkComplete(fresh, { unit: 'highlighting', cursor: { ...cursor, found, emitted: created } });\n },\n );\n\n onProgress(100, { code: 'complete-created', count: created, kind: 'highlight' }, echo);\n\n return {\n result: { kind: 'highlight-annotation', highlightsFound: found, highlightsCreated: created },\n };\n}\n\n/**\n * The user's own inputs, echoed back for the progress widget. Labels are CODES\n * (the client localizes them); values are the user's words and are shown\n * verbatim. Absent or blank inputs are omitted rather than rendered as empty\n * rows — \"Instructions:\" with nothing after it is noise, not information.\n *\n * Returned as the `extra` object rather than a bare array because every\n * `onProgress` in the run passes it: `progress$` REPLACES its value per event\n * (mark-state-unit), so a field sent once would flash at 10% and disappear.\n * The parameters describe the whole run, so every event carries them — the\n * same convention `processReferenceJob` already follows for its entity types.\n */\nfunction detectionEcho(p: {\n instructions?: string;\n tone?: string;\n density?: number;\n}): Partial<JobProgress> {\n const requestParams: Array<{ label: 'instructions' | 'tone' | 'density'; value: string }> = [];\n if (p.instructions?.trim()) requestParams.push({ label: 'instructions', value: p.instructions.trim() });\n if (p.tone?.trim()) requestParams.push({ label: 'tone', value: p.tone.trim() });\n if (p.density !== undefined) requestParams.push({ label: 'density', value: String(p.density) });\n return requestParams.length > 0 ? { requestParams } : {};\n}\n\nexport async function processCommentJob(\n content: string,\n inferenceClient: InferenceClient,\n params: CommentDetectionParams,\n buildAnnotation: BuildAnnotation,\n onProgress: OnProgress,\n /** This chunk's novel annotations, awaited: the durability write. */\n onChunkComplete: (annotations: Annotation[], checkpoint: UnitCheckpoint) => Promise<void>,\n /** Where earlier attempts left each unit (CHUNK-GRAIN-RESUME P3), keyed the\n * same way the checkpoint is. A unit absent here starts at the top, which is\n * every unit of a first attempt. */\n resumeCursors?: Record<string, UnitCursor>,\n): Promise<ProcessorResult<JobCommentAnnotationResult>> {\n const echo = detectionEcho(params);\n\n onProgress(10, { code: 'loading' }, echo);\n onProgress(30, { code: 'analyzing' }, echo);\n\n // The body's `language` reflects the locale the LLM was asked to write in\n // (`params.language` — the user's UI locale). Defaults to 'en' when the\n // caller didn't specify, matching what the LLM produces by default.\n const bodyLanguage = params.language ?? 'en';\n const dedupe = makeSpanDeduper();\n // Seeded from what an earlier attempt already counted for this unit, so a\n // resumed job's terminal record describes the document rather than the\n // remainder it happened to run (CHUNK-GRAIN-RESUME HD3).\n const prior = resumeCursors?.['commenting'];\n let found = prior?.found ?? 0;\n let created = prior?.emitted ?? 0;\n await AnnotationDetection.detectComments(\n content, inferenceClient, params.instructions, params.tone, params.density,\n params.language, params.sourceLanguage,\n // Liveness (chunk boundaries + in-flight heartbeat): 30–60 band.\n (consumedChars, totalChars) => onProgress(30 + Math.round((consumedChars / totalChars) * 30), { code: 'analyzing' }, echo),\n resumeCursors?.['commenting'],\n async (comments, cursor) => {\n found += comments.length;\n const fresh = dedupe(comments.map((c) =>\n // Match the pre-#651 CommentAnnotationWorker: include format and\n // language on the body TextualBody. Optional in the schema, but\n // consumers that do language-aware rendering rely on them.\n buildAnnotation('commenting', c, [\n { type: 'TextualBody', value: c.comment, purpose: 'commenting', format: 'text/plain' satisfies SupportedMediaType, language: bodyLanguage },\n ]),\n ));\n created += fresh.length;\n onProgress(60, { code: 'creating-annotations', count: created }, echo);\n await onChunkComplete(fresh, { unit: 'commenting', cursor: { ...cursor, found, emitted: created } });\n },\n );\n\n onProgress(100, { code: 'complete-created', count: created, kind: 'comment' }, echo);\n\n return {\n result: { kind: 'comment-annotation', commentsFound: found, commentsCreated: created },\n };\n}\n\nexport async function processAssessmentJob(\n content: string,\n inferenceClient: InferenceClient,\n params: AssessmentDetectionParams,\n buildAnnotation: BuildAnnotation,\n onProgress: OnProgress,\n /** This chunk's novel annotations, awaited: the durability write. */\n onChunkComplete: (annotations: Annotation[], checkpoint: UnitCheckpoint) => Promise<void>,\n /** Where earlier attempts left each unit (CHUNK-GRAIN-RESUME P3), keyed the\n * same way the checkpoint is. A unit absent here starts at the top, which is\n * every unit of a first attempt. */\n resumeCursors?: Record<string, UnitCursor>,\n): Promise<ProcessorResult<JobAssessmentAnnotationResult>> {\n const echo = detectionEcho(params);\n\n onProgress(10, { code: 'loading' }, echo);\n onProgress(30, { code: 'analyzing' }, echo);\n\n const bodyLanguage = params.language ?? 'en';\n const dedupe = makeSpanDeduper();\n // Seeded from what an earlier attempt already counted for this unit, so a\n // resumed job's terminal record describes the document rather than the\n // remainder it happened to run (CHUNK-GRAIN-RESUME HD3).\n const prior = resumeCursors?.['assessing'];\n let found = prior?.found ?? 0;\n let created = prior?.emitted ?? 0;\n await AnnotationDetection.detectAssessments(\n content, inferenceClient, params.instructions, params.tone, params.density,\n params.language, params.sourceLanguage,\n // Liveness (chunk boundaries + in-flight heartbeat): 30–60 band.\n (consumedChars, totalChars) => onProgress(30 + Math.round((consumedChars / totalChars) * 30), { code: 'analyzing' }, echo),\n resumeCursors?.['assessing'],\n async (assessments, cursor) => {\n found += assessments.length;\n const fresh = dedupe(assessments.map((a) =>\n // Single-object body with purpose aligned to motivation, matching the\n // pre-#651 AssessmentAnnotationWorker's shape and the majority of\n // persisted assessments. Do not switch to an array or to\n // purpose='describing' — that loses the \"this is an assessment, not\n // a description\" signal and breaks existing readers that access\n // `body.value` directly on the object.\n buildAnnotation('assessing', a, {\n type: 'TextualBody', value: a.assessment, purpose: 'assessing', format: 'text/plain' satisfies SupportedMediaType, language: bodyLanguage,\n }),\n ));\n created += fresh.length;\n onProgress(60, { code: 'creating-annotations', count: created }, echo);\n await onChunkComplete(fresh, { unit: 'assessing', cursor: { ...cursor, found, emitted: created } });\n },\n );\n\n onProgress(100, { code: 'complete-created', count: created, kind: 'assessment' }, echo);\n\n return {\n result: { kind: 'assessment-annotation', assessmentsFound: found, assessmentsCreated: created },\n };\n}\n\n/**\n * Reference detection commits per UNIT — one entity type — through\n * `onUnitComplete` (ABANDONED-INFERENCE P2, checkpointed resume): the\n * callback receives the unit's deduped annotations, and only after it\n * resolves does the unit count as complete. Emission belongs to the\n * callback alone; the processor returns only the result — returning the\n * annotations as well would recreate the post-run batch that N2 showed\n * discards completed work wholesale.\n */\nexport async function processReferenceJob(\n content: string,\n inferenceClient: InferenceClient,\n params: DetectionParams,\n buildAnnotation: BuildAnnotation,\n onProgress: OnProgress,\n logger: Logger,\n /**\n * The CHECKPOINT, fired once per unit after every one of its chunks has\n * committed. It carries no annotations — the effect already happened per\n * chunk through `onChunkComplete`, and a unit callback that also carried\n * them would be a second commit path.\n */\n onUnitComplete: (entityType: string) => Promise<void>,\n signal?: AbortSignal,\n /** This chunk's novel annotations, awaited: the durability write. */\n onChunkComplete?: (annotations: Annotation[], checkpoint: UnitCheckpoint) => Promise<void>,\n /** Where earlier attempts left each entity-type unit (CHUNK-GRAIN-RESUME P3).\n * A unit absent here starts at the top; units already COMPLETE never reach\n * this function at all, the caller having filtered them out. */\n resumeCursors?: Record<string, UnitCursor>,\n): Promise<{ result: JobReferenceAnnotationResult }> {\n const entityTypeNames = params.entityTypes.map(String);\n const requestParams = [{ label: 'entity-types' as const, value: entityTypeNames.join(', ') }];\n const completedItems: CompletedItem[] = [];\n // Seeded with what earlier attempts already counted for the units this\n // attempt is RESUMING. Units that completed earlier carry no cursor — they\n // are filtered out before this function sees them — so their share is still\n // missing from the job total. That is the pre-existing unit-grain gap, named\n // in CHUNK-GRAIN-RESUME rather than silently half-fixed here.\n let totalFound = Object.values(resumeCursors ?? {}).reduce((n, c) => n + c.found, 0);\n let totalEmitted = Object.values(resumeCursors ?? {}).reduce((n, c) => n + c.emitted, 0);\n let errors = 0;\n let totalUnderReportedPieces = 0;\n // The denominator: cumulative count-verifier expectations over accepted\n // pieces. Zero means no piece was priced — the frame then carries nothing.\n let totalExpected = 0;\n\n onProgress(10, { code: 'loading' }, { requestParams });\n\n const bodyLanguage = params.language ?? 'en';\n\n // Entity types run BOUNDED-CONCURRENT (DETECTION-QUALITY-THROUGHPUT P6).\n // They are independent units — own extraction, own commit, own checkpoint —\n // and a single sequential job used a sliver of the provider's rate limit, so\n // the old `for … await` was the 9×-sequential ≈ 2.5 h/document. The bound is\n // the point: unbounded fan-out just trades sequential waiting for 429 thrash.\n //\n // Shared counters and `completedItems` are mutated SYNCHRONOUSLY between\n // awaits inside the worker — safe under the single-threaded event loop (no\n // read-modify-write straddles an await), so no locking is needed. Progress is\n // now \"M of N done\" rather than \"on type i\": concurrent types finish out of\n // order, and `completedItems` already tolerates that.\n let completed = 0;\n const total = entityTypeNames.length;\n\n const emitTypeProgress = (entityTypeName: string): void => {\n const pct = 20 + Math.round((completed / total) * 60);\n onProgress(pct, { code: 'detecting-entities', entityType: entityTypeName }, {\n current: { kind: 'entity-type', value: entityTypeName },\n processed: completed,\n total,\n entitiesFound: totalFound,\n ...(totalExpected > 0 ? { entitiesExpected: totalExpected } : {}),\n entitiesEmitted: totalEmitted,\n completedItems: [...completedItems],\n requestParams,\n });\n };\n\n // Concurrency is the PROVIDER's capability, not a flat constant: a hosted\n // API parallelizes, a local single-model server does not (P6). Reading it\n // from the client is what makes the same code correct on both.\n await runBounded(entityTypeNames, inferenceClient.maxConcurrency, async (entityTypeName) => {\n if (!entityTypeName) return;\n // Cooperative cancellation (JOB-RESTART-SAFETY P4): once aborted, a pending\n // type is skipped when its turn comes; types already in flight finish and\n // commit, so committed units stay checkpointed. The caller reads\n // `signal.aborted` to move the job to cancelled/ rather than complete/.\n if (signal?.aborted) return;\n\n emitTypeProgress(entityTypeName);\n\n // Unresolved reference body: the entity type as a tagging TextualBody,\n // stamped with the body locale to match the comment/assess/tag pattern.\n // The bind flow later appends a SpecificResource (purpose: 'linking') via\n // mark:body-updated to produce the resolved shape. Emitting an empty body\n // would break the append contract.\n const unresolvedBody: Annotation['body'] = [\n { type: 'TextualBody' as const, value: entityTypeName, purpose: 'tagging' as const, format: 'text/plain' satisfies SupportedMediaType, language: bodyLanguage },\n ];\n\n // One deduper per unit, held across its chunks.\n const dedupe = makeSpanDeduper();\n // Seeded from what earlier attempts already counted for THIS unit, so the\n // terminal record describes the document rather than the remainder this\n // attempt happened to run. Absent (a first attempt) means zero, which is\n // the truth rather than a default.\n const priorUnit = resumeCursors?.[entityTypeName];\n let unitFound = priorUnit?.found ?? 0;\n let unitPersisted = priorUnit?.emitted ?? 0;\n // What remains unknown at the unit's end: floor-accepted pieces, folded.\n let underReported: { pieces: number; found: number; counted: number } | undefined;\n await extractEntities(\n content, [entityTypeName], inferenceClient, params.includeDescriptiveReferences ?? false, logger,\n params.sourceLanguage,\n // Liveness heartbeat (DETECTION-HEARTBEAT): fires at chunk boundaries and\n // every ~15 s while a call is in flight, so a long single-chunk call is\n // not silent. It repeats the current position rather than inventing an\n // advance — the stall watchdog, janitor and client timeout need a signal,\n // not a monotone.\n () => emitTypeProgress(entityTypeName),\n (verdict) => {\n underReported = {\n pieces: (underReported?.pieces ?? 0) + 1,\n found: (underReported?.found ?? 0) + verdict.found,\n counted: (underReported?.counted ?? 0) + verdict.counted,\n };\n },\n (counted) => {\n totalExpected += counted;\n emitTypeProgress(entityTypeName);\n },\n resumeCursors?.[entityTypeName],\n async (chunkEntities, cursor) => {\n const built: Annotation[] = [];\n for (const entity of chunkEntities) {\n const reconciled = reconcileSelector(content, {\n exact: entity.exact,\n ...(entity.prefix !== undefined ? { prefix: entity.prefix } : {}),\n ...(entity.suffix !== undefined ? { suffix: entity.suffix } : {}),\n });\n if (!reconciled) {\n logger.error('Entity dropped — text not found in source', {\n text: entity.exact,\n entityType: entity.entityType,\n });\n errors++;\n continue;\n }\n noteAnchor('reference', entity.exact, reconciled.anchorMethod, logger);\n built.push(buildAnnotation('linking', toMatch(reconciled), unresolvedBody));\n }\n const fresh = dedupe(built);\n // What this chunk makes true ONCE IT IS DURABLE. Computed before the\n // commit because the checkpoint is written inside it and has to carry\n // the running totals — a checkpoint reporting only this attempt's share\n // would reset the count on every death — but assigned after, so the\n // invariant below still holds.\n const nextFound = unitFound + chunkEntities.length;\n const nextEmitted = unitPersisted + fresh.length;\n // Awaited: a failed commit fails the unit before it can checkpoint;\n // the cursor rides with it so the checkpoint trails the log by\n // construction rather than by the caller remembering to order them.\n await onChunkComplete?.(fresh, {\n unit: entityTypeName,\n cursor: { ...cursor, found: nextFound, emitted: nextEmitted },\n });\n // Tallies move only PAST the awaited commit — a chunk that fails to\n // commit contributes nothing anywhere — and the numerator advances at\n // the same grain as the denominator: per chunk, in the same frame\n // family the viewer's found-of-~expected tally reads.\n unitFound = nextFound;\n unitPersisted = nextEmitted;\n totalFound += chunkEntities.length;\n totalEmitted += fresh.length;\n emitTypeProgress(entityTypeName);\n },\n );\n\n // Every chunk of this unit is durable; only now may it checkpoint.\n await onUnitComplete(entityTypeName);\n // Found vs persisted, per unit — the gap between them is this flow's yield.\n completedItems.push({\n value: entityTypeName,\n foundCount: unitFound,\n persistedCount: unitPersisted,\n ...(underReported ? { underReported } : {}),\n });\n if (underReported) totalUnderReportedPieces += underReported.pieces;\n completed++;\n emitTypeProgress(entityTypeName);\n });\n\n // The terminal frame carries the completed set, and it is the only frame\n // that can: the per-unit entries ride the progress emitted at the START of\n // each unit, so the LAST unit's entry — and on a single-type job, every\n // entry — was never reported anywhere (DETECTION-QUALITY-THROUGHPUT P1).\n onProgress(100, { code: 'complete-created', count: totalEmitted, kind: 'reference' }, {\n ...(totalExpected > 0 ? { entitiesExpected: totalExpected } : {}),\n completedItems: [...completedItems],\n requestParams,\n });\n\n return {\n result: {\n kind: 'reference-annotation', totalFound, totalEmitted, errors,\n ...(totalUnderReportedPieces > 0 ? { underReportedPieces: totalUnderReportedPieces } : {}),\n },\n };\n}\n\nexport async function processTagJob(\n content: string,\n inferenceClient: InferenceClient,\n params: TagDetectionParams,\n buildAnnotation: BuildAnnotation,\n onProgress: OnProgress,\n /** This chunk's novel annotations, awaited: the durability write. */\n onChunkComplete: (annotations: Annotation[], checkpoint: UnitCheckpoint) => Promise<void>,\n /** Where earlier attempts left each CATEGORY (CHUNK-GRAIN-RESUME P3) — a tag\n * job's units are its categories, not its motivation: each walks the whole\n * document, so one shared cursor would skip text for all but one of them. */\n resumeCursors?: Record<string, UnitCursor>,\n): Promise<ProcessorResult<JobTagAnnotationResult>> {\n onProgress(10, { code: 'loading' });\n onProgress(30, { code: 'analyzing-tags' });\n\n const bodyLanguage = params.language ?? 'en';\n // One deduper across every category: they share an emission stream, and\n // the key includes the body, so only true repeats collapse.\n const dedupe = makeSpanDeduper();\n // Resumed tallies (CHUNK-GRAIN-RESUME HD3), and the two are seeded\n // DIFFERENTLY because they accumulate differently. `found` sums each\n // category's own running total at the end of that category, and those totals\n // are already seeded — seeding here too would count the earlier attempt\n // twice. `created` accumulates per chunk from this attempt only, so it must\n // start at what earlier attempts committed.\n let found = 0;\n let created = Object.values(resumeCursors ?? {}).reduce((n, c) => n + c.emitted, 0);\n // byCategory counts the DEDUPED set so the per-category counts match what\n // is actually stored. The category is the first (tagging) TextualBody.\n const byCategory: Record<string, number> = {};\n const completedItems: CompletedItem[] = [];\n\n for (let c = 0; c < params.categories.length; c++) {\n const category = params.categories[c]!;\n // The loop always existed; it just never reported itself, so the tag flow\n // was the one counting flow with no subject line (CLEAN-PROGRESS A5).\n const position = () => ({\n current: { kind: 'category' as const, value: category },\n processed: c,\n total: params.categories.length,\n completedItems: [...completedItems],\n });\n onProgress(\n 30 + Math.round((c / params.categories.length) * 30),\n { code: 'analyzing-tags' },\n position(),\n );\n // This category's own running tallies, seeded from its cursor so the\n // checkpoint it writes stays continuable for a third attempt.\n const priorCategory = resumeCursors?.[category];\n let categoryFound = priorCategory?.found ?? 0;\n let categoryCreated = priorCategory?.emitted ?? 0;\n await AnnotationDetection.detectTags(\n content, inferenceClient, params.schema, category, params.sourceLanguage,\n // Liveness (chunk boundaries + in-flight heartbeat): this category's\n // slice of the 30–60 band.\n (consumedChars, totalChars) => onProgress(\n 30 + Math.round(((c + consumedChars / totalChars) / params.categories.length) * 30),\n { code: 'analyzing-tags' },\n position(),\n ),\n resumeCursors?.[category],\n async (matches, cursor) => {\n categoryFound += matches.length;\n const fresh = dedupe(matches.map((t) => {\n const cat = t.category ?? 'unknown';\n // Two-body shape matches the pre-#651 TagAnnotationWorker and every\n // persisted tag annotation: the category as a tagging TextualBody,\n // plus the tagging-schema id as a classifying TextualBody. The\n // classifying body is the only trace of schema provenance in the\n // event log — do not drop it.\n return buildAnnotation('tagging', t, [\n { type: 'TextualBody', value: cat, purpose: 'tagging', format: 'text/plain' satisfies SupportedMediaType, language: bodyLanguage },\n { type: 'TextualBody', value: params.schema.id, purpose: 'classifying', format: 'text/plain' satisfies SupportedMediaType },\n ]);\n }));\n created += fresh.length;\n for (const ann of fresh) {\n const body = (ann as { body?: Array<{ value?: unknown }> }).body;\n const cat = Array.isArray(body) && typeof body[0]?.value === 'string' ? body[0].value : 'unknown';\n byCategory[cat] = (byCategory[cat] ?? 0) + 1;\n }\n onProgress(60, { code: 'creating-tag-annotations', count: created });\n // The unit is the CATEGORY, not the motivation. `tag-annotation` loops\n // over `params.categories`, each walking the whole document from zero,\n // so a single 'tagging' key would have every category overwriting one\n // cursor — and the monotone merge would keep the furthest, which is the\n // right answer for at most one of them and silently skips text for the\n // rest. Same shape as reference's entity types, for the same reason.\n categoryCreated += fresh.length;\n await onChunkComplete(fresh, {\n unit: category,\n cursor: { ...cursor, found: categoryFound, emitted: categoryCreated },\n });\n },\n );\n found += categoryFound;\n completedItems.push({ value: category, foundCount: categoryFound });\n }\n\n onProgress(100, { code: 'complete-created', count: created, kind: 'tag' });\n\n return {\n result: { kind: 'tag-annotation', tagsFound: found, tagsCreated: created, byCategory },\n };\n}\n\n/**\n * Output bound (PDF-GENERATION P5), symmetric with #1124's extraction budget\n * and deliberately the SAME threshold: an artifact larger than what extraction\n * accepts would be a resource our own Smelter declines as 'too-large'. One\n * judgment, two enforcement points. A runaway generation fails loudly\n * (job:fail); it never uploads.\n */\nexport function assertWithinOutputBudget(byteLength: number): void {\n if (!withinByteBudget(byteLength)) {\n throw new Error(\n `Generated artifact exceeds the output byte budget: ${byteLength} bytes > ${MAX_PDF_BYTES}. Refusing a runaway generation.`,\n );\n }\n}\n\nexport async function processGenerationJob(\n inferenceClient: InferenceClient,\n params: GenerationJobParams,\n onProgress: OnProgress,\n logger: Logger,\n): Promise<{ content: Uint8Array; title: string; format: SupportedMediaType; citations: GenerationCitation[]; result: GenerationResult }> {\n // Refuse any requested media type the registry doesn't mark `generatable` —\n // loudly (the throw propagates as job:fail), never a silent markdown fallback\n // under a mislabeled format. The gate reads the registry capability\n // (PDF-GENERATION P1), not a local table. Validate before the LLM call.\n const outputMediaType: SupportedMediaType = params.outputMediaType ?? 'text/markdown';\n if (!GENERATABLE_MEDIA_TYPES.includes(outputMediaType)) {\n throw new Error(\n `Unsupported outputMediaType for generation: ${outputMediaType}. Generation produces ${GENERATABLE_MEDIA_TYPES.join(' or ')}.`,\n );\n }\n\n const title = params.title ?? 'Untitled';\n const entityTypes = (params.entityTypes ?? []).map(String);\n\n // PDF path (PDF-GENERATION P3): the model authors Typst; the worker's pinned\n // binary compiles it, with the legible compile errors fed back for a bounded\n // number of repairs. Citations on PDFs need page geometry, not text offsets —\n // until the citation branch (P4) provides it, `cite` fails fast and loudly\n // rather than minting selectors that would render nothing.\n if (outputMediaType === 'application/pdf') {\n onProgress(5, { code: 'generating-resource' });\n\n // Under `cite`, [[<id>]] tokens are stripped from the SOURCE before every\n // compile — they must never render into the artifact. The citations carry\n // the claim text; the worker re-anchors it by page geometry after\n // extraction (P4). Offsets in these citations index the Typst source and\n // are NOT used for PDF anchoring.\n const validIds = params.cite === true ? collectContextResourceIds(params.context) : null;\n\n let generated = await generateResourceFromTopic(\n title, entityTypes, inferenceClient, logger,\n params.prompt, params.language, params.context, params.temperature,\n params.maxTokens, params.sourceLanguage, outputMediaType,\n params.task, params.structure, params.cite,\n );\n let source = generated.content;\n let citations: GenerationCitation[] = [];\n if (validIds) {\n const resolved = resolveCitationTokens(generated.content, validIds, logger);\n source = resolved.content;\n citations = resolved.citations;\n }\n let compiled = compileTypst(source);\n let repairs = 0;\n while ('error' in compiled) {\n // A truncated source that fails to compile is cut off, not wrong —\n // every repair regenerates under the same ceiling and cannot restore\n // content that was never generated. Fail immediately with the actual\n // cause instead of burning the repair budget and blaming the compiler\n // (GENERATE-FROM-RESOURCE P3a).\n if (generated.truncated) {\n throw new Error(\n `Generation stopped at the maxTokens ceiling (${params.maxTokens ?? DEFAULT_MAX_TOKENS} tokens) and the cut-off Typst source does not compile — repair cannot help; raise maxTokens. Compile error: ${compiled.error}`,\n );\n }\n if (repairs >= MAX_COMPILE_REPAIRS) {\n throw new Error(\n `Typst compilation failed after ${MAX_COMPILE_REPAIRS} repair attempts: ${compiled.error}`,\n );\n }\n repairs++;\n logger.warn('Typst compile failed — feeding the error back for repair', {\n attempt: repairs,\n error: compiled.error.slice(0, 500),\n });\n generated = await generateResourceFromTopic(\n title, entityTypes, inferenceClient, logger,\n params.prompt, params.language, params.context, params.temperature,\n params.maxTokens, params.sourceLanguage, outputMediaType,\n params.task, params.structure, params.cite,\n { source, error: compiled.error },\n );\n if (validIds) {\n const resolved = resolveCitationTokens(generated.content, validIds, logger);\n source = resolved.content;\n citations = resolved.citations;\n } else {\n source = generated.content;\n }\n compiled = compileTypst(source);\n }\n\n assertWithinOutputBudget(compiled.pdf.byteLength);\n onProgress(95, { code: 'creating-resource' });\n // The producer owns terminality (GENERATE-FROM-RESOURCE P1/D1): without\n // this, the client's last frame is forever the 95% payload. Generic by\n // design — the outcome (name + link) travels on job:complete (D8).\n // `truncated` rides both surfaces (P3a/D6): truncation that lands at a\n // syntactic boundary still compiles, and the artifact is still cut off.\n onProgress(100, { code: 'complete-generated', truncated: generated.truncated });\n\n return {\n content: compiled.pdf,\n title,\n format: outputMediaType,\n citations,\n result: {\n kind: 'generation',\n resourceId: '' as ResourceId,\n resourceName: title,\n truncated: generated.truncated,\n },\n };\n }\n\n // Generation has exactly two observable transitions: the LLM call starting\n // ('generating') and content finalized / creation beginning ('creating').\n // There is no fetch — context arrives pre-gathered in params. Percentages\n // approximate the share of expected wall-clock complete at each transition\n // (a single atomic LLM call has no measurable progress, and inference\n // dominates the job): its start is ~5, its end ~95.\n onProgress(5, { code: 'generating-resource' });\n\n const generated = await generateResourceFromTopic(\n title,\n entityTypes,\n inferenceClient,\n logger,\n params.prompt,\n params.language,\n params.context,\n params.temperature,\n params.maxTokens,\n params.sourceLanguage,\n outputMediaType,\n params.task,\n params.structure,\n params.cite,\n );\n\n // Under `cite`, the model emitted [[<id>]] transport tokens — resolve them:\n // validate against the ids the context actually contained, strip from the\n // stored content, and carry claim-span citations for the worker to mint.\n // When cite is off, bracketed text is legitimate content — leave it alone.\n let content = generated.content;\n let citations: GenerationCitation[] = [];\n if (params.cite === true) {\n const resolved = resolveCitationTokens(content, collectContextResourceIds(params.context), logger);\n content = resolved.content;\n citations = resolved.citations;\n }\n\n onProgress(95, { code: 'creating-resource' });\n\n // The artifact is bytes; text is an encoding of them. One shape for every\n // output media type, so a string can never travel mislabeled as a binary\n // format (PDF-GENERATION P1). Citation offsets index the decoded text.\n const artifact = new TextEncoder().encode(content);\n assertWithinOutputBudget(artifact.byteLength);\n\n // The producer owns terminality (GENERATE-FROM-RESOURCE P1/D1): without\n // this, the client's last frame is forever the 95% payload. Generic by\n // design — the outcome (name + link) travels on job:complete (D8).\n // `truncated` rides both surfaces (P3a/D6): the event is the frame the\n // client renders, the result is the record.\n onProgress(100, { code: 'complete-generated', truncated: generated.truncated });\n\n return {\n content: artifact,\n title,\n format: outputMediaType,\n citations,\n result: {\n kind: 'generation',\n resourceId: '' as ResourceId,\n resourceName: title,\n truncated: generated.truncated,\n },\n };\n}\n","/**\n * Worker Runtime — the importable half of the worker host.\n *\n * `worker-main.ts` is a process entrypoint (config at module scope,\n * `main()` at import) and therefore untestable by construction; everything\n * a unit test needs to reach lives here instead, fully parameterized — no\n * module-scope env reads, no side effects at import.\n *\n * The load-bearing contract this module owns (and the reason it was\n * extracted): a worker's stamped identity is the DID the\n * `/api/tokens/agent` exchange MINTED for it, carried verbatim — never\n * re-derived from the URL the worker happens to dial. One logical agent\n * previously got two DIDs that way (.plans/bugs/agent-did-host-skew.md).\n */\n\nimport type { EventMap } from '@semiont/core';\nimport { startWorkerProcess } from './worker-process';\nimport type { MarkCommitAwaits, DescriptorReadAwaits, DurabilityProbeAwaits } from './worker-process';\nimport type { WorkerVitals, JobClaimAwaits } from './job-claim-adapter';\nimport type { ConsultAnchoredTextAwaits } from './workers/detection/prepare-detection';\nimport type { InferenceClient } from '@semiont/inference';\nimport { hostname } from 'os';\nimport {\n replyChannelsFor,\n didToAgent,\n baseUrl,\n retryWithBackoff,\n isTransientFetchError,\n STARTUP_FETCH_RETRY,\n type BusOperationKey,\n type RetryPolicy,\n type components,\n type Logger,\n type AccessToken,\n} from '@semiont/core';\nimport {\n InMemorySessionStorage,\n SemiontClient,\n SemiontSession,\n kbGatewayUrl,\n setStoredSession,\n type HttpEndpoint,\n type KbTarget,\n} from '@semiont/sdk';\nimport { HttpContentTransport, HttpTransport } from '@semiont/http-transport';\nimport type { ContentReads } from '@semiont/content';\nimport { BehaviorSubject } from 'rxjs';\n\ntype Agent = components['schemas']['Agent'];\n\n/** Shape of each resolved worker inference entry under `_metadata.workers`. */\nexport type ResolvedInference = {\n type: 'anthropic' | 'ollama';\n model: string;\n apiKey?: string;\n endpoint?: string;\n baseURL?: string;\n};\n\n/** One agent identity: an inference engine and the job types it serves. */\nexport interface AgentGroup {\n inference: ResolvedInference;\n jobTypes: string[];\n client: InferenceClient;\n}\n\nexport interface WorkerRuntimeOptions {\n group: AgentGroup;\n /** The gateway URL this worker dials — connection topology ONLY, never identity. */\n gatewayBaseUrl: string;\n /** Shared secret for `/api/tokens/agent`, and the bearer the byte reads\n * below show the Archivist. */\n workerSecret: string;\n /**\n * The resource's bytes, for detection's extraction seam. Built by the\n * entrypoint (`worker-main`) rather than here, so a worker with no\n * Archivist configured refuses at boot instead of failing every job\n * (SINGLE-KB-MOUNT P4).\n */\n contentReads: ContentReads;\n logger: Logger;\n}\n\n/** Per-agent liveness: the adapter's snapshot plus this agent's identity. */\nexport interface AgentVitals extends WorkerVitals {\n provider: string;\n model: string;\n did: string;\n jobTypes: string[];\n}\n\nexport interface AgentWorkerHandle {\n session: SemiontSession;\n vitals(): AgentVitals;\n dispose(): Promise<void>;\n}\n\nexport interface WorkerHealthPayload {\n status: 'ok';\n agents: number;\n workers: AgentVitals[];\n}\n\n/**\n * The `/health` body (WORKER-LIVENESS.md P1). Additive: existing\n * consumers (image HEALTHCHECK, compose `service_healthy`, `semiont start`)\n * keep reading `status`/`agents`; the per-agent vitals expose\n * claim-loop progress so a stalled worker is *visible*, not just alive.\n */\nexport function buildHealthPayload(workers: ReadonlyArray<{ vitals(): AgentVitals }>): WorkerHealthPayload {\n return {\n status: 'ok',\n agents: workers.length,\n workers: workers.map((w) => w.vitals()),\n };\n}\n\n/**\n * Stall watchdog (WORKER-LIVENESS.md P3) — the fail-fast line behind the\n * inference timeout. There is no poll loop to heartbeat; the honest\n * stall signal in this push-driven architecture is *processing without\n * activity*: an agent holding a claimed job whose `lastActivityAt`\n * (claim / progress / finish) has stopped advancing is wedged — the\n * adapter ignores every announcement while `isProcessing`, so a wedged\n * agent never recovers on its own. Silent hang → loud crash → whatever\n * restart policy the deployment chose.\n *\n * Thresholds are fixed by design (no env knobs) and deliberately\n * layered: inference timeout (10 min, P2) fires first; this watchdog\n * (15 min) catches wedges where the loop still turns but activity has\n * stopped; the gateway's dead-worker janitor (30 min) re-queues the job\n * regardless.\n *\n * The layering matters because this watchdog has a hard limit: it is an\n * IN-PROCESS timer, so it cannot fire while the event loop itself is\n * blocked — the exact condition a blocked loop creates\n * (JOB-RESTART-SAFETY P7, the 2026-09-03 finalization hang: 18 min silent,\n * this watchdog never fired, an empty /health confirming the loop was\n * wedged). The unbounded emit that caused that specific hang is now bounded\n * at the transport (`EMIT_TIMEOUT_MS`), so the loop errors instead of\n * blocking; but for any future blocked-loop bug the ONLY backstop is the\n * out-of-process one — the gateway's janitor sweeping this worker's job\n * files by mtime (`fs-job-queue.ts` `recoverStaleRunningJobs`). A liveness\n * guarantee a blocked loop defeats is not one; the janitor is the guarantee.\n */\nexport const STALL_THRESHOLD_MS = 15 * 60_000;\nexport const STALL_CHECK_INTERVAL_MS = 60_000;\n\nexport interface StallWatchdogOptions {\n workers: ReadonlyArray<{ vitals(): AgentVitals }>;\n logger: Logger;\n /** Test seam; defaults to process.exit. */\n exit?: (code: number) => void;\n}\n\nexport function startStallWatchdog(opts: StallWatchdogOptions): { dispose(): void } {\n const { workers, logger, exit = (code: number) => process.exit(code) } = opts;\n\n const timer = setInterval(() => {\n const now = Date.now();\n for (const worker of workers) {\n const v = worker.vitals();\n if (!v.activeJob || !v.lastActivityAt) continue;\n\n const silentForMs = now - Date.parse(v.lastActivityAt);\n if (silentForMs <= STALL_THRESHOLD_MS) continue;\n\n logger.error('Worker stalled — exiting for restart', {\n provider: v.provider,\n model: v.model,\n did: v.did,\n jobId: v.activeJob.jobId,\n jobType: v.activeJob.type,\n processingSince: v.activeJob.since,\n lastActivityAt: v.lastActivityAt,\n silentForMs,\n thresholdMs: STALL_THRESHOLD_MS,\n });\n clearInterval(timer);\n exit(1);\n return;\n }\n }, STALL_CHECK_INTERVAL_MS);\n timer.unref?.();\n\n return { dispose: () => clearInterval(timer) };\n}\n\n/**\n * The bus operations a worker process ever AWAITS a reply to. Reply channels\n * are global fan-out on the gateway, so a full `BRIDGED_CHANNELS`\n * subscription made every worker receive every other client's reply traffic\n * — measured at ~85 multi-MB `browse:annotations-result` frames/min during\n * the 2026-09-03 worker OOM, all parsed and dropped by cid filtering. The\n * worker subscribes exactly its own operations' reply channels instead.\n *\n * This list restates a fact the code owns (which operations worker code\n * paths call `busRequest` on); its gate is the build-time census below\n * (`workerAwaitCensus`): every awaiting site declares its operation next to\n * the call, and an operation awaited but missing here fails COMPILATION with\n * the operation named. `busRequest`'s `isSubscribed` probe remains the\n * runtime backstop for an await nobody declared — a loud `bus.unsubscribed`\n * at first use, never a silent 30 s timeout.\n * (`job:queued` is not here: it is a broadcast, not an awaited reply — the\n * claim adapter widens the worker's narrowed subscription set for it via\n * `addChannels`.)\n */\nexport const WORKER_AWAITED_OPERATIONS = [\n 'job:claim',\n 'browse:resource-requested',\n // Canonical geometry for a geometry-bearing detection: the consult behind\n // `ConsultAnchoredText` (SMELTER-OWNS-OCR P2). Its omission broke every\n // PDF detection job at the transport probe\n // (.plans/WORKER-ANCHORED-TEXT-CHANNEL.md); the census below now fails the\n // BUILD when this list and the declared awaits drift.\n 'browse:anchored-text-requested',\n // Durability acknowledgement for a unit's annotations (JOB-RESTART-SAFETY\n // P6). The worker AWAITS this one — a unit may not advance until its\n // annotations are in the event log — so its replies must be in the narrow\n // channel set or every commit fails fast with `bus.unsubscribed`.\n 'mark:commit',\n // The durability probe for a commit whose acknowledgement never routed\n // (COMMIT-ACK-FALSE-FAILURE F1). SINGULAR by design: the annotation LIST\n // channel is the multi-MB fan-out this narrowing exists to keep out, and a\n // rare error path is no reason to let it back in.\n 'browse:annotation-requested',\n] as const satisfies readonly BusOperationKey[];\n\n/** The derived global SSE channel set for a worker's transport. */\n// `replyChannelsFor` already returns `EventName[]`; annotating this\n// `readonly string[]` threw that proof away and was the only reason a\n// transport's channel roster was ever wider than the registry.\nexport const WORKER_CHANNELS: readonly (keyof EventMap)[] = replyChannelsFor(WORKER_AWAITED_OPERATIONS);\n\n/**\n * The build-time census gate (WORKER-ANCHORED-TEXT-CHANNEL F2).\n *\n * `WORKER_AWAITED_OPERATIONS` restates a fact the code owns — which\n * operations worker paths call `busRequest` on — and one of those calls\n * hides behind an injected seam (`ConsultAnchoredText` → the SDK), where no\n * grep and no runtime probe-before-shipping can see it. So every awaiting\n * site DECLARES its operation next to the call (the `*Awaits` aliases), the\n * declarations are assembled here, and this constant compiles ONLY when the\n * list and the declarations agree in BOTH directions. Drift fails the build\n * with the drifted operation named in the type error — at build time, not at\n * the first live job. `busRequest`'s `bus.unsubscribed` probe remains the\n * runtime backstop for an await nobody declared.\n */\ntype DeclaredWorkerAwaits =\n | JobClaimAwaits // job-claim-adapter.ts — claiming an announced job\n | DescriptorReadAwaits // worker-process.ts — the resource descriptor read\n | MarkCommitAwaits // worker-process.ts — the durability ack (JOB-RESTART-SAFETY P6)\n | DurabilityProbeAwaits // worker-process.ts — did the batch land? (COMMIT-ACK-FALSE-FAILURE F1)\n | ConsultAnchoredTextAwaits; // prepare-detection.ts — canonical geometry (SMELTER-OWNS-OCR P2)\n\ntype WorkerAwaitCensusDrift =\n | Exclude<DeclaredWorkerAwaits, (typeof WORKER_AWAITED_OPERATIONS)[number]>\n | Exclude<(typeof WORKER_AWAITED_OPERATIONS)[number], DeclaredWorkerAwaits>;\n\nexport const workerAwaitCensus: [WorkerAwaitCensusDrift] extends [never]\n ? 'in-census'\n : WorkerAwaitCensusDrift = 'in-census';\n\nexport function parseGatewayUrl(url: string): { protocol: 'http' | 'https'; host: string; port: number } {\n const parsed = new URL(url);\n const protocol = (parsed.protocol.replace(':', '') === 'https' ? 'https' : 'http') as 'http' | 'https';\n const host = parsed.hostname;\n const port = parsed.port\n ? Number(parsed.port)\n : protocol === 'https' ? 443 : 80;\n return { protocol, host, port };\n}\n\n/**\n * Exchange the worker secret for this agent's JWT and its canonical DID.\n * The DID is minted by the gateway (from its `site.domain`) — the caller\n * carries it verbatim.\n *\n * Connection-level failures (`TypeError: fetch failed`) are retried with\n * exponential backoff: the gateway may be mid-restart or the container\n * network still warming up when this process starts, and orchestration\n * runs workers with `--rm` and no restart policy — exiting on the first\n * failed fetch is permanent death. HTTP-level rejections (401 on a bad\n * secret) are NOT retried; the gateway is up and said no.\n */\nexport async function authenticateAgent(opts: {\n gatewayBaseUrl: string;\n workerSecret: string;\n provider: string;\n model: string;\n logger?: Logger;\n retry?: RetryPolicy;\n}): Promise<{ token: string; did: string }> {\n const { gatewayBaseUrl, workerSecret, provider, model, logger, retry = STARTUP_FETCH_RETRY } = opts;\n if (!workerSecret) {\n throw new Error('SEMIONT_WORKER_SECRET is required to authenticate worker agents');\n }\n\n return retryWithBackoff(\n async () => {\n const response = await fetch(`${gatewayBaseUrl}/api/tokens/agent`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ secret: workerSecret, provider, model }),\n });\n\n if (!response.ok) {\n throw new Error(`Agent authentication failed for ${provider}:${model}: ${response.status} ${response.statusText}`);\n }\n\n return await response.json() as { token: string; did: string };\n },\n isTransientFetchError,\n retry,\n ({ attempt, attempts, delayMs, error }) => {\n logger?.warn('Gateway unreachable, retrying agent authentication', {\n agent: `${provider}:${model}`,\n attempt,\n attempts,\n retryInMs: delayMs,\n error: error instanceof Error ? error.message : String(error),\n });\n },\n );\n}\n\nexport async function startAgentWorker(\n opts: WorkerRuntimeOptions,\n): Promise<AgentWorkerHandle> {\n const { group, gatewayBaseUrl, workerSecret, contentReads, logger } = opts;\n const { inference } = group;\n\n const { protocol, host, port } = parseGatewayUrl(gatewayBaseUrl);\n const { token: initialToken, did } = await authenticateAgent({\n gatewayBaseUrl,\n workerSecret,\n provider: inference.type,\n model: inference.model,\n logger,\n });\n\n // The exchange minted this worker's canonical DID (from the gateway's\n // site.domain) and we carry it VERBATIM — never re-derive identity from\n // the URL we happen to dial (`host` is connection topology only). One\n // logical agent previously got two DIDs this way:\n // .plans/bugs/agent-did-host-skew.md.\n const generator: Agent = didToAgent(did);\n\n const kbId = `agent-${inference.type}-${inference.model}-${hostname()}`;\n const endpoint: HttpEndpoint = { kind: 'http', host, port, protocol };\n const kb: KbTarget = {\n id: kbId,\n label: `${inference.type} / ${inference.model} @ ${host}`,\n email: `agent@${host}`,\n endpoint,\n };\n const storage = new InMemorySessionStorage();\n setStoredSession(storage, kbId, { access: initialToken, refresh: '' });\n\n const token$ = new BehaviorSubject<AccessToken | null>(null);\n let session!: SemiontSession;\n const transport = new HttpTransport({\n baseUrl: baseUrl(kbGatewayUrl(endpoint)),\n token$,\n tokenRefresher: () => session.refresh().then((t) => t ?? null),\n // Only the reply channels this process awaits — not the full bridged\n // set. See WORKER_AWAITED_OPERATIONS.\n channels: WORKER_CHANNELS,\n });\n const content = new HttpContentTransport(transport);\n const client = new SemiontClient(transport, content, transport);\n session = new SemiontSession({\n kb,\n storage,\n client,\n token$,\n refresh: async () => {\n try {\n const { token } = await authenticateAgent({\n gatewayBaseUrl,\n workerSecret,\n provider: inference.type,\n model: inference.model,\n logger,\n });\n return token;\n } catch (err) {\n logger.error('Agent token refresh failed', {\n error: err instanceof Error ? err.message : String(err),\n agent: did,\n });\n return null;\n }\n },\n onError: (err) => {\n logger.error('Session error', { code: err.code, message: err.message, agent: did });\n },\n });\n await session.ready;\n\n const adapter = startWorkerProcess({\n session,\n jobTypes: group.jobTypes,\n inferenceClient: group.client,\n generator,\n // Byte reads for decode-path media only. A geometry-bearing type's text\n // never comes from bytes here — it is CONSULTED from the Smelter's\n // canonical anchored text over the bus (SMELTER-OWNS-OCR P2), and this\n // worker cannot derive even by mistake (READ-VS-EXTRACT P2).\n contentReads,\n logger,\n });\n\n logger.info('Agent ready', {\n did,\n provider: inference.type,\n model: inference.model,\n jobTypes: group.jobTypes,\n });\n\n return {\n session,\n vitals: () => ({\n provider: inference.type,\n model: inference.model,\n did,\n jobTypes: group.jobTypes,\n ...adapter.vitals(),\n }),\n dispose: async () => {\n adapter.dispose();\n await session.dispose();\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/job-queue-interface.ts","../src/will-retry.ts","../src/checkpoint-merge.ts","../src/fs-job-queue.ts","../src/jetstream-job-queue.ts","../src/types.ts","../src/workers/inference-call.ts","../src/failure-class.ts","../src/workers/detection/chunk-size-controller.ts","../src/workers/detection/detection-chunking.ts","../src/workers/detection/motivation-prompts.ts","../src/workers/detection/anchor-audit.ts","../src/workers/detection/motivation-parsers.ts","../src/workers/annotation-detection.ts","../src/workers/detection/entity-extractor.ts","../src/workers/generation/resource-generation.ts","../src/workers/generation/typst-compiler.ts","../src/workers/generation/citation-resolver.ts","../src/workers/detection/bounded-concurrency.ts","../src/processors.ts","../src/worker-runtime.ts"],"names":["fs","toJobId","PROGRESS_WRITE_MIN_INTERVAL_MS","StructuredReadError","isObject","isString","getLocaleEnglishName","estimateTokens","response","join","reconcileSelector","generated","citations"],"mappings":";;;;;;;;;;;;;;;AAgFO,IAAM,eAAA,GAAkB,CAAC,YAAY;;;AC1DrC,SAAS,cAAA,CACd,UACA,YAAA,EACS;AACT,EAAA,OAAO,YAAA,KAAiB,eAAA,IAAmB,QAAA,CAAS,UAAA,GAAa,QAAA,CAAS,UAAA;AAC5E;;;ACHO,SAAS,gBAAA,CACd,QAAA,EACA,QAAA,EACA,SAAA,EAC4B;AAC5B,EAAA,MAAM,IAAA,GAAO,IAAI,GAAA,CAAI,SAAS,CAAA;AAC9B,EAAA,MAAM,SAAqC,EAAC;AAC5C,EAAA,KAAA,MAAW,CAAC,IAAA,EAAM,MAAM,CAAA,IAAK,MAAA,CAAO,QAAQ,EAAE,GAAG,QAAA,EAAU,CAAA,EAAG;AAC5D,IAAA,IAAI,CAAC,IAAA,CAAK,GAAA,CAAI,IAAI,CAAA,EAAG,MAAA,CAAO,IAAI,CAAA,GAAI,MAAA;AAAA,EACtC;AACA,EAAA,KAAA,MAAW,CAAC,IAAA,EAAM,MAAM,CAAA,IAAK,MAAA,CAAO,QAAQ,EAAE,GAAG,QAAA,EAAU,CAAA,EAAG;AAC5D,IAAA,IAAI,IAAA,CAAK,GAAA,CAAI,IAAI,CAAA,EAAG;AACpB,IAAA,MAAM,IAAA,GAAO,OAAO,IAAI,CAAA;AACxB,IAAA,IAAI,CAAC,QAAQ,MAAA,CAAO,IAAA,GAAO,KAAK,IAAA,EAAM,MAAA,CAAO,IAAI,CAAA,GAAI,MAAA;AAAA,EACvD;AACA,EAAA,OAAO,MAAA;AACT;;;ACXA,IAAM,sBAAA,GAAyB,GAAA;AAU/B,IAAM,mBAAmB,EAAA,GAAK,GAAA;AAG9B,IAAM,8BAAA,GAAiC,GAAA;AAGvC,IAAM,eAAA,GAAkB,EAAA;AAGxB,IAAM,mBAAA,GAAsB,IAAA;AAErB,IAAM,aAAN,MAAqC;AAAA,EAQ1C,WAAA,CAGE,KAAA,EACA,MAAA,EACQ,QAAA,EACR;AADQ,IAAA,IAAA,CAAA,QAAA,GAAA,QAAA;AAER,IAAA,IAAA,CAAK,UAAU,KAAA,CAAM,OAAA;AACrB,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AAAA,EAChB;AAAA,EAJU,QAAA;AAAA,EAZF,OAAA;AAAA,EACA,MAAA;AAAA,EACA,eAAA,GAAyD,IAAA;AAAA,EACzD,YAAA,GAAsD,IAAA;AAAA;AAAA,EAEtD,iBAAA,uBAAwB,GAAA,EAAoB;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBpD,MAAM,UAAA,GAA4B;AAChC,IAAA,MAAM,WAAwB,CAAC,SAAA,EAAW,SAAA,EAAW,UAAA,EAAY,UAAU,WAAW,CAAA;AAEtF,IAAA,KAAA,MAAW,UAAU,QAAA,EAAU;AAC7B,MAAA,MAAM,GAAA,GAAW,IAAA,CAAA,IAAA,CAAK,IAAA,CAAK,OAAA,EAAS,MAAM,CAAA;AAC1C,MAAA,MAAMA,SAAG,KAAA,CAAM,GAAA,EAAK,EAAE,SAAA,EAAW,MAAM,CAAA;AAAA,IACzC;AAEA,IAAA,IAAI,IAAA,CAAK,QAAA,IAAY,CAAC,IAAA,CAAK,eAAA,EAAiB;AAE1C,MAAA,MAAM,KAAK,mBAAA,EAAoB;AAG/B,MAAA,IAAA,CAAK,eAAA,GAAkB,YAAY,MAAM;AACvC,QAAA,IAAA,CAAK,mBAAA,EAAoB,CAAE,KAAA,CAAM,CAAC,KAAA,KAAU;AAC1C,UAAA,IAAA,CAAK,MAAA,CAAO,KAAK,gCAAA,EAAkC;AAAA,YACjD,OAAO,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU,OAAO,KAAK;AAAA,WAC7D,CAAA;AAAA,QACH,CAAC,CAAA;AACD,QAAA,IAAA,CAAK,uBAAA,EAAwB,CAAE,KAAA,CAAM,CAAC,KAAA,KAAU;AAC9C,UAAA,IAAA,CAAK,MAAA,CAAO,KAAK,+BAAA,EAAiC;AAAA,YAChD,OAAO,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU,OAAO,KAAK;AAAA,WAC7D,CAAA;AAAA,QACH,CAAC,CAAA;AAAA,MACH,GAAG,sBAAsB,CAAA;AAEzB,MAAA,IAAA,CAAK,gBAAgB,KAAA,IAAQ;AAAA,IAC/B;AAEA,IAAA,IAAI,CAAC,KAAK,YAAA,EAAc;AACtB,MAAA,IAAA,CAAK,YAAA,GAAe,YAAY,MAAM;AACpC,QAAA,IAAA,CAAK,cAAA,CAAe,eAAe,CAAA,CAAE,KAAA,CAAM,CAAC,KAAA,KAAU;AACpD,UAAA,IAAA,CAAK,MAAA,CAAO,KAAK,8BAAA,EAAgC;AAAA,YAC/C,OAAO,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU,OAAO,KAAK;AAAA,WAC7D,CAAA;AAAA,QACH,CAAC,CAAA;AAAA,MACH,GAAG,mBAAmB,CAAA;AACtB,MAAA,IAAA,CAAK,aAAa,KAAA,IAAQ;AAAA,IAC5B;AAEA,IAAA,IAAA,CAAK,MAAA,CAAO,KAAK,uBAAuB,CAAA;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA,EAKA,OAAA,GAAgB;AACd,IAAA,IAAI,KAAK,eAAA,EAAiB;AACxB,MAAA,aAAA,CAAc,KAAK,eAAe,CAAA;AAClC,MAAA,IAAA,CAAK,eAAA,GAAkB,IAAA;AAAA,IACzB;AACA,IAAA,IAAI,KAAK,YAAA,EAAc;AACrB,MAAA,aAAA,CAAc,KAAK,YAAY,CAAA;AAC/B,MAAA,IAAA,CAAK,YAAA,GAAe,IAAA;AAAA,IACtB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,SAAS,GAAA,EAAmB;AAClC,IAAA,IAAI,KAAK,QAAA,IAAY,QAAA,IAAY,GAAA,IAAO,YAAA,IAAgB,IAAI,MAAA,EAAQ;AAClE,MAAA,IAAA,CAAK,QAAA,CAAS,KAAK,YAAA,EAAc;AAAA,QAC/B,KAAA,EAAO,IAAI,QAAA,CAAS,EAAA;AAAA,QACpB,OAAA,EAAS,IAAI,QAAA,CAAS,IAAA;AAAA,QACtB,UAAA,EAAY,IAAI,MAAA,CAAO,UAAA;AAAA,QACvB,MAAA,EAAQ,IAAI,QAAA,CAAS;AAAA,OACtB,CAAA;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,mBAAA,GAAqC;AACjD,IAAA,MAAM,UAAA,GAAkB,IAAA,CAAA,IAAA,CAAK,IAAA,CAAK,OAAA,EAAS,SAAS,CAAA;AACpD,IAAA,IAAI,KAAA;AACJ,IAAA,IAAI;AACF,MAAA,KAAA,GAAQ,MAAMA,QAAA,CAAG,OAAA,CAAQ,UAAU,CAAA;AAAA,IACrC,CAAA,CAAA,MAAQ;AACN,MAAA;AAAA,IACF;AACA,IAAA,KAAA,CAAM,IAAA,EAAK;AACX,IAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,MAAA,IAAI;AACF,QAAA,MAAM,OAAA,GAAU,MAAMA,QAAA,CAAG,QAAA,CAAc,UAAK,UAAA,EAAY,IAAI,GAAG,OAAO,CAAA;AACtE,QAAA,IAAA,CAAK,QAAA,CAAS,IAAA,CAAK,KAAA,CAAM,OAAO,CAAW,CAAA;AAAA,MAC7C,CAAA,CAAA,MAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UAAU,GAAA,EAA4B;AAC1C,IAAA,MAAM,UAAU,IAAA,CAAK,UAAA,CAAW,IAAI,QAAA,CAAS,EAAA,EAAI,IAAI,MAAM,CAAA;AAC3D,IAAA,MAAMA,QAAA,CAAG,UAAU,OAAA,EAAS,IAAA,CAAK,UAAU,GAAA,EAAK,IAAA,EAAM,CAAC,CAAA,EAAG,OAAO,CAAA;AACjE,IAAA,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,aAAA,EAAe,EAAE,KAAA,EAAO,GAAA,CAAI,QAAA,CAAS,EAAA,EAAI,MAAA,EAAQ,GAAA,CAAI,MAAA,EAAQ,CAAA;AAE9E,IAAA,IAAI,GAAA,CAAI,WAAW,SAAA,EAAW;AAC5B,MAAA,IAAA,CAAK,SAAS,GAAG,CAAA;AAAA,IACnB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,OAAO,KAAA,EAAsC;AACjD,IAAA,MAAM,WAAwB,CAAC,SAAA,EAAW,SAAA,EAAW,UAAA,EAAY,UAAU,WAAW,CAAA;AAEtF,IAAA,KAAA,MAAW,UAAU,QAAA,EAAU;AAC7B,MAAA,MAAM,OAAA,GAAU,IAAA,CAAK,UAAA,CAAW,KAAA,EAAO,MAAM,CAAA;AAC7C,MAAA,IAAI;AACF,QAAA,MAAM,OAAA,GAAU,MAAMA,QAAA,CAAG,QAAA,CAAS,SAAS,OAAO,CAAA;AAClD,QAAA,OAAO,IAAA,CAAK,MAAM,OAAO,CAAA;AAAA,MAC3B,SAAS,KAAA,EAAO;AAEd,QAAA;AAAA,MACF;AAAA,IACF;AAEA,IAAA,OAAO,IAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,UAAA,GAA+B,QAAQ,OAAA,EAAQ;AAAA,EAEvD,MAAM,aAAa,KAAA,EAA4E;AAC7F,IAAA,MAAM,KAAA,GAAQ,KAAK,UAAA,CAAW,IAAA,CAAK,MAAM,IAAA,CAAK,WAAA,CAAY,KAAK,CAAC,CAAA;AAEhE,IAAA,IAAA,CAAK,UAAA,GAAa,KAAA,CAAM,KAAA,CAAM,MAAM,MAAS,CAAA;AAC7C,IAAA,OAAO,KAAA;AAAA,EACT;AAAA,EAEA,MAAc,YAAY,KAAA,EAA4E;AACpG,IAAA,MAAM,OAAA,GAAU,MAAM,IAAA,CAAK,QAAA,CAAS,EAAE,QAAQ,SAAA,EAAW,KAAA,EAAO,MAAA,CAAO,gBAAA,EAAkB,CAAA;AACzF,IAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,IAAA,CAAK,CAAC,CAAA,KAAM,KAAA,CAAM,MAAA,KAAW,CAAA,IAAK,KAAA,CAAM,QAAA,CAAS,CAAA,CAAE,QAAA,CAAS,IAAI,CAAC,CAAA;AACvF,IAAA,IAAI,CAAC,KAAA,EAAO,OAAO,EAAE,UAAU,gBAAA,EAAiB;AAKhD,IAAA,MAAM,OAAA,GAAgC;AAAA,MACpC,MAAA,EAAQ,SAAA;AAAA,MACR,UAAU,KAAA,CAAM,QAAA;AAAA,MAChB,QAAQ,KAAA,CAAM,MAAA;AAAA,MACd,SAAA,EAAA,iBAAW,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY;AAAA,MAClC,UAAU;AAAC,KACb;AACA,IAAA,MAAM,IAAA,CAAK,UAAA,CAAW,OAAA,EAAS,SAAS,CAAA;AACxC,IAAA,OAAO,EAAE,KAAK,OAAA,EAAQ;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAc,UAAA,CAAW,GAAA,EAAa,SAAA,EAAsC;AAE1E,IAAA,IAAI,SAAA,IAAa,SAAA,KAAc,GAAA,CAAI,MAAA,EAAQ;AACzC,MAAA,MAAM,UAAU,IAAA,CAAK,UAAA,CAAW,GAAA,CAAI,QAAA,CAAS,IAAI,SAAS,CAAA;AAC1D,MAAA,IAAI;AACF,QAAA,MAAMA,QAAA,CAAG,OAAO,OAAO,CAAA;AAAA,MACzB,SAAS,KAAA,EAAO;AAAA,MAEhB;AAAA,IACF;AAGA,IAAA,MAAM,UAAU,IAAA,CAAK,UAAA,CAAW,IAAI,QAAA,CAAS,EAAA,EAAI,IAAI,MAAM,CAAA;AAC3D,IAAA,MAAMA,QAAA,CAAG,UAAU,OAAA,EAAS,IAAA,CAAK,UAAU,GAAA,EAAK,IAAA,EAAM,CAAC,CAAA,EAAG,OAAO,CAAA;AAEjE,IAAA,IAAI,SAAA,IAAa,SAAA,KAAc,GAAA,CAAI,MAAA,EAAQ;AACzC,MAAA,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,WAAA,EAAa,EAAE,KAAA,EAAO,GAAA,CAAI,QAAA,CAAS,EAAA,EAAI,SAAA,EAAW,SAAA,EAAW,GAAA,CAAI,MAAA,EAAQ,CAAA;AAE1F,MAAA,IAAI,GAAA,CAAI,WAAW,SAAA,EAAW;AAC5B,QAAA,IAAA,CAAK,SAAS,GAAG,CAAA;AAAA,MACnB;AAAA,IACF,CAAA,MAAO;AACL,MAAA,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,aAAA,EAAe,EAAE,KAAA,EAAO,GAAA,CAAI,QAAA,CAAS,EAAA,EAAI,MAAA,EAAQ,GAAA,CAAI,MAAA,EAAQ,CAAA;AAAA,IAChF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,WAAA,CAAY,KAAA,EAAc,MAAA,EAAmD;AACjF,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,MAAA,CAAO,KAAK,CAAA;AACnC,IAAA,IAAI,CAAC,GAAA,IAAO,GAAA,CAAI,MAAA,KAAW,SAAA,EAAW;AACpC,MAAA,OAAO,KAAA;AAAA,IACT;AAEA,IAAA,IAAA,CAAK,iBAAA,CAAkB,OAAO,KAAK,CAAA;AACnC,IAAA,MAAM,SAAA,GAAmC;AAAA,MACvC,MAAA,EAAQ,UAAA;AAAA,MACR,UAAU,GAAA,CAAI,QAAA;AAAA,MACd,QAAQ,GAAA,CAAI,MAAA;AAAA,MACZ,WAAW,GAAA,CAAI,SAAA;AAAA,MACf,WAAA,EAAA,iBAAa,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY;AAAA,MACpC;AAAA,KACF;AACA,IAAA,MAAM,IAAA,CAAK,UAAA,CAAW,SAAA,EAAW,SAAS,CAAA;AAC1C,IAAA,OAAO,IAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,OAAA,CAAQ,KAAA,EAAc,KAAA,EAAe,cAAA,EAA2B,cAA8C,WAAA,EAAgF;AAClM,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,MAAA,CAAO,KAAK,CAAA;AACnC,IAAA,IAAI,CAAC,GAAA,IAAO,GAAA,CAAI,MAAA,KAAW,SAAA,EAAW;AACpC,MAAA,OAAO,IAAA;AAAA,IACT;AAEA,IAAA,IAAA,CAAK,iBAAA,CAAkB,OAAO,KAAK,CAAA;AAOnC,IAAA,MAAM,aAAa,CAAC,mBAAG,IAAI,GAAA,CAAI,CAAC,GAAI,GAAA,CAAI,QAAA,CAAS,cAAA,IAAkB,EAAC,EAAI,GAAI,kBAAkB,EAAG,CAAC,CAAC,CAAA;AAKnG,IAAA,MAAM,UAAU,gBAAA,CAAiB,GAAA,CAAI,QAAA,CAAS,WAAA,EAAa,aAAa,UAAU,CAAA;AAClF,IAAA,MAAM,EAAE,WAAA,EAAa,UAAA,EAAY,GAAG,IAAA,KAAS,GAAA,CAAI,QAAA;AAEjD,IAAA,MAAM,QAAA,GAAW;AAAA,MACf,GAAG,IAAA;AAAA,MACH,GAAI,WAAW,MAAA,GAAS,CAAA,GAAI,EAAE,cAAA,EAAgB,UAAA,KAAe,EAAC;AAAA,MAC9D,GAAI,MAAA,CAAO,IAAA,CAAK,OAAO,CAAA,CAAE,MAAA,GAAS,CAAA,GAAI,EAAE,WAAA,EAAa,OAAA,EAAQ,GAAI;AAAC,KACpE;AAMA,IAAA,IAAI,cAAA,CAAe,GAAA,CAAI,QAAA,EAAU,YAAY,CAAA,EAAG;AAC9C,MAAA,MAAM,OAAA,GAA2B;AAAA,QAC/B,MAAA,EAAQ,SAAA;AAAA,QACR,QAAA,EAAU,EAAE,GAAG,QAAA,EAAU,YAAY,GAAA,CAAI,QAAA,CAAS,aAAa,CAAA,EAAE;AAAA,QACjE,QAAQ,GAAA,CAAI;AAAA,OACd;AACA,MAAA,MAAM,IAAA,CAAK,UAAA,CAAW,OAAA,EAAS,SAAS,CAAA;AACxC,MAAA,OAAO,SAAA;AAAA,IACT;AAEA,IAAA,IAAI,iBAAiB,eAAA,IAAmB,GAAA,CAAI,SAAS,UAAA,GAAa,GAAA,CAAI,SAAS,UAAA,EAAY;AACzF,MAAA,IAAA,CAAK,OAAO,IAAA,CAAK,uDAAA,EAAoD,EAAE,KAAA,EAAO,OAAO,CAAA;AAAA,IACvF;AAEA,IAAA,MAAM,MAAA,GAAyB;AAAA,MAC7B,MAAA,EAAQ,QAAA;AAAA,MACR,QAAA;AAAA,MACA,QAAQ,GAAA,CAAI,MAAA;AAAA,MACZ,WAAW,GAAA,CAAI,SAAA;AAAA,MACf,WAAA,EAAA,iBAAa,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY;AAAA,MACpC;AAAA,KACF;AACA,IAAA,MAAM,IAAA,CAAK,UAAA,CAAW,MAAA,EAAQ,SAAS,CAAA;AACvC,IAAA,OAAO,QAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,MAAM,eAAA,CAAgB,KAAA,EAAc,cAAA,EAA0B,WAAA,EAAyD;AACrH,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,MAAA,CAAO,KAAK,CAAA;AACnC,IAAA,IAAI,CAAC,GAAA,IAAO,GAAA,CAAI,MAAA,KAAW,SAAA,EAAW;AACpC,MAAA;AAAA,IACF;AACA,IAAA,MAAM,MAAA,GAAS,CAAC,mBAAG,IAAI,IAAI,CAAC,GAAI,GAAA,CAAI,QAAA,CAAS,kBAAkB,EAAC,EAAI,GAAG,cAAc,CAAC,CAAC,CAAA;AACvF,IAAA,MAAM,UAAU,gBAAA,CAAiB,GAAA,CAAI,QAAA,CAAS,WAAA,EAAa,aAAa,MAAM,CAAA;AAI9E,IAAA,MAAM,EAAE,WAAA,EAAa,UAAA,EAAY,GAAG,QAAA,KAAa,GAAA,CAAI,QAAA;AAErD,IAAA,MAAM,OAAA,GAAgC;AAAA,MACpC,GAAG,GAAA;AAAA,MACH,QAAA,EAAU;AAAA,QACR,GAAG,QAAA;AAAA,QACH,cAAA,EAAgB,MAAA;AAAA;AAAA;AAAA;AAAA,QAIhB,GAAI,MAAA,CAAO,IAAA,CAAK,OAAO,CAAA,CAAE,MAAA,GAAS,CAAA,GAAI,EAAE,WAAA,EAAa,OAAA,EAAQ,GAAI;AAAC;AACpE,KACF;AACA,IAAA,MAAMA,QAAA,CAAG,SAAA,CAAU,IAAA,CAAK,UAAA,CAAW,KAAA,EAAO,SAAS,CAAA,EAAG,IAAA,CAAK,SAAA,CAAU,OAAA,EAAS,IAAA,EAAM,CAAC,GAAG,OAAO,CAAA;AAAA,EACjG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,cAAA,CAAe,KAAA,EAAc,QAAA,EAAkD;AACnF,IAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AACrB,IAAA,MAAM,SAAA,GAAY,IAAA,CAAK,iBAAA,CAAkB,GAAA,CAAI,KAAK,CAAA,IAAK,CAAA;AACvD,IAAA,IAAI,GAAA,GAAM,YAAY,8BAAA,EAAgC;AACpD,MAAA;AAAA,IACF;AACA,IAAA,IAAA,CAAK,iBAAA,CAAkB,GAAA,CAAI,KAAA,EAAO,GAAG,CAAA;AAErC,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,MAAA,CAAO,KAAK,CAAA;AACnC,IAAA,IAAI,CAAC,GAAA,IAAO,GAAA,CAAI,MAAA,KAAW,SAAA,EAAW;AACpC,MAAA,IAAA,CAAK,iBAAA,CAAkB,OAAO,KAAK,CAAA;AACnC,MAAA;AAAA,IACF;AAIA,IAAA,MAAM,OAAA,GAAgC,EAAE,GAAG,GAAA,EAAK,QAAA,EAAS;AACzD,IAAA,MAAMA,QAAA,CAAG,SAAA,CAAU,IAAA,CAAK,UAAA,CAAW,KAAA,EAAO,SAAS,CAAA,EAAG,IAAA,CAAK,SAAA,CAAU,OAAA,EAAS,IAAA,EAAM,CAAC,GAAG,OAAO,CAAA;AAAA,EACjG;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAA,CAAS,OAAA,GAA2B,EAAC,EAAsB;AAC/D,IAAA,MAAM,OAAiB,EAAC;AAGxB,IAAA,MAAM,QAAA,GAAwB,OAAA,CAAQ,MAAA,GAClC,CAAC,OAAA,CAAQ,MAAM,CAAA,GACf,CAAC,SAAA,EAAW,SAAA,EAAW,UAAA,EAAY,QAAA,EAAU,WAAW,CAAA;AAE5D,IAAA,KAAA,MAAW,UAAU,QAAA,EAAU;AAC7B,MAAA,MAAM,SAAA,GAAiB,IAAA,CAAA,IAAA,CAAK,IAAA,CAAK,OAAA,EAAS,MAAM,CAAA;AAEhD,MAAA,IAAI;AACF,QAAA,MAAM,KAAA,GAAQ,MAAMA,QAAA,CAAG,OAAA,CAAQ,SAAS,CAAA;AAExC,QAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,UAAA,MAAM,OAAA,GAAe,IAAA,CAAA,IAAA,CAAK,SAAA,EAAW,IAAI,CAAA;AACzC,UAAA,MAAM,OAAA,GAAU,MAAMA,QAAA,CAAG,QAAA,CAAS,SAAS,OAAO,CAAA;AAClD,UAAA,MAAM,GAAA,GAAM,IAAA,CAAK,KAAA,CAAM,OAAO,CAAA;AAG9B,UAAA,IAAI,QAAQ,IAAA,IAAQ,GAAA,CAAI,QAAA,CAAS,IAAA,KAAS,QAAQ,IAAA,EAAM;AACxD,UAAA,IAAI,QAAQ,MAAA,IAAU,GAAA,CAAI,QAAA,CAAS,MAAA,KAAW,QAAQ,MAAA,EAAQ;AAE9D,UAAA,IAAA,CAAK,KAAK,GAAG,CAAA;AAAA,QACf;AAAA,MACF,SAAS,KAAA,EAAO;AAEd,QAAA;AAAA,MACF;AAAA,IACF;AAGA,IAAA,IAAA,CAAK,KAAK,CAAC,CAAA,EAAG,MAAM,IAAI,IAAA,CAAK,EAAE,QAAA,CAAS,OAAO,EAAE,OAAA,EAAQ,GAAI,IAAI,IAAA,CAAK,CAAA,CAAE,SAAS,OAAO,CAAA,CAAE,SAAS,CAAA;AAGnG,IAAA,MAAM,MAAA,GAAS,QAAQ,MAAA,IAAU,CAAA;AACjC,IAAA,MAAM,KAAA,GAAQ,QAAQ,KAAA,IAAS,GAAA;AAE/B,IAAA,OAAO,IAAA,CAAK,KAAA,CAAM,MAAA,EAAQ,MAAA,GAAS,KAAK,CAAA;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UAAU,KAAA,EAAgC;AAC9C,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,MAAA,CAAO,KAAK,CAAA;AAEnC,IAAA,IAAI,CAAC,GAAA,EAAK;AACR,MAAA,OAAO,KAAA;AAAA,IACT;AAGA,IAAA,IAAI,GAAA,CAAI,MAAA,KAAW,SAAA,IAAa,GAAA,CAAI,WAAW,SAAA,EAAW;AACxD,MAAA,OAAO,KAAA;AAAA,IACT;AAEA,IAAA,MAAM,YAAY,GAAA,CAAI,MAAA;AAGtB,IAAA,MAAM,YAAA,GAAkC;AAAA,MACtC,MAAA,EAAQ,WAAA;AAAA,MACR,UAAU,GAAA,CAAI,QAAA;AAAA,MACd,QAAQ,GAAA,CAAI,MAAA,KAAW,SAAA,GAAY,GAAA,CAAI,SAAS,GAAA,CAAI,MAAA;AAAA,MACpD,SAAA,EAAW,GAAA,CAAI,MAAA,KAAW,SAAA,GAAY,IAAI,SAAA,GAAY,MAAA;AAAA,MACtD,WAAA,EAAA,iBAAa,IAAI,IAAA,EAAK,EAAE,WAAA;AAAY,KACtC;AAEA,IAAA,MAAM,IAAA,CAAK,UAAA,CAAW,YAAA,EAAc,SAAS,CAAA;AAC7C,IAAA,OAAO,IAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,kBAAkB,QAAA,EAAwD;AAC9E,IAAA,MAAM,OAAA,GAAU,QAAA,KAAa,YAAA,GACzB,CAAC,IAAA,KAAiB,IAAA,KAAS,YAAA,GAC3B,CAAC,IAAA,KAAiB,IAAA,CAAK,QAAA,CAAS,aAAa,CAAA;AAEjD,IAAA,MAAM,OAAA,GAAU,MAAM,IAAA,CAAK,QAAA,CAAS,EAAE,QAAQ,SAAA,EAAW,KAAA,EAAO,MAAA,CAAO,gBAAA,EAAkB,CAAA;AACzF,IAAA,IAAI,SAAA,GAAY,CAAA;AAChB,IAAA,KAAA,MAAW,OAAO,OAAA,EAAS;AACzB,MAAA,IAAI,CAAC,OAAA,CAAQ,GAAA,CAAI,QAAA,CAAS,IAAI,CAAA,EAAG;AACjC,MAAA,IAAI,MAAM,IAAA,CAAK,SAAA,CAAU,GAAA,CAAI,QAAA,CAAS,EAAE,CAAA,EAAG;AACzC,QAAA,SAAA,EAAA;AAAA,MACF;AAAA,IACF;AAEA,IAAA,IAAI,YAAY,CAAA,EAAG;AACjB,MAAA,IAAA,CAAK,OAAO,IAAA,CAAK,wBAAA,EAA0B,EAAE,QAAA,EAAU,WAAW,CAAA;AAAA,IACpE;AACA,IAAA,OAAO,SAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,uBAAA,GAA2C;AAC/C,IAAA,MAAM,UAAA,GAAkB,IAAA,CAAA,IAAA,CAAK,IAAA,CAAK,OAAA,EAAS,SAAS,CAAA;AACpD,IAAA,IAAI,KAAA;AACJ,IAAA,IAAI;AACF,MAAA,KAAA,GAAQ,MAAMA,QAAA,CAAG,OAAA,CAAQ,UAAU,CAAA;AAAA,IACrC,CAAA,CAAA,MAAQ;AACN,MAAA,OAAO,CAAA;AAAA,IACT;AAEA,IAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AACrB,IAAA,IAAI,SAAA,GAAY,CAAA;AAChB,IAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,MAAA,IAAI,CAAC,IAAA,CAAK,QAAA,CAAS,OAAO,CAAA,EAAG;AAC7B,MAAA,IAAI;AACF,QAAA,MAAM,OAAO,MAAMA,QAAA,CAAG,KAAU,IAAA,CAAA,IAAA,CAAK,UAAA,EAAY,IAAI,CAAC,CAAA;AACtD,QAAA,IAAI,GAAA,GAAM,IAAA,CAAK,OAAA,GAAU,gBAAA,EAAkB;AAE3C,QAAA,MAAM,OAAA,GAAUC,MAAQ,IAAA,CAAK,KAAA,CAAM,GAAG,CAAC,OAAA,CAAQ,MAAM,CAAC,CAAA;AACtD,QAAA,MAAM,OAAA,GAAU,MAAM,IAAA,CAAK,OAAA;AAAA,UACzB,OAAA;AAAA,UACA,CAAA,+CAAA,EAA6C,mBAAmB,GAAM,CAAA,QAAA;AAAA,SACxE;AACA,QAAA,IAAI,OAAA,EAAS;AACX,UAAA,IAAA,CAAK,OAAO,IAAA,CAAK,6BAAA,EAA+B,EAAE,KAAA,EAAO,OAAA,EAAS,SAAS,CAAA;AAC3E,UAAA,SAAA,EAAA;AAAA,QACF;AAAA,MACF,CAAA,CAAA,MAAQ;AAAA,MAER;AAAA,IACF;AACA,IAAA,OAAO,SAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,cAAA,CAAe,cAAA,GAAyB,EAAA,EAAqB;AACjE,IAAA,MAAM,aAAa,IAAA,CAAK,GAAA,EAAI,GAAK,cAAA,GAAiB,KAAK,EAAA,GAAK,GAAA;AAC5D,IAAA,IAAI,YAAA,GAAe,CAAA;AAEnB,IAAA,MAAM,eAAA,GAA+B,CAAC,UAAA,EAAY,QAAA,EAAU,WAAW,CAAA;AAEvE,IAAA,KAAA,MAAW,UAAU,eAAA,EAAiB;AACpC,MAAA,MAAM,SAAA,GAAiB,IAAA,CAAA,IAAA,CAAK,IAAA,CAAK,OAAA,EAAS,MAAM,CAAA;AAEhD,MAAA,IAAI;AACF,QAAA,MAAM,KAAA,GAAQ,MAAMD,QAAA,CAAG,OAAA,CAAQ,SAAS,CAAA;AAExC,QAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,UAAA,MAAM,OAAA,GAAe,IAAA,CAAA,IAAA,CAAK,SAAA,EAAW,IAAI,CAAA;AACzC,UAAA,MAAM,OAAA,GAAU,MAAMA,QAAA,CAAG,QAAA,CAAS,SAAS,OAAO,CAAA;AAClD,UAAA,MAAM,GAAA,GAAM,IAAA,CAAK,KAAA,CAAM,OAAO,CAAA;AAE9B,UAAA,IAAI,GAAA,CAAI,WAAW,UAAA,IAAc,GAAA,CAAI,WAAW,QAAA,IAAY,GAAA,CAAI,WAAW,WAAA,EAAa;AACtF,YAAA,MAAM,gBAAgB,IAAI,IAAA,CAAK,GAAA,CAAI,WAAW,EAAE,OAAA,EAAQ;AAExD,YAAA,IAAI,gBAAgB,UAAA,EAAY;AAC9B,cAAA,MAAMA,QAAA,CAAG,OAAO,OAAO,CAAA;AACvB,cAAA,YAAA,EAAA;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF,SAAS,KAAA,EAAO;AACd,QAAA,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,wBAAA,EAA0B,EAAE,MAAA,EAAQ,KAAA,EAAO,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU,MAAA,CAAO,KAAK,GAAG,CAAA;AAAA,MACvH;AAAA,IACF;AAEA,IAAA,IAAI,eAAe,CAAA,EAAG;AACpB,MAAA,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,iBAAA,EAAmB,EAAE,cAAc,CAAA;AAAA,IACtD;AAEA,IAAA,OAAO,YAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,UAAA,CAAW,OAAc,MAAA,EAA2B;AAC1D,IAAA,OAAY,UAAK,IAAA,CAAK,OAAA,EAAS,MAAA,EAAQ,CAAA,EAAG,KAAK,CAAA,KAAA,CAAO,CAAA;AAAA,EACxD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAA,GAMH;AACD,IAAA,MAAM,KAAA,GAAQ;AAAA,MACZ,OAAA,EAAS,CAAA;AAAA,MACT,OAAA,EAAS,CAAA;AAAA,MACT,QAAA,EAAU,CAAA;AAAA,MACV,MAAA,EAAQ,CAAA;AAAA,MACR,SAAA,EAAW;AAAA,KACb;AAEA,IAAA,MAAM,WAAwB,CAAC,SAAA,EAAW,SAAA,EAAW,UAAA,EAAY,UAAU,WAAW,CAAA;AAEtF,IAAA,KAAA,MAAW,UAAU,QAAA,EAAU;AAC7B,MAAA,MAAM,SAAA,GAAiB,IAAA,CAAA,IAAA,CAAK,IAAA,CAAK,OAAA,EAAS,MAAM,CAAA;AAEhD,MAAA,IAAI;AACF,QAAA,MAAM,KAAA,GAAQ,MAAMA,QAAA,CAAG,OAAA,CAAQ,SAAS,CAAA;AACxC,QAAA,KAAA,CAAM,MAAM,IAAI,KAAA,CAAM,MAAA;AAAA,MACxB,SAAS,KAAA,EAAO;AAEd,QAAA,KAAA,CAAM,MAAM,CAAA,GAAI,CAAA;AAAA,MAClB;AAAA,IACF;AAEA,IAAA,OAAO,KAAA;AAAA,EACT;AACF;AC7lBA,IAAM,MAAA,GAAS,MAAA;AACf,IAAM,QAAA,GAAW,gBAAA;AACjB,IAAM,MAAA,GAAS,MAAA;AAGf,IAAME,+BAAAA,GAAiC,GAAA;AAGvC,IAAM,gBAAA,GAAmB,EAAA;AAEzB,IAAM,GAAA,GAAM,IAAI,WAAA,EAAY;AAC5B,IAAM,GAAA,GAAM,IAAI,WAAA,EAAY;AAerB,IAAM,oBAAA,GAAuB,CAAC,QAAQ;AAe7C,SAAS,WAAW,IAAA,EAA2C;AAC7D,EAAA,OAAO,IAAA,KAAS,eAAe,YAAA,GAAe,YAAA;AAChD;AAEA,SAAS,WAAW,GAAA,EAAqB;AACvC,EAAA,OAAO,CAAA,KAAA,EAAQ,WAAW,GAAA,CAAI,QAAA,CAAS,IAAI,CAAC,CAAA,CAAA,EAAI,GAAA,CAAI,QAAA,CAAS,IAAI,CAAA,CAAA;AACnE;AAEO,IAAM,oBAAN,MAA4C;AAAA,EAkBjD,WAAA,CACmB,OAAA,EACA,MAAA,EACA,QAAA,EACjB;AAHiB,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA;AACA,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AACA,IAAA,IAAA,CAAA,QAAA,GAAA,QAAA;AAEjB,IAAA,IAAA,CAAK,cAAA,GAAiB,OAAA,CAAQ,cAAA,IAAkB,EAAA,GAAK,GAAA;AACrD,IAAA,IAAA,CAAK,SAAA,GAAY,QAAQ,SAAA,IAAa,GAAA;AACtC,IAAA,IAAA,CAAK,MAAA,GAAS,QAAQ,MAAA,IAAU,GAAA;AAAA,EAClC;AAAA,EAPmB,OAAA;AAAA,EACA,MAAA;AAAA,EACA,QAAA;AAAA,EApBX,EAAA;AAAA,EACA,EAAA;AAAA,EACA,GAAA;AAAA,EACA,EAAA;AAAA,EACA,IAAA,GAAgC,IAAA;AAAA;AAAA;AAAA,EAGvB,IAAA,uBAAW,GAAA,EAAwC;AAAA,EAC5D,SAAA,GAAmD,IAAA;AAAA,EACnD,WAAA,GAAc,KAAA;AAAA,EACd,IAAA,GAA8C,IAAA;AAAA,EAC9C,OAAA,GAAU,KAAA;AAAA,EACD,iBAAA,uBAAwB,GAAA,EAAoB;AAAA,EAC5C,cAAA;AAAA,EACA,SAAA;AAAA,EACA,MAAA;AAAA,EAYjB,MAAM,UAAA,GAA4B;AAChC,IAAA,IAAA,CAAK,EAAA,GAAK,MAAM,OAAA,CAAQ;AAAA,MACtB,OAAA,EAAS,KAAK,OAAA,CAAQ,OAAA;AAAA,MACtB,SAAA,EAAW,IAAA,CAAK,OAAA,CAAQ,SAAA,IAAa,IAAA;AAAA,MACrC,OAAA,EAAS;AAAA,KACV,CAAA;AACD,IAAA,IAAA,CAAK,EAAA,GAAK,IAAA,CAAK,EAAA,CAAG,SAAA,EAAU;AAC5B,IAAA,IAAA,CAAK,GAAA,GAAM,MAAM,IAAA,CAAK,EAAA,CAAG,gBAAA,EAAiB;AAC1C,IAAA,IAAA,CAAK,KAAK,MAAM,IAAA,CAAK,EAAA,CAAG,KAAA,CAAM,GAAG,MAAM,CAAA;AAEvC,IAAA,IAAI;AACF,MAAA,MAAM,IAAA,CAAK,GAAA,CAAI,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAA;AAAA,IACpC,CAAA,CAAA,MAAQ;AACN,MAAA,MAAM,IAAA,CAAK,GAAA,CAAI,OAAA,CAAQ,GAAA,CAAI;AAAA,QACzB,IAAA,EAAM,MAAA;AAAA,QACN,QAAA,EAAU,CAAC,GAAG,oBAAoB,CAAA;AAAA,QAClC,WAAW,eAAA,CAAgB;AAAA,OAC5B,CAAA;AAAA,IACH;AACA,IAAA,IAAI;AACF,MAAA,MAAM,IAAA,CAAK,GAAA,CAAI,SAAA,CAAU,IAAA,CAAK,QAAQ,QAAQ,CAAA;AAAA,IAChD,CAAA,CAAA,MAAQ;AACN,MAAA,MAAM,IAAA,CAAK,GAAA,CAAI,SAAA,CAAU,GAAA,CAAI,MAAA,EAAQ;AAAA,QACnC,YAAA,EAAc,QAAA;AAAA,QACd,YAAY,SAAA,CAAU,QAAA;AAAA,QACtB,gBAAgB,aAAA,CAAc,GAAA;AAAA,QAC9B,QAAA,EAAU,KAAA,CAAM,IAAA,CAAK,SAAS,CAAA;AAAA;AAAA;AAAA,QAG9B,WAAA,EAAa;AAAA,OACd,CAAA;AAAA,IACH;AAEA,IAAA,MAAM,WAAW,MAAM,IAAA,CAAK,GAAG,SAAA,CAAU,GAAA,CAAI,QAAQ,QAAQ,CAAA;AAC7D,IAAA,IAAA,CAAK,IAAA,GAAO,MAAM,QAAA,CAAS,OAAA,CAAQ;AAAA,MACjC,QAAA,EAAU,CAAC,CAAA,KAAM;AACf,QAAA,KAAK,KAAK,UAAA,CAAW,CAAC,CAAA,CAAE,KAAA,CAAM,CAAC,KAAA,KAAU;AACvC,UAAA,IAAA,CAAK,MAAA,CAAO,KAAK,8BAAA,EAAgC;AAAA,YAC/C,OAAO,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU,OAAO,KAAK;AAAA,WAC7D,CAAA;AAAA,QACH,CAAC,CAAA;AAAA,MACH;AAAA,KACD,CAAA;AAUD,IAAA,IAAA,CAAK,SAAA,GAAY,YAAY,MAAM;AACjC,MAAA,IAAI,KAAK,WAAA,EAAa;AACtB,MAAA,IAAA,CAAK,WAAA,GAAc,IAAA;AACnB,MAAA,KAAK,IAAA,CAAK,aAAA,EAAc,CACrB,KAAA,CAAM,CAAC,KAAA,KAAU;AAChB,QAAA,IAAA,CAAK,MAAA,CAAO,KAAK,kCAAA,EAAoC;AAAA,UACnD,OAAO,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU,OAAO,KAAK;AAAA,SAC7D,CAAA;AAAA,MACH,CAAC,CAAA,CACA,OAAA,CAAQ,MAAM;AACb,QAAA,IAAA,CAAK,WAAA,GAAc,KAAA;AAAA,MACrB,CAAC,CAAA;AAAA,IACL,CAAA,EAAG,IAAA,CAAK,GAAA,CAAI,GAAA,EAAK,IAAA,CAAK,MAAM,IAAA,CAAK,SAAA,GAAY,CAAC,CAAC,CAAC,CAAA;AAChD,IAAA,IAAA,CAAK,UAAU,KAAA,IAAQ;AASvB,IAAA,IAAA,CAAK,IAAA,GAAO,YAAY,MAAM;AAC5B,MAAA,IAAI,KAAK,OAAA,EAAS;AAClB,MAAA,IAAA,CAAK,OAAA,GAAU,IAAA;AACf,MAAA,KAAA,CAAM,YAAY;AAChB,QAAA,KAAA,MAAW,CAAC,IAAI,IAAI,CAAA,IAAK,CAAC,GAAG,IAAA,CAAK,IAAI,CAAA,EAAG;AACvC,UAAA,MAAM,WAAW,MAAM,IAAA,CAAK,IAAA,CAAKD,KAAAA,CAAQ,EAAE,CAAC,CAAA;AAC5C,UAAA,IAAI,UAAU,GAAA,CAAI,MAAA,KAAW,WAAW,IAAA,CAAK,QAAA,CAAS,SAAS,GAAG,CAAA;AAC7D,QACP;AACA,QAAA,MAAM,KAAK,uBAAA,EAAwB;AAAA,MACrC,CAAA,GAAG,CACA,KAAA,CAAM,CAAC,KAAA,KAAU;AAChB,QAAA,IAAA,CAAK,MAAA,CAAO,KAAK,uBAAA,EAAyB;AAAA,UACxC,OAAO,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU,OAAO,KAAK;AAAA,SAC7D,CAAA;AAAA,MACH,CAAC,CAAA,CACA,OAAA,CAAQ,MAAM;AACb,QAAA,IAAA,CAAK,OAAA,GAAU,KAAA;AAAA,MACjB,CAAC,CAAA;AAAA,IACL,CAAA,EAAG,KAAK,MAAM,CAAA;AACd,IAAA,IAAA,CAAK,KAAK,KAAA,IAAQ;AAAA,EACpB;AAAA,EAEA,OAAA,GAAgB;AACd,IAAA,IAAA,CAAK,MAAM,IAAA,EAAK;AAChB,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AACZ,IAAA,IAAI,IAAA,CAAK,SAAA,EAAW,aAAA,CAAc,IAAA,CAAK,SAAS,CAAA;AAChD,IAAA,IAAA,CAAK,SAAA,GAAY,IAAA;AACjB,IAAA,IAAI,IAAA,CAAK,IAAA,EAAM,aAAA,CAAc,IAAA,CAAK,IAAI,CAAA;AACtC,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AACZ,IAAA,IAAA,CAAK,KAAK,KAAA,EAAM;AAChB,IAAA,KAAK,IAAA,CAAK,IAAI,KAAA,EAAM;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAc,WAAW,CAAA,EAAyB;AAChD,IAAA,IAAI,EAAA;AACJ,IAAA,IAAI;AACF,MAAA,EAAA,GAAM,KAAK,KAAA,CAAM,GAAA,CAAI,OAAO,CAAA,CAAE,IAAI,CAAC,CAAA,CAAwB,KAAA;AAAA,IAC7D,CAAA,CAAA,MAAQ;AACN,MAAA,CAAA,CAAE,IAAA,EAAK;AACP,MAAA;AAAA,IACF;AACA,IAAA,MAAM,WAAW,MAAM,IAAA,CAAK,IAAA,CAAKA,KAAAA,CAAQ,EAAE,CAAC,CAAA;AAC5C,IAAA,IAAI,CAAC,QAAA,EAAU;AACb,MAAA,CAAA,CAAE,IAAA,EAAK;AACP,MAAA;AAAA,IACF;AACA,IAAA,QAAQ,QAAA,CAAS,IAAI,MAAA;AAAQ,MAC3B,KAAK,SAAA;AACH,QAAA,IAAA,CAAK,IAAA,CAAK,GAAA,CAAI,EAAA,EAAI,EAAE,CAAA,EAAG,MAAM,QAAA,CAAS,GAAA,CAAI,QAAA,CAAS,IAAA,EAAM,CAAA;AACzD,QAAA,IAAA,CAAK,QAAA,CAAS,SAAS,GAAG,CAAA;AAC1B,QAAA;AAAA,MACF,KAAK,SAAA;AACH,QAAA,IAAA,CAAK,IAAA,CAAK,GAAA,CAAI,EAAA,EAAI,EAAE,CAAA,EAAG,MAAM,QAAA,CAAS,GAAA,CAAI,QAAA,CAAS,IAAA,EAAM,CAAA;AACzD,QAAA;AAAA,MACF;AACE,QAAA,CAAA,CAAE,GAAA,EAAI;AACN,QAAA,IAAA,CAAK,IAAA,CAAK,OAAO,EAAE,CAAA;AAAA;AACvB,EACF;AAAA;AAAA,EAGQ,SAAS,GAAA,EAAmB;AAClC,IAAA,IAAI,KAAK,QAAA,IAAY,QAAA,IAAY,GAAA,IAAO,YAAA,IAAiB,IAAI,MAAA,EAAoC;AAC/F,MAAA,IAAA,CAAK,QAAA,CAAS,KAAK,YAAA,EAAc;AAAA,QAC/B,KAAA,EAAO,IAAI,QAAA,CAAS,EAAA;AAAA,QACpB,OAAA,EAAS,IAAI,QAAA,CAAS,IAAA;AAAA,QACtB,UAAA,EAAa,IAAI,MAAA,CAAmC,UAAA;AAAA,QACpD,MAAA,EAAQ,IAAI,QAAA,CAAS;AAAA,OACtB,CAAA;AAAA,IACH;AAAA,EACF;AAAA,EAEA,MAAc,KAAK,QAAA,EAA4F;AAC7G,IAAA,MAAM,KAAA,GAAQ,MAAM,IAAA,CAAK,EAAA,CAAG,IAAI,QAAkB,CAAA;AAClD,IAAA,IAAI,CAAC,KAAA,IAAS,KAAA,CAAM,SAAA,KAAc,OAAO,OAAO,IAAA;AAChD,IAAA,MAAM,WAAW,IAAA,CAAK,KAAA,CAAM,IAAI,MAAA,CAAO,KAAA,CAAM,KAAK,CAAC,CAAA;AACnD,IAAA,OAAO,EAAE,GAAG,QAAA,EAAU,QAAA,EAAU,MAAM,QAAA,EAAS;AAAA,EACjD;AAAA,EAEA,MAAc,KAAA,CAAM,QAAA,EAAiB,QAAA,EAAuB,QAAA,EAAoC;AAC9F,IAAA,MAAM,UAAU,GAAA,CAAI,MAAA,CAAO,IAAA,CAAK,SAAA,CAAU,QAAQ,CAAC,CAAA;AACnD,IAAA,IAAI,aAAa,MAAA,EAAW,OAAO,KAAK,EAAA,CAAG,GAAA,CAAI,UAAoB,OAAO,CAAA;AAC1E,IAAA,OAAO,IAAA,CAAK,EAAA,CAAG,MAAA,CAAO,QAAA,EAAoB,SAAS,QAAQ,CAAA;AAAA,EAC7D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAc,GAAA,CACZ,QAAA,EACA,SAAA,EACA,SAAA,EACY;AACZ,IAAA,KAAA,IAAS,OAAA,GAAU,CAAA,EAAG,OAAA,GAAU,gBAAA,EAAkB,OAAA,EAAA,EAAW;AAC3D,MAAA,MAAM,OAAA,GAAU,MAAM,IAAA,CAAK,IAAA,CAAK,QAAQ,CAAA;AACxC,MAAA,IAAI,CAAC,SAAS,OAAO,SAAA;AACrB,MAAA,MAAM,GAAA,GAAM,SAAA,CAAU,OAAA,CAAQ,GAAA,EAAK,QAAQ,cAAc,CAAA;AACzD,MAAA,IAAI,GAAA,KAAQ,MAAM,OAAO,SAAA;AACzB,MAAA,IAAI,EAAE,UAAA,IAAc,GAAA,CAAA,EAAM,OAAO,GAAA,CAAI,MAAA;AACrC,MAAA,IAAI;AACF,QAAA,MAAM,KAAK,KAAA,CAAM,QAAA,EAAU,GAAA,CAAI,QAAA,EAAU,QAAQ,QAAQ,CAAA;AACzD,QAAA,OAAO,GAAA,CAAI,MAAA;AAAA,MACb,CAAA,CAAA,MAAQ;AAAA,MAER;AAAA,IACF;AACA,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,IAAA,EAAO,QAAQ,CAAA,yBAAA,EAA4B,gBAAgB,CAAA,aAAA,CAAe,CAAA;AAAA,EAC5F;AAAA,EAEA,MAAM,UAAU,GAAA,EAA4B;AAC1C,IAAA,MAAM,GAAA,GAAA,iBAAM,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY;AACnC,IAAA,MAAM,KAAK,EAAA,CAAG,MAAA,CAAO,GAAA,CAAI,QAAA,CAAS,IAAc,GAAA,CAAI,MAAA,CAAO,IAAA,CAAK,SAAA,CAAU,EAAE,GAAA,EAAK,cAAA,EAAgB,GAAA,EAA2B,CAAC,CAAC,CAAA;AAG9H,IAAA,IAAI,GAAA,CAAI,WAAW,SAAA,EAAW;AAC5B,MAAA,MAAM,KAAK,EAAA,CAAG,OAAA,CAAQ,UAAA,CAAW,GAAG,GAAG,GAAA,CAAI,MAAA,CAAO,IAAA,CAAK,SAAA,CAAU,EAAE,KAAA,EAAO,GAAA,CAAI,SAAS,EAAA,EAAI,CAAC,CAAC,CAAA;AAAA,IAC/F;AAAA,EACF;AAAA,EAEA,MAAM,OAAO,QAAA,EAAyC;AACpD,IAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,IAAA,CAAK,QAAQ,CAAA;AACzC,IAAA,OAAO,UAAU,GAAA,IAAO,IAAA;AAAA,EAC1B;AAAA,EAEA,MAAM,aAAa,KAAA,EAA4E;AAC7F,IAAA,MAAM,OAAA,GAAU,CAAC,CAAA,KAAc,KAAA,CAAM,WAAW,CAAA,IAAK,KAAA,CAAM,SAAS,CAAC,CAAA;AAGrE,IAAA,KAAA,MAAW,CAAC,IAAI,IAAI,CAAA,IAAK,CAAC,GAAG,IAAA,CAAK,IAAI,CAAA,EAAG;AACvC,MAAA,IAAI,CAAC,OAAA,CAAQ,IAAA,CAAK,IAAI,CAAA,EAAG;AACzB,MAAA,MAAM,MAAM,MAAM,IAAA,CAAK,QAAA,CAASA,KAAAA,CAAQ,EAAE,CAAC,CAAA;AAC3C,MAAA,IAAI,KAAK,OAAO,GAAA;AAAA,IAClB;AAKA,IAAA,KAAA,MAAW,GAAA,IAAO,MAAM,IAAA,CAAK,OAAA,EAAQ,EAAG;AACtC,MAAA,MAAM,WAAW,MAAM,IAAA,CAAK,IAAA,CAAKA,KAAAA,CAAQ,GAAG,CAAC,CAAA;AAC7C,MAAA,IAAI,CAAC,QAAA,IAAY,QAAA,CAAS,GAAA,CAAI,MAAA,KAAW,SAAA,IAAa,CAAC,OAAA,CAAQ,QAAA,CAAS,GAAA,CAAI,QAAA,CAAS,IAAI,CAAA,EAAG;AAC5F,MAAA,MAAM,MAAM,MAAM,IAAA,CAAK,QAAA,CAASA,KAAAA,CAAQ,GAAG,CAAC,CAAA;AAC5C,MAAA,IAAI,KAAK,OAAO,GAAA;AAAA,IAClB;AACA,IAAA,OAAO,EAAE,UAAU,gBAAA,EAAiB;AAAA,EACtC;AAAA;AAAA,EAGA,MAAc,SAAS,QAAA,EAAkD;AACvE,IAAA,OAAO,IAAA,CAAK,GAAA;AAAA,MACV,QAAA;AAAA,MACA,CAAC,GAAA,KAAQ;AACP,QAAA,IAAI,IAAI,MAAA,KAAW,SAAA,EAAW,OAAO,EAAE,QAAQ,IAAA,EAAK;AACpD,QAAA,MAAM,OAAA,GAAgC;AAAA,UACpC,MAAA,EAAQ,SAAA;AAAA,UACR,UAAU,GAAA,CAAI,QAAA;AAAA,UACd,QAAQ,GAAA,CAAI,MAAA;AAAA,UACZ,SAAA,EAAA,iBAAW,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY;AAAA,UAClC,UAAU;AAAC,SACb;AACA,QAAA,OAAO;AAAA,UACL,QAAA,EAAU,EAAE,GAAA,EAAK,OAAA,EAAS,iCAAgB,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY,EAAE;AAAA,UACnE,MAAA,EAAQ,EAAE,GAAA,EAAK,OAAA;AAAkB,SACnC;AAAA,MACF,CAAA;AAAA,MACA;AAAA,KACF;AAAA,EACF;AAAA,EAEA,MAAM,WAAA,CAAY,QAAA,EAAiB,MAAA,EAAmD;AACpF,IAAA,MAAM,KAAA,GAAQ,MAAM,IAAA,CAAK,GAAA;AAAA,MACvB,QAAA;AAAA,MACA,CAAC,GAAA,KAAQ;AACP,QAAA,IAAI,IAAI,MAAA,KAAW,SAAA,EAAW,OAAO,EAAE,QAAQ,KAAA,EAAM;AACrD,QAAA,MAAM,SAAA,GAAmC;AAAA,UACvC,MAAA,EAAQ,UAAA;AAAA,UACR,UAAU,GAAA,CAAI,QAAA;AAAA,UACd,QAAQ,GAAA,CAAI,MAAA;AAAA,UACZ,WAAW,GAAA,CAAI,SAAA;AAAA,UACf,WAAA,EAAA,iBAAa,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY;AAAA,UACpC;AAAA,SACF;AACA,QAAA,OAAO,EAAE,QAAA,EAAU,EAAE,GAAA,EAAK,SAAA,EAAW,cAAA,EAAA,iBAAgB,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY,EAAE,EAAG,QAAQ,IAAA,EAAK;AAAA,MAChG,CAAA;AAAA,MACA;AAAA,KACF;AACA,IAAA,IAAI,KAAA,EAAO,IAAA,CAAK,WAAA,CAAY,QAAA,EAAU,KAAK,CAAA;AAC3C,IAAA,OAAO,KAAA;AAAA,EACT;AAAA,EAEA,MAAM,OAAA,CACJ,QAAA,EACA,KAAA,EACA,cAAA,EACA,cACA,WAAA,EACsC;AACtC,IAAA,MAAM,OAAA,GAAU,MAAM,IAAA,CAAK,GAAA;AAAA,MACzB,QAAA;AAAA,MACA,CAAC,GAAA,KAAQ;AACP,QAAA,IAAI,IAAI,MAAA,KAAW,SAAA,EAAW,OAAO,EAAE,QAAQ,IAAA,EAAK;AACpD,QAAA,MAAM,QAAQ,CAAC,mBAAG,IAAI,GAAA,CAAI,CAAC,GAAI,GAAA,CAAI,QAAA,CAAS,cAAA,IAAkB,EAAC,EAAI,GAAI,kBAAkB,EAAG,CAAC,CAAC,CAAA;AAC9F,QAAA,MAAM,UAAU,gBAAA,CAAiB,GAAA,CAAI,QAAA,CAAS,WAAA,EAAa,aAAa,KAAK,CAAA;AAC7E,QAAA,MAAM,QAAA,GAAW;AAAA,UACf,GAAG,GAAA,CAAI,QAAA;AAAA,UACP,GAAI,MAAM,MAAA,GAAS,CAAA,GAAI,EAAE,cAAA,EAAgB,KAAA,KAAU,EAAC;AAAA,UACpD,GAAI,MAAA,CAAO,IAAA,CAAK,OAAO,CAAA,CAAE,MAAA,GAAS,CAAA,GAAI,EAAE,WAAA,EAAa,OAAA,EAAQ,GAAI;AAAC,SACpE;AACA,QAAA,IAAI,aAAA,IAAiB,YAAY,MAAA,CAAO,IAAA,CAAK,OAAO,CAAA,CAAE,MAAA,KAAW,CAAA,EAAG,OAAQ,QAAA,CAAuC,WAAA;AAEnH,QAAA,IAAI,cAAA,CAAe,GAAA,CAAI,QAAA,EAAU,YAAY,CAAA,EAAG;AAC9C,UAAA,MAAM,OAAA,GAA2B;AAAA,YAC/B,MAAA,EAAQ,SAAA;AAAA,YACR,QAAA,EAAU,EAAE,GAAG,QAAA,EAAU,YAAY,GAAA,CAAI,QAAA,CAAS,aAAa,CAAA,EAAE;AAAA,YACjE,QAAQ,GAAA,CAAI;AAAA,WACd;AACA,UAAA,OAAO,EAAE,QAAA,EAAU,EAAE,GAAA,EAAK,OAAA,EAAS,cAAA,EAAA,iBAAgB,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY,EAAE,EAAG,QAAQ,SAAA,EAAmB;AAAA,QAC5G;AACA,QAAA,MAAM,MAAA,GAAyB;AAAA,UAC7B,MAAA,EAAQ,QAAA;AAAA,UACR,QAAA;AAAA,UACA,QAAQ,GAAA,CAAI,MAAA;AAAA,UACZ,WAAW,GAAA,CAAI,SAAA;AAAA,UACf,WAAA,EAAA,iBAAa,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY;AAAA,UACpC;AAAA,SACF;AACA,QAAA,OAAO,EAAE,QAAA,EAAU,EAAE,GAAA,EAAK,MAAA,EAAQ,cAAA,EAAA,iBAAgB,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY,EAAE,EAAG,QAAQ,QAAA,EAAkB;AAAA,MAC1G,CAAA;AAAA,MACA;AAAA,KACF;AAEA,IAAA,IAAI,YAAY,SAAA,EAAW;AAGzB,MAAA,MAAM,IAAA,GAAO,IAAA,CAAK,IAAA,CAAK,GAAA,CAAI,QAAkB,CAAA;AAC7C,MAAA,IAAI,IAAA,EAAM;AACR,QAAA,IAAA,CAAK,IAAA,CAAK,OAAO,QAAkB,CAAA;AACnC,QAAA,IAAA,CAAK,EAAE,GAAA,EAAI;AAAA,MACb,CAAA,MAAO;AACL,QAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,IAAA,CAAK,QAAQ,CAAA;AACzC,QAAA,IAAI,UAAU,MAAM,IAAA,CAAK,GAAG,OAAA,CAAQ,UAAA,CAAW,SAAS,GAAG,CAAA,EAAG,GAAA,CAAI,MAAA,CAAO,KAAK,SAAA,CAAU,EAAE,OAAO,QAAA,EAAU,CAAC,CAAC,CAAA;AAAA,MAC/G;AAAA,IACF,CAAA,MAAA,IAAW,YAAY,QAAA,EAAU;AAE/B,MAAA,IAAA,CAAK,WAAA,CAAY,UAAU,MAAM,CAAA;AAAA,IACnC;AACA,IAAA,OAAO,OAAA;AAAA,EACT;AAAA,EAEA,MAAM,eAAA,CAAgB,QAAA,EAAiB,cAAA,EAA0B,WAAA,EAAyD;AACxH,IAAA,MAAM,IAAA,CAAK,GAAA;AAAA,MACT,QAAA;AAAA,MACA,CAAC,GAAA,KAAQ;AACP,QAAA,IAAI,IAAI,MAAA,KAAW,SAAA,EAAW,OAAO,EAAE,QAAQ,MAAA,EAAU;AACzD,QAAA,MAAM,KAAA,GAAQ,CAAC,mBAAG,IAAI,IAAI,CAAC,GAAI,GAAA,CAAI,QAAA,CAAS,kBAAkB,EAAC,EAAI,GAAG,cAAc,CAAC,CAAC,CAAA;AACtF,QAAA,MAAM,UAAU,gBAAA,CAAiB,GAAA,CAAI,QAAA,CAAS,WAAA,EAAa,aAAa,KAAK,CAAA;AAC7E,QAAA,MAAM,WAAW,EAAE,GAAG,GAAA,CAAI,QAAA,EAAU,gBAAgB,KAAA,EAAM;AAC1D,QAAA,IAAI,OAAO,IAAA,CAAK,OAAO,EAAE,MAAA,GAAS,CAAA,WAAY,WAAA,GAAc,OAAA;AAAA,oBAC/C,QAAA,CAAuC,WAAA;AACpD,QAAA,MAAM,OAAA,GAAU,EAAE,GAAG,GAAA,EAAK,QAAA,EAAS;AAEnC,QAAA,OAAO,EAAE,QAAA,EAAU,EAAE,GAAA,EAAK,OAAA,EAAS,cAAA,EAAA,iBAAgB,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY,EAAE,EAAG,QAAQ,MAAA,EAAU;AAAA,MACnG,CAAA;AAAA,MACA;AAAA,KACF;AAAA,EACF;AAAA,EAEA,MAAM,cAAA,CAAe,QAAA,EAAiB,QAAA,EAAkD;AACtF,IAAA,MAAM,IAAA,GAAO,IAAA,CAAK,iBAAA,CAAkB,GAAA,CAAI,QAAkB,CAAA,IAAK,CAAA;AAC/D,IAAA,IAAI,IAAA,CAAK,GAAA,EAAI,GAAI,IAAA,GAAOC,+BAAAA,EAAgC;AACxD,IAAA,IAAA,CAAK,iBAAA,CAAkB,GAAA,CAAI,QAAA,EAAoB,IAAA,CAAK,KAAK,CAAA;AAEzD,IAAA,IAAA,CAAK,IAAA,CAAK,GAAA,CAAI,QAAkB,CAAA,EAAG,EAAE,OAAA,EAAQ;AAC7C,IAAA,MAAM,IAAA,CAAK,GAAA;AAAA,MACT,QAAA;AAAA,MACA,CAAC,GAAA,KAAQ;AACP,QAAA,IAAI,IAAI,MAAA,KAAW,SAAA,EAAW,OAAO,EAAE,QAAQ,MAAA,EAAU;AACzD,QAAA,MAAM,OAAA,GAAgC;AAAA,UACpC,MAAA,EAAQ,SAAA;AAAA,UACR,UAAU,GAAA,CAAI,QAAA;AAAA,UACd,QAAQ,GAAA,CAAI,MAAA;AAAA,UACZ,WAAW,GAAA,CAAI,SAAA;AAAA,UACf;AAAA,SACF;AACA,QAAA,OAAO,EAAE,QAAA,EAAU,EAAE,GAAA,EAAK,OAAA,EAAS,cAAA,EAAA,iBAAgB,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY,EAAE,EAAG,QAAQ,MAAA,EAAU;AAAA,MACnG,CAAA;AAAA,MACA;AAAA,KACF;AAAA,EACF;AAAA,EAEA,MAAM,kBAAkB,QAAA,EAAwD;AAC9E,IAAA,MAAM,OAAA,GAAU,QAAA,KAAa,YAAA,GACzB,CAAC,IAAA,KAAiB,IAAA,KAAS,YAAA,GAC3B,CAAC,IAAA,KAAiB,IAAA,CAAK,QAAA,CAAS,aAAa,CAAA;AAEjD,IAAA,IAAI,SAAA,GAAY,CAAA;AAChB,IAAA,KAAA,MAAW,GAAA,IAAO,MAAM,IAAA,CAAK,OAAA,EAAQ,EAAG;AACtC,MAAA,MAAM,WAAW,MAAM,IAAA,CAAK,IAAA,CAAKD,KAAAA,CAAQ,GAAG,CAAC,CAAA;AAC7C,MAAA,IAAI,CAAC,QAAA,IAAY,QAAA,CAAS,GAAA,CAAI,MAAA,KAAW,SAAA,IAAa,CAAC,OAAA,CAAQ,QAAA,CAAS,GAAA,CAAI,QAAA,CAAS,IAAI,CAAA,EAAG;AAC5F,MAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,GAAA;AAAA,QACtBA,MAAQ,GAAG,CAAA;AAAA,QACX,CAAC,GAAA,KAAQ;AACP,UAAA,IAAI,GAAA,CAAI,MAAA,KAAW,SAAA,IAAa,CAAC,OAAA,CAAQ,GAAA,CAAI,QAAA,CAAS,IAAI,CAAA,EAAG,OAAO,EAAE,MAAA,EAAQ,KAAA,EAAM;AACpF,UAAA,MAAM,MAAA,GAA4B;AAAA,YAChC,MAAA,EAAQ,WAAA;AAAA,YACR,UAAU,GAAA,CAAI,QAAA;AAAA,YACd,QAAQ,GAAA,CAAI,MAAA;AAAA,YACZ,SAAA,EAAW,MAAA;AAAA,YACX,WAAA,EAAA,iBAAa,IAAI,IAAA,EAAK,EAAE,WAAA;AAAY,WACtC;AACA,UAAA,OAAO,EAAE,QAAA,EAAU,EAAE,GAAA,EAAK,MAAA,EAAQ,cAAA,EAAA,iBAAgB,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY,EAAE,EAAG,QAAQ,IAAA,EAAK;AAAA,QAC7F,CAAA;AAAA,QACA;AAAA,OACF;AACA,MAAA,IAAI,IAAA,EAAM;AACR,QAAA,SAAA,EAAA;AACA,QAAA,IAAA,CAAK,WAAA,CAAYA,KAAAA,CAAQ,GAAG,CAAA,EAAG,MAAM,CAAA;AAAA,MACvC;AAAA,IACF;AAEA,IAAA,MAAM,IAAA,CAAK,GAAA,CAAI,OAAA,CAAQ,KAAA,CAAM,MAAA,EAAQ,EAAE,MAAA,EAAQ,CAAA,KAAA,EAAQ,QAAQ,CAAA,EAAA,CAAA,EAAM,CAAA;AACrE,IAAA,OAAO,SAAA;AAAA,EACT;AAAA,EAEA,MAAM,UAAU,QAAA,EAAmC;AACjD,IAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,GAAA;AAAA,MACtB,QAAA;AAAA,MACA,CAAC,GAAA,KAAQ;AACP,QAAA,IAAI,GAAA,CAAI,WAAW,SAAA,IAAa,GAAA,CAAI,WAAW,SAAA,EAAW,OAAO,EAAE,MAAA,EAAQ,KAAA,EAAM;AACjF,QAAA,MAAM,MAAA,GAA4B;AAAA,UAChC,MAAA,EAAQ,WAAA;AAAA,UACR,UAAU,GAAA,CAAI,QAAA;AAAA,UACd,QAAQ,GAAA,CAAI,MAAA;AAAA,UACZ,SAAA,EAAW,GAAA,CAAI,MAAA,KAAW,SAAA,GAAY,IAAI,SAAA,GAAY,MAAA;AAAA,UACtD,WAAA,EAAA,iBAAa,IAAI,IAAA,EAAK,EAAE,WAAA;AAAY,SACtC;AACA,QAAA,OAAO,EAAE,QAAA,EAAU,EAAE,GAAA,EAAK,MAAA,EAAQ,cAAA,EAAA,iBAAgB,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY,EAAE,EAAG,QAAQ,IAAA,EAAK;AAAA,MAC7F,CAAA;AAAA,MACA;AAAA,KACF;AACA,IAAA,IAAI,IAAA,EAAM,IAAA,CAAK,WAAA,CAAY,QAAA,EAAU,MAAM,CAAA;AAC3C,IAAA,OAAO,IAAA;AAAA,EACT;AAAA,EAEA,MAAM,QAAA,GAA+G;AACnH,IAAA,MAAM,KAAA,GAAQ,EAAE,OAAA,EAAS,CAAA,EAAG,OAAA,EAAS,CAAA,EAAG,QAAA,EAAU,CAAA,EAAG,MAAA,EAAQ,CAAA,EAAG,SAAA,EAAW,CAAA,EAAE;AAC7E,IAAA,KAAA,MAAW,GAAA,IAAO,MAAM,IAAA,CAAK,OAAA,EAAQ,EAAG;AACtC,MAAA,MAAM,WAAW,MAAM,IAAA,CAAK,IAAA,CAAKA,KAAAA,CAAQ,GAAG,CAAC,CAAA;AAC7C,MAAA,IAAI,QAAA,EAAU,KAAA,CAAM,QAAA,CAAS,GAAA,CAAI,MAAM,CAAA,EAAA;AAAA,IACzC;AACA,IAAA,OAAO,KAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,uBAAA,GAA2C;AAC/C,IAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AACrB,IAAA,IAAI,SAAA,GAAY,CAAA;AAChB,IAAA,KAAA,MAAW,GAAA,IAAO,MAAM,IAAA,CAAK,OAAA,EAAQ,EAAG;AACtC,MAAA,MAAM,WAAW,MAAM,IAAA,CAAK,IAAA,CAAKA,KAAAA,CAAQ,GAAG,CAAC,CAAA;AAC7C,MAAA,IAAI,CAAC,QAAA,IAAY,QAAA,CAAS,GAAA,CAAI,WAAW,SAAA,EAAW;AACpD,MAAA,IAAI,MAAM,IAAA,CAAK,KAAA,CAAM,SAAS,cAAc,CAAA,GAAI,KAAK,cAAA,EAAgB;AACrE,MAAA,MAAM,OAAA,GAAU,MAAM,IAAA,CAAK,OAAA;AAAA,QACzBA,MAAQ,GAAG,CAAA;AAAA,QACX,CAAA,+CAAA,EAA6C,IAAA,CAAK,cAAA,GAAiB,GAAM,CAAA,QAAA;AAAA,OAC3E;AACA,MAAA,IAAI,OAAA,EAAS;AACX,QAAA,IAAA,CAAK,OAAO,IAAA,CAAK,6BAAA,EAA+B,EAAE,KAAA,EAAO,GAAA,EAAK,SAAS,CAAA;AACvE,QAAA,SAAA,EAAA;AAAA,MACF;AAAA,IACF;AACA,IAAA,OAAO,SAAA;AAAA,EACT;AAAA;AAAA,EAGA,MAAc,aAAA,GAA+B;AAC3C,IAAA,KAAA,MAAW,CAAC,IAAI,IAAI,CAAA,IAAK,CAAC,GAAG,IAAA,CAAK,IAAI,CAAA,EAAG;AACvC,MAAA,MAAM,WAAW,MAAM,IAAA,CAAK,IAAA,CAAKA,KAAAA,CAAQ,EAAE,CAAC,CAAA;AAC5C,MAAA,MAAM,MAAA,GAAS,UAAU,GAAA,CAAI,MAAA;AAC7B,MAAA,IAAI,CAAC,QAAA,IAAY,MAAA,KAAW,cAAc,MAAA,KAAW,QAAA,IAAY,WAAW,WAAA,EAAa;AACvF,QAAA,IAAA,CAAK,IAAA,CAAK,OAAO,EAAE,CAAA;AACnB,QAAA,IAAA,CAAK,EAAE,GAAA,EAAI;AAAA,MACb,CAAA,MAAO;AACL,QAAA,IAAA,CAAK,EAAE,OAAA,EAAQ;AAAA,MACjB;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,OAAA,GAA6B;AACzC,IAAA,MAAM,OAAiB,EAAC;AACxB,IAAA,WAAA,MAAiB,GAAA,IAAO,MAAM,IAAA,CAAK,EAAA,CAAG,MAAK,EAAG,IAAA,CAAK,KAAK,GAAG,CAAA;AAC3D,IAAA,OAAO,IAAA;AAAA,EACT;AAAA;AAAA,EAGQ,WAAA,CAAY,UAAiB,GAAA,EAA2B;AAC9D,IAAA,MAAM,IAAA,GAAO,IAAA,CAAK,IAAA,CAAK,GAAA,CAAI,QAAkB,CAAA;AAC7C,IAAA,IAAI,CAAC,IAAA,EAAM;AACX,IAAA,IAAA,CAAK,IAAA,CAAK,OAAO,QAAkB,CAAA;AACnC,IAAA,IAAI,GAAA,KAAQ,KAAA,EAAO,IAAA,CAAK,CAAA,CAAE,GAAA,EAAI;AAAA,SACzB,IAAA,CAAK,EAAE,IAAA,EAAK;AAAA,EACnB;AACF;;;ACrPO,SAAS,aAAa,GAAA,EAAqC;AAChE,EAAA,OAAO,IAAI,MAAA,KAAW,SAAA;AACxB;AAEO,SAAS,aAAa,GAAA,EAA0C;AACrE,EAAA,OAAO,IAAI,MAAA,KAAW,SAAA;AACxB;AAEO,SAAS,cAAc,GAAA,EAA2C;AACvE,EAAA,OAAO,IAAI,MAAA,KAAW,UAAA;AACxB;AAEO,SAAS,YAAY,GAAA,EAAoC;AAC9D,EAAA,OAAO,IAAI,MAAA,KAAW,QAAA;AACxB;AAEO,SAAS,eAAe,GAAA,EAAuC;AACpE,EAAA,OAAO,IAAI,MAAA,KAAW,WAAA;AACxB;AClWO,IAAM,uBAAuB,EAAA,GAAK,GAAA;AAOlC,IAAM,qBAAA,GAAN,cAAoC,KAAA,CAAM;AAAA,EAC7B,IAAA,GAAO,uBAAA;AAC3B,CAAA;AAgBO,IAAM,sBAAA,GAAyB,IAAA;AAiBtC,SAAS,OAAA,CAAW,MAAA,EAAyB,IAAA,EAAc,SAAA,EAAmB,IAAA,EAAoC;AAChH,EAAA,OAAO,QAAA,CAAS,CAAA,UAAA,EAAa,IAAI,CAAA,CAAA,EAAI,IAAA,EAAM;AAAA,IACzC,KAAA,EAAO;AAAA,MACL,sBAAsB,MAAA,CAAO,IAAA;AAAA,MAC7B,mBAAmB,MAAA,CAAO,OAAA;AAAA,MAC1B,sBAAA,EAAwB;AAAA;AAC1B,GACD,CAAA;AACH;AAEA,eAAe,WAAA,CACb,IAAA,EACA,IAAA,EACA,WAAA,EACA,MAAA,EACY;AACZ,EAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,EAAA,IAAI,KAAA;AACJ,EAAA,MAAM,QAAA,GAAW,IAAI,OAAA,CAAe,CAAC,GAAG,MAAA,KAAW;AACjD,IAAA,KAAA,GAAQ,WAAW,MAAM;AAKvB,MAAA,MAAA,EAAQ,KAAK,wDAAA,EAA0D;AAAA,QACrE,UAAU,IAAA,CAAK,QAAA;AAAA,QACf,OAAO,IAAA,CAAK,KAAA;AAAA,QACZ,OAAO,IAAA,CAAK,KAAA;AAAA,QACZ,OAAA,EAAS;AAAA,OACV,CAAA;AACD,MAAA,UAAA,CAAW,KAAA,EAAM;AACjB,MAAA,MAAA,CAAO,IAAI,qBAAA;AAAA,QACT,CAAA,+BAAA,EAAkC,oBAAA,GAAuB,GAAM,CAAA,UAAA,EAAa,KAAK,KAAK,CAAA,oDAAA;AAAA,OACvF,CAAA;AAAA,IACH,GAAG,oBAAoB,CAAA;AACvB,IAAA,KAAA,CAAM,KAAA,IAAQ;AAAA,EAChB,CAAC,CAAA;AAKD,EAAA,IAAI,SAAA;AACJ,EAAA,IAAI,WAAA,EAAa;AACf,IAAA,SAAA,GAAY,YAAY,MAAM;AAC5B,MAAA,IAAI;AACF,QAAA,WAAA,EAAY;AAAA,MACd,CAAA,CAAA,MAAQ;AAAA,MAGR;AAAA,IACF,GAAG,sBAAsB,CAAA;AACzB,IAAA,SAAA,CAAU,KAAA,IAAQ;AAAA,EACpB;AAEA,EAAA,MAAM,OAAA,GAAU,IAAA,CAAK,UAAA,CAAW,MAAM,CAAA;AACtC,EAAA,IAAI;AACF,IAAA,OAAO,MAAM,OAAA,CAAQ,IAAA,CAAK,CAAC,OAAA,EAAS,QAAQ,CAAC,CAAA;AAAA,EAC/C,SAAS,GAAA,EAAK;AAKZ,IAAA,OAAA,CAAQ,MAAM,MAAM;AAAA,IAAC,CAAC,CAAA;AACtB,IAAA,MAAM,GAAA;AAAA,EACR,CAAA,SAAE;AACA,IAAA,YAAA,CAAa,KAAK,CAAA;AAGlB,IAAA,IAAI,SAAA,gBAAyB,SAAS,CAAA;AAAA,EACxC;AACF;AAEO,SAAS,4BACd,MAAA,EACA,MAAA,EACA,SAAA,EACA,WAAA,EACA,aACA,MAAA,EAC4B;AAC5B,EAAA,OAAO,OAAA,CAAQ,MAAA,EAAQ,MAAA,EAAQ,SAAA,EAAW,MAAM,WAAA;AAAA,IAC9C,CAAC,MAAA,KAAW,MAAA,CAAO,yBAAyB,MAAA,EAAQ,SAAA,EAAW,aAAa,MAAM,CAAA;AAAA,IAClF,EAAE,QAAA,EAAU,MAAA,CAAO,IAAA,EAAM,OAAO,MAAA,CAAO,OAAA,EAAS,KAAA,EAAO,CAAA,EAAG,MAAA,CAAO,IAAI,CAAA,CAAA,EAAI,MAAA,CAAO,OAAO,CAAA,CAAA,EAAG;AAAA,IAC1F,WAAA;AAAA,IACA;AAAA,GACD,CAAA;AACH;AAEO,SAAS,0BACd,MAAA,EACA,MAAA,EACA,WACA,WAAA,EACA,aAAA,EACA,aACA,MAAA,EACgC;AAChC,EAAA,OAAO,OAAA,CAAQ,MAAA,EAAQ,YAAA,EAAc,SAAA,EAAW,MAAM,WAAA;AAAA,IACpD,CAAC,WAAW,MAAA,CAAO,kBAAA,CAAsB,QAAQ,SAAA,EAAW,WAAA,EAAa,eAAe,MAAM,CAAA;AAAA,IAC9F,EAAE,QAAA,EAAU,MAAA,CAAO,IAAA,EAAM,OAAO,MAAA,CAAO,OAAA,EAAS,KAAA,EAAO,CAAA,EAAG,MAAA,CAAO,IAAI,CAAA,CAAA,EAAI,MAAA,CAAO,OAAO,CAAA,CAAA,EAAG;AAAA,IAC1F,WAAA;AAAA,IACA;AAAA,GACD,CAAA;AACH;ACrJO,IAAM,qBAAA,GAAN,cAAoC,KAAA,CAAM;AAAA;AAAA;AAAA,EAG7B,IAAA,GAAe,uBAAA;AACnC,CAAA;;;AC6DO,IAAM,2BAAA,GAAiD;AAAA,EAC5D,SAAA,EAAW,GAAA;AAAA,EACX,WAAA,EAAa,GAAA;AAAA,EACb,UAAA,EAAY,GAAA;AAAA,EACZ,YAAA,EAAc;AAChB,CAAA;AAEA,SAAS,KAAA,CAAM,KAAA,EAAe,EAAA,EAAY,EAAA,EAAoB;AAC5D,EAAA,OAAO,KAAK,GAAA,CAAI,EAAA,EAAI,KAAK,GAAA,CAAI,EAAA,EAAI,KAAK,CAAC,CAAA;AACzC;AAUO,SAAS,aAAA,CACd,OAAA,EACA,OAAA,EACA,MAAA,EACA,SAA4B,2BAAA,EACpB;AACR,EAAA,MAAM,IAAA,GAAO,MAAM,KAAA,CAAM,IAAA,CAAK,KAAA,CAAM,OAAA,GAAU,MAAA,CAAO,UAAU,CAAA,EAAG,MAAA,CAAO,KAAA,EAAO,MAAA,CAAO,OAAO,CAAA;AAC9F,EAAA,MAAM,MAAA,GAAS,MAAM,KAAA,CAAM,IAAA,CAAK,KAAA,CAAM,OAAA,GAAU,MAAA,CAAO,YAAY,CAAA,EAAG,MAAA,CAAO,KAAA,EAAO,MAAA,CAAO,OAAO,CAAA;AAClG,EAAA,MAAM,OAAO,MAAM,KAAA,CAAM,SAAS,MAAA,CAAO,KAAA,EAAO,OAAO,OAAO,CAAA;AAE9D,EAAA,IAAI,OAAA,CAAQ,SAAA,EAAW,OAAO,MAAA,EAAO;AAGrC,EAAA,IAAI,OAAA,CAAQ,YAAA,KAAiB,MAAA,EAAW,OAAO,IAAA,EAAK;AAGpD,EAAA,IAAI,MAAA,CAAO,YAAA,IAAgB,CAAA,EAAG,OAAO,IAAA,EAAK;AAE1C,EAAA,MAAM,WAAA,GAAc,OAAA,CAAQ,YAAA,GAAe,MAAA,CAAO,YAAA;AAClD,EAAA,IAAI,WAAA,GAAc,MAAA,CAAO,SAAA,EAAW,OAAO,IAAA,EAAK;AAChD,EAAA,IAAI,WAAA,GAAc,MAAA,CAAO,WAAA,EAAa,OAAO,MAAA,EAAO;AACpD,EAAA,OAAO,IAAA,EAAK;AACd;;;AC3FO,SAAS,kBAAA,CAAmB,QAAA,EAAkC,KAAA,EAAe,EAAA,EAAY,YAAoB,YAAA,EAA4B;AAC9I,EAAA,IAAI,QAAA,CAAS,eAAe,YAAA,EAAc;AACxC,IAAA,MAAM,IAAI,qBAAA,CAAsB,CAAA,EAAG,KAAK,CAAA,8CAAA,EAAiD,EAAE,CAAA,IAAA,EAAO,UAAU,CAAA,sCAAA,EAAyC,YAAY,CAAA,uEAAA,CAAoE,CAAA;AAAA,EACvO;AACF;AAQA,IAAM,sBAAA,GAAyB,EAAA;AAC/B,IAAM,aAAA,GACJ,sBAAA;AACA,sBAAA;AACA,CAAA,GAAI,sBAAA;AAEN,IAAM,cAAA,GAAiB,IAAA,CAAK,IAAA,CAAK,aAAA,GAAgB,CAAC,CAAA;AAQ3C,IAAM,qBAAA,GAAwB,CAAA;AAmB9B,IAAM,8BAAA,GAAiC,KAAA;AAWvC,IAAM,mBAAA,GAAsB,CAAA;AA0B5B,IAAM,kBAAA,GAAN,cAAiC,qBAAA,CAAsB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQ5D,WAAA,CAAY,OAAA,EAA0B,OAAA,EAA6B,OAAA,EAA6B;AAC9F,IAAA,KAAA,CAAM,OAAO,CAAA;AADuB,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA;AAA6B,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA;AAAA,EAEnE;AAAA,EAFsC,OAAA;AAAA,EAA6B,OAAA;AAAA,EAPjD,IAAA,GAAO,oBAAA;AAU3B,CAAA;AA4BO,SAAS,qBAAA,CACd,MAAA,EACA,cAAA,EACA,YAAA,EACiB;AACjB,EAAA,MAAM,EAAE,aAAA,EAAe,eAAA,EAAgB,GAAI,MAAA;AAC3C,EAAA,MAAM,YAAY,aAAA,GAAgB,cAAA;AAElC,EAAA,IAAI,WAAA;AACJ,EAAA,IAAI,YAAA;AAEJ,EAAA,IAAI,mBAAmB,aAAA,EAAe;AAEpC,IAAA,WAAA,GAAc,IAAA,CAAK,KAAA,CAAM,SAAA,GAAY,CAAC,CAAA;AACtC,IAAA,YAAA,GAAe,SAAA,GAAY,WAAA;AAAA,EAC7B,CAAA,MAAO;AACL,IAAA,YAAA,GAAe,eAAA;AACf,IAAA,WAAA,GAAc,gBAAgB,YAAA,GAAe,cAAA;AAC7C,IAAA,IAAI,eAAe,CAAA,EAAG;AAGpB,MAAA,WAAA,GAAc,IAAA,CAAK,KAAA,CAAM,SAAA,GAAY,CAAC,CAAA;AACtC,MAAA,YAAA,GAAe,SAAA,GAAY,WAAA;AAAA,IAC7B;AAAA,EACF;AAiBA,EAAA,MAAM,mBAAA,GAAsB,OAAO,mBAAA,IAAuB,8BAAA;AAC1D,EAAA;AAKE,IAAA,MAAM,qBAAqB,IAAA,CAAK,KAAA,CAAM,mBAAA,IAAuB,oBAAA,GAAuB,KAAK,IAAS,CAAA;AAClG,IAAA,IAAI,eAAe,kBAAA,EAAoB;AACrC,MAAA,WAAA,GAAc,IAAA,CAAK,KAAA,CAAM,WAAA,IAAe,kBAAA,GAAqB,YAAA,CAAa,CAAA;AAC1E,MAAA,YAAA,GAAe,kBAAA;AAAA,IACjB;AAAA,EACF;AAuBA,EAAA,MAAM,aAAA,GAAgB,gBAAgB,cAAA,GAAiB,YAAA;AAEvD,EAAA,WAAA,GAAc,IAAA,CAAK,IAAI,WAAA,EAAa,IAAA,CAAK,MAAM,YAAA,IAAgB,CAAA,GAAI,aAAa,CAAC,CAAA;AAEjF,EAAA,IAAI,eAAe,cAAA,EAAgB;AACjC,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,qDAAqD,aAAa,CAAA,uBAAA,EAA0B,cAAc,CAAA,2BAAA,EAA8B,WAAW,YAAY,cAAc,CAAA,0EAAA;AAAA,KAC/K;AAAA,EACF;AAEA,EAAA,OAAO;AAAA,IACL,QAAA,EAAU,EAAE,SAAA,EAAW,WAAA,EAAa,SAAS,cAAA,EAAe;AAAA,IAC5D,YAAA;AAAA,IACA,MAAA,EAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAMN,KAAA,EAAO,IAAA,CAAK,GAAA,CAAI,WAAA,EAAa,IAAI,cAAc,CAAA;AAAA;AAAA;AAAA;AAAA,MAI/C,OAAA,EAAS,IAAA,CAAK,GAAA,CAAI,WAAA,EAAa,aAAa,CAAA;AAAA,MAC5C;AAAA;AACF,GACF;AACF;AAoDA,eAAsB,iBAAA,CACpB,IAAA,EACA,MAAA,EACA,OAAA,EACA,MAAA,EACe;AACf,EAAA,IAAI,EAAA,GAAK,QAAQ,IAAA,IAAQ,CAAA;AACzB,EAAA,IAAI,IAAA,GAAO,MAAA,GACP,aAAA,CAAc,EAAE,SAAA,EAAW,IAAA,EAAK,EAAG,MAAA,CAAO,IAAA,EAAM,MAAA,CAAO,MAAM,CAAA,GAC7D,OAAO,QAAA,CAAS,SAAA;AAEpB,EAAA,OAAO,EAAA,GAAK,KAAK,MAAA,EAAQ;AACvB,IAAA,MAAM,EAAE,KAAA,EAAO,IAAA,EAAK,GAAI,SAAS,IAAA,EAAM,EAAA,EAAI,EAAE,SAAA,EAAW,IAAA,EAAM,OAAA,EAAS,MAAA,CAAO,QAAA,CAAS,SAAS,CAAA;AAChG,IAAA,MAAM,OAAA,GAAU,MAAM,OAAA,CAAQ,EAAE,KAAA,EAAO,IAAA,EAAM,EAAA,EAAI,IAAA,EAAM,UAAA,EAAY,IAAA,CAAK,MAAA,EAAQ,CAAA;AAChF,IAAA,EAAA,GAAK,IAAA;AACL,IAAA,IAAA,GAAO,aAAA,CAAc,OAAA,EAAS,IAAA,EAAM,MAAA,CAAO,MAAM,CAAA;AAAA,EACnD;AACF;AAQO,IAAM,qBAAA,GAAwB,CAAA;AAarC,SAAS,kBAAkB,KAAA,EAAyB;AAClD,EAAA,OAAO,KAAA,YAAiBE,mBAAAA,IAAuB,KAAA,CAAM,UAAA,KAAe,SAAA;AACtE;AAsBA,SAAS,2BAA2B,KAAA,EAAyB;AAC3D,EAAA,OAAO,KAAA,YAAiBA,mBAAAA,IAAuB,KAAA,CAAM,UAAA,KAAe,UAAA;AACtE;AAEA,SAAS,aAAa,KAAA,EAAyB;AAC7C,EAAA,OAAO,KAAA,YAAiB,yBAAyB,UAAA,CAAW,KAAK,KAC5D,iBAAA,CAAkB,KAAK,CAAA,IAAK,0BAAA,CAA2B,KAAK,CAAA;AACnE;AAKA,SAAS,WAAW,KAAA,EAAyB;AAC3C,EAAA,OACE,KAAA,YAAiB,qBAAA,IAChB,KAAA,YAAiBA,mBAAAA,IAAuB,MAAM,UAAA,KAAe,YAAA;AAElE;AA8BA,SAAS,UAAU,KAAA,EAAiE;AAIlF,EAAA,IAAI,KAAA,YAAiB,oBAAoB,OAAO,WAAA;AAChD,EAAA,IAAI,UAAA,CAAW,KAAK,CAAA,EAAG,OAAO,WAAA;AAC9B,EAAA,IAAI,KAAA,YAAiB,uBAAuB,OAAO,SAAA;AACnD,EAAA,OAAO,OAAA;AACT;AAEA,eAAsB,qBACpB,KAAA,EACA,KAAA,EACA,UACA,IAAA,EACA,MAAA,EAGA,eAKA,SAAA,EAC4B;AAI5B,EAAA,IAAI,YAAA,GAAe,CAAA;AACnB,EAAA,IAAI,UAAA,GAAa,KAAA;AAIjB,EAAA,IAAI,SAAA,GAAY,CAAA;AAChB,EAAA,IAAI,aAAA,GAAgB,CAAA;AAIpB,EAAA,eAAe,QAAA,CAAS,KAAA,EAAe,KAAA,EAAe,MAAA,EAA8C;AAClG,IAAA,MAAM,KAAA,GAAQ,YAAY,GAAA,EAAI;AAC9B,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,KAAK,CAAA;AAC/B,MAAA,SAAA,IAAa,CAAA;AACb,MAAA,IAAI,OAAO,KAAA,EAAO;AAChB,QAAA,aAAA,IAAiB,CAAA;AACjB,QAAA,YAAA,IAAgB,OAAO,KAAA,CAAM,YAAA;AAAA,MAC/B;AACA,MAAA,mBAAA,CAAoB;AAAA,QAClB,KAAA;AAAA,QAAO,YAAY,KAAA,CAAM,MAAA;AAAA,QAAQ,UAAA,EAAY,WAAA,CAAY,GAAA,EAAI,GAAI,KAAA;AAAA,QACjE,KAAA,EAAO,OAAO,KAAA,CAAM,MAAA;AAAA,QAAQ,KAAA;AAAA,QAAO,MAAA;AAAA,QAAQ,OAAA,EAAS,SAAA;AAAA,QACpD,GAAI,MAAA,CAAO,KAAA,GAAQ,EAAE,WAAA,EAAa,MAAA,CAAO,KAAA,CAAM,WAAA,EAAa,YAAA,EAAc,MAAA,CAAO,KAAA,CAAM,YAAA,KAAiB;AAAC,OAC1G,CAAA;AAED,MAAA,IAAI,MAAA,CAAO,OAAA,KAAY,KAAA,CAAA,EAAW,SAAA,GAAY,OAAO,OAAO,CAAA;AAC5D,MAAA,OAAO,MAAA;AAAA,IACT,SAAS,KAAA,EAAO;AAGd,MAAA,mBAAA,CAAoB;AAAA,QAClB,KAAA;AAAA,QAAO,YAAY,KAAA,CAAM,MAAA;AAAA,QAAQ,UAAA,EAAY,WAAA,CAAY,GAAA,EAAI,GAAI,KAAA;AAAA,QACjE,KAAA,EAAO,CAAA;AAAA,QAAG,KAAA;AAAA,QAAO,MAAA;AAAA,QAAQ,OAAA,EAAS,UAAU,KAAK;AAAA,OAClD,CAAA;AACD,MAAA,MAAM,KAAA;AAAA,IACR;AAAA,EACF;AAEA,EAAA,eAAe,OAAA,CAAQ,KAAA,EAAe,SAAA,EAAmB,KAAA,EAA6B;AACpF,IAAA,IAAI;AACF,MAAA,OAAA,CAAQ,MAAM,QAAA,CAAS,KAAA,EAAO,KAAA,EAAO,KAAK,CAAA,EAAG,KAAA;AAAA,IAC/C,SAAS,KAAA,EAAO;AACd,MAAA,IAAI,CAAC,YAAA,CAAa,KAAK,CAAA,EAAG,MAAM,KAAA;AAIhC,MAAA,UAAA,GAAa,IAAA;AACb,MAAA,MAAM,IAAA,GAAO,IAAA,CAAK,KAAA,CAAM,SAAA,GAAY,CAAC,CAAA;AAcrC,MAAA,MAAM,MAAA,GAAS,UAAU,KAAA,EAAO,EAAE,WAAW,IAAA,EAAM,OAAA,EAAS,QAAA,CAAS,OAAA,EAAS,CAAA;AAC9E,MAAA,MAAM,UAAU,MAAA,CAAO,MAAA,GAAS,CAAA,IAAK,MAAA,CAAO,CAAC,CAAA,KAAM,KAAA;AAInD,MAAA,MAAM,UAAA,GAAa,OAAA,KAAY,UAAA,CAAW,KAAK,CAAA,IAAK,iBAAA,CAAkB,KAAK,CAAA,GACvE,IAAA,GAAO,CAAA,GAAI,cAAA,GACX,KAAA,GAAQ,qBAAA,CAAA;AACZ,MAAA,IAAI,CAAC,UAAA,EAAY;AAKf,QAAA,IAAI,iBAAiB,kBAAA,EAAoB;AACvC,UAAA,MAAA,EAAQ,KAAK,wGAAA,EAAqG;AAAA,YAChH,YAAY,KAAA,CAAM,MAAA;AAAA,YAClB,QAAA,EAAU,MAAM,OAAA,CAAQ,MAAA;AAAA,YACxB,OAAO,KAAA,CAAM;AAAA,WACd,CAAA;AACD,UAAA,aAAA,GAAgB,MAAM,OAAO,CAAA;AAC7B,UAAA,SAAA,GAAY,KAAA,CAAM,QAAQ,OAAO,CAAA;AACjC,UAAA,OAAO,KAAA,CAAM,OAAA;AAAA,QACf;AAIA,QAAA,IAAI,CAAC,UAAA,CAAW,KAAK,CAAA,EAAG,MAAM,KAAA;AAC9B,QAAA,MAAA,EAAQ,KAAK,oEAAA,EAAiE;AAAA,UAC5E,YAAY,KAAA,CAAM,MAAA;AAAA,UAClB,OAAO,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU,OAAO,KAAK;AAAA,SAC7D,CAAA;AACD,QAAA,OAAA,CAAQ,MAAM,QAAA,CAAS,KAAA,EAAO,KAAA,EAAO,IAAI,CAAA,EAAG,KAAA;AAAA,MAC9C;AACA,MAAA,MAAA,EAAQ,KAAK,kFAAA,EAA+E;AAAA,QAC1F,OAAO,KAAA,GAAQ,CAAA;AAAA,QACf,YAAY,KAAA,CAAM,MAAA;AAAA,QAClB,mBAAA,EAAqB,IAAA;AAAA,QACrB,OAAO,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU,OAAO,KAAK;AAAA,OAC7D,CAAA;AACD,MAAA,MAAM,YAAiB,EAAC;AACxB,MAAA,KAAA,MAAW,KAAK,MAAA,EAAQ;AACtB,QAAA,SAAA,CAAU,IAAA,CAAK,GAAI,MAAM,OAAA,CAAQ,GAAG,IAAA,EAAM,KAAA,GAAQ,CAAC,CAAE,CAAA;AAAA,MACvD;AACA,MAAA,OAAO,SAAA;AAAA,IACT;AAAA,EACF;AACA,EAAA,MAAM,QAAQ,MAAM,OAAA,CAAQ,KAAA,EAAO,QAAA,CAAS,WAAW,CAAC,CAAA;AACxD,EAAA,MAAM,QAAA,GAAW,SAAA,GAAY,CAAA,IAAK,aAAA,KAAkB,SAAA;AACpD,EAAA,OAAO;AAAA,IACL,KAAA;AAAA,IACA,OAAA,EAAS,EAAE,SAAA,EAAW,UAAA,EAAY,GAAI,WAAW,EAAE,YAAA,EAAa,GAAI,EAAC;AAAG,GAC1E;AACF;AC3iBA,SAAS,aAAa,GAAA,EAAqB;AACzC,EAAA,OAAO,oBAAA,CAAqB,GAAG,CAAA,IAAK,GAAA;AACtC;AAGA,SAAS,uBAAuB,cAAA,EAAiC;AAC/D,EAAA,IAAI,CAAC,gBAAgB,OAAO,EAAA;AAC5B,EAAA,OAAO;;AAAA,sBAAA,EAA6B,YAAA,CAAa,cAAc,CAAC,CAAA,CAAA,CAAA;AAClE;AAGA,SAAS,oBAAA,CAAqB,UAA8B,IAAA,EAAsB;AAChF,EAAA,IAAI,CAAC,QAAA,IAAY,QAAA,KAAa,IAAA,EAAM,OAAO,EAAA;AAC3C,EAAA,OAAO;;AAAA,sBAAA,EAA6B,IAAI,CAAA,IAAA,EAAO,YAAA,CAAa,QAAQ,CAAC,CAAA,CAAA,CAAA;AACvE;AAEO,IAAM,oBAAN,MAAwB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAY7B,OAAO,kBAAA,CACL,OAAA,EACA,cACA,IAAA,EACA,OAAA,EACA,UACA,cAAA,EACQ;AACR,IAAA,IAAI,MAAA;AACJ,IAAA,MAAM,UAAA,GAAa,uBAAuB,cAAc,CAAA;AACxD,IAAA,MAAM,QAAA,GAAW,oBAAA,CAAqB,QAAA,EAAU,UAAU,CAAA;AAE1D,IAAA,IAAI,YAAA,EAAc;AAEhB,MAAA,MAAM,YAAA,GAAe,IAAA,GAAO,CAAA,OAAA,EAAU,IAAI,CAAA,MAAA,CAAA,GAAW,EAAA;AACrD,MAAA,MAAM,kBAAkB,OAAA,GACpB;;AAAA,sBAAA,EAA6B,OAAO,CAAA,iCAAA,CAAA,GACpC,EAAA;AAEJ,MAAA,MAAA,GAAS,CAAA;;AAAA,EAEb,YAAY,GAAG,YAAY,CAAA,EAAG,eAAe,CAAA,EAAG,UAAU,GAAG,QAAQ;;AAAA;AAAA;AAAA,EAIrE,OAAO;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA,CAAA,CAAA;AAAA,IAeL,CAAA,MAAO;AAEL,MAAA,MAAM,eAAe,IAAA,GACjB;;AAAA,YAAA,EAAmB,IAAI,CAAA,wBAAA,CAAA,GACvB,EAAA;AACJ,MAAA,MAAM,kBAAkB,OAAA,GACpB;AAAA,wBAAA,EAA6B,OAAO,CAAA,wBAAA,CAAA,GACpC;AAAA,+DAAA,CAAA;AAEJ,MAAA,MAAA,GAAS,CAAA;AAAA,+EAAA,EACkE,YAAY;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,iDAAA,EAO1C,eAAe,CAAA,EAAG,UAAU,CAAA,EAAG,QAAQ;;AAAA;AAAA;AAAA,EAIxF,OAAO;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA,CAAA,CAAA;AAAA,IAeL;AAEA,IAAA,OAAO,MAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,OAAO,oBAAA,CACL,OAAA,EACA,YAAA,EACA,SACA,cAAA,EACQ;AACR,IAAA,IAAI,MAAA;AACJ,IAAA,MAAM,UAAA,GAAa,uBAAuB,cAAc,CAAA;AAExD,IAAA,IAAI,YAAA,EAAc;AAEhB,MAAA,MAAM,kBAAkB,OAAA,GACpB;;AAAA,sBAAA,EAA6B,OAAO,CAAA,mCAAA,CAAA,GACpC,EAAA;AAEJ,MAAA,MAAA,GAAS,CAAA;;AAAA,EAEb,YAAY,CAAA,EAAG,eAAe,CAAA,EAAG,UAAU;;AAAA;AAAA;AAAA,EAI3C,OAAO;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA,CAAA,CAAA;AAAA,IAcL,CAAA,MAAO;AAEL,MAAA,MAAM,kBAAkB,OAAA,GACpB;AAAA,wBAAA,EAA6B,OAAO,CAAA,0BAAA,CAAA,GACpC;AAAA,sDAAA,CAAA;AAEJ,MAAA,MAAA,GAAS,CAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kCAAA,EASqB,eAAe,GAAG,UAAU;;AAAA;AAAA;AAAA,EAI9D,OAAO;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA,CAAA,CAAA;AAAA,IAcL;AAEA,IAAA,OAAO,MAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,OAAO,qBAAA,CACL,OAAA,EACA,cACA,IAAA,EACA,OAAA,EACA,UACA,cAAA,EACQ;AACR,IAAA,IAAI,MAAA;AACJ,IAAA,MAAM,UAAA,GAAa,uBAAuB,cAAc,CAAA;AACxD,IAAA,MAAM,QAAA,GAAW,oBAAA,CAAqB,QAAA,EAAU,aAAa,CAAA;AAE7D,IAAA,IAAI,YAAA,EAAc;AAEhB,MAAA,MAAM,YAAA,GAAe,IAAA,GAAO,CAAA,OAAA,EAAU,IAAI,CAAA,MAAA,CAAA,GAAW,EAAA;AACrD,MAAA,MAAM,kBAAkB,OAAA,GACpB;;AAAA,sBAAA,EAA6B,OAAO,CAAA,oCAAA,CAAA,GACpC,EAAA;AAEJ,MAAA,MAAA,GAAS,CAAA;;AAAA,EAEb,YAAY,GAAG,YAAY,CAAA,EAAG,eAAe,CAAA,EAAG,UAAU,GAAG,QAAQ;;AAAA;AAAA;AAAA,EAIrE,OAAO;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA,CAAA,CAAA;AAAA,IAeL,CAAA,MAAO;AAEL,MAAA,MAAM,eAAe,IAAA,GACjB;;AAAA,YAAA,EAAmB,IAAI,CAAA,2BAAA,CAAA,GACvB,EAAA;AACJ,MAAA,MAAM,kBAAkB,OAAA,GACpB;AAAA,wBAAA,EAA6B,OAAO,CAAA,2BAAA,CAAA,GACpC;AAAA,gEAAA,CAAA;AAEJ,MAAA,MAAA,GAAS,CAAA;AAAA,8EAAA,EACiE,YAAY;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oEAAA,EAOtB,eAAe,CAAA,EAAG,UAAU,CAAA,EAAG,QAAQ;;AAAA;AAAA;AAAA,EAI3G,OAAO;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA,CAAA,CAAA;AAAA,IAeL;AAEA,IAAA,OAAO,MAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,OAAO,eACL,OAAA,EACA,QAAA,EACA,YACA,iBAAA,EACA,YAAA,EACA,mBAAA,EACA,gBAAA,EACA,cAAA,EACQ;AAKR,IAAA,MAAM,UAAA,GAAa,uBAAuB,cAAc,CAAA;AAExD,IAAA,MAAM,MAAA,GAAS,sCAAsC,UAAU,CAAA;;AAAA,QAAA,EAEzD,iBAAiB;AAAA,QAAA,EACjB,YAAY;;AAAA,gEAAA,EAE4C,QAAQ,CAAA;;AAAA,UAAA,EAE9D,QAAQ;AAAA,aAAA,EACL,mBAAmB;AAAA;AAAA,EAEhC,gBAAA,CAAiB,IAAI,CAAA,EAAA,KAAM,CAAA,EAAA,EAAK,EAAE,CAAA,CAAE,CAAA,CAAE,IAAA,CAAK,IAAI,CAAC;;AAAA;AAAA;AAAA,wBAAA,EAIxB,QAAQ,CAAA;AAAA;AAAA;AAAA;AAAA,yEAAA,EAIyC,UAAU;;AAAA;AAAA;AAAA,EAInF,OAAO;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA,CAAA;AAgBL,IAAA,OAAO,MAAA;AAAA,EACT;AACF,CAAA;ACjWA,IAAM,2BAAgC,IAAI,GAAA,CAAkB,CAAC,eAAA,EAAiB,aAAa,CAAC,CAAA;AAUrF,SAAS,UAAA,CAAW,KAAA,EAAe,KAAA,EAAe,MAAA,EAAsB,MAAA,EAAuB;AACpG,EAAA,mBAAA,CAAoB,OAAO,MAAM,CAAA;AACjC,EAAA,IAAI,CAAC,QAAA,CAAS,GAAA,CAAI,MAAM,CAAA,EAAG;AAC3B,EAAA,MAAM,MAAA,GAAS,EAAE,IAAA,EAAM,KAAA,EAAO,cAAc,MAAA,EAAO;AACnD,EAAA,IAAI,MAAA,SAAe,IAAA,CAAK,yCAAA,EAA2C,EAAE,KAAA,EAAO,GAAG,QAAQ,CAAA;AAAA,OAClF,OAAA,CAAQ,KAAK,CAAA,CAAA,EAAI,KAAK,kBAAkB,MAAM,CAAA,GAAA,EAAM,KAAK,CAAA,CAAA,CAAG,CAAA;AACnE;;;ACeO,IAAM,sBAAA,GAAwC;AAAA,EACnD,IAAA,EAAM,QAAA;AAAA,EACN,UAAA,EAAY;AAAA,IACV,KAAA,EAAO,EAAE,IAAA,EAAM,QAAA,EAAS;AAAA,IACxB,MAAA,EAAQ,EAAE,IAAA,EAAM,QAAA,EAAS;AAAA,IACzB,MAAA,EAAQ,EAAE,IAAA,EAAM,QAAA,EAAS;AAAA,IACzB,OAAA,EAAS,EAAE,IAAA,EAAM,QAAA;AAAS,GAC5B;AAAA,EACA,QAAA,EAAU,CAAC,OAAA,EAAS,SAAS,CAAA;AAAA,EAC7B,oBAAA,EAAsB;AACxB,CAAA;AAcO,IAAM,wBAAA,GAA0C;AAAA,EACrD,IAAA,EAAM,QAAA;AAAA,EACN,UAAA,EAAY;AAAA,IACV,KAAA,EAAO,EAAE,IAAA,EAAM,QAAA,EAAS;AAAA,IACxB,MAAA,EAAQ,EAAE,IAAA,EAAM,QAAA,EAAS;AAAA,IACzB,MAAA,EAAQ,EAAE,IAAA,EAAM,QAAA;AAAS,GAC3B;AAAA,EACA,QAAA,EAAU,CAAC,OAAO,CAAA;AAAA,EAClB,oBAAA,EAAsB;AACxB,CAAA;AAeO,IAAM,yBAAA,GAA2C;AAAA,EACtD,IAAA,EAAM,QAAA;AAAA,EACN,UAAA,EAAY;AAAA,IACV,KAAA,EAAO,EAAE,IAAA,EAAM,QAAA,EAAS;AAAA,IACxB,MAAA,EAAQ,EAAE,IAAA,EAAM,QAAA,EAAS;AAAA,IACzB,MAAA,EAAQ,EAAE,IAAA,EAAM,QAAA,EAAS;AAAA,IACzB,UAAA,EAAY,EAAE,IAAA,EAAM,QAAA;AAAS,GAC/B;AAAA,EACA,QAAA,EAAU,CAAC,OAAA,EAAS,YAAY,CAAA;AAAA,EAChC,oBAAA,EAAsB;AACxB,CAAA;AAkBO,IAAM,kBAAA,GAAoC;AAAA,EAC/C,IAAA,EAAM,QAAA;AAAA,EACN,UAAA,EAAY;AAAA,IACV,KAAA,EAAO,EAAE,IAAA,EAAM,QAAA,EAAS;AAAA,IACxB,MAAA,EAAQ,EAAE,IAAA,EAAM,QAAA,EAAS;AAAA,IACzB,MAAA,EAAQ,EAAE,IAAA,EAAM,QAAA;AAAS,GAC3B;AAAA,EACA,QAAA,EAAU,CAAC,OAAO,CAAA;AAAA,EAClB,oBAAA,EAAsB;AACxB,CAAA;AAEO,IAAM,oBAAN,MAAwB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQ7B,OAAO,aAAA,CAAc,MAAA,EAAmB,OAAA,EAAiC;AAEvE,IAAA,MAAM,QAAQ,MAAA,CAAO,MAAA;AAAA,MAAO,CAAC,CAAA,KAC3BC,QAAAA,CAAS,CAAC,CAAA,IACVC,SAAS,CAAA,CAAE,KAAK,CAAA,IAChBA,QAAAA,CAAS,EAAE,OAAO,CAAA,IAClB,EAAE,OAAA,CAAQ,IAAA,GAAO,MAAA,GAAS;AAAA,KAC5B;AAEA,IAAA,OAAA,CAAQ,IAAI,CAAA,2BAAA,EAA8B,KAAA,CAAM,MAAM,CAAA,qBAAA,EAAwB,MAAA,CAAO,MAAM,CAAA,MAAA,CAAQ,CAAA;AAEnG,IAAA,MAAM,oBAAoC,EAAC;AAC3C,IAAA,KAAA,MAAW,WAAW,KAAA,EAAO;AAC3B,MAAA,MAAM,UAAA,GAAa,kBAAkB,OAAA,EAAS;AAAA,QAC5C,OAAO,OAAA,CAAQ,KAAA;AAAA,QACf,GAAI,OAAO,OAAA,CAAQ,MAAA,KAAW,QAAA,GAAW,EAAE,MAAA,EAAQ,OAAA,CAAQ,MAAA,EAAO,GAAI,EAAC;AAAA,QACvE,GAAI,OAAO,OAAA,CAAQ,MAAA,KAAW,QAAA,GAAW,EAAE,MAAA,EAAQ,OAAA,CAAQ,MAAA,EAAO,GAAI;AAAC,OACxE,CAAA;AACD,MAAA,IAAI,CAAC,UAAA,EAAY;AACf,QAAA,OAAA,CAAQ,IAAA,CAAK,CAAA,kDAAA,EAAqD,OAAA,CAAQ,KAAK,CAAA,CAAA,CAAG,CAAA;AAClF,QAAA;AAAA,MACF;AACA,MAAA,eAAA,CAAgB,SAAA,EAAW,OAAA,CAAQ,KAAA,EAAO,UAAA,CAAW,YAAY,CAAA;AACjE,MAAA,iBAAA,CAAkB,IAAA,CAAK;AAAA,QACrB,SAAS,OAAA,CAAQ,OAAA;AAAA,QACjB,OAAO,UAAA,CAAW,KAAA;AAAA,QAClB,OAAO,UAAA,CAAW,KAAA;AAAA,QAClB,KAAK,UAAA,CAAW,GAAA;AAAA,QAChB,GAAI,WAAW,MAAA,KAAW,MAAA,GAAY,EAAE,MAAA,EAAQ,UAAA,CAAW,MAAA,EAAO,GAAI,EAAC;AAAA,QACvE,GAAI,WAAW,MAAA,KAAW,MAAA,GAAY,EAAE,MAAA,EAAQ,UAAA,CAAW,MAAA,EAAO,GAAI;AAAC,OACxE,CAAA;AAAA,IACH;AAEA,IAAA,OAAO,iBAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,OAAO,eAAA,CAAgB,MAAA,EAAmB,OAAA,EAAmC;AAE3E,IAAA,MAAM,aAAa,MAAA,CAAO,MAAA;AAAA,MAAO,CAAC,CAAA,KAChCD,QAAAA,CAAS,CAAC,CAAA,IAAKC,QAAAA,CAAS,EAAE,KAAK;AAAA,KACjC;AAEA,IAAA,MAAM,sBAAwC,EAAC;AAC/C,IAAA,KAAA,MAAW,aAAa,UAAA,EAAY;AAClC,MAAA,MAAM,UAAA,GAAa,kBAAkB,OAAA,EAAS;AAAA,QAC5C,OAAO,SAAA,CAAU,KAAA;AAAA,QACjB,GAAI,OAAO,SAAA,CAAU,MAAA,KAAW,QAAA,GAAW,EAAE,MAAA,EAAQ,SAAA,CAAU,MAAA,EAAO,GAAI,EAAC;AAAA,QAC3E,GAAI,OAAO,SAAA,CAAU,MAAA,KAAW,QAAA,GAAW,EAAE,MAAA,EAAQ,SAAA,CAAU,MAAA,EAAO,GAAI;AAAC,OAC5E,CAAA;AACD,MAAA,IAAI,CAAC,UAAA,EAAY;AACf,QAAA,OAAA,CAAQ,IAAA,CAAK,CAAA,oDAAA,EAAuD,SAAA,CAAU,KAAK,CAAA,CAAA,CAAG,CAAA;AACtF,QAAA;AAAA,MACF;AACA,MAAA,eAAA,CAAgB,WAAA,EAAa,SAAA,CAAU,KAAA,EAAO,UAAA,CAAW,YAAY,CAAA;AACrE,MAAA,mBAAA,CAAoB,IAAA,CAAK;AAAA,QACvB,OAAO,UAAA,CAAW,KAAA;AAAA,QAClB,OAAO,UAAA,CAAW,KAAA;AAAA,QAClB,KAAK,UAAA,CAAW,GAAA;AAAA,QAChB,GAAI,WAAW,MAAA,KAAW,MAAA,GAAY,EAAE,MAAA,EAAQ,UAAA,CAAW,MAAA,EAAO,GAAI,EAAC;AAAA,QACvE,GAAI,WAAW,MAAA,KAAW,MAAA,GAAY,EAAE,MAAA,EAAQ,UAAA,CAAW,MAAA,EAAO,GAAI;AAAC,OACxE,CAAA;AAAA,IACH;AAEA,IAAA,OAAO,mBAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,OAAO,gBAAA,CAAiB,MAAA,EAAmB,OAAA,EAAoC;AAE7E,IAAA,MAAM,cAAc,MAAA,CAAO,MAAA;AAAA,MAAO,CAAC,CAAA,KACjCD,QAAAA,CAAS,CAAC,CAAA,IAAKC,QAAAA,CAAS,CAAA,CAAE,KAAK,CAAA,IAAKA,QAAAA,CAAS,CAAA,CAAE,UAAU;AAAA,KAC3D;AAEA,IAAA,MAAM,uBAA0C,EAAC;AACjD,IAAA,KAAA,MAAW,cAAc,WAAA,EAAa;AACpC,MAAA,MAAM,UAAA,GAAa,kBAAkB,OAAA,EAAS;AAAA,QAC5C,OAAO,UAAA,CAAW,KAAA;AAAA,QAClB,GAAI,OAAO,UAAA,CAAW,MAAA,KAAW,QAAA,GAAW,EAAE,MAAA,EAAQ,UAAA,CAAW,MAAA,EAAO,GAAI,EAAC;AAAA,QAC7E,GAAI,OAAO,UAAA,CAAW,MAAA,KAAW,QAAA,GAAW,EAAE,MAAA,EAAQ,UAAA,CAAW,MAAA,EAAO,GAAI;AAAC,OAC9E,CAAA;AACD,MAAA,IAAI,CAAC,UAAA,EAAY;AACf,QAAA,OAAA,CAAQ,IAAA,CAAK,CAAA,qDAAA,EAAwD,UAAA,CAAW,KAAK,CAAA,CAAA,CAAG,CAAA;AACxF,QAAA;AAAA,MACF;AACA,MAAA,eAAA,CAAgB,YAAA,EAAc,UAAA,CAAW,KAAA,EAAO,UAAA,CAAW,YAAY,CAAA;AACvE,MAAA,oBAAA,CAAqB,IAAA,CAAK;AAAA,QACxB,YAAY,UAAA,CAAW,UAAA;AAAA,QACvB,OAAO,UAAA,CAAW,KAAA;AAAA,QAClB,OAAO,UAAA,CAAW,KAAA;AAAA,QAClB,KAAK,UAAA,CAAW,GAAA;AAAA,QAChB,GAAI,WAAW,MAAA,KAAW,MAAA,GAAY,EAAE,MAAA,EAAQ,UAAA,CAAW,MAAA,EAAO,GAAI,EAAC;AAAA,QACvE,GAAI,WAAW,MAAA,KAAW,MAAA,GAAY,EAAE,MAAA,EAAQ,UAAA,CAAW,MAAA,EAAO,GAAI;AAAC,OACxE,CAAA;AAAA,IACH;AAEA,IAAA,OAAO,oBAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,OAAO,UAAU,MAAA,EAAkC;AAEjD,IAAA,MAAM,QAAQ,MAAA,CAAO,MAAA;AAAA,MAAO,CAAC,CAAA,KAC3BD,QAAAA,CAAS,CAAC,CAAA,IAAKC,QAAAA,CAAS,CAAA,CAAE,KAAK,CAAA,IAAK,CAAA,CAAE,KAAA,CAAM,IAAA,GAAO,MAAA,GAAS;AAAA,KAC9D;AAEA,IAAA,OAAA,CAAQ,IAAI,CAAA,2BAAA,EAA8B,KAAA,CAAM,MAAM,CAAA,iBAAA,EAAoB,MAAA,CAAO,MAAM,CAAA,MAAA,CAAQ,CAAA;AAE/F,IAAA,OAAO,KAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,kBAAA,CACL,IAAA,EACA,OAAA,EACA,QAAA,EACY;AACZ,IAAA,MAAM,gBAA4B,EAAC;AACnC,IAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,MAAA,MAAM,UAAA,GAAa,kBAAkB,OAAA,EAAS;AAAA,QAC5C,OAAO,GAAA,CAAI,KAAA;AAAA,QACX,GAAI,OAAO,GAAA,CAAI,MAAA,KAAW,QAAA,GAAW,EAAE,MAAA,EAAQ,GAAA,CAAI,MAAA,EAAO,GAAI,EAAC;AAAA,QAC/D,GAAI,OAAO,GAAA,CAAI,MAAA,KAAW,QAAA,GAAW,EAAE,MAAA,EAAQ,GAAA,CAAI,MAAA,EAAO,GAAI;AAAC,OAChE,CAAA;AACD,MAAA,IAAI,CAAC,UAAA,EAAY;AACf,QAAA,OAAA,CAAQ,KAAK,CAAA,8CAAA,EAAiD,GAAA,CAAI,KAAK,CAAA,gBAAA,EAAmB,QAAQ,CAAA,CAAA,CAAG,CAAA;AACrG,QAAA;AAAA,MACF;AACA,MAAA,eAAA,CAAgB,KAAA,EAAO,GAAA,CAAI,KAAA,EAAO,UAAA,CAAW,YAAY,CAAA;AACzD,MAAA,aAAA,CAAc,IAAA,CAAK;AAAA,QACjB,QAAA;AAAA,QACA,OAAO,UAAA,CAAW,KAAA;AAAA,QAClB,OAAO,UAAA,CAAW,KAAA;AAAA,QAClB,KAAK,UAAA,CAAW,GAAA;AAAA,QAChB,GAAI,WAAW,MAAA,KAAW,MAAA,GAAY,EAAE,MAAA,EAAQ,UAAA,CAAW,MAAA,EAAO,GAAI,EAAC;AAAA,QACvE,GAAI,WAAW,MAAA,KAAW,MAAA,GAAY,EAAE,MAAA,EAAQ,UAAA,CAAW,MAAA,EAAO,GAAI;AAAC,OACxE,CAAA;AAAA,IACH;AACA,IAAA,OAAO,aAAA;AAAA,EACT;AACF,CAAA;AAcA,SAAS,eAAA,CAAgB,UAAA,EAAoB,KAAA,EAAe,YAAA,EAAkC;AAC5F,EAAA,UAAA,CAAW,UAAA,EAAY,OAAO,YAAY,CAAA;AAC5C;;;AC7QA,eAAe,cAAA,CACb,QACA,OAAA,EACA,WAAA,EACA,YACA,aAAA,EACA,KAAA,EACA,UAAA,EAaA,MAAA,EACA,cAAA,EACc;AACd,EAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,MAAA,EAAO;AACnC,EAAA,MAAM,cAAA,GAAiB,cAAA,CAAe,WAAA,CAAY,EAAE,CAAC,CAAA;AAGrD,EAAA,MAAM,MAAA,GAAS,qBAAA,CAAsB,MAAA,EAAQ,cAAA,EAAgB,CAAC,CAAA;AAC9D,EAAA,MAAM,EAAE,cAAa,GAAI,MAAA;AAEzB,EAAA,MAAM,YAAiB,EAAC;AACxB,EAAA,MAAM,iBAAA,CAAkB,OAAA,EAAS,MAAA,EAAQ,OAAO,EAAE,KAAA,EAAO,KAAA,EAAO,IAAA,EAAM,EAAA,EAAI,IAAA,EAAM,UAAA,EAAW,KAAM;AAK/F,IAAA,MAAM,EAAE,KAAA,EAAO,OAAA,EAAQ,GAAI,MAAM,oBAAA;AAAA,MAC/B,UAAA;AAAA,MAAY,KAAA;AAAA,MAAO,EAAE,SAAA,EAAW,IAAA,EAAM,OAAA,EAAS,MAAA,CAAO,SAAS,OAAA,EAAQ;AAAA,MACvE,OAAO,KAAA,KAAU;AACf,QAAA,MAAM,WAAW,MAAM,yBAAA;AAAA,UACrB,MAAA;AAAA,UAAQ,YAAY,KAAK,CAAA;AAAA,UAAG,YAAA;AAAA,UAAc,qBAAA;AAAA,UAAuB,aAAA;AAAA;AAAA,UAEjE,MAAM,UAAA,GAAa,EAAA,EAAI,UAAU;AAAA,SACnC;AACA,QAAA,kBAAA,CAAmB,UAAU,CAAA,EAAG,UAAU,CAAA,UAAA,CAAA,EAAc,EAAA,EAAI,YAAY,YAAY,CAAA;AAGpF,QAAA,OAAO,EAAE,KAAA,EAAO,QAAA,CAAS,KAAA,EAAO,GAAI,QAAA,CAAS,KAAA,GAAQ,EAAE,KAAA,EAAO,QAAA,CAAS,KAAA,EAAM,GAAI,EAAC,EAAG;AAAA,MACvF;AAAA,KACF;AACA,IAAA,MAAM,SAAA,GAAY,MAAM,KAAK,CAAA;AAC7B,IAAA,SAAA,CAAU,IAAA,CAAK,GAAG,SAAS,CAAA;AAC3B,IAAA,MAAM,cAAA,GAAiB,SAAA,EAAW,EAAE,IAAA,EAAM,MAAM,CAAA;AAIhD,IAAA,IAAI,IAAA,GAAO,UAAA,EAAY,UAAA,GAAa,IAAA,EAAM,UAAU,CAAA;AACpD,IAAA,OAAO,OAAA;AAAA,EACT,GAAG,MAAM,CAAA;AACT,EAAA,OAAO,SAAA;AACT;AAEO,IAAM,sBAAN,MAA0B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAU/B,aAAa,cAAA,CACX,OAAA,EACA,MAAA,EACA,YAAA,EACA,IAAA,EACA,OAAA,EACA,QAAA,EACA,cAAA,EACA,UAAA,EAGA,MAAA,EAEA,cAAA,EACyB;AACzB,IAAA,OAAO,cAAA;AAAA,MACL,MAAA;AAAA,MAAQ,OAAA;AAAA,MACR,CAAC,UAAU,iBAAA,CAAkB,kBAAA,CAAmB,OAAO,YAAA,EAAc,IAAA,EAAM,OAAA,EAAS,QAAA,EAAU,cAAc,CAAA;AAAA,MAC5G,SAAA;AAAA,MAAW,sBAAA;AAAA,MACX,CAAC,KAAA,KAAU,iBAAA,CAAkB,aAAA,CAAc,OAAO,OAAO,CAAA;AAAA,MACzD,UAAA;AAAA,MACA,MAAA;AAAA,MACA;AAAA,KACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,aAAa,iBACX,OAAA,EACA,MAAA,EACA,cACA,OAAA,EACA,cAAA,EACA,UAAA,EAGA,MAAA,EAEA,cAAA,EAC2B;AAC3B,IAAA,OAAO,cAAA;AAAA,MACL,MAAA;AAAA,MAAQ,OAAA;AAAA,MACR,CAAC,KAAA,KAAU,iBAAA,CAAkB,qBAAqB,KAAA,EAAO,YAAA,EAAc,SAAS,cAAc,CAAA;AAAA,MAC9F,WAAA;AAAA,MAAa,wBAAA;AAAA,MACb,CAAC,KAAA,KAAU,iBAAA,CAAkB,eAAA,CAAgB,OAAO,OAAO,CAAA;AAAA,MAC3D,UAAA;AAAA,MACA,MAAA;AAAA,MACA;AAAA,KACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,aAAa,iBAAA,CACX,OAAA,EACA,MAAA,EACA,YAAA,EACA,IAAA,EACA,OAAA,EACA,QAAA,EACA,cAAA,EACA,UAAA,EAGA,MAAA,EAEA,cAAA,EAC4B;AAC5B,IAAA,OAAO,cAAA;AAAA,MACL,MAAA;AAAA,MAAQ,OAAA;AAAA,MACR,CAAC,UAAU,iBAAA,CAAkB,qBAAA,CAAsB,OAAO,YAAA,EAAc,IAAA,EAAM,OAAA,EAAS,QAAA,EAAU,cAAc,CAAA;AAAA,MAC/G,YAAA;AAAA,MAAc,yBAAA;AAAA,MACd,CAAC,KAAA,KAAU,iBAAA,CAAkB,gBAAA,CAAiB,OAAO,OAAO,CAAA;AAAA,MAC5D,UAAA;AAAA,MACA,MAAA;AAAA,MACA;AAAA,KACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,aAAa,WACX,OAAA,EACA,MAAA,EACA,QACA,QAAA,EACA,cAAA,EACA,UAAA,EAOA,MAAA,EAEA,cAAA,EACqB;AACrB,IAAA,MAAM,YAAA,GAAe,OAAO,IAAA,CAAK,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,SAAS,QAAQ,CAAA;AAChE,IAAA,IAAI,CAAC,YAAA,EAAc;AACjB,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,kBAAA,EAAqB,QAAQ,CAAA,aAAA,EAAgB,MAAA,CAAO,EAAE,CAAA,CAAE,CAAA;AAAA,IAC1E;AAGA,IAAA,MAAM,aAAa,MAAM,cAAA;AAAA,MACvB,MAAA;AAAA,MAAQ,OAAA;AAAA,MACR,CAAC,UAAU,iBAAA,CAAkB,cAAA;AAAA,QAC3B,KAAA;AAAA,QACA,QAAA;AAAA,QACA,MAAA,CAAO,IAAA;AAAA,QACP,MAAA,CAAO,WAAA;AAAA,QACP,MAAA,CAAO,MAAA;AAAA,QACP,YAAA,CAAa,WAAA;AAAA,QACb,YAAA,CAAa,QAAA;AAAA,QACb;AAAA,OACF;AAAA,MACA,KAAA;AAAA,MAAO,kBAAA;AAAA,MACP,CAAC,KAAA,KAAU,iBAAA,CAAkB,SAAA,CAAU,KAAK,CAAA;AAAA,MAC5C,UAAA;AAAA,MACA,MAAA;AAAA,MACA,cAAA,GACI,OAAO,GAAA,EAAK,MAAA,KAAW,cAAA,CAAe,iBAAA,CAAkB,kBAAA,CAAmB,GAAA,EAAK,OAAA,EAAS,QAAQ,CAAA,EAAG,MAAM,CAAA,GAC1G;AAAA,KACN;AACA,IAAA,OAAO,iBAAA,CAAkB,kBAAA,CAAmB,UAAA,EAAY,OAAA,EAAS,QAAQ,CAAA;AAAA,EAC3E;AACF;ACvOA,IAAM,qBAAA,GAAuC;AAAA,EAC3C,IAAA,EAAM,QAAA;AAAA,EACN,UAAA,EAAY;AAAA,IACV,KAAA,EAAO,EAAE,IAAA,EAAM,QAAA,EAAS;AAAA,IACxB,UAAA,EAAY,EAAE,IAAA,EAAM,QAAA,EAAS;AAAA,IAC7B,MAAA,EAAQ,EAAE,IAAA,EAAM,QAAA,EAAS;AAAA,IACzB,MAAA,EAAQ,EAAE,IAAA,EAAM,QAAA;AAAS,GAC3B;AAAA,EACA,QAAA,EAAU,CAAC,OAAA,EAAS,YAAY,CAAA;AAAA,EAChC,oBAAA,EAAsB;AACxB,CAAA;AAkCA,IAAM,gBAAA,GAAmB,EAAA;AAKzB,SAAS,WAAW,IAAA,EAAkC;AACpD,EAAA,MAAM,CAAA,GAAI,IAAA,CAAK,IAAA,EAAK,CAAE,MAAM,KAAK,CAAA;AACjC,EAAA,OAAO,CAAA,GAAI,MAAA,CAAO,CAAA,CAAE,CAAC,CAAC,CAAA,GAAI,MAAA;AAC5B;AAoBA,eAAe,uBAAA,CACb,MAAA,EACA,KAAA,EACA,KAAA,EACA,wBACA,MAAA,EAC6B;AAC7B,EAAA,MAAM,MAAA,GAAS,2BAA2B,sBAAsB,CAAA;;AAAA;AAAA;AAAA,EAIhE,KAAK;AAAA,GAAA,CAAA;AAGL,EAAA,IAAI,OAAA;AACJ,EAAA,IAAI;AACF,IAAA,MAAM,QAAA,GAAW,MAAM,2BAAA,CAA4B,MAAA,EAAQ,QAAQ,gBAAA,EAAkB,qBAAA,EAAuB,QAAW,MAAM,CAAA;AAC7H,IAAA,OAAA,GAAU,UAAA,CAAW,SAAS,IAAI,CAAA;AAAA,EACpC,SAAS,GAAA,EAAK;AACZ,IAAA,MAAA,CAAO,KAAK,sEAAA,EAAmE;AAAA,MAC7E,YAAY,KAAA,CAAM,MAAA;AAAA,MAClB,OAAO,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU,OAAO,GAAG;AAAA,KACvD,CAAA;AACD,IAAA,OAAO,MAAA;AAAA,EACT;AACA,EAAA,IAAI,YAAY,MAAA,EAAW;AACzB,IAAA,MAAA,CAAO,KAAK,mFAAA,EAAgF,EAAE,UAAA,EAAY,KAAA,CAAM,QAAQ,CAAA;AACxH,IAAA,OAAO,MAAA;AAAA,EACT;AACA,EAAA,IAAI,KAAA,CAAM,MAAA,GAAS,mBAAA,GAAsB,OAAA,EAAS;AAIhD,IAAA,MAAM,IAAI,kBAAA;AAAA,MACR,CAAA,iBAAA,EAAoB,MAAM,MAAM,CAAA,sCAAA,EAAyC,OAAO,CAAA,oBAAA,EAAoB,mBAAmB,CAAA,OAAA,EAAU,KAAA,CAAM,MAAM,CAAA,yHAAA,CAAA;AAAA,MAC7I,CAAC,GAAG,KAAK,CAAA;AAAA,MACT,EAAE,KAAA,EAAO,KAAA,CAAM,QAAQ,OAAA,EAAS,UAAA,EAAY,MAAM,MAAA;AAAO,KAC3D;AAAA,EACF;AACA,EAAA,OAAO,OAAA;AACT;AAEA,eAAsB,eAAA,CACpB,KAAA,EACA,WAAA,EACA,MAAA,EACA,4BAAA,EACA,MAAA,EACA,cAAA,EACA,UAAA,EAEA,aAAA,EAEA,SAAA,EAIA,MAAA,EAeA,cAAA,EAC4B;AAG5B,EAAA,MAAM,sBAAA,GAAyB,WAAA,CAAY,GAAA,CAAI,CAAA,EAAA,KAAM;AACnD,IAAA,IAAI,OAAO,OAAO,QAAA,EAAU;AAC1B,MAAA,OAAO,EAAA;AAAA,IACT;AACA,IAAA,OAAO,EAAA,CAAG,YAAY,EAAA,CAAG,QAAA,CAAS,SAAS,CAAA,GACvC,CAAA,EAAG,GAAG,IAAI,CAAA,YAAA,EAAe,GAAG,QAAA,CAAS,KAAA,CAAM,GAAG,CAAC,CAAA,CAAE,KAAK,IAAI,CAAC,MAC3D,EAAA,CAAG,IAAA;AAAA,EACT,CAAC,CAAA,CAAE,IAAA,CAAK,IAAI,CAAA;AAMZ,EAAA,MAAM,+BAA+B,4BAAA,GACjC;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA,CAAA,GAoBA;AAAA;AAAA,CAAA;AAIJ,EAAA,MAAM,qBAAqB,cAAA,GACvB;AAAA,sBAAA,EAA2BC,oBAAAA,CAAqB,cAAc,CAAA,IAAK,cAAc,CAAA;AAAA,CAAA,GACjF,EAAA;AAMJ,EAAA,MAAM,WAAA,GAAc,CAAC,IAAA,KAAyB,CAAA,wEAAA,EAA2E,sBAAsB,CAAA;AAAA,EAC/I,4BAA4B,GAAG,kBAAkB;AAAA;AAAA;AAAA,EAGjD,IAAI;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA,6JAAA,CAAA;AAkBJ,EAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,MAAA,EAAO;AAMnC,EAAA,MAAM,cAAc,MAAA,CAAO,oBAAA;AAC3B,EAAA,MAAM,cAAA,GAAiBC,cAAAA,CAAe,WAAA,CAAY,EAAE,CAAC,CAAA;AAGrD,EAAA,MAAM,MAAA,GAAS,qBAAA,CAAsB,MAAA,EAAQ,cAAA,EAAgB,YAAY,MAAM,CAAA;AAC/E,EAAA,MAAM,EAAE,QAAA,EAAU,YAAA,EAAa,GAAI,MAAA;AAEnC,EAAA,MAAA,CAAO,MAAM,mCAAA,EAAqC;AAAA,IAChD,WAAA,EAAa,sBAAA;AAAA,IACb,OAAO,KAAA,CAAM,MAAA;AAAA;AAAA;AAAA;AAAA,IAIb,wBAAwB,QAAA,CAAS,SAAA;AAAA,IACjC,sBAAA,EAAwB,OAAO,MAAA,CAAO,OAAA;AAAA,IACtC;AAAA,GACD,CAAA;AAED,EAAA,MAAM,YAA+B,EAAC;AACtC,EAAA,MAAM,iBAAA,CAAkB,KAAA,EAAO,MAAA,EAAQ,OAAO,EAAE,KAAA,EAAO,KAAA,EAAO,IAAA,EAAM,EAAA,EAAI,IAAA,EAAM,UAAA,EAAW,KAAM;AAK7F,IAAA,MAAM,EAAE,KAAA,EAAO,OAAA,EAAQ,GAAI,MAAM,oBAAA;AAAA,MAC/B,WAAA;AAAA,MAAa,KAAA;AAAA,MAAO,EAAE,SAAA,EAAW,IAAA,EAAM,OAAA,EAAS,SAAS,OAAA,EAAQ;AAAA,MACjE,OAAO,KAAA,KAAU;AACf,QAAA,MAAM,WAAW,MAAM,yBAAA;AAAA,UACrB,MAAA;AAAA,UACA,YAAY,KAAK,CAAA;AAAA,UACjB,YAAA;AAAA,UACA,qBAAA;AAAA,UACA,qBAAA;AAAA;AAAA;AAAA,UAGA,MAAM,UAAA,GAAa,EAAA,EAAI,UAAU,CAAA;AAAA,UACjC;AAAA,SACF;AACA,QAAA,MAAA,CAAO,MAAM,gCAAA,EAAkC;AAAA,UAC7C,EAAA;AAAA,UACA,UAAA;AAAA,UACA,eAAA,EAAiB,IAAA;AAAA,UACjB,YAAY,KAAA,CAAM,MAAA;AAAA,UAClB,KAAA,EAAO,SAAS,KAAA,CAAM;AAAA,SACvB,CAAA;AAKD,QAAA,kBAAA,CAAmB,QAAA,EAAU,mBAAA,EAAqB,EAAA,EAAI,UAAA,EAAY,YAAY,CAAA;AAI9E,QAAA,MAAM,OAAA,GAAU,WAAA,GACZ,MAAM,uBAAA,CAAwB,MAAA,EAAQ,OAAO,QAAA,CAAS,KAAA,EAAO,sBAAA,EAAwB,MAAM,CAAA,GAC3F,MAAA;AAGJ,QAAA,OAAO;AAAA,UACL,OAAO,QAAA,CAAS,KAAA;AAAA,UAChB,GAAI,SAAS,KAAA,GAAQ,EAAE,OAAO,QAAA,CAAS,KAAA,KAAU,EAAC;AAAA,UAClD,GAAI,OAAA,KAAY,MAAA,GAAY,EAAE,OAAA,KAAY;AAAC,SAC7C;AAAA,MACF,CAAA;AAAA,MACA,MAAA;AAAA,MAAQ,aAAA;AAAA,MAAe;AAAA,KACzB;AAEA,IAAA,MAAM,YAA+B,EAAC;AACtC,IAAA,KAAA,MAAW,KAAK,KAAA,EAAO;AAGrB,MAAA,IAAIH,QAAAA,CAAS,CAAC,CAAA,IAAKC,QAAAA,CAAS,CAAA,CAAE,KAAK,CAAA,IAAKA,QAAAA,CAAS,CAAA,CAAE,UAAU,CAAA,EAAG;AAC9D,QAAA,SAAA,CAAU,IAAA,CAAK;AAAA,UACb,OAAO,CAAA,CAAE,KAAA;AAAA,UACT,YAAY,CAAA,CAAE,UAAA;AAAA,UACd,GAAIA,QAAAA,CAAS,CAAA,CAAE,MAAM,CAAA,GAAI,EAAE,MAAA,EAAQ,CAAA,CAAE,MAAA,EAAO,GAAI,EAAC;AAAA,UACjD,GAAIA,QAAAA,CAAS,CAAA,CAAE,MAAM,CAAA,GAAI,EAAE,MAAA,EAAQ,CAAA,CAAE,MAAA,EAAO,GAAI;AAAC,SAClD,CAAA;AAAA,MACH,CAAA,MAAO;AACL,QAAA,MAAA,CAAO,KAAA,CAAM,8BAAA,EAAgC,EAAE,MAAA,EAAQ,GAAG,CAAA;AAAA,MAC5D;AAAA,IACF;AACA,IAAA,SAAA,CAAU,IAAA,CAAK,GAAG,SAAS,CAAA;AAC3B,IAAA,MAAM,cAAA,GAAiB,SAAA,EAAW,EAAE,IAAA,EAAM,MAAM,CAAA;AAKhD,IAAA,IAAI,IAAA,GAAO,UAAA,EAAY,UAAA,GAAa,IAAA,EAAM,UAAU,CAAA;AACpD,IAAA,OAAO,OAAA;AAAA,EACT,GAAG,MAAM,CAAA;AAET,EAAA,OAAO,SAAA;AACT;AClVA,SAAS,gBAAgB,MAAA,EAAwB;AAC/C,EAAA,OAAOC,oBAAAA,CAAqB,MAAM,CAAA,IAAK,MAAA;AACzC;AAKA,IAAM,oBAAA,GAAuB,GAAA;AAC7B,IAAM,oBAAA,GAAuB,CAAA;AAC7B,IAAM,oBAAA,GAAuB,GAAA;AAO7B,SAAS,OAAA,CAAQ,YAAoB,YAAA,EAA+B;AAClE,EAAA,OAAO,IAAI,UAAU,CAAA,EAAG,eAAe,CAAA,CAAA,EAAI,YAAY,KAAK,EAAE,CAAA,CAAA,CAAA;AAChE;AASO,IAAM,kBAAA,GAAqB,GAAA;AAalC,eAAsB,0BACpB,KAAA,EACA,WAAA,EACA,QACA,MAAA,EACA,UAAA,EACA,QACA,OAAA,EACA,WAAA,EACA,SAAA,EACA,cAAA,EACA,kBAAsC,eAAA,EACtC,IAAA,GAAe,YACf,SAAA,EACA,IAAA,GAAgB,OAChB,MAAA,EACiE;AACjE,EAAA,MAAA,CAAO,MAAM,gCAAA,EAAkC;AAAA,IAC7C,YAAA,EAAc,KAAA,CAAM,SAAA,CAAU,CAAA,EAAG,GAAG,CAAA;AAAA,IACpC,WAAA;AAAA,IACA,aAAA,EAAe,CAAC,CAAC,UAAA;AAAA,IACjB,MAAA;AAAA,IACA,cAAA;AAAA,IACA,UAAA,EAAY,CAAC,CAAC,OAAA;AAAA,IACd,WAAA;AAAA,IACA,SAAA;AAAA,IACA,eAAA;AAAA,IACA,IAAA;AAAA,IACA;AAAA,GACD,CAAA;AAGD,EAAA,MAAM,mBAAmB,WAAA,IAAe,GAAA;AACxC,EAAA,MAAM,iBAAiB,SAAA,IAAa,kBAAA;AAKpC,EAAA,MAAM,mBAAA,GAAsB,MAAA,IAAU,MAAA,KAAW,IAAA,GAC7C;;AAAA,wCAAA,EAA+C,eAAA,CAAgB,MAAM,CAAC,CAAA,CAAA,CAAA,GACtE,EAAA;AACJ,EAAA,MAAM,4BAA4B,cAAA,GAC9B;;AAAA,gDAAA,EAAuD,eAAA,CAAgB,cAAc,CAAC,CAAA,CAAA,CAAA,GACtF,EAAA;AAOJ,EAAA,IAAI,iBAAA,GAAoB,EAAA;AACxB,EAAA,IAAI,cAAA,GAAiB,EAAA;AACrB,EAAA,IAAI,eAAA,GAAkB,EAAA;AACtB,EAAA,IAAI,YAAA,GAAe,EAAA;AAEnB,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,MAAM,EAAE,OAAM,GAAI,OAAA;AAKlB,IAAA,MAAM,cAAA,GACJ,KAAA,CAAM,IAAA,KAAS,YAAA,GACX,KAAA,CAAM,eAAe,KAAK,CAAA,GAC1B,KAAA,CAAM,QAAA,CAAS,KAAK,CAAA;AAC1B,IAAA,MAAM,oBAAoB,KAAA,CAAM,IAAA,KAAS,YAAA,GAAe,KAAA,CAAM,WAAW,EAAA,GAAK,MAAA;AAE9E,IAAA,IAAI,KAAA,CAAM,SAAS,YAAA,EAAc;AAC/B,MAAA,MAAM,QAAkB,EAAC;AACzB,MAAA,KAAA,CAAM,IAAA,CAAK,CAAA,yBAAA,EAA4B,KAAA,CAAM,UAAA,CAAW,UAAU,CAAA,CAAE,CAAA;AACpE,MAAA,KAAA,CAAM,IAAA,CAAK,sBAAsB,KAAA,CAAM,cAAA,CAAe,IAAI,CAAA,CAAA,EAAI,OAAA,CAAQ,cAAc,CAAC,CAAA,CAAE,CAAA;AAEvF,MAAA,MAAM,EAAE,UAAA,EAAY,IAAA,EAAK,GAAI,KAAA,CAAM,UAAA;AACnC,MAAA,IAAI,UAAA,KAAe,YAAA,IAAgB,UAAA,KAAe,WAAA,EAAa;AAC7D,QAAA,MAAM,WAAW,KAAA,CAAM,OAAA,CAAQ,IAAI,CAAA,GAAI,IAAA,CAAK,CAAC,CAAA,GAAI,IAAA;AACjD,QAAA,IAAI,QAAA,IAAY,OAAA,IAAW,QAAA,IAAY,QAAA,CAAS,KAAA,EAAO;AACrD,UAAA,MAAM,KAAA,GAAQ,UAAA,KAAe,YAAA,GAAe,SAAA,GAAY,YAAA;AACxD,UAAA,KAAA,CAAM,KAAK,CAAA,EAAA,EAAK,KAAK,CAAA,EAAA,EAAK,QAAA,CAAS,KAAK,CAAA,CAAE,CAAA;AAAA,QAC5C;AAAA,MACF;AAIA,MAAA,IAAI,MAAM,QAAA,EAAU;AAClB,QAAA,KAAA,CAAM,IAAA,CAAK,CAAA,uCAAA,EAA0C,KAAA,CAAM,QAAQ,CAAA,CAAE,CAAA;AAAA,MACvE;AACA,MAAA,iBAAA,GAAoB;;AAAA;AAAA,EAA4B,KAAA,CAAM,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA;AAEhE,MAAA,IAAI,MAAM,QAAA,EAAU;AAClB,QAAA,MAAM,EAAE,MAAA,EAAQ,IAAA,EAAM,KAAA,KAAU,KAAA,CAAM,QAAA;AACtC,QAAA,cAAA,GAAiB;;AAAA;AAAA;AAAA,EAEvB,MAAA,GAAS,CAAA,GAAA,EAAM,MAAM,CAAA,CAAA,GAAK,EAAE;AAAA,GAAA,EACzB,IAAI,CAAA;AAAA,EACP,KAAA,GAAQ,CAAA,EAAG,KAAK,CAAA,GAAA,CAAA,GAAQ,EAAE;AAAA;AAAA,CAAA;AAAA,MAGtB;AAAA,IACF,CAAA,MAAO;AAIL,MAAA,MAAM,KAAA,GAAQ,CAAC,CAAA,YAAA,EAAe,KAAA,CAAM,QAAA,CAAS,IAAI,CAAA,CAAA,EAAI,OAAA,CAAQ,cAAc,CAAC,CAAA,CAAE,CAAA;AAC9E,MAAA,IAAI,MAAM,OAAA,EAAS,KAAA,CAAM,KAAK,CAAA,WAAA,EAAc,KAAA,CAAM,OAAO,CAAA,CAAE,CAAA;AAC3D,MAAA,IAAI,KAAA,CAAM,mBAAA,IAAuB,KAAA,CAAM,mBAAA,CAAoB,SAAS,CAAA,EAAG;AACrE,QAAA,KAAA,CAAM,KAAK,CAAA,wBAAA,EAA2B,KAAA,CAAM,oBAAoB,IAAA,CAAK,IAAI,CAAC,CAAA,CAAE,CAAA;AAAA,MAC9E;AACA,MAAA,eAAA,GAAkB;;AAAA;AAAA,EAA0B,KAAA,CAAM,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA;AAE5D,MAAA,IAAI,KAAA,CAAM,SAAS,IAAA,EAAM;AACvB,QAAA,eAAA,IAAmB;;AAAA;AAAA;AAAA,EAA+B,MAAM,OAAA,CAAQ,IAAA,CAAK,KAAA,CAAM,CAAA,EAAG,oBAAoB,CAAC;AAAA,GAAA,CAAA;AAAA,MACrG;AACA,MAAA,MAAM,UAAU,MAAA,CAAO,OAAA,CAAQ,MAAM,OAAA,EAAS,OAAA,IAAW,EAAE,CAAA;AAC3D,MAAA,IAAI,OAAA,CAAQ,SAAS,CAAA,EAAG;AACtB,QAAA,MAAM,MAAA,GAAS,QACZ,GAAA,CAAI,CAAC,CAAC,EAAA,EAAI,IAAI,CAAA,KAAM,CAAA,CAAA,EAAI,EAAE,CAAA;AAAA,EAAM,IAAA,CAAK,MAAM,CAAA,EAAG,oBAAoB,CAAC,CAAA,CAAE,CAAA,CACrE,KAAK,MAAM,CAAA;AACd,QAAA,eAAA,IAAmB;;AAAA;AAAA;AAAA,EAAuC,MAAM;AAAA,GAAA,CAAA;AAAA,MAClE;AAAA,IACF;AAIA,IAAA,IAAI,cAAA,EAAgB;AAClB,MAAA,MAAM,KAAA,GAAQ,WAAA,CAAY,OAAA,CAAQ,KAAA,EAAO,gBAAgB,iBAAiB,CAAA;AAC1E,MAAA,MAAM,QAAkB,EAAC;AAEzB,MAAA,IAAI,KAAA,CAAM,WAAA,CAAY,MAAA,GAAS,CAAA,EAAG;AAChC,QAAA,MAAM,QAAA,GAAW,KAAA,CAAM,WAAA,CACpB,GAAA,CAAI,CAAA,CAAA,KAAK,CAAA,EAAG,CAAA,CAAE,YAAY,CAAA,EAAG,CAAA,CAAE,WAAA,CAAY,MAAA,GAAS,CAAA,EAAA,EAAK,CAAA,CAAE,WAAA,CAAY,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA,CAAA,GAAM,EAAE,CAAA,CAAA,EAAI,OAAA,CAAQ,CAAA,CAAE,UAAU,CAAC,CAAA,CAAE,CAAA,CACpH,IAAA,CAAK,IAAI,CAAA;AACZ,QAAA,KAAA,CAAM,IAAA,CAAK,CAAA,uBAAA,EAA0B,QAAQ,CAAA,CAAE,CAAA;AAAA,MACjD;AAEA,MAAA,IAAI,KAAA,CAAM,eAAe,CAAA,EAAG;AAC1B,QAAA,MAAM,aAAa,KAAA,CAAM,OAAA,CAAQ,GAAA,CAAI,CAAA,CAAA,KAAK,GAAG,CAAA,CAAE,YAAY,CAAA,CAAA,EAAI,OAAA,CAAQ,EAAE,UAAU,CAAC,CAAA,CAAE,CAAA,CAAE,KAAK,IAAI,CAAA;AACjG,QAAA,KAAA,CAAM,KAAK,CAAA,4BAAA,EAA+B,KAAA,CAAM,YAAY,CAAA,eAAA,EAAkB,MAAM,YAAA,GAAe,CAAA,GAAI,GAAA,GAAM,EAAE,GAAG,UAAA,GAAa,CAAA,EAAA,EAAK,UAAU,CAAA,CAAA,GAAK,EAAE,CAAA,CAAE,CAAA;AAAA,MACzJ;AAEA,MAAA,IAAI,KAAA,CAAM,kBAAA,CAAmB,MAAA,GAAS,CAAA,EAAG;AACvC,QAAA,KAAA,CAAM,KAAK,CAAA,yCAAA,EAA4C,KAAA,CAAM,mBAAmB,IAAA,CAAK,IAAI,CAAC,CAAA,CAAE,CAAA;AAAA,MAC9F;AAEA,MAAA,IAAI,QAAQ,2BAAA,EAA6B;AACvC,QAAA,KAAA,CAAM,IAAA,CAAK,CAAA,wBAAA,EAA2B,OAAA,CAAQ,2BAA2B,CAAA,CAAE,CAAA;AAAA,MAC7E;AAEA,MAAA,IAAI,KAAA,CAAM,SAAS,CAAA,EAAG;AACpB,QAAA,YAAA,GAAe;;AAAA;AAAA,EAAiC,KAAA,CAAM,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA;AAAA,MAClE;AAAA,IACF;AAAA,EACF;AAQA,EAAA,IAAI,sBAAA,GAAyB,EAAA;AAC7B,EAAA,MAAM,OAAA,GAAU,OAAA,EAAS,eAAA,EAAiB,OAAA,IAAW,EAAC;AACtD,EAAA,IAAI,OAAA,CAAQ,SAAS,CAAA,EAAG;AACtB,IAAA,MAAM,KAAA,GAAQ,CAAC,GAAG,OAAO,EACtB,IAAA,CAAK,CAAC,GAAG,CAAA,KAAM,CAAA,CAAE,QAAQ,CAAA,CAAE,KAAK,EAChC,KAAA,CAAM,CAAA,EAAG,oBAAoB,CAAA,CAC7B,GAAA,CAAI,OAAK,CAAA,EAAA,EAAK,OAAA,CAAQ,EAAE,UAAA,EAAY,CAAA,CAAE,YAAY,CAAC,CAAA,EAAA,EAAK,EAAE,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAC,CAAA,CAAA,EAAI,EAAE,WAAA,GAAc,QAAA,GAAW,EAAE,CAAA,CAAA,EAAI,CAAA,CAAE,KAAK,KAAA,CAAM,CAAA,EAAG,oBAAoB,CAAC,CAAA,CAAE,CAAA;AAMzJ,IAAA,MAAM,UAAU,OAAA,CAAQ,IAAA,CAAK,OAAK,CAAA,CAAE,WAAW,IAC3C,oKAAA,GACA,EAAA;AACJ,IAAA,sBAAA,GAAyB;;AAAA;AAAA,EAAkD,KAAA,CAAM,IAAA,CAAK,IAAI,CAAC,GAAG,OAAO,CAAA,CAAA;AAAA,EACvG;AAKA,EAAA,IAAI,QAAA;AACJ,EAAA,IAAI,SAAS,UAAA,EAAY;AACvB,IAAA,QAAA,GAAW,mDAAmD,KAAK,CAAA,EAAA,CAAA;AAAA,EACrE,CAAA,MAAA,IAAW,SAAS,QAAA,EAAU;AAC5B,IAAA,QAAA,GAAW,4FAA4F,KAAK,CAAA,CAAA,CAAA;AAAA,EAC9G,CAAA,MAAA,IAAW,SAAS,SAAA,EAAW;AAC7B,IAAA,QAAA,GAAW,+BAA+B,KAAK,CAAA,EAAA,CAAA;AAAA,EACjD,CAAA,MAAO;AACL,IAAA,MAAA,CAAO,IAAA,CAAK,kEAAA,EAA+D,EAAE,IAAA,EAAM,CAAA;AACnF,IAAA,QAAA,GAAW,GAAG,IAAI;AAAA,QAAA,EAAa,KAAK,CAAA,CAAA,CAAA;AAAA,EACtC;AAMA,EAAA,MAAM,cAAc,eAAA,KAAoB,YAAA;AAIxC,EAAA,MAAM,QAAQ,eAAA,KAAoB,iBAAA;AAClC,EAAA,IAAI,oBAAA,GAAuB,EAAA;AAC3B,EAAA,IAAI,gBAAA,GAAmB,EAAA;AACvB,EAAA,IAAI,cAAc,UAAA,EAAY;AAC5B,IAAA,oBAAA,GAAuB,KAAA,GACnB,2FAAA,GACA,WAAA,GACE,+EAAA,GACA,4FAAA;AACN,IAAA,IAAI,CAAC,WAAA,IAAe,CAAC,KAAA,EAAO;AAC1B,MAAA,gBAAA,GAAmB,0CAAA;AAAA,IACrB;AAAA,EACF,CAAA,MAAA,IAAW,cAAc,OAAA,EAAS;AAChC,IAAA,oBAAA,GAAuB,wEAAA;AAAA,EACzB,CAAA,MAAA,IAAW,cAAc,MAAA,EAAQ;AAC/B,IAAA,oBAAA,GAAuB,6IAAA;AAAA,EACzB,WAAW,SAAA,EAAW;AACpB,IAAA,MAAA,CAAO,IAAA,CAAK,+EAAA,EAA4E,EAAE,SAAA,EAAW,CAAA;AACrG,IAAA,oBAAA,GAAuB;AAAA,0BAAA,EAA+B,SAAS,CAAA,CAAA;AAAA,EACjE;AAMA,EAAA,MAAM,eAAA,GAAkB,OACpB,8QAAA,GACA,EAAA;AAEJ,EAAA,MAAM,qBAAqB,KAAA,GACvB,CAAA;AAAA;AAAA,4CAAA,CAAA,GAGA,WAAA,GACE,CAAA;AAAA,4CAAA,CAAA,GAEA,CAAA;AAAA,gCAAA,CAAA;AAMN,EAAA,MAAM,gBAAgB,MAAA,GAClB;;AAAA;AAAA;AAAA,EAEJ,OAAO,KAAK;AAAA;AAAA,EAEZ,MAAA,CAAO,MAAM,CAAA,CAAA,GACT,EAAA;AAIJ,EAAA,MAAM,MAAA,GAAS,GAAG,QAAQ;AAAA,EAC1B,aAAa,CAAA,aAAA,EAAgB,UAAU,CAAA,CAAA,GAAK,EAAE,GAAG,aAAa;AAAA,EAC9D,WAAA,CAAY,SAAS,CAAA,GAAI,CAAA,6BAAA,EAAgC,YAAY,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA,CAAA,GAAM,EAAE,CAAA,EAAG,iBAAiB,CAAA,EAAG,cAAc,CAAA,EAAG,eAAe,CAAA,EAAG,YAAY,GAAG,sBAAsB,CAAA,EAAG,yBAAyB,CAAA,EAAG,mBAAmB;;AAAA;AAAA,wBAAA,EAGhN,cAAc,CAAA;AAAA,4BAAA,EACV,oBAAoB,CAAA,EAAG,gBAAgB,CAAA,EAAG,eAAe;AAAA,EACrF,kBAAkB,CAAA,CAAA;AAGlB,EAAA,MAAM,aAAA,GAAgB,CAACE,SAAAA,KAAyD;AAE9E,IAAA,IAAI,OAAA,GAAUA,UAAS,IAAA,EAAK;AAC5B,IAAA,IAAI,OAAA,CAAQ,UAAA,CAAW,aAAa,CAAA,IAAK,OAAA,CAAQ,UAAA,CAAW,OAAO,CAAA,IAAK,OAAA,CAAQ,UAAA,CAAW,UAAU,CAAA,EAAG;AACtG,MAAA,OAAA,GAAU,QAAQ,KAAA,CAAM,OAAA,CAAQ,OAAA,CAAQ,IAAI,IAAI,CAAC,CAAA;AACjD,MAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,WAAA,CAAY,KAAK,CAAA;AAC1C,MAAA,IAAI,aAAa,EAAA,EAAI;AACnB,QAAA,OAAA,GAAU,OAAA,CAAQ,KAAA,CAAM,CAAA,EAAG,QAAQ,CAAA;AAAA,MACrC;AAAA,IACF,CAAA,MAAA,IAAW,OAAA,CAAQ,UAAA,CAAW,KAAK,CAAA,EAAG;AACpC,MAAA,OAAA,GAAU,OAAA,CAAQ,MAAM,CAAC,CAAA;AACzB,MAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,WAAA,CAAY,KAAK,CAAA;AAC1C,MAAA,IAAI,aAAa,EAAA,EAAI;AACnB,QAAA,OAAA,GAAU,OAAA,CAAQ,KAAA,CAAM,CAAA,EAAG,QAAQ,CAAA;AAAA,MACrC;AAAA,IACF;AAEA,IAAA,OAAA,GAAU,QAAQ,IAAA,EAAK;AAGvB,IAAA,OAAO;AAAA,MACL,KAAA,EAAO,KAAA;AAAA,MACP;AAAA,KACF;AAAA,EACF,CAAA;AAEA,EAAA,MAAA,CAAO,MAAM,6BAAA,EAA+B;AAAA,IAC1C,cAAc,MAAA,CAAO,MAAA;AAAA,IACrB,WAAA,EAAa,gBAAA;AAAA,IACb,SAAA,EAAW;AAAA,GACZ,CAAA;AACD,EAAA,MAAM,QAAA,GAAW,MAAM,2BAAA,CAA4B,MAAA,EAAQ,QAAQ,cAAA,EAAgB,gBAAA,EAAkB,QAAW,MAAM,CAAA;AACtH,EAAA,MAAA,CAAO,KAAA,CAAM,6BAAA,EAA+B,EAAE,cAAA,EAAgB,QAAA,CAAS,KAAK,MAAA,EAAQ,UAAA,EAAY,QAAA,CAAS,UAAA,EAAY,CAAA;AAErH,EAAA,MAAM,MAAA,GAAS,aAAA,CAAc,QAAA,CAAS,IAAI,CAAA;AAC1C,EAAA,MAAA,CAAO,MAAM,iBAAA,EAAmB;AAAA,IAC9B,QAAA,EAAU,CAAC,CAAC,MAAA,CAAO,KAAA;AAAA,IACnB,WAAA,EAAa,OAAO,KAAA,EAAO,MAAA;AAAA,IAC3B,UAAA,EAAY,CAAC,CAAC,MAAA,CAAO,OAAA;AAAA,IACrB,aAAA,EAAe,OAAO,OAAA,EAAS;AAAA,GAChC,CAAA;AAKD,EAAA,OAAO,EAAE,GAAG,MAAA,EAAQ,SAAA,EAAW,QAAA,CAAS,eAAe,YAAA,EAAa;AACtE;AClVO,IAAM,yBAAA,GAA4B,IAAA;AAQlC,IAAM,mBAAA,GAAsB,CAAA;AAE5B,SAAS,aAAa,MAAA,EAAyD;AACpF,EAAA,MAAM,MAAM,WAAA,CAAYC,IAAAA,CAAK,MAAA,EAAO,EAAG,QAAQ,CAAC,CAAA;AAChD,EAAA,IAAI;AACF,IAAA,MAAM,MAAA,GAASA,IAAAA,CAAK,GAAA,EAAK,SAAS,CAAA;AAClC,IAAA,MAAM,OAAA,GAAUA,IAAAA,CAAK,GAAA,EAAK,SAAS,CAAA;AACnC,IAAA,aAAA,CAAc,QAAQ,MAAM,CAAA;AAC5B,IAAA,IAAI;AACF,MAAA,YAAA;AAAA,QACE,OAAA;AAAA,QACA,CAAC,SAAA,EAAW,sBAAA,EAAwB,OAAO,yBAAyB,CAAA,EAAG,QAAQ,OAAO,CAAA;AAAA,QACtF,EAAE,KAAA,EAAO,CAAC,QAAA,EAAU,MAAA,EAAQ,MAAM,CAAA;AAAE,OACtC;AAAA,IACF,SAAS,GAAA,EAAK;AACZ,MAAA,MAAM,SAAU,GAAA,CAA4B,MAAA;AAC5C,MAAA,OAAO,EAAE,KAAA,EAAO,MAAA,EAAQ,MAAA,GAAS,MAAA,CAAO,SAAS,MAAM,CAAA,GAAI,MAAA,CAAO,GAAG,CAAA,EAAE;AAAA,IACzE;AACA,IAAA,OAAO,EAAE,GAAA,EAAK,IAAI,WAAW,YAAA,CAAa,OAAO,CAAC,CAAA,EAAE;AAAA,EACtD,CAAA,SAAE;AACA,IAAA,MAAA,CAAO,KAAK,EAAE,SAAA,EAAW,IAAA,EAAM,KAAA,EAAO,MAAM,CAAA;AAAA,EAC9C;AACF;;;AC1BA,IAAM,cAAA,GAAiB,0CAAA;AAOhB,SAAS,0BAA0B,OAAA,EAAmD;AAC3F,EAAA,MAAM,GAAA,uBAAU,GAAA,EAAY;AAC5B,EAAA,IAAI,CAAC,SAAS,OAAO,GAAA;AAErB,EAAA,MAAM,EAAE,OAAM,GAAI,OAAA;AAClB,EAAA,GAAA,CAAI,GAAA,CAAI,KAAA,CAAM,IAAA,KAAS,YAAA,GAAe,KAAA,CAAM,cAAA,CAAe,KAAK,CAAA,GAAI,KAAA,CAAM,QAAA,CAAS,KAAK,CAAC,CAAA;AACzF,EAAA,KAAA,MAAW,IAAA,IAAQ,OAAA,CAAQ,KAAA,CAAM,KAAA,EAAO;AACtC,IAAA,IAAI,KAAK,IAAA,KAAS,UAAA,EAAY,GAAA,CAAI,GAAA,CAAI,KAAK,EAAE,CAAA;AAAA,EAC/C;AACA,EAAA,KAAA,MAAW,CAAA,IAAK,QAAQ,eAAA,EAAiB,OAAA,IAAW,EAAC,EAAG,GAAA,CAAI,GAAA,CAAI,CAAA,CAAE,UAAU,CAAA;AAC5E,EAAA,IAAI,KAAA,CAAM,SAAS,UAAA,EAAY;AAC7B,IAAA,KAAA,MAAW,EAAA,IAAM,MAAA,CAAO,IAAA,CAAK,KAAA,CAAM,OAAA,EAAS,OAAA,IAAW,EAAE,CAAA,EAAG,GAAA,CAAI,GAAA,CAAI,EAAE,CAAA;AAAA,EACxE;AACA,EAAA,OAAO,GAAA;AACT;AAEO,SAAS,qBAAA,CACd,OAAA,EACA,gBAAA,EACA,MAAA,EACsD;AACtD,EAAA,MAAM,YAAkC,EAAC;AACzC,EAAA,IAAI,KAAA,GAAQ,EAAA;AACZ,EAAA,IAAI,IAAA,GAAO,CAAA;AAEX,EAAA,KAAA,MAAW,KAAA,IAAS,OAAA,CAAQ,QAAA,CAAS,cAAc,CAAA,EAAG;AACpD,IAAA,MAAM,KAAA,GAAQ,MAAM,CAAC,CAAA;AACrB,IAAA,MAAM,eAAA,GAAkB,MAAM,CAAC,CAAA;AAC/B,IAAA,MAAM,YAAA,GAAe,MAAM,CAAC,CAAA;AAI5B,IAAA,KAAA,IAAS,OAAA,CAAQ,MAAM,IAAA,EAAM,KAAA,CAAM,KAAK,CAAA,CAAE,OAAA,CAAQ,WAAW,EAAE,CAAA;AAC/D,IAAA,IAAA,GAAO,KAAA,CAAM,QAAS,KAAA,CAAM,MAAA;AAE5B,IAAA,IAAI,CAAC,gBAAA,CAAiB,GAAA,CAAI,eAAe,CAAA,EAAG;AAC1C,MAAA,MAAA,CAAO,KAAK,iFAAA,EAA8E;AAAA,QACxF,UAAA,EAAY;AAAA,OACb,CAAA;AACD,MAAA;AAAA,IACF;AAIA,IAAA,MAAM,MAAM,KAAA,CAAM,MAAA;AAClB,IAAA,IAAI,KAAA,GAAQ,CAAA;AACZ,IAAA,KAAA,IAAS,CAAA,GAAI,GAAA,GAAM,CAAA,EAAG,CAAA,IAAK,GAAG,CAAA,EAAA,EAAK;AACjC,MAAA,MAAM,EAAA,GAAK,MAAM,CAAC,CAAA;AAClB,MAAA,IAAI,OAAO,GAAA,IAAO,EAAA,KAAO,OAAO,EAAA,KAAO,GAAA,IAAO,OAAO,IAAA,EAAM;AACzD,QAAA,KAAA,GAAQ,CAAA,GAAI,CAAA;AACZ,QAAA;AAAA,MACF;AAAA,IACF;AACA,IAAA,OAAO,QAAQ,GAAA,IAAO,IAAA,CAAK,KAAK,KAAA,CAAM,KAAK,CAAE,CAAA,EAAG,KAAA,EAAA;AAChD,IAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,KAAA,CAAM,KAAA,EAAO,GAAG,CAAA;AACpC,IAAA,IAAI,KAAA,CAAM,WAAW,CAAA,EAAG;AACtB,MAAA,MAAA,CAAO,KAAK,2DAAA,EAAwD;AAAA,QAClE,UAAA,EAAY;AAAA,OACb,CAAA;AACD,MAAA;AAAA,IACF;AAEA,IAAA,SAAA,CAAU,IAAA,CAAK;AAAA,MACb,UAAA,EAAY,eAAA;AAAA,MACZ,GAAI,YAAA,GAAe,EAAE,YAAA,KAAiB,EAAC;AAAA,MACvC,KAAA;AAAA,MACA,GAAA;AAAA,MACA;AAAA,KACD,CAAA;AAAA,EACH;AAEA,EAAA,KAAA,IAAS,OAAA,CAAQ,MAAM,IAAI,CAAA;AAC3B,EAAA,OAAO,EAAE,OAAA,EAAS,KAAA,EAAO,SAAA,EAAU;AACrC;;;ACxFA,eAAsB,UAAA,CACpB,KAAA,EACA,KAAA,EACA,MAAA,EACc;AACd,EAAA,MAAM,OAAA,GAAU,IAAI,KAAA,CAAS,KAAA,CAAM,MAAM,CAAA;AACzC,EAAA,IAAI,IAAA,GAAO,CAAA;AAEX,EAAA,eAAe,IAAA,GAAsB;AACnC,IAAA,OAAO,IAAA,EAAM;AACX,MAAA,MAAM,CAAA,GAAI,IAAA,EAAA;AACV,MAAA,IAAI,CAAA,IAAK,MAAM,MAAA,EAAQ;AACvB,MAAA,OAAA,CAAQ,CAAC,CAAA,GAAI,MAAM,OAAO,KAAA,CAAM,CAAC,GAAI,CAAC,CAAA;AAAA,IACxC;AAAA,EACF;AAEA,EAAA,MAAM,QAAA,GAAW,KAAK,GAAA,CAAI,CAAA,EAAG,KAAK,GAAA,CAAI,KAAA,EAAO,KAAA,CAAM,MAAM,CAAC,CAAA;AAC1D,EAAA,MAAM,OAAA,CAAQ,GAAA,CAAI,KAAA,CAAM,IAAA,CAAK,EAAE,MAAA,EAAQ,QAAA,EAAS,EAAG,MAAM,IAAA,EAAM,CAAC,CAAA;AAChE,EAAA,OAAO,OAAA;AACT;;;AC6EA,SAAS,QAAQ,CAAA,EAAwG;AACvH,EAAA,OAAO;AAAA,IACL,OAAO,CAAA,CAAE,KAAA;AAAA,IACT,OAAO,CAAA,CAAE,KAAA;AAAA,IACT,KAAK,CAAA,CAAE,GAAA;AAAA,IACP,GAAI,EAAE,MAAA,KAAW,MAAA,GAAY,EAAE,MAAA,EAAQ,CAAA,CAAE,MAAA,EAAO,GAAI,EAAC;AAAA,IACrD,GAAI,EAAE,MAAA,KAAW,MAAA,GAAY,EAAE,MAAA,EAAQ,CAAA,CAAE,MAAA,EAAO,GAAI;AAAC,GACvD;AACF;AAMA,SAAS,oBAAoB,GAAA,EAAsC;AACjE,EAAA,MAAM,SAAS,GAAA,CAAI,MAAA;AAGnB,EAAA,MAAM,SAAA,GAAY,MAAM,OAAA,CAAQ,MAAA,EAAQ,QAAQ,CAAA,GAAI,MAAA,CAAO,WAAW,EAAC;AACvE,EAAA,MAAM,MAAM,SAAA,CAAU,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,SAAS,sBAAsB,CAAA;AAOnE,EAAA,IAAI,MAAA;AACJ,EAAA,IAAI,GAAA,EAAK;AACP,IAAA,MAAA,GAAS,OAAO,GAAA,CAAI,KAAA,IAAS,GAAG,CAAA,CAAA,EAAI,GAAA,CAAI,OAAO,GAAG,CAAA,CAAA;AAAA,EACpD,CAAA,MAAO;AACL,IAAA,MAAM,QAAQ,SAAA,CAAU,MAAA,CAAO,CAAC,CAAA,KAAM,EAAE,IAAA,KAAS,kBAAkB,CAAA,CAAE,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,SAAS,EAAE,CAAA,CAAE,KAAK,GAAG,CAAA;AACvG,IAAA,MAAM,QAAQ,SAAA,CAAU,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,SAAS,mBAAmB,CAAA;AAClE,IAAA,MAAA,GAAS,CAAA,KAAA,EAAQ,KAAK,CAAA,OAAA,EAAU,KAAA,EAAO,KAAA,IAAS,EAAE,CAAA,CAAA,EAAI,KAAA,EAAO,MAAA,IAAU,EAAE,CAAA,CAAA,EAAI,KAAA,EAAO,UAAU,EAAE,CAAA,CAAA;AAAA,EAClG;AACA,EAAA,OAAO,CAAC,GAAA,CAAI,UAAA,EAAsB,MAAA,EAAQ,IAAA,CAAK,SAAA,CAAU,GAAA,CAAI,IAAA,IAAQ,IAAI,CAAC,CAAA,CAAE,IAAA,CAAK,GAAG,CAAA;AACtF;AA0BA,SAAS,eAAA,GAA+D;AACtE,EAAA,MAAM,IAAA,uBAAW,GAAA,EAAY;AAC7B,EAAA,OAAO,CAAC,WAAA,KAAgB;AACtB,IAAA,MAAM,MAAoB,EAAC;AAC3B,IAAA,KAAA,MAAW,OAAO,WAAA,EAAa;AAC7B,MAAA,MAAM,GAAA,GAAM,oBAAoB,GAAG,CAAA;AACnC,MAAA,IAAI,IAAA,CAAK,GAAA,CAAI,GAAG,CAAA,EAAG;AACnB,MAAA,IAAA,CAAK,IAAI,GAAG,CAAA;AACZ,MAAA,GAAA,CAAI,KAAK,GAAG,CAAA;AAAA,IACd;AACA,IAAA,OAAO,GAAA;AAAA,EACT,CAAA;AACF;AAgNA,eAAsB,oBACpB,OAAA,EACA,eAAA,EACA,QACA,eAAA,EACA,UAAA,EAEA,iBAIA,aAAA,EACwD;AACxD,EAAA,MAAM,IAAA,GAAO,cAAc,MAAM,CAAA;AAEjC,EAAA,UAAA,CAAW,EAAA,EAAI,EAAE,IAAA,EAAM,SAAA,IAAa,IAAI,CAAA;AACxC,EAAA,UAAA,CAAW,EAAA,EAAI,EAAE,IAAA,EAAM,WAAA,IAAe,IAAI,CAAA;AAE1C,EAAA,MAAM,SAAS,eAAA,EAAgB;AAI/B,EAAA,MAAM,KAAA,GAAQ,gBAAgB,cAAc,CAAA;AAC5C,EAAA,IAAI,KAAA,GAAQ,OAAO,KAAA,IAAS,CAAA;AAC5B,EAAA,IAAI,OAAA,GAAU,OAAO,OAAA,IAAW,CAAA;AAChC,EAAA,MAAM,mBAAA,CAAoB,gBAAA;AAAA,IACxB,OAAA;AAAA,IAAS,eAAA;AAAA,IAAiB,MAAA,CAAO,YAAA;AAAA,IAAc,MAAA,CAAO,OAAA;AAAA,IAAS,MAAA,CAAO,cAAA;AAAA;AAAA,IAEtE,CAAC,aAAA,EAAe,UAAA,KAAe,UAAA,CAAW,KAAK,IAAA,CAAK,KAAA,CAAO,aAAA,GAAgB,UAAA,GAAc,EAAE,CAAA,EAAG,EAAE,IAAA,EAAM,WAAA,IAAe,IAAI,CAAA;AAAA,IACzH,gBAAgB,cAAc,CAAA;AAAA,IAC9B,OAAO,SAAS,MAAA,KAAW;AACzB,MAAA,KAAA,IAAS,OAAA,CAAQ,MAAA;AAGjB,MAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,OAAA,CAAQ,GAAA,CAAI,CAAC,MAAM,eAAA,CAAgB,cAAA,EAAgB,CAAC,CAAC,CAAC,CAAA;AAC3E,MAAA,OAAA,IAAW,KAAA,CAAM,MAAA;AACjB,MAAA,UAAA,CAAW,IAAI,EAAE,IAAA,EAAM,wBAAwB,KAAA,EAAO,OAAA,IAAW,IAAI,CAAA;AAIrE,MAAA,MAAM,eAAA,CAAgB,KAAA,EAAO,EAAE,IAAA,EAAM,cAAA,EAAgB,MAAA,EAAQ,EAAE,GAAG,MAAA,EAAQ,KAAA,EAAO,OAAA,EAAS,OAAA,IAAW,CAAA;AAAA,IACvG;AAAA,GACF;AAEA,EAAA,UAAA,CAAW,GAAA,EAAK,EAAE,IAAA,EAAM,kBAAA,EAAoB,OAAO,OAAA,EAAS,IAAA,EAAM,WAAA,EAAY,EAAG,IAAI,CAAA;AAErF,EAAA,OAAO;AAAA,IACL,QAAQ,EAAE,IAAA,EAAM,wBAAwB,eAAA,EAAiB,KAAA,EAAO,mBAAmB,OAAA;AAAQ,GAC7F;AACF;AAcA,SAAS,cAAc,CAAA,EAIE;AACvB,EAAA,MAAM,gBAAsF,EAAC;AAC7F,EAAA,IAAI,CAAA,CAAE,YAAA,EAAc,IAAA,EAAK,gBAAiB,IAAA,CAAK,EAAE,KAAA,EAAO,cAAA,EAAgB,KAAA,EAAO,CAAA,CAAE,YAAA,CAAa,IAAA,IAAQ,CAAA;AACtG,EAAA,IAAI,CAAA,CAAE,IAAA,EAAM,IAAA,EAAK,gBAAiB,IAAA,CAAK,EAAE,KAAA,EAAO,MAAA,EAAQ,KAAA,EAAO,CAAA,CAAE,IAAA,CAAK,IAAA,IAAQ,CAAA;AAC9E,EAAA,IAAI,CAAA,CAAE,OAAA,KAAY,MAAA,EAAW,aAAA,CAAc,IAAA,CAAK,EAAE,KAAA,EAAO,SAAA,EAAW,KAAA,EAAO,MAAA,CAAO,CAAA,CAAE,OAAO,GAAG,CAAA;AAC9F,EAAA,OAAO,cAAc,MAAA,GAAS,CAAA,GAAI,EAAE,aAAA,KAAkB,EAAC;AACzD;AAEA,eAAsB,kBACpB,OAAA,EACA,eAAA,EACA,QACA,eAAA,EACA,UAAA,EAEA,iBAIA,aAAA,EACsD;AACtD,EAAA,MAAM,IAAA,GAAO,cAAc,MAAM,CAAA;AAEjC,EAAA,UAAA,CAAW,EAAA,EAAI,EAAE,IAAA,EAAM,SAAA,IAAa,IAAI,CAAA;AACxC,EAAA,UAAA,CAAW,EAAA,EAAI,EAAE,IAAA,EAAM,WAAA,IAAe,IAAI,CAAA;AAK1C,EAAA,MAAM,YAAA,GAAe,OAAO,QAAA,IAAY,IAAA;AACxC,EAAA,MAAM,SAAS,eAAA,EAAgB;AAI/B,EAAA,MAAM,KAAA,GAAQ,gBAAgB,YAAY,CAAA;AAC1C,EAAA,IAAI,KAAA,GAAQ,OAAO,KAAA,IAAS,CAAA;AAC5B,EAAA,IAAI,OAAA,GAAU,OAAO,OAAA,IAAW,CAAA;AAChC,EAAA,MAAM,mBAAA,CAAoB,cAAA;AAAA,IACxB,OAAA;AAAA,IAAS,eAAA;AAAA,IAAiB,MAAA,CAAO,YAAA;AAAA,IAAc,MAAA,CAAO,IAAA;AAAA,IAAM,MAAA,CAAO,OAAA;AAAA,IACnE,MAAA,CAAO,QAAA;AAAA,IAAU,MAAA,CAAO,cAAA;AAAA;AAAA,IAExB,CAAC,aAAA,EAAe,UAAA,KAAe,UAAA,CAAW,KAAK,IAAA,CAAK,KAAA,CAAO,aAAA,GAAgB,UAAA,GAAc,EAAE,CAAA,EAAG,EAAE,IAAA,EAAM,WAAA,IAAe,IAAI,CAAA;AAAA,IACzH,gBAAgB,YAAY,CAAA;AAAA,IAC5B,OAAO,UAAU,MAAA,KAAW;AAC1B,MAAA,KAAA,IAAS,QAAA,CAAS,MAAA;AAClB,MAAA,MAAM,KAAA,GAAQ,OAAO,QAAA,CAAS,GAAA;AAAA,QAAI,CAAC,CAAA;AAAA;AAAA;AAAA;AAAA,UAIjC,eAAA,CAAgB,cAAc,CAAA,EAAG;AAAA,YAC/B,EAAE,IAAA,EAAM,aAAA,EAAe,KAAA,EAAO,CAAA,CAAE,OAAA,EAAS,OAAA,EAAS,YAAA,EAAc,MAAA,EAAQ,YAAA,EAA2C,QAAA,EAAU,YAAA;AAAa,WAC3I;AAAA;AAAA,OACF,CAAA;AACD,MAAA,OAAA,IAAW,KAAA,CAAM,MAAA;AACjB,MAAA,UAAA,CAAW,IAAI,EAAE,IAAA,EAAM,wBAAwB,KAAA,EAAO,OAAA,IAAW,IAAI,CAAA;AACrE,MAAA,MAAM,eAAA,CAAgB,KAAA,EAAO,EAAE,IAAA,EAAM,YAAA,EAAc,MAAA,EAAQ,EAAE,GAAG,MAAA,EAAQ,KAAA,EAAO,OAAA,EAAS,OAAA,IAAW,CAAA;AAAA,IACrG;AAAA,GACF;AAEA,EAAA,UAAA,CAAW,GAAA,EAAK,EAAE,IAAA,EAAM,kBAAA,EAAoB,OAAO,OAAA,EAAS,IAAA,EAAM,SAAA,EAAU,EAAG,IAAI,CAAA;AAEnF,EAAA,OAAO;AAAA,IACL,QAAQ,EAAE,IAAA,EAAM,sBAAsB,aAAA,EAAe,KAAA,EAAO,iBAAiB,OAAA;AAAQ,GACvF;AACF;AAEA,eAAsB,qBACpB,OAAA,EACA,eAAA,EACA,QACA,eAAA,EACA,UAAA,EAEA,iBAIA,aAAA,EACyD;AACzD,EAAA,MAAM,IAAA,GAAO,cAAc,MAAM,CAAA;AAEjC,EAAA,UAAA,CAAW,EAAA,EAAI,EAAE,IAAA,EAAM,SAAA,IAAa,IAAI,CAAA;AACxC,EAAA,UAAA,CAAW,EAAA,EAAI,EAAE,IAAA,EAAM,WAAA,IAAe,IAAI,CAAA;AAE1C,EAAA,MAAM,YAAA,GAAe,OAAO,QAAA,IAAY,IAAA;AACxC,EAAA,MAAM,SAAS,eAAA,EAAgB;AAI/B,EAAA,MAAM,KAAA,GAAQ,gBAAgB,WAAW,CAAA;AACzC,EAAA,IAAI,KAAA,GAAQ,OAAO,KAAA,IAAS,CAAA;AAC5B,EAAA,IAAI,OAAA,GAAU,OAAO,OAAA,IAAW,CAAA;AAChC,EAAA,MAAM,mBAAA,CAAoB,iBAAA;AAAA,IACxB,OAAA;AAAA,IAAS,eAAA;AAAA,IAAiB,MAAA,CAAO,YAAA;AAAA,IAAc,MAAA,CAAO,IAAA;AAAA,IAAM,MAAA,CAAO,OAAA;AAAA,IACnE,MAAA,CAAO,QAAA;AAAA,IAAU,MAAA,CAAO,cAAA;AAAA;AAAA,IAExB,CAAC,aAAA,EAAe,UAAA,KAAe,UAAA,CAAW,KAAK,IAAA,CAAK,KAAA,CAAO,aAAA,GAAgB,UAAA,GAAc,EAAE,CAAA,EAAG,EAAE,IAAA,EAAM,WAAA,IAAe,IAAI,CAAA;AAAA,IACzH,gBAAgB,WAAW,CAAA;AAAA,IAC3B,OAAO,aAAa,MAAA,KAAW;AAC7B,MAAA,KAAA,IAAS,WAAA,CAAY,MAAA;AACrB,MAAA,MAAM,KAAA,GAAQ,OAAO,WAAA,CAAY,GAAA;AAAA,QAAI,CAAC,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAOpC,eAAA,CAAgB,aAAa,CAAA,EAAG;AAAA,YAC9B,IAAA,EAAM,aAAA;AAAA,YAAe,OAAO,CAAA,CAAE,UAAA;AAAA,YAAY,OAAA,EAAS,WAAA;AAAA,YAAa,MAAA,EAAQ,YAAA;AAAA,YAA2C,QAAA,EAAU;AAAA,WAC9H;AAAA;AAAA,OACF,CAAA;AACD,MAAA,OAAA,IAAW,KAAA,CAAM,MAAA;AACjB,MAAA,UAAA,CAAW,IAAI,EAAE,IAAA,EAAM,wBAAwB,KAAA,EAAO,OAAA,IAAW,IAAI,CAAA;AACrE,MAAA,MAAM,eAAA,CAAgB,KAAA,EAAO,EAAE,IAAA,EAAM,WAAA,EAAa,MAAA,EAAQ,EAAE,GAAG,MAAA,EAAQ,KAAA,EAAO,OAAA,EAAS,OAAA,IAAW,CAAA;AAAA,IACpG;AAAA,GACF;AAEA,EAAA,UAAA,CAAW,GAAA,EAAK,EAAE,IAAA,EAAM,kBAAA,EAAoB,OAAO,OAAA,EAAS,IAAA,EAAM,YAAA,EAAa,EAAG,IAAI,CAAA;AAEtF,EAAA,OAAO;AAAA,IACL,QAAQ,EAAE,IAAA,EAAM,yBAAyB,gBAAA,EAAkB,KAAA,EAAO,oBAAoB,OAAA;AAAQ,GAChG;AACF;AAWA,eAAsB,mBAAA,CACpB,OAAA,EACA,eAAA,EACA,MAAA,EACA,eAAA,EACA,YACA,MAAA,EAOA,cAAA,EACA,MAAA,EAEA,eAAA,EAIA,aAAA,EACmD;AACnD,EAAA,MAAM,eAAA,GAAkB,MAAA,CAAO,WAAA,CAAY,GAAA,CAAI,MAAM,CAAA;AACrD,EAAA,MAAM,aAAA,GAAgB,CAAC,EAAE,KAAA,EAAO,cAAA,EAAyB,OAAO,eAAA,CAAgB,IAAA,CAAK,IAAI,CAAA,EAAG,CAAA;AAC5F,EAAA,MAAM,iBAAkC,EAAC;AAMzC,EAAA,IAAI,UAAA,GAAa,MAAA,CAAO,MAAA,CAAO,aAAA,IAAiB,EAAE,CAAA,CAAE,MAAA,CAAO,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,GAAI,CAAA,CAAE,OAAO,CAAC,CAAA;AACnF,EAAA,IAAI,YAAA,GAAe,MAAA,CAAO,MAAA,CAAO,aAAA,IAAiB,EAAE,CAAA,CAAE,MAAA,CAAO,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,GAAI,CAAA,CAAE,SAAS,CAAC,CAAA;AACvF,EAAA,IAAI,MAAA,GAAS,CAAA;AACb,EAAA,IAAI,wBAAA,GAA2B,CAAA;AAG/B,EAAA,IAAI,aAAA,GAAgB,CAAA;AAEpB,EAAA,UAAA,CAAW,IAAI,EAAE,IAAA,EAAM,WAAU,EAAG,EAAE,eAAe,CAAA;AAErD,EAAA,MAAM,YAAA,GAAe,OAAO,QAAA,IAAY,IAAA;AAaxC,EAAA,IAAI,SAAA,GAAY,CAAA;AAChB,EAAA,MAAM,QAAQ,eAAA,CAAgB,MAAA;AAE9B,EAAA,MAAM,gBAAA,GAAmB,CAAC,cAAA,KAAiC;AACzD,IAAA,MAAM,MAAM,EAAA,GAAK,IAAA,CAAK,KAAA,CAAO,SAAA,GAAY,QAAS,EAAE,CAAA;AACpD,IAAA,UAAA,CAAW,KAAK,EAAE,IAAA,EAAM,oBAAA,EAAsB,UAAA,EAAY,gBAAe,EAAG;AAAA,MAC1E,OAAA,EAAS,EAAE,IAAA,EAAM,aAAA,EAAe,OAAO,cAAA,EAAe;AAAA,MACtD,SAAA,EAAW,SAAA;AAAA,MACX,KAAA;AAAA,MACA,aAAA,EAAe,UAAA;AAAA,MACf,GAAI,aAAA,GAAgB,CAAA,GAAI,EAAE,gBAAA,EAAkB,aAAA,KAAkB,EAAC;AAAA,MAC/D,eAAA,EAAiB,YAAA;AAAA,MACjB,cAAA,EAAgB,CAAC,GAAG,cAAc,CAAA;AAAA,MAClC;AAAA,KACD,CAAA;AAAA,EACH,CAAA;AAKA,EAAA,MAAM,UAAA,CAAW,eAAA,EAAiB,eAAA,CAAgB,cAAA,EAAgB,OAAO,cAAA,KAAmB;AAC1F,IAAA,IAAI,CAAC,cAAA,EAAgB;AAKrB,IAAA,IAAI,QAAQ,OAAA,EAAS;AAErB,IAAA,gBAAA,CAAiB,cAAc,CAAA;AAO/B,IAAA,MAAM,cAAA,GAAqC;AAAA,MACzC,EAAE,IAAA,EAAM,aAAA,EAAwB,KAAA,EAAO,cAAA,EAAgB,SAAS,SAAA,EAAoB,MAAA,EAAQ,YAAA,EAA2C,QAAA,EAAU,YAAA;AAAa,KAChK;AAGA,IAAA,MAAM,SAAS,eAAA,EAAgB;AAK/B,IAAA,MAAM,SAAA,GAAY,gBAAgB,cAAc,CAAA;AAChD,IAAA,IAAI,SAAA,GAAY,WAAW,KAAA,IAAS,CAAA;AACpC,IAAA,IAAI,aAAA,GAAgB,WAAW,OAAA,IAAW,CAAA;AAE1C,IAAA,IAAI,aAAA;AACJ,IAAA,MAAM,eAAA;AAAA,MACJ,OAAA;AAAA,MAAS,CAAC,cAAc,CAAA;AAAA,MAAG,eAAA;AAAA,MAAiB,OAAO,4BAAA,IAAgC,KAAA;AAAA,MAAO,MAAA;AAAA,MAC1F,MAAA,CAAO,cAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAMP,MAAM,iBAAiB,cAAc,CAAA;AAAA,MACrC,CAAC,OAAA,KAAY;AACX,QAAA,aAAA,GAAgB;AAAA,UACd,MAAA,EAAA,CAAS,aAAA,EAAe,MAAA,IAAU,CAAA,IAAK,CAAA;AAAA,UACvC,KAAA,EAAA,CAAQ,aAAA,EAAe,KAAA,IAAS,CAAA,IAAK,OAAA,CAAQ,KAAA;AAAA,UAC7C,OAAA,EAAA,CAAU,aAAA,EAAe,OAAA,IAAW,CAAA,IAAK,OAAA,CAAQ;AAAA,SACnD;AAAA,MACF,CAAA;AAAA,MACA,CAAC,OAAA,KAAY;AACX,QAAA,aAAA,IAAiB,OAAA;AACjB,QAAA,gBAAA,CAAiB,cAAc,CAAA;AAAA,MACjC,CAAA;AAAA,MACA,gBAAgB,cAAc,CAAA;AAAA,MAC9B,OAAO,eAAe,MAAA,KAAW;AAC/B,QAAA,MAAM,QAAsB,EAAC;AAC7B,QAAA,KAAA,MAAW,UAAU,aAAA,EAAe;AAClC,UAAA,MAAM,UAAA,GAAaC,kBAAkB,OAAA,EAAS;AAAA,YAC5C,OAAO,MAAA,CAAO,KAAA;AAAA,YACd,GAAI,OAAO,MAAA,KAAW,MAAA,GAAY,EAAE,MAAA,EAAQ,MAAA,CAAO,MAAA,EAAO,GAAI,EAAC;AAAA,YAC/D,GAAI,OAAO,MAAA,KAAW,MAAA,GAAY,EAAE,MAAA,EAAQ,MAAA,CAAO,MAAA,EAAO,GAAI;AAAC,WAChE,CAAA;AACD,UAAA,IAAI,CAAC,UAAA,EAAY;AACf,YAAA,MAAA,CAAO,MAAM,gDAAA,EAA6C;AAAA,cACxD,MAAM,MAAA,CAAO,KAAA;AAAA,cACb,YAAY,MAAA,CAAO;AAAA,aACpB,CAAA;AACD,YAAA,MAAA,EAAA;AACA,YAAA;AAAA,UACF;AACA,UAAA,UAAA,CAAW,WAAA,EAAa,MAAA,CAAO,KAAA,EAAO,UAAA,CAAW,cAAc,MAAM,CAAA;AACrE,UAAA,KAAA,CAAM,KAAK,eAAA,CAAgB,SAAA,EAAW,QAAQ,UAAU,CAAA,EAAG,cAAc,CAAC,CAAA;AAAA,QAC5E;AACA,QAAA,MAAM,KAAA,GAAQ,OAAO,KAAK,CAAA;AAM1B,QAAA,MAAM,SAAA,GAAY,YAAY,aAAA,CAAc,MAAA;AAC5C,QAAA,MAAM,WAAA,GAAc,gBAAgB,KAAA,CAAM,MAAA;AAI1C,QAAA,MAAM,kBAAkB,KAAA,EAAO;AAAA,UAC7B,IAAA,EAAM,cAAA;AAAA,UACN,QAAQ,EAAE,GAAG,QAAQ,KAAA,EAAO,SAAA,EAAW,SAAS,WAAA;AAAY,SAC7D,CAAA;AAKD,QAAA,SAAA,GAAY,SAAA;AACZ,QAAA,aAAA,GAAgB,WAAA;AAChB,QAAA,UAAA,IAAc,aAAA,CAAc,MAAA;AAC5B,QAAA,YAAA,IAAgB,KAAA,CAAM,MAAA;AACtB,QAAA,gBAAA,CAAiB,cAAc,CAAA;AAAA,MACjC;AAAA,KACF;AAGA,IAAA,MAAM,eAAe,cAAc,CAAA;AAEnC,IAAA,cAAA,CAAe,IAAA,CAAK;AAAA,MAClB,KAAA,EAAO,cAAA;AAAA,MACP,UAAA,EAAY,SAAA;AAAA,MACZ,cAAA,EAAgB,aAAA;AAAA,MAChB,GAAI,aAAA,GAAgB,EAAE,aAAA,KAAkB;AAAC,KAC1C,CAAA;AACD,IAAA,IAAI,aAAA,8BAA2C,aAAA,CAAc,MAAA;AAC7D,IAAA,SAAA,EAAA;AACA,IAAA,gBAAA,CAAiB,cAAc,CAAA;AAAA,EACjC,CAAC,CAAA;AAMD,EAAA,UAAA,CAAW,GAAA,EAAK,EAAE,IAAA,EAAM,kBAAA,EAAoB,OAAO,YAAA,EAAc,IAAA,EAAM,aAAY,EAAG;AAAA,IACpF,GAAI,aAAA,GAAgB,CAAA,GAAI,EAAE,gBAAA,EAAkB,aAAA,KAAkB,EAAC;AAAA,IAC/D,cAAA,EAAgB,CAAC,GAAG,cAAc,CAAA;AAAA,IAClC;AAAA,GACD,CAAA;AAED,EAAA,OAAO;AAAA,IACL,MAAA,EAAQ;AAAA,MACN,IAAA,EAAM,sBAAA;AAAA,MAAwB,UAAA;AAAA,MAAY,YAAA;AAAA,MAAc,MAAA;AAAA,MACxD,GAAI,wBAAA,GAA2B,CAAA,GAAI,EAAE,mBAAA,EAAqB,wBAAA,KAA6B;AAAC;AAC1F,GACF;AACF;AAEA,eAAsB,cACpB,OAAA,EACA,eAAA,EACA,QACA,eAAA,EACA,UAAA,EAEA,iBAIA,aAAA,EACkD;AAClD,EAAA,UAAA,CAAW,EAAA,EAAI,EAAE,IAAA,EAAM,SAAA,EAAW,CAAA;AAClC,EAAA,UAAA,CAAW,EAAA,EAAI,EAAE,IAAA,EAAM,gBAAA,EAAkB,CAAA;AAEzC,EAAA,MAAM,YAAA,GAAe,OAAO,QAAA,IAAY,IAAA;AAGxC,EAAA,MAAM,SAAS,eAAA,EAAgB;AAO/B,EAAA,IAAI,KAAA,GAAQ,CAAA;AACZ,EAAA,IAAI,OAAA,GAAU,MAAA,CAAO,MAAA,CAAO,aAAA,IAAiB,EAAE,CAAA,CAAE,MAAA,CAAO,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,GAAI,CAAA,CAAE,SAAS,CAAC,CAAA;AAGlF,EAAA,MAAM,aAAqC,EAAC;AAC5C,EAAA,MAAM,iBAAkC,EAAC;AAEzC,EAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,MAAA,CAAO,UAAA,CAAW,QAAQ,CAAA,EAAA,EAAK;AACjD,IAAA,MAAM,QAAA,GAAW,MAAA,CAAO,UAAA,CAAW,CAAC,CAAA;AAGpC,IAAA,MAAM,WAAW,OAAO;AAAA,MACtB,OAAA,EAAS,EAAE,IAAA,EAAM,UAAA,EAAqB,OAAO,QAAA,EAAS;AAAA,MACtD,SAAA,EAAW,CAAA;AAAA,MACX,KAAA,EAAO,OAAO,UAAA,CAAW,MAAA;AAAA,MACzB,cAAA,EAAgB,CAAC,GAAG,cAAc;AAAA,KACpC,CAAA;AACA,IAAA,UAAA;AAAA,MACE,KAAK,IAAA,CAAK,KAAA,CAAO,IAAI,MAAA,CAAO,UAAA,CAAW,SAAU,EAAE,CAAA;AAAA,MACnD,EAAE,MAAM,gBAAA,EAAiB;AAAA,MACzB,QAAA;AAAS,KACX;AAGA,IAAA,MAAM,aAAA,GAAgB,gBAAgB,QAAQ,CAAA;AAC9C,IAAA,IAAI,aAAA,GAAgB,eAAe,KAAA,IAAS,CAAA;AAC5C,IAAA,IAAI,eAAA,GAAkB,eAAe,OAAA,IAAW,CAAA;AAChD,IAAA,MAAM,mBAAA,CAAoB,UAAA;AAAA,MACxB,OAAA;AAAA,MAAS,eAAA;AAAA,MAAiB,MAAA,CAAO,MAAA;AAAA,MAAQ,QAAA;AAAA,MAAU,MAAA,CAAO,cAAA;AAAA;AAAA;AAAA,MAG1D,CAAC,eAAe,UAAA,KAAe,UAAA;AAAA,QAC7B,EAAA,GAAK,KAAK,KAAA,CAAA,CAAQ,CAAA,GAAI,gBAAgB,UAAA,IAAc,MAAA,CAAO,UAAA,CAAW,MAAA,GAAU,EAAE,CAAA;AAAA,QAClF,EAAE,MAAM,gBAAA,EAAiB;AAAA,QACzB,QAAA;AAAS,OACX;AAAA,MACA,gBAAgB,QAAQ,CAAA;AAAA,MACxB,OAAO,SAAS,MAAA,KAAW;AACzB,QAAA,aAAA,IAAiB,OAAA,CAAQ,MAAA;AACzB,QAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,OAAA,CAAQ,GAAA,CAAI,CAAC,CAAA,KAAM;AACtC,UAAA,MAAM,GAAA,GAAM,EAAE,QAAA,IAAY,SAAA;AAM1B,UAAA,OAAO,eAAA,CAAgB,WAAW,CAAA,EAAG;AAAA,YACnC,EAAE,IAAA,EAAM,aAAA,EAAe,KAAA,EAAO,GAAA,EAAkB,SAAS,SAAA,EAAe,MAAA,EAAQ,YAAA,EAA2C,QAAA,EAAU,YAAA,EAAa;AAAA,YAClJ,EAAE,IAAA,EAAM,aAAA,EAAe,KAAA,EAAO,MAAA,CAAO,OAAO,EAAA,EAAI,OAAA,EAAS,aAAA,EAAe,MAAA,EAAQ,YAAA;AAA0C,WAC3H,CAAA;AAAA,QACH,CAAC,CAAC,CAAA;AACF,QAAA,OAAA,IAAW,KAAA,CAAM,MAAA;AACjB,QAAA,KAAA,MAAW,OAAO,KAAA,EAAO;AACvB,UAAA,MAAM,OAAQ,GAAA,CAA8C,IAAA;AAC5D,UAAA,MAAM,GAAA,GAAM,KAAA,CAAM,OAAA,CAAQ,IAAI,KAAK,OAAO,IAAA,CAAK,CAAC,CAAA,EAAG,KAAA,KAAU,QAAA,GAAW,IAAA,CAAK,CAAC,EAAE,KAAA,GAAQ,SAAA;AACxF,UAAA,UAAA,CAAW,GAAG,CAAA,GAAA,CAAK,UAAA,CAAW,GAAG,KAAK,CAAA,IAAK,CAAA;AAAA,QAC7C;AACA,QAAA,UAAA,CAAW,IAAI,EAAE,IAAA,EAAM,0BAAA,EAA4B,KAAA,EAAO,SAAS,CAAA;AAOnE,QAAA,eAAA,IAAmB,KAAA,CAAM,MAAA;AACzB,QAAA,MAAM,gBAAgB,KAAA,EAAO;AAAA,UAC3B,IAAA,EAAM,QAAA;AAAA,UACN,QAAQ,EAAE,GAAG,QAAQ,KAAA,EAAO,aAAA,EAAe,SAAS,eAAA;AAAgB,SACrE,CAAA;AAAA,MACH;AAAA,KACF;AACA,IAAA,KAAA,IAAS,aAAA;AACT,IAAA,cAAA,CAAe,KAAK,EAAE,KAAA,EAAO,QAAA,EAAU,UAAA,EAAY,eAAe,CAAA;AAAA,EACpE;AAEA,EAAA,UAAA,CAAW,GAAA,EAAK,EAAE,IAAA,EAAM,kBAAA,EAAoB,OAAO,OAAA,EAAS,IAAA,EAAM,OAAO,CAAA;AAEzE,EAAA,OAAO;AAAA,IACL,MAAA,EAAQ,EAAE,IAAA,EAAM,gBAAA,EAAkB,WAAW,KAAA,EAAO,WAAA,EAAa,SAAS,UAAA;AAAW,GACvF;AACF;AASO,SAAS,yBAAyB,UAAA,EAA0B;AACjE,EAAA,IAAI,CAAC,gBAAA,CAAiB,UAAU,CAAA,EAAG;AACjC,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,mDAAA,EAAsD,UAAU,CAAA,SAAA,EAAY,aAAa,CAAA,gCAAA;AAAA,KAC3F;AAAA,EACF;AACF;AAEA,eAAsB,oBAAA,CACpB,eAAA,EACA,MAAA,EACA,UAAA,EACA,MAAA,EACwI;AAKxI,EAAA,MAAM,eAAA,GAAsC,OAAO,eAAA,IAAmB,eAAA;AACtE,EAAA,IAAI,CAAC,uBAAA,CAAwB,QAAA,CAAS,eAAe,CAAA,EAAG;AACtD,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,+CAA+C,eAAe,CAAA,sBAAA,EAAyB,uBAAA,CAAwB,IAAA,CAAK,MAAM,CAAC,CAAA,CAAA;AAAA,KAC7H;AAAA,EACF;AAEA,EAAA,MAAM,KAAA,GAAQ,OAAO,KAAA,IAAS,UAAA;AAC9B,EAAA,MAAM,eAAe,MAAA,CAAO,WAAA,IAAe,EAAC,EAAG,IAAI,MAAM,CAAA;AAOzD,EAAA,IAAI,oBAAoB,iBAAA,EAAmB;AACzC,IAAA,UAAA,CAAW,CAAA,EAAG,EAAE,IAAA,EAAM,qBAAA,EAAuB,CAAA;AAO7C,IAAA,MAAM,WAAW,MAAA,CAAO,IAAA,KAAS,OAAO,yBAAA,CAA0B,MAAA,CAAO,OAAO,CAAA,GAAI,IAAA;AAEpF,IAAA,IAAIC,aAAY,MAAM,yBAAA;AAAA,MACpB,KAAA;AAAA,MAAO,WAAA;AAAA,MAAa,eAAA;AAAA,MAAiB,MAAA;AAAA,MACrC,MAAA,CAAO,MAAA;AAAA,MAAQ,MAAA,CAAO,QAAA;AAAA,MAAU,MAAA,CAAO,OAAA;AAAA,MAAS,MAAA,CAAO,WAAA;AAAA,MACvD,MAAA,CAAO,SAAA;AAAA,MAAW,MAAA,CAAO,cAAA;AAAA,MAAgB,eAAA;AAAA,MACzC,MAAA,CAAO,IAAA;AAAA,MAAM,MAAA,CAAO,SAAA;AAAA,MAAW,MAAA,CAAO;AAAA,KACxC;AACA,IAAA,IAAI,SAASA,UAAAA,CAAU,OAAA;AACvB,IAAA,IAAIC,aAAkC,EAAC;AACvC,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA,MAAM,QAAA,GAAW,qBAAA,CAAsBD,UAAAA,CAAU,OAAA,EAAS,UAAU,MAAM,CAAA;AAC1E,MAAA,MAAA,GAAS,QAAA,CAAS,OAAA;AAClB,MAAAC,aAAY,QAAA,CAAS,SAAA;AAAA,IACvB;AACA,IAAA,IAAI,QAAA,GAAW,aAAa,MAAM,CAAA;AAClC,IAAA,IAAI,OAAA,GAAU,CAAA;AACd,IAAA,OAAO,WAAW,QAAA,EAAU;AAM1B,MAAA,IAAID,WAAU,SAAA,EAAW;AACvB,QAAA,MAAM,IAAI,KAAA;AAAA,UACR,gDAAgD,MAAA,CAAO,SAAA,IAAa,kBAAkB,CAAA,kHAAA,EAAgH,SAAS,KAAK,CAAA;AAAA,SACtN;AAAA,MACF;AACA,MAAA,IAAI,WAAW,mBAAA,EAAqB;AAClC,QAAA,MAAM,IAAI,KAAA;AAAA,UACR,CAAA,+BAAA,EAAkC,mBAAmB,CAAA,kBAAA,EAAqB,QAAA,CAAS,KAAK,CAAA;AAAA,SAC1F;AAAA,MACF;AACA,MAAA,OAAA,EAAA;AACA,MAAA,MAAA,CAAO,KAAK,+DAAA,EAA4D;AAAA,QACtE,OAAA,EAAS,OAAA;AAAA,QACT,KAAA,EAAO,QAAA,CAAS,KAAA,CAAM,KAAA,CAAM,GAAG,GAAG;AAAA,OACnC,CAAA;AACD,MAAAA,aAAY,MAAM,yBAAA;AAAA,QAChB,KAAA;AAAA,QAAO,WAAA;AAAA,QAAa,eAAA;AAAA,QAAiB,MAAA;AAAA,QACrC,MAAA,CAAO,MAAA;AAAA,QAAQ,MAAA,CAAO,QAAA;AAAA,QAAU,MAAA,CAAO,OAAA;AAAA,QAAS,MAAA,CAAO,WAAA;AAAA,QACvD,MAAA,CAAO,SAAA;AAAA,QAAW,MAAA,CAAO,cAAA;AAAA,QAAgB,eAAA;AAAA,QACzC,MAAA,CAAO,IAAA;AAAA,QAAM,MAAA,CAAO,SAAA;AAAA,QAAW,MAAA,CAAO,IAAA;AAAA,QACtC,EAAE,MAAA,EAAQ,KAAA,EAAO,QAAA,CAAS,KAAA;AAAM,OAClC;AACA,MAAA,IAAI,QAAA,EAAU;AACZ,QAAA,MAAM,QAAA,GAAW,qBAAA,CAAsBA,UAAAA,CAAU,OAAA,EAAS,UAAU,MAAM,CAAA;AAC1E,QAAA,MAAA,GAAS,QAAA,CAAS,OAAA;AAClB,QAAAC,aAAY,QAAA,CAAS,SAAA;AAAA,MACvB,CAAA,MAAO;AACL,QAAA,MAAA,GAASD,UAAAA,CAAU,OAAA;AAAA,MACrB;AACA,MAAA,QAAA,GAAW,aAAa,MAAM,CAAA;AAAA,IAChC;AAEA,IAAA,wBAAA,CAAyB,QAAA,CAAS,IAAI,UAAU,CAAA;AAChD,IAAA,UAAA,CAAW,EAAA,EAAI,EAAE,IAAA,EAAM,mBAAA,EAAqB,CAAA;AAM5C,IAAA,UAAA,CAAW,KAAK,EAAE,IAAA,EAAM,sBAAsB,SAAA,EAAWA,UAAAA,CAAU,WAAW,CAAA;AAE9E,IAAA,OAAO;AAAA,MACL,SAAS,QAAA,CAAS,GAAA;AAAA,MAClB,KAAA;AAAA,MACA,MAAA,EAAQ,eAAA;AAAA,MACR,SAAA,EAAAC,UAAAA;AAAA,MACA,MAAA,EAAQ;AAAA,QACN,IAAA,EAAM,YAAA;AAAA,QACN,UAAA,EAAY,EAAA;AAAA,QACZ,YAAA,EAAc,KAAA;AAAA,QACd,WAAWD,UAAAA,CAAU;AAAA;AACvB,KACF;AAAA,EACF;AAQA,EAAA,UAAA,CAAW,CAAA,EAAG,EAAE,IAAA,EAAM,qBAAA,EAAuB,CAAA;AAE7C,EAAA,MAAM,YAAY,MAAM,yBAAA;AAAA,IACtB,KAAA;AAAA,IACA,WAAA;AAAA,IACA,eAAA;AAAA,IACA,MAAA;AAAA,IACA,MAAA,CAAO,MAAA;AAAA,IACP,MAAA,CAAO,QAAA;AAAA,IACP,MAAA,CAAO,OAAA;AAAA,IACP,MAAA,CAAO,WAAA;AAAA,IACP,MAAA,CAAO,SAAA;AAAA,IACP,MAAA,CAAO,cAAA;AAAA,IACP,eAAA;AAAA,IACA,MAAA,CAAO,IAAA;AAAA,IACP,MAAA,CAAO,SAAA;AAAA,IACP,MAAA,CAAO;AAAA,GACT;AAMA,EAAA,IAAI,UAAU,SAAA,CAAU,OAAA;AACxB,EAAA,IAAI,YAAkC,EAAC;AACvC,EAAA,IAAI,MAAA,CAAO,SAAS,IAAA,EAAM;AACxB,IAAA,MAAM,WAAW,qBAAA,CAAsB,OAAA,EAAS,0BAA0B,MAAA,CAAO,OAAO,GAAG,MAAM,CAAA;AACjG,IAAA,OAAA,GAAU,QAAA,CAAS,OAAA;AACnB,IAAA,SAAA,GAAY,QAAA,CAAS,SAAA;AAAA,EACvB;AAEA,EAAA,UAAA,CAAW,EAAA,EAAI,EAAE,IAAA,EAAM,mBAAA,EAAqB,CAAA;AAK5C,EAAA,MAAM,QAAA,GAAW,IAAI,WAAA,EAAY,CAAE,OAAO,OAAO,CAAA;AACjD,EAAA,wBAAA,CAAyB,SAAS,UAAU,CAAA;AAO5C,EAAA,UAAA,CAAW,KAAK,EAAE,IAAA,EAAM,sBAAsB,SAAA,EAAW,SAAA,CAAU,WAAW,CAAA;AAE9E,EAAA,OAAO;AAAA,IACL,OAAA,EAAS,QAAA;AAAA,IACT,KAAA;AAAA,IACA,MAAA,EAAQ,eAAA;AAAA,IACR,SAAA;AAAA,IACA,MAAA,EAAQ;AAAA,MACN,IAAA,EAAM,YAAA;AAAA,MACN,UAAA,EAAY,EAAA;AAAA,MACZ,YAAA,EAAc,KAAA;AAAA,MACd,WAAW,SAAA,CAAU;AAAA;AACvB,GACF;AACF;ACt7BO,IAAM,qBAAqB,EAAA,GAAK;AA8DhC,IAAM,yBAAA,GAA4B;AAAA,EACvC,WAAA;AAAA,EACA,2BAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,gCAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAKA,aAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAKA;AACF,CAAA;AAgBO,IAAM,0BAAA,GAA6B;AAAA;AAAA,EAExC,YAAA;AAAA;AAAA,EAEA;AACF;AASO,IAAM,eAAA,GAA+C;AAAA,EAC1D,GAAG,iBAAiB,yBAAyB,CAAA;AAAA,EAC7C,GAAG;AACL","file":"index.js","sourcesContent":["import type { AnyJob } from './types';\nimport type { EventMap, JobId, UnitCursor } from '@semiont/core';\n\nexport interface JobQueue {\n initialize(): Promise<void>;\n destroy(): void;\n createJob(job: AnyJob): Promise<void>;\n getJob(jobId: JobId): Promise<AnyJob | null>;\n /**\n * Atomically claim the NEXT pending job matching one of `types` — the ONE\n * transition that makes this a queue rather than a state store\n * (JOB-QUEUE-DRIVER P0; reshaped claim-by-TYPE in P2, while every worker\n * is still ours). pending → running, `startedAt` stamped, progress empty.\n * An announcement is a WAKE-UP, not a reservation: the claimed job may\n * differ from any announced one, no ordering among matching pending jobs\n * is promised, and an empty `types` accepts any type. Simultaneous claims\n * admit one winner PER pending job; a claim that finds nothing is\n * DECLINED (`none-available`), never an error.\n */\n claimNextJob(types: string[]): Promise<{ job: AnyJob } | { declined: 'none-available' }>;\n /** Move a running job to `complete`. Returns false if the job isn't running. */\n completeJob(jobId: JobId, result: Record<string, unknown>): Promise<boolean>;\n /**\n * Move a running job back to `pending` (retry, re-announced) while\n * `retryCount < maxRetries`, else to `failed`. Returns what happened,\n * or null if the job isn't running. `completedUnits` — the units the\n * failing attempt fully emitted — are unioned into the record's\n * checkpoint (ABANDONED-INFERENCE P2) so a retry skips them. A\n * `failureClass` of 'deterministic' goes straight to `failed` with any\n * budget remaining — a second identical attempt cannot succeed (P3).\n */\n failJob(jobId: JobId, error: string, completedUnits?: string[], failureClass?: 'transient' | 'deterministic', unitCursors?: Record<string, UnitCursor>): Promise<'retried' | 'failed' | null>;\n /**\n * Persist a running job's completed-unit checkpoint AT unit completion —\n * not only when a job fails (JOB-RESTART-SAFETY P2). `failJob` carries the\n * checkpoint on a clean failure, but a worker that DIES (crash/OOM/kill)\n * never emits `job:fail`, so its finished units would be lost and the\n * janitor's recovery would redo them. This writes them into the running\n * file's metadata as each unit lands, unioned with any existing\n * checkpoint, so recovery resumes rather than restarts. Unthrottled (a\n * unit completion must never be dropped); a no-op for non-running jobs.\n *\n * `unitCursors` is the finer grain `completedUnits` cannot express\n * (CHUNK-GRAIN-RESUME P2): how far an UNFINISHED unit got, so a job that\n * dies mid-unit resumes there instead of at the top. It is written per\n * committed chunk, not per unit.\n *\n * **The two merge differently, and the difference is the contract.**\n * `completedUnits` is a set, so a union converges under concurrent snapshots\n * — a set only grows. A cursor converges only if the merge is **monotone per\n * unit**: a stale snapshot must never move one backward. `next` and `size`\n * move TOGETHER as one observation; taking `size` from one snapshot and\n * `next` from another would describe a chunk that never existed. A unit that\n * reaches `completedUnits` drops its cursor, so \"in progress with a cursor\"\n * and \"complete\" stay structurally exclusive rather than by convention.\n *\n * The rule belongs here rather than to any one driver because it is what\n * makes a cursor safe to merge at all.\n */\n checkpointUnits(jobId: JobId, completedUnits: string[], unitCursors?: Record<string, UnitCursor>): Promise<void>;\n /** Write progress into a running job's file (throttled, best-effort). */\n recordProgress(jobId: JobId, progress: Record<string, unknown>): Promise<void>;\n /**\n * Cancel all pending jobs in a category — 'generation' is the\n * `generation` type; 'annotation' is every `*-annotation` type.\n * Running jobs are left to finish. Returns the number cancelled.\n */\n cancelPendingJobs(category: 'annotation' | 'generation'): Promise<number>;\n cancelJob(jobId: JobId): Promise<boolean>;\n getStats(): Promise<{ pending: number; running: number; complete: number; failed: number; cancelled: number }>;\n}\n\n/**\n * Every bus channel a queue DRIVER emits, maintained beside the interface\n * both drivers implement and censused against their sources\n * (queue-emits-census.test.ts). The gateway's signal bridge forwards these\n * from its local bus onto the plane — a queue announcement that stays on\n * the raw bus reaches no worker under a remote driver (the job:queued\n * starvation, 2026-09-15).\n */\nexport const JOB_QUEUE_EMITS = ['job:queued'] as const satisfies readonly (keyof EventMap)[];\n","import type { JobMetadata } from './types';\n\n/**\n * Will this failure be re-queued for another attempt?\n *\n * ONE decision site, deliberately (JOB-RESTART-SAFETY P5). Two places need\n * the answer and they must never disagree:\n *\n * - `FsJobQueue.failJob` acts on it — re-queue or write the terminal record;\n * - the worker reports it on `job:fail` as `willRetry`, so a client's\n * job-watch stream knows whether the failure it just saw is the end.\n *\n * A second copy of this predicate would be the classic N-places-decide-one-\n * thing bug: the client would end a stream the queue then revived, or hold a\n * stream open on a job nobody will run again. The worker reads the budget off\n * the record it claimed, and `retryCount` only changes inside `failJob`, so\n * the two evaluations see the same numbers.\n *\n * A known-deterministic failure skips the budget outright — the same request\n * cannot succeed on a second attempt, so a retry is guaranteed waste at full\n * price. Unclassified failures are treated as transient.\n */\nexport function willRetryAfter(\n metadata: Pick<JobMetadata, 'retryCount' | 'maxRetries'>,\n failureClass?: 'transient' | 'deterministic',\n): boolean {\n return failureClass !== 'deterministic' && metadata.retryCount < metadata.maxRetries;\n}\n","import type { UnitCursor } from '@semiont/core';\n\n/**\n * Merge per-unit cursors monotonically (CHUNK-GRAIN-RESUME P2).\n *\n * `completedUnits` is a set, so unioning it converges under concurrent\n * snapshots for free — a set only grows. A cursor has no such property: two\n * checkpoints can be in flight at once and the OLDER one can land last, so a\n * last-writer-wins would drag the resume position backward and the retry would\n * re-pay for chunks it already committed. Keeping the furthest `next` per unit\n * is what makes the merge order-independent.\n *\n * `next` and `size` move together because they are ONE observation of one\n * chunk. Taking the furthest `next` from one snapshot and the `size` from\n * another would describe a chunk that was never cut.\n *\n * A unit in `completed` has no cursor at all: \"in progress, here\" and\n * \"finished\" are then structurally exclusive rather than a rule each reader has\n * to remember, and a stale snapshot cannot resurrect a finished unit's cursor.\n *\n * ONE module, shared by every driver (JOB-QUEUE-DRIVER P0): the interface\n * states this rule, and two copies of it would be two chances to disagree\n * about the one property that makes a cursor safe to merge at all.\n */\nexport function mergeUnitCursors(\n existing: Record<string, UnitCursor> | undefined,\n incoming: Record<string, UnitCursor> | undefined,\n completed: string[],\n): Record<string, UnitCursor> {\n const done = new Set(completed);\n const merged: Record<string, UnitCursor> = {};\n for (const [unit, cursor] of Object.entries({ ...existing })) {\n if (!done.has(unit)) merged[unit] = cursor;\n }\n for (const [unit, cursor] of Object.entries({ ...incoming })) {\n if (done.has(unit)) continue;\n const held = merged[unit];\n if (!held || cursor.next > held.next) merged[unit] = cursor;\n }\n return merged;\n}\n","/**\n * Job Queue Manager\n *\n * Filesystem-based job queue with atomic operations.\n * Jobs are stored in directories by status; status transitions are\n * atomic delete + write across directories.\n */\n\nimport { promises as fs } from 'fs';\nimport * as path from 'path';\nimport type { AnyJob, JobStatus, JobQueryFilters, CancelledJob, CompleteJob, FailedJob, PendingJob, RunningJob } from './types';\nimport type { SemiontState } from '@semiont/core/node';\nimport { jobId as toJobId, type JobId, type Logger, type EventBus, type UnitCursor } from '@semiont/core';\nimport type { JobQueue } from './job-queue-interface';\nimport { willRetryAfter } from './will-retry';\nimport { mergeUnitCursors } from './checkpoint-merge';\n\n/**\n * How often pending jobs are re-announced on `job:queued` and stale\n * running jobs are checked for recovery.\n *\n * The announcement in `createJob` only reaches workers that are\n * connected and idle at that moment. Re-announcing every pending job\n * on an interval restores catch-up for everything that announcement\n * misses: all eligible workers busy, a worker offline or mid-SSE-\n * reconnect, or a gateway restart with a pending backlog. Claim\n * arbitration (the `job:claim` handler refuses non-pending jobs)\n * makes duplicate announcements harmless.\n */\nconst REANNOUNCE_INTERVAL_MS = 30_000;\n\n/**\n * A running job whose file hasn't been touched for this long is\n * presumed orphaned by a dead worker and fed through the retry-or-fail\n * path. Progress writes (`recordProgress`) refresh the file's mtime,\n * so this is a heartbeat timeout, not a job-duration limit — but a\n * worker that emits no progress for the whole window will be falsely\n * recovered, so it stays deliberately generous.\n */\nconst STALE_RUNNING_MS = 30 * 60_000;\n\n/** Minimum spacing between progress writes per job — workers can be chatty. */\nconst PROGRESS_WRITE_MIN_INTERVAL_MS = 5_000;\n\n/** Terminal jobs (complete/failed/cancelled) are pruned after this long. */\nconst RETENTION_HOURS = 24;\n\n/** How often the retention pruning runs. */\nconst CLEANUP_INTERVAL_MS = 3_600_000;\n\nexport class FsJobQueue implements JobQueue {\n private jobsDir: string;\n private logger: Logger;\n private reannounceTimer: ReturnType<typeof setInterval> | null = null;\n private cleanupTimer: ReturnType<typeof setInterval> | null = null;\n /** Per-job timestamp of the last progress write, for throttling. */\n private lastProgressWrite = new Map<string, number>();\n\n constructor(\n /** `SemiontState` and not `SemiontProject`: the queue reads ONE path,\n * and the gateway that owns it mounts no KB tree (SINGLE-KB-MOUNT P5). */\n state: SemiontState,\n logger: Logger,\n private eventBus?: EventBus\n ) {\n this.jobsDir = state.jobsDir;\n this.logger = logger;\n }\n\n /**\n * Initialize job queue directories, announce any pending backlog,\n * and start the re-announce interval. Idempotent.\n */\n async initialize(): Promise<void> {\n const statuses: JobStatus[] = ['pending', 'running', 'complete', 'failed', 'cancelled'];\n\n for (const status of statuses) {\n const dir = path.join(this.jobsDir, status);\n await fs.mkdir(dir, { recursive: true });\n }\n\n if (this.eventBus && !this.reannounceTimer) {\n // Jobs left pending across a restart are announced right away…\n await this.announcePendingJobs();\n // …and anything that misses an announcement is retried here, along\n // with recovery of jobs orphaned by a dead worker.\n this.reannounceTimer = setInterval(() => {\n this.announcePendingJobs().catch((error) => {\n this.logger.warn('Pending-job re-announce failed', {\n error: error instanceof Error ? error.message : String(error),\n });\n });\n this.recoverStaleRunningJobs().catch((error) => {\n this.logger.warn('Stale-running recovery failed', {\n error: error instanceof Error ? error.message : String(error),\n });\n });\n }, REANNOUNCE_INTERVAL_MS);\n // Don't let the interval keep the process alive on shutdown.\n this.reannounceTimer.unref?.();\n }\n\n if (!this.cleanupTimer) {\n this.cleanupTimer = setInterval(() => {\n this.cleanupOldJobs(RETENTION_HOURS).catch((error) => {\n this.logger.warn('Job retention cleanup failed', {\n error: error instanceof Error ? error.message : String(error),\n });\n });\n }, CLEANUP_INTERVAL_MS);\n this.cleanupTimer.unref?.();\n }\n\n this.logger.info('Job queue initialized');\n }\n\n /**\n * Stop the re-announce and retention intervals\n */\n destroy(): void {\n if (this.reannounceTimer) {\n clearInterval(this.reannounceTimer);\n this.reannounceTimer = null;\n }\n if (this.cleanupTimer) {\n clearInterval(this.cleanupTimer);\n this.cleanupTimer = null;\n }\n }\n\n /**\n * Emit `job:queued` for a pending job, if an EventBus is wired and\n * the job carries a `resourceId` (every current job type does).\n */\n private announce(job: AnyJob): void {\n if (this.eventBus && 'params' in job && 'resourceId' in job.params) {\n this.eventBus.emit('job:queued', {\n jobId: job.metadata.id,\n jobType: job.metadata.type,\n resourceId: job.params.resourceId,\n userId: job.metadata.userId,\n });\n }\n }\n\n /**\n * Announce every job currently in `pending/`. Files that vanish or\n * fail to parse mid-scan (claimed, cancelled, partially written)\n * are skipped — they're either gone for a good reason or picked up\n * on the next tick.\n */\n private async announcePendingJobs(): Promise<void> {\n const pendingDir = path.join(this.jobsDir, 'pending');\n let files: string[];\n try {\n files = await fs.readdir(pendingDir);\n } catch {\n return;\n }\n files.sort();\n for (const file of files) {\n try {\n const content = await fs.readFile(path.join(pendingDir, file), 'utf-8');\n this.announce(JSON.parse(content) as AnyJob);\n } catch {\n // Skip unreadable files\n }\n }\n }\n\n /**\n * Create a new job\n */\n async createJob(job: AnyJob): Promise<void> {\n const jobPath = this.getJobPath(job.metadata.id, job.status);\n await fs.writeFile(jobPath, JSON.stringify(job, null, 2), 'utf-8');\n this.logger.info('Job created', { jobId: job.metadata.id, status: job.status });\n\n if (job.status === 'pending') {\n this.announce(job);\n }\n }\n\n /**\n * Get a job by ID (searches all status directories)\n */\n async getJob(jobId: JobId): Promise<AnyJob | null> {\n const statuses: JobStatus[] = ['pending', 'running', 'complete', 'failed', 'cancelled'];\n\n for (const status of statuses) {\n const jobPath = this.getJobPath(jobId, status);\n try {\n const content = await fs.readFile(jobPath, 'utf-8');\n return JSON.parse(content) as AnyJob;\n } catch (error) {\n // File doesn't exist in this status directory, try next\n continue;\n }\n }\n\n return null;\n }\n\n /**\n * Serializes claims. This driver's atomicity is single-process by design,\n * but `claimJob`'s contract must hold even against interleaved awaits in\n * that one process — a read-check-write with await points in it can admit\n * two winners without this chain (the TOCTOU the operation exists to kill).\n */\n private claimChain: Promise<unknown> = Promise.resolve();\n\n async claimNextJob(types: string[]): Promise<{ job: AnyJob } | { declined: 'none-available' }> {\n const claim = this.claimChain.then(() => this.doClaimNext(types));\n // A failed claim must not wedge every later one.\n this.claimChain = claim.catch(() => undefined);\n return claim;\n }\n\n private async doClaimNext(types: string[]): Promise<{ job: AnyJob } | { declined: 'none-available' }> {\n const pending = await this.listJobs({ status: 'pending', limit: Number.MAX_SAFE_INTEGER });\n const match = pending.find((j) => types.length === 0 || types.includes(j.metadata.type));\n if (!match) return { declined: 'none-available' };\n\n // Progress starts empty: a just-claimed job has reported nothing yet.\n // The typed progress shapes describe REPORTS, and the first report\n // arrives from the worker via recordProgress.\n const running: RunningJob<any, any> = {\n status: 'running',\n metadata: match.metadata,\n params: match.params,\n startedAt: new Date().toISOString(),\n progress: {},\n };\n await this.transition(running, 'pending');\n return { job: running };\n }\n\n /**\n * Update a job (atomic: delete old, write new)\n */\n /**\n * Cross-status move (or in-place rewrite): delete old slot, write new.\n * Private — the interface exposes named transitions only (JOB-QUEUE-DRIVER\n * P0); a generic public patch is trivial on files and impossible on an\n * in-flight message, so no driver may offer one. Re-entering `pending`\n * announces, which is how failJob's retry reaches a listening worker.\n */\n private async transition(job: AnyJob, oldStatus?: JobStatus): Promise<void> {\n // If oldStatus provided, delete from old location\n if (oldStatus && oldStatus !== job.status) {\n const oldPath = this.getJobPath(job.metadata.id, oldStatus);\n try {\n await fs.unlink(oldPath);\n } catch (error) {\n // Ignore if file doesn't exist\n }\n }\n\n // Write to new location\n const newPath = this.getJobPath(job.metadata.id, job.status);\n await fs.writeFile(newPath, JSON.stringify(job, null, 2), 'utf-8');\n\n if (oldStatus && oldStatus !== job.status) {\n this.logger.info('Job moved', { jobId: job.metadata.id, oldStatus, newStatus: job.status });\n // Re-entering pending (e.g. a retry) is a fresh announcement.\n if (job.status === 'pending') {\n this.announce(job);\n }\n } else {\n this.logger.info('Job updated', { jobId: job.metadata.id, status: job.status });\n }\n }\n\n /**\n * Move a running job to `complete`. Returns false (and changes\n * nothing) if the job is missing or not running — which also makes\n * duplicate `job:complete` events harmless.\n */\n async completeJob(jobId: JobId, result: Record<string, unknown>): Promise<boolean> {\n const job = await this.getJob(jobId);\n if (!job || job.status !== 'running') {\n return false;\n }\n\n this.lastProgressWrite.delete(jobId);\n const completed: CompleteJob<any, any> = {\n status: 'complete',\n metadata: job.metadata,\n params: job.params,\n startedAt: job.startedAt,\n completedAt: new Date().toISOString(),\n result,\n };\n await this.transition(completed, 'running');\n return true;\n }\n\n /**\n * Retry-or-fail a running job. While `retryCount < maxRetries` the\n * job goes back to `pending` with the count bumped (and is\n * re-announced); after that it lands in `failed` with the error.\n * Returns null (and changes nothing) if the job isn't running.\n */\n async failJob(jobId: JobId, error: string, completedUnits?: string[], failureClass?: 'transient' | 'deterministic', unitCursors?: Record<string, UnitCursor>): Promise<'retried' | 'failed' | null> {\n const job = await this.getJob(jobId);\n if (!job || job.status !== 'running') {\n return null;\n }\n\n this.lastProgressWrite.delete(jobId);\n\n // Checkpointed resume (ABANDONED-INFERENCE P2): union this attempt's\n // completed units with earlier attempts' — attempt 3 must keep what\n // attempt 1 finished. Stored in metadata so the spread below (and in\n // every later rebuild) carries it forward; the terminal failed record\n // keeps it too, so the waste of a discarded run stays visible (D3).\n const checkpoint = [...new Set([...(job.metadata.completedUnits ?? []), ...(completedUnits ?? [])])];\n // The mid-unit half of the same checkpoint (CHUNK-GRAIN-RESUME P2), merged\n // by the same monotone rule the durable per-chunk writes use — `failJob`\n // and `checkpointUnits` race on the same record, and whichever lands last\n // must not drag a cursor back.\n const cursors = mergeUnitCursors(job.metadata.unitCursors, unitCursors, checkpoint);\n const { unitCursors: superseded, ...base } = job.metadata;\n void superseded;\n const metadata = {\n ...base,\n ...(checkpoint.length > 0 ? { completedUnits: checkpoint } : {}),\n ...(Object.keys(cursors).length > 0 ? { unitCursors: cursors } : {}),\n };\n\n // A known-deterministic failure skips the budget outright: the same\n // request cannot succeed on a second attempt, so a retry is guaranteed\n // waste at full price (ABANDONED-INFERENCE P3, A4). Unclassified and\n // transient failures retry as before.\n if (willRetryAfter(job.metadata, failureClass)) {\n const retried: PendingJob<any> = {\n status: 'pending',\n metadata: { ...metadata, retryCount: job.metadata.retryCount + 1 },\n params: job.params,\n };\n await this.transition(retried, 'running');\n return 'retried';\n }\n\n if (failureClass === 'deterministic' && job.metadata.retryCount < job.metadata.maxRetries) {\n this.logger.info('Job failed without retry — deterministic failure', { jobId, error });\n }\n\n const failed: FailedJob<any> = {\n status: 'failed',\n metadata,\n params: job.params,\n startedAt: job.startedAt,\n completedAt: new Date().toISOString(),\n error,\n };\n await this.transition(failed, 'running');\n return 'failed';\n }\n\n /**\n * Persist a running job's completed-unit checkpoint at unit completion\n * (JOB-RESTART-SAFETY P2). `failJob` writes this checkpoint on a clean\n * failure, but a worker that dies without emitting `job:fail` would lose\n * its finished units — so the janitor's recovery (which re-queues with\n * whatever `metadata.completedUnits` the running file holds) would redo\n * them. Writing it HERE, as each unit lands, makes a crash lose at most\n * the in-flight unit. Unioned with any existing checkpoint, unthrottled\n * (a unit completion is rare and must never be dropped), a no-op for\n * non-running jobs. Written directly, like `recordProgress`, so it also\n * refreshes the mtime heartbeat.\n */\n async checkpointUnits(jobId: JobId, completedUnits: string[], unitCursors?: Record<string, UnitCursor>): Promise<void> {\n const job = await this.getJob(jobId);\n if (!job || job.status !== 'running') {\n return;\n }\n const merged = [...new Set([...(job.metadata.completedUnits ?? []), ...completedUnits])];\n const cursors = mergeUnitCursors(job.metadata.unitCursors, unitCursors, merged);\n // The prior value is destructured OUT rather than spread over: omitting a\n // key from a spread leaves the old one in place, so the last unit to finish\n // would keep a cursor pointing into work that is done.\n const { unitCursors: superseded, ...metadata } = job.metadata;\n void superseded;\n const updated: RunningJob<any, any> = {\n ...job,\n metadata: {\n ...metadata,\n completedUnits: merged,\n // Absent rather than `{}`: an empty object would assert that units were\n // tracked and none had progress, a different claim from a job that\n // never reported a cursor at all.\n ...(Object.keys(cursors).length > 0 ? { unitCursors: cursors } : {}),\n },\n };\n await fs.writeFile(this.getJobPath(jobId, 'running'), JSON.stringify(updated, null, 2), 'utf-8');\n }\n\n /**\n * Write progress into a running job's file. Throttled per job, and\n * a no-op for jobs that aren't running. Beyond surfacing live\n * progress to `job:status-requested`, each write refreshes the\n * file's mtime — the heartbeat `recoverStaleRunningJobs` watches.\n */\n async recordProgress(jobId: JobId, progress: Record<string, unknown>): Promise<void> {\n const now = Date.now();\n const lastWrite = this.lastProgressWrite.get(jobId) ?? 0;\n if (now - lastWrite < PROGRESS_WRITE_MIN_INTERVAL_MS) {\n return;\n }\n this.lastProgressWrite.set(jobId, now);\n\n const job = await this.getJob(jobId);\n if (!job || job.status !== 'running') {\n this.lastProgressWrite.delete(jobId);\n return;\n }\n\n // Written directly (not via updateJob) so chatty progress doesn't\n // flood the info log.\n const updated: RunningJob<any, any> = { ...job, progress };\n await fs.writeFile(this.getJobPath(jobId, 'running'), JSON.stringify(updated, null, 2), 'utf-8');\n }\n\n /**\n * List jobs with filters\n */\n async listJobs(filters: JobQueryFilters = {}): Promise<AnyJob[]> {\n const jobs: AnyJob[] = [];\n\n // Determine which status directories to scan\n const statuses: JobStatus[] = filters.status\n ? [filters.status]\n : ['pending', 'running', 'complete', 'failed', 'cancelled'];\n\n for (const status of statuses) {\n const statusDir = path.join(this.jobsDir, status);\n\n try {\n const files = await fs.readdir(statusDir);\n\n for (const file of files) {\n const jobPath = path.join(statusDir, file);\n const content = await fs.readFile(jobPath, 'utf-8');\n const job = JSON.parse(content) as AnyJob;\n\n // Apply filters\n if (filters.type && job.metadata.type !== filters.type) continue;\n if (filters.userId && job.metadata.userId !== filters.userId) continue;\n\n jobs.push(job);\n }\n } catch (error) {\n // Directory might not exist yet\n continue;\n }\n }\n\n // Sort by created descending (newest first)\n jobs.sort((a, b) => new Date(b.metadata.created).getTime() - new Date(a.metadata.created).getTime());\n\n // Apply pagination\n const offset = filters.offset || 0;\n const limit = filters.limit || 100;\n\n return jobs.slice(offset, offset + limit);\n }\n\n /**\n * Cancel a job\n */\n async cancelJob(jobId: JobId): Promise<boolean> {\n const job = await this.getJob(jobId);\n\n if (!job) {\n return false;\n }\n\n // Can only cancel pending or running jobs\n if (job.status !== 'pending' && job.status !== 'running') {\n return false;\n }\n\n const oldStatus = job.status;\n\n // Create cancelled job with proper structure\n const cancelledJob: CancelledJob<any> = {\n status: 'cancelled',\n metadata: job.metadata,\n params: job.status === 'pending' ? job.params : job.params,\n startedAt: job.status === 'running' ? job.startedAt : undefined,\n completedAt: new Date().toISOString(),\n };\n\n await this.transition(cancelledJob, oldStatus);\n return true;\n }\n\n /**\n * Cancel all pending jobs in a category — the granularity of the\n * `job:cancel-requested` UI signal. Running jobs are left to finish:\n * interrupting a worker mid-inference would need a worker-side kill\n * channel that doesn't exist.\n */\n async cancelPendingJobs(category: 'annotation' | 'generation'): Promise<number> {\n const matches = category === 'generation'\n ? (type: string) => type === 'generation'\n : (type: string) => type.endsWith('-annotation');\n\n const pending = await this.listJobs({ status: 'pending', limit: Number.MAX_SAFE_INTEGER });\n let cancelled = 0;\n for (const job of pending) {\n if (!matches(job.metadata.type)) continue;\n if (await this.cancelJob(job.metadata.id)) {\n cancelled++;\n }\n }\n\n if (cancelled > 0) {\n this.logger.info('Cancelled pending jobs', { category, cancelled });\n }\n return cancelled;\n }\n\n /**\n * Recover running jobs orphaned by a dead worker: any `running/`\n * file whose mtime is older than the stale window is fed through\n * the same retry-or-fail path as `job:fail`. Progress writes\n * refresh the mtime, so a live worker is never recovered out from\n * under itself as long as it reports within the window.\n */\n async recoverStaleRunningJobs(): Promise<number> {\n const runningDir = path.join(this.jobsDir, 'running');\n let files: string[];\n try {\n files = await fs.readdir(runningDir);\n } catch {\n return 0;\n }\n\n const now = Date.now();\n let recovered = 0;\n for (const file of files) {\n if (!file.endsWith('.json')) continue;\n try {\n const stat = await fs.stat(path.join(runningDir, file));\n if (now - stat.mtimeMs < STALE_RUNNING_MS) continue;\n\n const staleId = toJobId(file.slice(0, -'.json'.length));\n const outcome = await this.failJob(\n staleId,\n `worker presumed dead — no progress within ${STALE_RUNNING_MS / 60_000} minutes`,\n );\n if (outcome) {\n this.logger.warn('Recovered stale running job', { jobId: staleId, outcome });\n recovered++;\n }\n } catch {\n // File vanished mid-scan (job finished) — nothing to recover\n }\n }\n return recovered;\n }\n\n /**\n * Clean up old completed/failed jobs (older than retention period)\n */\n async cleanupOldJobs(retentionHours: number = 24): Promise<number> {\n const cutoffTime = Date.now() - (retentionHours * 60 * 60 * 1000);\n let deletedCount = 0;\n\n const cleanupStatuses: JobStatus[] = ['complete', 'failed', 'cancelled'];\n\n for (const status of cleanupStatuses) {\n const statusDir = path.join(this.jobsDir, status);\n\n try {\n const files = await fs.readdir(statusDir);\n\n for (const file of files) {\n const jobPath = path.join(statusDir, file);\n const content = await fs.readFile(jobPath, 'utf-8');\n const job = JSON.parse(content) as AnyJob;\n\n if (job.status === 'complete' || job.status === 'failed' || job.status === 'cancelled') {\n const completedTime = new Date(job.completedAt).getTime();\n\n if (completedTime < cutoffTime) {\n await fs.unlink(jobPath);\n deletedCount++;\n }\n }\n }\n } catch (error) {\n this.logger.error('Error cleaning up jobs', { status, error: error instanceof Error ? error.message : String(error) });\n }\n }\n\n if (deletedCount > 0) {\n this.logger.info('Jobs cleaned up', { deletedCount });\n }\n\n return deletedCount;\n }\n\n /**\n * Get job file path\n */\n private getJobPath(jobId: JobId, status: JobStatus): string {\n return path.join(this.jobsDir, status, `${jobId}.json`);\n }\n\n /**\n * Get statistics about the queue\n */\n async getStats(): Promise<{\n pending: number;\n running: number;\n complete: number;\n failed: number;\n cancelled: number;\n }> {\n const stats = {\n pending: 0,\n running: 0,\n complete: 0,\n failed: 0,\n cancelled: 0\n };\n\n const statuses: JobStatus[] = ['pending', 'running', 'complete', 'failed', 'cancelled'];\n\n for (const status of statuses) {\n const statusDir = path.join(this.jobsDir, status);\n\n try {\n const files = await fs.readdir(statusDir);\n stats[status] = files.length;\n } catch (error) {\n // Directory might not exist yet\n stats[status] = 0;\n }\n }\n\n return stats;\n }\n}\n","/**\n * JetStreamJobQueue — the JetStream driver behind the `JobQueue` interface\n * (JOB-QUEUE-DRIVER P1, topology ruling M: gateway-mediated).\n *\n * Two primitives, one authority each:\n *\n * - **KV bucket `jobs`** is the AUTHORITATIVE operational state: one entry\n * per job (`{ job, lastProgressAt }`), every transition a revision-CAS —\n * which is what makes `claimJob` atomic: simultaneous claims race on one\n * revision and exactly one update wins.\n * - **Stream `JOBS`** (subjects `jobs.<category>.<type>`, work-queue\n * retention) is the DELIVERY vehicle and redelivery timer. A delivered\n * message is the lease this process holds for a job; `working()`\n * heartbeats extend it, and a gateway that dies stops heartbeating, so\n * `AckWait` redelivers the job to a live instance — gateway-death\n * recovery, protocol-native.\n *\n * Worker-death recovery is NOT AckWait's job under the mediated topology\n * (the gateway holding the lease is alive; the worker vanished): it is the\n * `lastProgressAt` sweep (`recoverStaleRunningJobs`), the same contract the\n * fs driver's mtime janitor implemented. The redelivery handler checks KV\n * state, so the two recovery paths can never double-apply.\n *\n * Battery ownership (JOB-QUEUE-DRIVER P0): the retry decision belongs to\n * `will-retry.ts` — `max_deliver` is unlimited and a deterministic failure\n * is `term()`ed, so the backend's retry engine never becomes a second\n * authority. The cursor merge belongs to `checkpoint-merge.ts`, shared with\n * every driver.\n *\n * The boundary is the point: no NATS type, subject string, or delivery\n * handle escapes this file.\n */\n\nimport { connect, RetentionPolicy, AckPolicy, DeliverPolicy, nanos } from 'nats';\nimport type { NatsConnection, JetStreamClient, JetStreamManager, JsMsg, KV, ConsumerMessages } from 'nats';\nimport type { AnyJob, PendingJob, RunningJob, FailedJob, CompleteJob, CancelledJob } from './types';\nimport { jobId as toJobId, type JobId, type Logger, type EventBus, type UnitCursor } from '@semiont/core';\nimport type { JobQueue } from './job-queue-interface';\nimport { willRetryAfter } from './will-retry';\nimport { mergeUnitCursors } from './checkpoint-merge';\n\nconst STREAM = 'JOBS';\nconst CONSUMER = 'gateway-claims';\nconst BUCKET = 'jobs';\n\n/** Minimum spacing between progress writes per job — workers can be chatty. */\nconst PROGRESS_WRITE_MIN_INTERVAL_MS = 5_000;\n\n/** Bounded CAS retries: past this, something is systemically wrong. */\nconst MAX_CAS_ATTEMPTS = 20;\n\nconst enc = new TextEncoder();\nconst dec = new TextDecoder();\n\n/** The KV record: the job plus the liveness fact the sweep reads. */\ninterface JobEnvelope {\n job: AnyJob;\n lastProgressAt: string;\n}\n\n/**\n * The JOBS stream's capture filter — exported as the ONE home of the fact.\n * A JetStream stream is a server-side subscription: anything published under\n * these subjects is persisted regardless of which client API produced it.\n * The signal plane's disjointness gate derives from this export instead of\n * restating it (SIGNAL-PLANE D3 gate 3).\n */\nexport const JOBS_STREAM_SUBJECTS = ['jobs.>'] as const;\n\nexport interface JetStreamJobQueueOptions {\n /** NATS server address(es), e.g. \"192.168.64.42:4222\". */\n servers: string | string[];\n /** Worker presumed dead after this long without progress (default 30 min). */\n staleRunningMs?: number;\n /** Lease redelivery window when THIS process stops heartbeating (default 30 s). */\n ackWaitMs?: number;\n /** Re-announce + worker-death-sweep cadence (default 30 s; tests shrink it). */\n tickMs?: number;\n /** Reconnect on connection loss (default true; tests turn it off). */\n reconnect?: boolean;\n}\n\nfunction categoryOf(type: string): 'annotation' | 'generation' {\n return type === 'generation' ? 'generation' : 'annotation';\n}\n\nfunction subjectFor(job: AnyJob): string {\n return `jobs.${categoryOf(job.metadata.type)}.${job.metadata.type}`;\n}\n\nexport class JetStreamJobQueue implements JobQueue {\n private nc!: NatsConnection;\n private js!: JetStreamClient;\n private jsm!: JetStreamManager;\n private kv!: KV;\n private iter: ConsumerMessages | null = null;\n /** Delivered messages this process holds — the lease, keyed by jobId,\n * with the job's type so a by-type claim can walk them without a read. */\n private readonly held = new Map<string, { m: JsMsg; type: string }>();\n private heartbeat: ReturnType<typeof setInterval> | null = null;\n private reconciling = false;\n private tick: ReturnType<typeof setInterval> | null = null;\n private ticking = false;\n private readonly lastProgressWrite = new Map<string, number>();\n private readonly staleRunningMs: number;\n private readonly ackWaitMs: number;\n private readonly tickMs: number;\n\n constructor(\n private readonly options: JetStreamJobQueueOptions,\n private readonly logger: Logger,\n private readonly eventBus?: EventBus,\n ) {\n this.staleRunningMs = options.staleRunningMs ?? 30 * 60_000;\n this.ackWaitMs = options.ackWaitMs ?? 30_000;\n this.tickMs = options.tickMs ?? 30_000;\n }\n\n async initialize(): Promise<void> {\n this.nc = await connect({\n servers: this.options.servers,\n reconnect: this.options.reconnect ?? true,\n timeout: 10_000,\n });\n this.js = this.nc.jetstream();\n this.jsm = await this.nc.jetstreamManager();\n this.kv = await this.js.views.kv(BUCKET);\n\n try {\n await this.jsm.streams.info(STREAM);\n } catch {\n await this.jsm.streams.add({\n name: STREAM,\n subjects: [...JOBS_STREAM_SUBJECTS],\n retention: RetentionPolicy.Workqueue,\n });\n }\n try {\n await this.jsm.consumers.info(STREAM, CONSUMER);\n } catch {\n await this.jsm.consumers.add(STREAM, {\n durable_name: CONSUMER,\n ack_policy: AckPolicy.Explicit,\n deliver_policy: DeliverPolicy.All,\n ack_wait: nanos(this.ackWaitMs),\n // ONE retry authority: will-retry.ts decides; the stream never\n // gives up a message on its own count.\n max_deliver: -1,\n });\n }\n\n const consumer = await this.js.consumers.get(STREAM, CONSUMER);\n this.iter = await consumer.consume({\n callback: (m) => {\n void this.onDelivery(m).catch((error) => {\n this.logger.warn('Job delivery handling failed', {\n error: error instanceof Error ? error.message : String(error),\n });\n });\n },\n });\n\n // The lease heartbeat doubles as RECONCILIATION (the multi-instance\n // lease-settle pin): a held delivery whose job concluded on ANOTHER\n // instance — its claim lost the CAS race, or a redelivery landed here\n // after a gateway death — can only be settled by the holder, because\n // nobody else has the delivery to ack. Terminal in KV → ack and drop;\n // otherwise the lease extends. While this process is alive its leases\n // never expire; when it dies they redeliver after ackWait — that IS the\n // gateway-death recovery path.\n this.heartbeat = setInterval(() => {\n if (this.reconciling) return;\n this.reconciling = true;\n void this.reconcileHeld()\n .catch((error) => {\n this.logger.warn('Held-lease reconciliation failed', {\n error: error instanceof Error ? error.message : String(error),\n });\n })\n .finally(() => {\n this.reconciling = false;\n });\n }, Math.max(250, Math.floor(this.ackWaitMs / 4)));\n this.heartbeat.unref?.();\n\n // The queue's periodic tick, mirroring the fs driver's internal janitor:\n // (a) RE-ANNOUNCE held pending deliveries — an announcement is a wake-up\n // with no memory, so a worker that was busy (or not yet connected)\n // when one fired must hear about still-unclaimed work again;\n // (b) SWEEP for worker death (`recoverStaleRunningJobs`) — `AckWait`\n // covers a dead GATEWAY; only this sweep covers a dead WORKER, and\n // rows do not sweep themselves (JOB-RESTART-SAFETY P7).\n this.tick = setInterval(() => {\n if (this.ticking) return;\n this.ticking = true;\n void (async () => {\n for (const [id, held] of [...this.held]) {\n const envelope = await this.read(toJobId(id));\n if (envelope?.job.status === 'pending') this.announce(envelope.job);\n void held;\n }\n await this.recoverStaleRunningJobs();\n })()\n .catch((error) => {\n this.logger.warn('Job-queue tick failed', {\n error: error instanceof Error ? error.message : String(error),\n });\n })\n .finally(() => {\n this.ticking = false;\n });\n }, this.tickMs);\n this.tick.unref?.();\n }\n\n destroy(): void {\n this.iter?.stop();\n this.iter = null;\n if (this.heartbeat) clearInterval(this.heartbeat);\n this.heartbeat = null;\n if (this.tick) clearInterval(this.tick);\n this.tick = null;\n this.held.clear();\n void this.nc?.close();\n }\n\n /**\n * A delivery is a job arriving at this gateway. What it means depends on\n * the job's authoritative (KV) state:\n * - pending → hold the lease, announce for a worker to claim;\n * - running → hold silently (a claim raced ahead of the delivery, or a\n * redelivery reached a fresh instance after a gateway death);\n * - terminal → the work already concluded elsewhere; consume the message;\n * - unknown → not ours to run; terminate it.\n */\n private async onDelivery(m: JsMsg): Promise<void> {\n let id: string;\n try {\n id = (JSON.parse(dec.decode(m.data)) as { jobId: string }).jobId;\n } catch {\n m.term();\n return;\n }\n const envelope = await this.read(toJobId(id));\n if (!envelope) {\n m.term();\n return;\n }\n switch (envelope.job.status) {\n case 'pending':\n this.held.set(id, { m, type: envelope.job.metadata.type });\n this.announce(envelope.job);\n break;\n case 'running':\n this.held.set(id, { m, type: envelope.job.metadata.type });\n break;\n default:\n m.ack();\n this.held.delete(id);\n }\n }\n\n /** Same wire shape as the fs driver: only jobs with a resourceId announce. */\n private announce(job: AnyJob): void {\n if (this.eventBus && 'params' in job && 'resourceId' in (job.params as Record<string, unknown>)) {\n this.eventBus.emit('job:queued', {\n jobId: job.metadata.id,\n jobType: job.metadata.type,\n resourceId: (job.params as { resourceId: unknown }).resourceId as never,\n userId: job.metadata.userId,\n });\n }\n }\n\n private async read(jobIdArg: JobId): Promise<{ job: AnyJob; lastProgressAt: string; revision: number } | null> {\n const entry = await this.kv.get(jobIdArg as string);\n if (!entry || entry.operation !== 'PUT') return null;\n const envelope = JSON.parse(dec.decode(entry.value)) as JobEnvelope;\n return { ...envelope, revision: entry.revision };\n }\n\n private async write(jobIdArg: JobId, envelope: JobEnvelope, revision?: number): Promise<number> {\n const payload = enc.encode(JSON.stringify(envelope));\n if (revision === undefined) return this.kv.put(jobIdArg as string, payload);\n return this.kv.update(jobIdArg as string, payload, revision);\n }\n\n /**\n * Read-transform-CAS with bounded retries: the transform sees the current\n * job and returns the replacement (or null to stop). Every state\n * transition goes through here, which is what makes each one atomic.\n */\n private async cas<T>(\n jobIdArg: JobId,\n transform: (job: AnyJob, lastProgressAt: string) => { envelope: JobEnvelope; result: T } | { result: T } | null,\n onMissing: T,\n ): Promise<T> {\n for (let attempt = 0; attempt < MAX_CAS_ATTEMPTS; attempt++) {\n const current = await this.read(jobIdArg);\n if (!current) return onMissing;\n const out = transform(current.job, current.lastProgressAt);\n if (out === null) return onMissing;\n if (!('envelope' in out)) return out.result;\n try {\n await this.write(jobIdArg, out.envelope, current.revision);\n return out.result;\n } catch {\n // Revision conflict — someone else transitioned first; re-read.\n }\n }\n throw new Error(`Job ${jobIdArg} transition failed after ${MAX_CAS_ATTEMPTS} CAS attempts`);\n }\n\n async createJob(job: AnyJob): Promise<void> {\n const now = new Date().toISOString();\n await this.kv.create(job.metadata.id as string, enc.encode(JSON.stringify({ job, lastProgressAt: now } satisfies JobEnvelope)));\n // Only pending jobs need delivery — the stream is the claim vehicle,\n // not the record (KV is).\n if (job.status === 'pending') {\n await this.js.publish(subjectFor(job), enc.encode(JSON.stringify({ jobId: job.metadata.id })));\n }\n }\n\n async getJob(jobIdArg: JobId): Promise<AnyJob | null> {\n const envelope = await this.read(jobIdArg);\n return envelope?.job ?? null;\n }\n\n async claimNextJob(types: string[]): Promise<{ job: AnyJob } | { declined: 'none-available' }> {\n const matches = (t: string) => types.length === 0 || types.includes(t);\n // Lease-aligned fast path (topology memo, mechanic 1): deliveries this\n // gateway already holds — the claim lands where the worker is connected.\n for (const [id, held] of [...this.held]) {\n if (!matches(held.type)) continue;\n const won = await this.tryClaim(toJobId(id));\n if (won) return won;\n }\n // Fallback: pending state whose delivery has not reached us — yet (a\n // claim racing ahead of its delivery) or ever (another gateway holds\n // it; KV is the authority, and that holder's reconcile settles the\n // stale lease once this claim concludes the job).\n for (const key of await this.allKeys()) {\n const envelope = await this.read(toJobId(key));\n if (!envelope || envelope.job.status !== 'pending' || !matches(envelope.job.metadata.type)) continue;\n const won = await this.tryClaim(toJobId(key));\n if (won) return won;\n }\n return { declined: 'none-available' };\n }\n\n /** CAS one pending job to running; null when someone else won it. */\n private async tryClaim(jobIdArg: JobId): Promise<{ job: AnyJob } | null> {\n return this.cas<{ job: AnyJob } | null>(\n jobIdArg,\n (job) => {\n if (job.status !== 'pending') return { result: null };\n const running: RunningJob<any, any> = {\n status: 'running',\n metadata: job.metadata,\n params: job.params,\n startedAt: new Date().toISOString(),\n progress: {},\n };\n return {\n envelope: { job: running, lastProgressAt: new Date().toISOString() },\n result: { job: running as AnyJob },\n };\n },\n null,\n );\n }\n\n async completeJob(jobIdArg: JobId, result: Record<string, unknown>): Promise<boolean> {\n const moved = await this.cas<boolean>(\n jobIdArg,\n (job) => {\n if (job.status !== 'running') return { result: false };\n const completed: CompleteJob<any, any> = {\n status: 'complete',\n metadata: job.metadata,\n params: job.params,\n startedAt: job.startedAt,\n completedAt: new Date().toISOString(),\n result,\n };\n return { envelope: { job: completed, lastProgressAt: new Date().toISOString() }, result: true };\n },\n false,\n );\n if (moved) this.settleLease(jobIdArg, 'ack');\n return moved;\n }\n\n async failJob(\n jobIdArg: JobId,\n error: string,\n completedUnits?: string[],\n failureClass?: 'transient' | 'deterministic',\n unitCursors?: Record<string, UnitCursor>,\n ): Promise<'retried' | 'failed' | null> {\n const outcome = await this.cas<'retried' | 'failed' | null>(\n jobIdArg,\n (job) => {\n if (job.status !== 'running') return { result: null };\n const units = [...new Set([...(job.metadata.completedUnits ?? []), ...(completedUnits ?? [])])];\n const cursors = mergeUnitCursors(job.metadata.unitCursors, unitCursors, units);\n const metadata = {\n ...job.metadata,\n ...(units.length > 0 ? { completedUnits: units } : {}),\n ...(Object.keys(cursors).length > 0 ? { unitCursors: cursors } : {}),\n };\n if ('unitCursors' in metadata && Object.keys(cursors).length === 0) delete (metadata as { unitCursors?: unknown }).unitCursors;\n\n if (willRetryAfter(job.metadata, failureClass)) {\n const retried: PendingJob<any> = {\n status: 'pending',\n metadata: { ...metadata, retryCount: job.metadata.retryCount + 1 },\n params: job.params,\n };\n return { envelope: { job: retried, lastProgressAt: new Date().toISOString() }, result: 'retried' as const };\n }\n const failed: FailedJob<any> = {\n status: 'failed',\n metadata,\n params: job.params,\n startedAt: job.startedAt,\n completedAt: new Date().toISOString(),\n error,\n };\n return { envelope: { job: failed, lastProgressAt: new Date().toISOString() }, result: 'failed' as const };\n },\n null,\n );\n\n if (outcome === 'retried') {\n // The nak'd (or republished) delivery arrives as a fresh pending job\n // and re-announces — retry IS redelivery under this driver.\n const held = this.held.get(jobIdArg as string);\n if (held) {\n this.held.delete(jobIdArg as string);\n held.m.nak();\n } else {\n const envelope = await this.read(jobIdArg);\n if (envelope) await this.js.publish(subjectFor(envelope.job), enc.encode(JSON.stringify({ jobId: jobIdArg })));\n }\n } else if (outcome === 'failed') {\n // Deterministic or exhausted: the message must never redeliver.\n this.settleLease(jobIdArg, 'term');\n }\n return outcome;\n }\n\n async checkpointUnits(jobIdArg: JobId, completedUnits: string[], unitCursors?: Record<string, UnitCursor>): Promise<void> {\n await this.cas<void>(\n jobIdArg,\n (job) => {\n if (job.status !== 'running') return { result: undefined };\n const units = [...new Set([...(job.metadata.completedUnits ?? []), ...completedUnits])];\n const cursors = mergeUnitCursors(job.metadata.unitCursors, unitCursors, units);\n const metadata = { ...job.metadata, completedUnits: units } as typeof job.metadata;\n if (Object.keys(cursors).length > 0) metadata.unitCursors = cursors;\n else delete (metadata as { unitCursors?: unknown }).unitCursors;\n const updated = { ...job, metadata } as AnyJob;\n // A checkpoint is progress — it refreshes the liveness clock.\n return { envelope: { job: updated, lastProgressAt: new Date().toISOString() }, result: undefined };\n },\n undefined,\n );\n }\n\n async recordProgress(jobIdArg: JobId, progress: Record<string, unknown>): Promise<void> {\n const last = this.lastProgressWrite.get(jobIdArg as string) ?? 0;\n if (Date.now() - last < PROGRESS_WRITE_MIN_INTERVAL_MS) return;\n this.lastProgressWrite.set(jobIdArg as string, Date.now());\n\n this.held.get(jobIdArg as string)?.m.working();\n await this.cas<void>(\n jobIdArg,\n (job) => {\n if (job.status !== 'running') return { result: undefined };\n const updated: RunningJob<any, any> = {\n status: 'running',\n metadata: job.metadata,\n params: job.params,\n startedAt: job.startedAt,\n progress,\n };\n return { envelope: { job: updated, lastProgressAt: new Date().toISOString() }, result: undefined };\n },\n undefined,\n );\n }\n\n async cancelPendingJobs(category: 'annotation' | 'generation'): Promise<number> {\n const matches = category === 'generation'\n ? (type: string) => type === 'generation'\n : (type: string) => type.endsWith('-annotation');\n\n let cancelled = 0;\n for (const key of await this.allKeys()) {\n const envelope = await this.read(toJobId(key));\n if (!envelope || envelope.job.status !== 'pending' || !matches(envelope.job.metadata.type)) continue;\n const done = await this.cas<boolean>(\n toJobId(key),\n (job) => {\n if (job.status !== 'pending' || !matches(job.metadata.type)) return { result: false };\n const record: CancelledJob<any> = {\n status: 'cancelled',\n metadata: job.metadata,\n params: job.params,\n startedAt: undefined,\n completedAt: new Date().toISOString(),\n };\n return { envelope: { job: record, lastProgressAt: new Date().toISOString() }, result: true };\n },\n false,\n );\n if (done) {\n cancelled++;\n this.settleLease(toJobId(key), 'term');\n }\n }\n // Undelivered messages for the category die with the state change.\n await this.jsm.streams.purge(STREAM, { filter: `jobs.${category}.>` });\n return cancelled;\n }\n\n async cancelJob(jobIdArg: JobId): Promise<boolean> {\n const done = await this.cas<boolean>(\n jobIdArg,\n (job) => {\n if (job.status !== 'pending' && job.status !== 'running') return { result: false };\n const record: CancelledJob<any> = {\n status: 'cancelled',\n metadata: job.metadata,\n params: job.params,\n startedAt: job.status === 'running' ? job.startedAt : undefined,\n completedAt: new Date().toISOString(),\n };\n return { envelope: { job: record, lastProgressAt: new Date().toISOString() }, result: true };\n },\n false,\n );\n if (done) this.settleLease(jobIdArg, 'term');\n return done;\n }\n\n async getStats(): Promise<{ pending: number; running: number; complete: number; failed: number; cancelled: number }> {\n const stats = { pending: 0, running: 0, complete: 0, failed: 0, cancelled: 0 };\n for (const key of await this.allKeys()) {\n const envelope = await this.read(toJobId(key));\n if (envelope) stats[envelope.job.status]++;\n }\n return stats;\n }\n\n /**\n * Worker-death recovery: the same contract the fs driver's mtime janitor\n * implemented — a running job with no progress inside the stale window is\n * retried or failed, checkpoint intact. Gateway-death recovery never comes\n * through here; that is AckWait redelivery.\n */\n async recoverStaleRunningJobs(): Promise<number> {\n const now = Date.now();\n let recovered = 0;\n for (const key of await this.allKeys()) {\n const envelope = await this.read(toJobId(key));\n if (!envelope || envelope.job.status !== 'running') continue;\n if (now - Date.parse(envelope.lastProgressAt) < this.staleRunningMs) continue;\n const outcome = await this.failJob(\n toJobId(key),\n `worker presumed dead — no progress within ${this.staleRunningMs / 60_000} minutes`,\n );\n if (outcome) {\n this.logger.warn('Recovered stale running job', { jobId: key, outcome });\n recovered++;\n }\n }\n return recovered;\n }\n\n /** The heartbeat body — extend live leases, settle concluded ones. */\n private async reconcileHeld(): Promise<void> {\n for (const [id, held] of [...this.held]) {\n const envelope = await this.read(toJobId(id));\n const status = envelope?.job.status;\n if (!envelope || status === 'complete' || status === 'failed' || status === 'cancelled') {\n this.held.delete(id);\n held.m.ack();\n } else {\n held.m.working();\n }\n }\n }\n\n /**\n * Materialize the KV key list BEFORE touching any entry: interleaving\n * `get`/`update` with an open `keys()` iterator on the same connection\n * makes the iterator drop entries (measured: the second pending job\n * vanished from every sweep until this was split into two passes).\n */\n private async allKeys(): Promise<string[]> {\n const keys: string[] = [];\n for await (const key of await this.kv.keys()) keys.push(key);\n return keys;\n }\n\n /** Conclude this process's lease on a job, if it holds one. */\n private settleLease(jobIdArg: JobId, how: 'ack' | 'term'): void {\n const held = this.held.get(jobIdArg as string);\n if (!held) return;\n this.held.delete(jobIdArg as string);\n if (how === 'ack') held.m.ack();\n else held.m.term();\n }\n}\n","/**\n * Job Queue Type Definitions - Discriminated Union Design\n *\n * Jobs represent async work that can be queued, processed, and monitored.\n * Uses TypeScript discriminated unions to enforce valid state transitions.\n *\n * Design principles:\n * - Each job status has specific valid fields\n * - Type narrowing works automatically via status discriminant\n * - No optional fields that may or may not exist\n * - State machine is explicit and type-safe\n */\n\nimport type { JobId, EntityType, ResourceId, UserId, GenerationJobParams, TagSchema, UnitCursor } from '@semiont/core';\nimport type { JobReferenceAnnotationResult, JobHighlightAnnotationResult, JobCommentAnnotationResult, JobAssessmentAnnotationResult, JobTagAnnotationResult } from '@semiont/core';\n\nexport type JobType = 'reference-annotation' | 'generation' | 'highlight-annotation' | 'assessment-annotation' | 'comment-annotation' | 'tag-annotation';\nexport type JobStatus = 'pending' | 'running' | 'complete' | 'failed' | 'cancelled';\n\n// ============================================================================\n// Core Metadata and Parameters\n// ============================================================================\n\n/**\n * Job metadata - common to all states\n */\nexport interface JobMetadata {\n id: JobId;\n type: JobType;\n userId: UserId;\n /**\n * Audit-only snapshot of the requesting user (with `userEmail` and\n * `userDomain` below), stamped at job creation and persisted in the\n * on-disk job file. No code path reads these back — annotation\n * `creator` attribution is derived from `userId` via `didToAgent()`.\n * Kept intentionally so job files are self-describing to a human.\n */\n userName: string;\n userEmail: string;\n userDomain: string;\n created: string;\n retryCount: number;\n maxRetries: number;\n /**\n * Checkpointed resume (ABANDONED-INFERENCE P2, HD1): the entity-type\n * units whose annotations were fully emitted by earlier failed\n * attempts. Written only by `failJob`, unioned across attempts — and\n * because `failJob` rebuilds the retried record by spreading metadata,\n * the checkpoint survives every subsequent rebuild for free. A retried\n * claim skips these units, so completed work is neither redone nor\n * duplicated.\n */\n completedUnits?: string[];\n /**\n * The finer grain `completedUnits` cannot express (CHUNK-GRAIN-RESUME P2):\n * how far each UNFINISHED unit got, keyed by unit. Written per committed\n * chunk, so a job that dies mid-unit resumes there rather than at the top —\n * which for a one-unit job (every motivation job, and the 1958 document's\n * single `Person` type) is the difference between resuming and restarting.\n *\n * A unit here is in progress, never complete; the two sets are disjoint by\n * construction in `checkpointUnits`. Merged monotonically per unit, never\n * unioned — see `mergeUnitCursors`.\n */\n unitCursors?: Record<string, UnitCursor>;\n}\n\n/**\n * Locale conventions for detection/generation params.\n *\n * Two independent locales flow through these jobs:\n *\n * - `language` — *annotation body* locale. The BCP-47 tag the LLM should\n * write generated body text in (comment text, assessment text, generated\n * resource content, tag category label). Sourced from the user's UI\n * locale. Stamped onto the W3C `TextualBody.language` field.\n *\n * - `sourceLanguage` — *source resource* locale. The BCP-47 tag of the\n * content being analyzed. Sourced from `ResourceDescriptor` (carried as\n * `Representation.language` on the primary representation). Used in\n * prompts so the LLM analyzes non-English source correctly even when\n * the user's UI locale differs.\n *\n * Examples: a German user analyzing an English document → `language='de'`,\n * `sourceLanguage='en'`. An English user detecting entities in a French\n * document → `language='en'` (unused for entity references), `sourceLanguage='fr'`.\n */\n\n/**\n * Detection job parameters\n */\nexport interface DetectionParams {\n resourceId: ResourceId;\n entityTypes: EntityType[];\n includeDescriptiveReferences?: boolean;\n /** Annotation body locale — see locale conventions above. */\n language?: string;\n /** Source-resource locale — see locale conventions above. */\n sourceLanguage?: string;\n}\n\n\n/**\n * Highlight detection job parameters\n */\nexport interface HighlightDetectionParams {\n resourceId: ResourceId;\n instructions?: string;\n density?: number;\n /** Source-resource locale — see locale conventions above. */\n sourceLanguage?: string;\n}\n\n/**\n * Assessment detection job parameters\n */\nexport interface AssessmentDetectionParams {\n resourceId: ResourceId;\n instructions?: string;\n tone?: 'analytical' | 'critical' | 'balanced' | 'constructive';\n density?: number;\n /** Annotation body locale — see locale conventions above. */\n language?: string;\n /** Source-resource locale — see locale conventions above. */\n sourceLanguage?: string;\n}\n\n/**\n * Comment detection job parameters\n */\nexport interface CommentDetectionParams {\n resourceId: ResourceId;\n instructions?: string;\n tone?: 'scholarly' | 'explanatory' | 'conversational' | 'technical';\n density?: number;\n /** Annotation body locale — see locale conventions above. */\n language?: string;\n /** Source-resource locale — see locale conventions above. */\n sourceLanguage?: string;\n}\n\n/**\n * Tag detection job parameters.\n *\n * Carries the *full* `TagSchema` (not just an id). The dispatcher resolves\n * the caller-supplied `schemaId` against the per-KB tag-schema projection\n * at job-creation time and embeds the resolved schema here, keeping the\n * worker independent of the registry.\n */\nexport interface TagDetectionParams {\n resourceId: ResourceId;\n schema: TagSchema;\n categories: string[];\n /** Annotation body locale — see locale conventions above. */\n language?: string;\n /** Source-resource locale — see locale conventions above. */\n sourceLanguage?: string;\n}\n\n// ============================================================================\n// Progress and Result Types\n// ============================================================================\n\n/**\n * Detection job progress\n */\nexport interface DetectionProgress {\n totalEntityTypes: number;\n processedEntityTypes: number;\n entitiesFound: number;\n entitiesEmitted: number;\n}\n\n\n/**\n * Generation job progress\n */\nexport interface YieldProgress {\n /** The two real generation transitions — LLM call running, then persisting. */\n stage: 'generating' | 'creating';\n percentage: number;\n message?: string;\n}\n\n/**\n * Generation job result\n */\nexport interface GenerationResult {\n kind: 'generation';\n resourceId: ResourceId;\n resourceName: string;\n /** True when the model stopped at the maxTokens ceiling — the artifact is cut off, not complete (GENERATE-FROM-RESOURCE D6). */\n truncated: boolean;\n}\n\n/**\n * Highlight detection job progress\n */\nexport interface HighlightDetectionProgress {\n stage: 'analyzing' | 'creating';\n percentage: number;\n message?: string;\n}\n\n\n/**\n * Assessment detection job progress\n */\nexport interface AssessmentDetectionProgress {\n stage: 'analyzing' | 'creating';\n percentage: number;\n message?: string;\n}\n\n\n/**\n * Comment detection job progress\n */\nexport interface CommentDetectionProgress {\n stage: 'analyzing' | 'creating';\n percentage: number;\n message?: string;\n}\n\n\n/**\n * Tag detection job progress\n */\nexport interface TagDetectionProgress {\n stage: 'analyzing' | 'creating';\n percentage: number;\n currentCategory?: string;\n processedCategories: number;\n totalCategories: number;\n message?: string;\n}\n\n\n// ============================================================================\n// Generic Job State Types\n// ============================================================================\n\n/**\n * Pending job - just created, waiting to be picked up\n */\nexport interface PendingJob<P> {\n status: 'pending';\n metadata: JobMetadata;\n params: P;\n}\n\n/**\n * Running job - actively being processed\n */\nexport interface RunningJob<P, PG> {\n status: 'running';\n metadata: JobMetadata;\n params: P;\n startedAt: string;\n progress: PG;\n}\n\n/**\n * Complete job - successfully finished\n */\nexport interface CompleteJob<P, R> {\n status: 'complete';\n metadata: JobMetadata;\n params: P;\n startedAt: string;\n completedAt: string;\n result: R;\n}\n\n/**\n * Failed job - encountered an error\n */\nexport interface FailedJob<P> {\n status: 'failed';\n metadata: JobMetadata;\n params: P;\n startedAt?: string;\n completedAt: string;\n error: string;\n}\n\n/**\n * Cancelled job - stopped by user\n */\nexport interface CancelledJob<P> {\n status: 'cancelled';\n metadata: JobMetadata;\n params: P;\n startedAt?: string;\n completedAt: string;\n}\n\n/**\n * Generic job - discriminated union of all states\n */\nexport type Job<P, PG, R> =\n | PendingJob<P>\n | RunningJob<P, PG>\n | CompleteJob<P, R>\n | FailedJob<P>\n | CancelledJob<P>;\n\n// ============================================================================\n// Concrete Job Types\n// ============================================================================\n\nexport type DetectionJob = Job<DetectionParams, DetectionProgress, JobReferenceAnnotationResult>;\nexport type GenerationJob = Job<GenerationJobParams, YieldProgress, GenerationResult>;\nexport type HighlightDetectionJob = Job<HighlightDetectionParams, HighlightDetectionProgress, JobHighlightAnnotationResult>;\nexport type AssessmentDetectionJob = Job<AssessmentDetectionParams, AssessmentDetectionProgress, JobAssessmentAnnotationResult>;\nexport type CommentDetectionJob = Job<CommentDetectionParams, CommentDetectionProgress, JobCommentAnnotationResult>;\nexport type TagDetectionJob = Job<TagDetectionParams, TagDetectionProgress, JobTagAnnotationResult>;\n\n/**\n * Discriminated union of all job types\n */\nexport type AnyJob = DetectionJob | GenerationJob | HighlightDetectionJob | AssessmentDetectionJob | CommentDetectionJob | TagDetectionJob;\n\n// ============================================================================\n// Type Guards\n// ============================================================================\n\nconst JOB_TYPES: ReadonlySet<string> = new Set<JobType>([\n 'reference-annotation',\n 'generation',\n 'highlight-annotation',\n 'assessment-annotation',\n 'comment-annotation',\n 'tag-annotation',\n]);\n\n/**\n * Narrow a job type arriving off the bus, where it is only `string`.\n *\n * Worth checking rather than asserting: `jobType` is a required, enumerated\n * field on every job lifecycle command, so an unrecognized value produces a\n * payload the gateway will reject — better caught at the worker with a clear\n * error than as an opaque emit failure.\n */\nexport function isJobType(value: string): value is JobType {\n return JOB_TYPES.has(value);\n}\n\n/**\n * Narrow bus-delivered job params to the shape a processor expects.\n *\n * Job params cross the bus as `Record<string, unknown>`. Every params type in\n * this file requires exactly one field — `resourceId` — with the rest\n * optional, so that is what gets verified; absent optionals are legitimately\n * absent, not a validation failure. Callers pass the params type for the job\n * they have already branched on.\n */\nexport function asJobParams<T extends { resourceId: ResourceId }>(\n params: Record<string, unknown>,\n): T {\n if (typeof params.resourceId !== 'string') {\n throw new Error('Job params are missing a resourceId');\n }\n return params as unknown as T;\n}\n\n// Generation params are deliberately NOT covered: generation is the one job\n// that does not read a source resource (it mints one), so it requires no\n// `resourceId` and there is nothing to verify.\n\nexport function isPendingJob(job: AnyJob): job is PendingJob<any> {\n return job.status === 'pending';\n}\n\nexport function isRunningJob(job: AnyJob): job is RunningJob<any, any> {\n return job.status === 'running';\n}\n\nexport function isCompleteJob(job: AnyJob): job is CompleteJob<any, any> {\n return job.status === 'complete';\n}\n\nexport function isFailedJob(job: AnyJob): job is FailedJob<any> {\n return job.status === 'failed';\n}\n\nexport function isCancelledJob(job: AnyJob): job is CancelledJob<any> {\n return job.status === 'cancelled';\n}\n\n// ============================================================================\n// Job Query Types\n// ============================================================================\n\nexport interface JobQueryFilters {\n status?: JobStatus;\n type?: JobType;\n userId?: UserId;\n limit?: number;\n offset?: number;\n}\n\n","/**\n * Bounded inference calls — WORKER-LIVENESS.md P2 (G1: prevention).\n *\n * The claim loop's only unbounded await is the model call: bus\n * operations gained transport timeouts in 0.5.6, the inference HTTP\n * request did not. One request that never settles used to wedge the\n * worker forever — the adapter ignores announcements while\n * `isProcessing`, so a single stuck call silenced the whole agent.\n * Bounding the call converts that silent hang into an ordinary job\n * failure that flows through the existing `job:fail` path (and the\n * gateway's retry budget — a timeout is transient-shaped, so retrying\n * is correct) and frees the claim loop.\n *\n * This is a timeout AND a cancellation (ABANDONED-INFERENCE P1): on expiry\n * the bound aborts the underlying request through the `InferenceClient`\n * signal, so the transport — and, on the Anthropic path, the SDK's internal\n * retry loop — is torn down rather than left running as a billed zombie\n * (P0 caught one completing 24–34 minutes after its job was gone). The\n * timeout stays as the last line either way, exactly as before; the abort is\n * the addition, not the replacement (D1). The eventual settlement of the\n * aborted promise is still swallowed so it cannot surface as an unhandled\n * rejection.\n */\n\nimport type { ElementSchema, InferenceClient, InferenceResponse, StructuredResponse } from '@semiont/inference';\nimport type { Logger } from '@semiont/core';\nimport { withSpan } from '@semiont/observability';\n\n/**\n * Generous single-call bound. Slow local models on large prompts run\n * minutes, not tens of minutes; the stall watchdog (P3) sits above\n * this at 15 minutes, and the gateway's dead-worker janitor above\n * that at 30. Fixed by design — no env knob.\n */\nexport const INFERENCE_TIMEOUT_MS = 10 * 60_000;\n\n/**\n * The bound's own rejection, typed so classification (ABANDONED-INFERENCE\n * P3) never string-matches our own error message. A timeout says nothing\n * about the request — it classifies transient.\n */\nexport class InferenceTimeoutError extends Error {\n override readonly name = 'InferenceTimeoutError';\n}\n\n/**\n * How often an in-flight call reports that it is still alive\n * (DETECTION-HEARTBEAT D2).\n *\n * Detection's other liveness signal — the chunk-boundary heartbeat — emits\n * `N − 1` events for `N` chunks, which is ZERO for the single-chunk case that\n * every realistic document falls into (the derived input budget is ~935 K\n * tokens). A 7-minute call then emits nothing at all, and the client's\n * *inter-emission* timeout (`mark-state-unit`, 180 s) kills a perfectly\n * healthy job.\n *\n * Sized against that consumer: 15 s gives ~12 beats of margin inside the\n * 180 s window. Fixed by design, like the bound above — no env knob.\n */\nexport const INFERENCE_HEARTBEAT_MS = 15_000;\n\n/**\n * Called while a provider call is still in flight. Liveness only — the\n * caller repeats its current stage rather than inventing an advancing\n * percentage (D3): nothing here knows how far a single model call has got.\n */\nexport type InferenceHeartbeat = () => void;\n\n/**\n * One span per provider call. Before this, a 411-second detection job was a\n * SINGLE span with no children on a fully instrumented stack — it was not\n * possible to tell extraction from inference from telemetry, which is what\n * made the sibling silent-empty bug expensive to find. Attributes stay to\n * what is known before the answer arrives; token counts are recorded by\n * `recordInferenceUsage` in the client.\n */\nfunction spanned<T>(client: InferenceClient, kind: string, maxTokens: number, work: () => Promise<T>): Promise<T> {\n return withSpan(`inference:${kind}`, work, {\n attrs: {\n 'inference.provider': client.type,\n 'inference.model': client.modelId,\n 'inference.max_tokens': maxTokens,\n },\n });\n}\n\nasync function withTimeout<T>(\n work: (signal: AbortSignal) => Promise<T>,\n meta: { provider: string; model: string; label: string },\n onHeartbeat?: InferenceHeartbeat,\n logger?: Logger,\n): Promise<T> {\n const controller = new AbortController();\n let timer!: ReturnType<typeof setTimeout>;\n const timedOut = new Promise<never>((_, reject) => {\n timer = setTimeout(() => {\n // True cancellation: tear the request down at the transport so it\n // cannot keep running (and billing) against a job that no longer\n // exists — and name the abort in the log, because an invisible\n // abandonment is what let a zombie burn 24+ minutes unrecorded (D3).\n logger?.warn('Aborting in-flight inference call at the timeout bound', {\n provider: meta.provider,\n model: meta.model,\n label: meta.label,\n boundMs: INFERENCE_TIMEOUT_MS,\n });\n controller.abort();\n reject(new InferenceTimeoutError(\n `Inference call timed out after ${INFERENCE_TIMEOUT_MS / 60_000} minutes (${meta.label}) — failing the job to keep the claim loop live`,\n ));\n }, INFERENCE_TIMEOUT_MS);\n timer.unref?.();\n });\n\n // One timer at one site covers every provider call, present and future —\n // putting it in the detection loops instead would re-couple liveness to\n // detection's own structure, which is the coupling this exists to undo.\n let heartbeat: ReturnType<typeof setInterval> | undefined;\n if (onHeartbeat) {\n heartbeat = setInterval(() => {\n try {\n onHeartbeat();\n } catch {\n // A failing progress emit must never take down the inference call\n // it is merely reporting on.\n }\n }, INFERENCE_HEARTBEAT_MS);\n heartbeat.unref?.();\n }\n\n const pending = work(controller.signal);\n try {\n return await Promise.race([pending, timedOut]);\n } catch (err) {\n // The aborted call settles promptly now (AbortError from the transport)\n // rather than minutes later — but its rejection still lands after the\n // race is lost, so it is still swallowed here to keep it from surfacing\n // as an unhandled one and killing the process.\n pending.catch(() => {});\n throw err;\n } finally {\n clearTimeout(timer);\n // Cleared with the timeout, in the same finally: a leaked interval would\n // beat forever on a completed job.\n if (heartbeat) clearInterval(heartbeat);\n }\n}\n\nexport function boundedGenerateWithMetadata(\n client: InferenceClient,\n prompt: string,\n maxTokens: number,\n temperature: number,\n onHeartbeat?: InferenceHeartbeat,\n logger?: Logger,\n): Promise<InferenceResponse> {\n return spanned(client, 'text', maxTokens, () => withTimeout(\n (signal) => client.generateTextWithMetadata(prompt, maxTokens, temperature, signal),\n { provider: client.type, model: client.modelId, label: `${client.type}:${client.modelId}` },\n onHeartbeat,\n logger,\n ));\n}\n\nexport function boundedGenerateStructured<T>(\n client: InferenceClient,\n prompt: string,\n maxTokens: number,\n temperature: number,\n elementSchema: ElementSchema,\n onHeartbeat?: InferenceHeartbeat,\n logger?: Logger,\n): Promise<StructuredResponse<T>> {\n return spanned(client, 'structured', maxTokens, () => withTimeout(\n (signal) => client.generateStructured<T>(prompt, maxTokens, temperature, elementSchema, signal),\n { provider: client.type, model: client.modelId, label: `${client.type}:${client.modelId}` },\n onHeartbeat,\n logger,\n ));\n}\n","/**\n * Failure classification — ABANDONED-INFERENCE P3 (A4, HD2).\n *\n * A retried deterministic failure always costs exactly double: the second\n * attempt of the same request cannot succeed. HD2's ruling is one-sided —\n * only KNOWN-deterministic failures skip the retry budget; everything\n * unrecognized stays retryable (`undefined`), because mis-classifying a\n * transient failure as deterministic silently halves reliability, while the\n * reverse merely costs one wasted attempt (today's behavior).\n *\n * Classification happens HERE, in the worker, where errors are still typed —\n * at the gateway's `job:fail` handler the failure is already a flattened\n * string, and message-regex classification is the drift this module exists\n * to avoid. The class rides the `job:fail` payload; `failJob` consumes it.\n */\n\nimport { isNumber, isObject, isString, RETRY_RULES, type components } from '@semiont/core';\nimport { StructuredReadError } from '@semiont/inference';\nimport { InferenceTimeoutError } from './workers/inference-call';\n\n/** Derived from the spec, not restated: the wire owns this vocabulary\n * (`FailureClass.json`, referenced by job:fail and job:failed alike). */\nexport type FailureClass = components['schemas']['FailureClass'];\n\n/**\n * Marker for failures WE know cannot succeed on a second identical attempt —\n * thrown at the sites that judge the work itself rather than the transport:\n * response truncation despite the derived budget, a media type with nothing\n * to extract. Throw it instead of `Error` wherever that knowledge exists.\n */\nexport class DeterministicJobError extends Error {\n // Typed string, not the literal: subclasses (YieldCollapseError) carry\n // their own name — classification is instanceof, never name-matching.\n override readonly name: string = 'DeterministicJobError';\n}\n\n/**\n * The taxonomy, and its provider coupling — THE part that will drift:\n *\n * - `@anthropic-ai/sdk` errors carry a numeric `status`, and WHICH statuses are\n * worth another attempt is not decided here: it is `RETRY_RULES.job`\n * (RETRY-CLASSIFICATION P2). This file used to restate the same three\n * conditions, which made it a second opinion on a question core already\n * answered — and the census that found it showed that where a bare list and a\n * reasoned rule disagree, the list wins silently. The rule carries the\n * reasoning; this file carries the ONE thing the rule cannot know: that\n * anything else at or above 400 is a rejected request, and re-sending it\n * unchanged is guaranteed waste → deterministic.\n * - Aborts (`APIUserAbortError` from the SDK, `AbortError` from fetch/mock)\n * are our own bound or shutdown tearing the transport down — nothing was\n * judged → transient.\n * - Ollama surfaces plain `Error`s (no status) and network failures as\n * `TypeError: fetch failed`; the SDK's `MessageStream terminated` is a\n * dropped connection. All land `undefined` → retryable, the safe default.\n * - `@semiont/inference`'s `StructuredReadError` carries the stop reason\n * because the cause classifies differently: `max_tokens` is truncation of\n * an over-demanded answer — the adapter throws before the caller's\n * `assertNotTruncated` can see the stop reason, so the classification\n * must happen on the typed error itself (measured live 2026-09-02) —\n * while any other reason is model misbehavior a retry may fix.\n */\nexport function classifyFailure(error: unknown): FailureClass | undefined {\n if (error instanceof DeterministicJobError) return 'deterministic';\n if (error instanceof InferenceTimeoutError) return 'transient';\n if (error instanceof StructuredReadError && error.stopReason === 'max_tokens') return 'deterministic';\n // A `StructuredReadError` with any OTHER stop reason falls through to\n // `undefined` — retryable, and DECIDED rather than defaulted\n // (RETRY-CLASSIFICATION P2, 2026-09-12).\n //\n // It only reaches here having exhausted `MAX_SUBDIVISION_DEPTH`, so\n // `subdividable()` has already argued the opposite: at\n // `DETECTION_TEMPERATURE` 0 the identical call returns the identical failure.\n // That argument was decisive while chunk boundaries were fixed up front. It is\n // not any more — CHUNK-GRAIN-RESUME HD2 (option C) seeds a resumed unit's size\n // from the checkpoint and then takes one shrink step, so the retry re-cuts the\n // poison piece at a DIFFERENT size and can genuinely come out differently. The\n // price of being wrong fell with it: one chunk, not the whole prefix.\n //\n // It stays `undefined` rather than becoming `'transient'` on purpose. The wire\n // vocabulary has two values, and this is neither: it is not weather, it is\n // \"retryable because the next attempt reads different input\". Claiming\n // `transient` would assert an environmental cause nobody established, and\n // absent already means exactly what is true — unrecognised, so retryable.\n if (!isObject(error)) return undefined;\n\n const name = isString(error.name) ? error.name : '';\n if (name === 'DeterministicJobError') return 'deterministic';\n if (name === 'APIUserAbortError' || name === 'AbortError') return 'transient';\n\n const status = isNumber(error.status) ? error.status : undefined;\n if (status !== undefined) {\n if (RETRY_RULES.job.retryable({ status })) return 'transient';\n if (status >= 400) return 'deterministic';\n }\n\n return undefined;\n}\n","/**\n * Adaptive chunk sizing (DETECTION-QUALITY-THROUGHPUT P2) — the sizing RULE.\n *\n * `deriveDetectionBudget` picks ONE input size for every document from provider\n * limits alone. It cannot see the document, so it is sized for a worst-case\n * dense one (input = half the output budget) — and a sparse document then pays\n * that pessimism as many tiny, overhead-dominated calls. This adjusts the chunk\n * size AS THE UNIT RUNS, from what each call actually produced, so a document\n * that turns out sparse grows into bigger chunks and a dense stretch pulls back.\n *\n * It steers ONE number: how full of the output budget each chunk came in. That\n * is the whole model, and it is enough because both hard failures are already\n * caught downstream by `callChunkSubdividing` — a chunk that truncates is\n * halved-and-retried, a chunk that outruns the guillotine times out and is\n * halved-and-retried. So the sizer does not model truncation or duration; it\n * only keeps chunks in a band that uses the budget well, and the net below it\n * forgives an overshoot. `bounds.ceiling` is therefore the WINDOW fit, not either\n * derived input size: on a shared window both of those come back at exactly the\n * density guess this function exists to replace, leaving nothing to grow into. That safety net is why this is a step rule and not a\n * control loop (user direction, 2026-09-04: keep it simple).\n *\n * \"Maximize, don't predict\": the only thing that grows a chunk is a MEASURED\n * under-use of the budget. No document statistic, no a-priori density model\n * (#1121's ban). Re-evaluated every chunk, so it tracks a gradient (sparse\n * intro → dense index) rather than betting the run on the first sample.\n *\n * Pure and feedback-only — unit-testable in isolation. The loop that applies it is\n * `runAdaptiveChunks` in `detection-chunking.ts`, which both detection paths drive:\n * `extractEntities` (reference) and `detectInChunks` (highlight, comment, assessment,\n * tag). It cuts chunk N+1 only after chunk N has reported, which is the only ordering\n * in which this function's output can reach anything.\n */\n\n/** What one chunk's call produced — the minimum the sizer needs. Duration and\n * input are NOT here: the guillotine and truncation are the hard bounds, both\n * backstopped by subdivision, so utilization is the only signal that sizing\n * acts on. (P1 telemetry records the fuller picture separately.) */\nexport interface CallOutcome {\n /** Provider-reported output tokens for the chunk, ABSENT when the provider\n * reported none.\n *\n * Absent is not zero. `usage` is optional on the inference interface and both\n * real clients emit it conditionally, so a run against a provider that stays\n * silent would read as \"this chunk produced nothing\", i.e. 0%% of the budget,\n * i.e. grow — every chunk, to the ceiling, on no evidence whatsoever. That is\n * the precise inverse of \"maximize, don't predict\". Unmeasured holds. */\n outputTokens?: number;\n /** The chunk hit a size-shaped bound (truncation or the guillotine), even if\n * subdivision then recovered it. Forces a shrink regardless of the count —\n * the signal that the last size was too big for this stretch. */\n truncated: boolean;\n}\n\n/** Provider-DERIVED sizing bounds for one job — not tuning, not policy. From\n * `deriveDetectionBudget` and the model's window; recomputed per job, never\n * user-facing. */\nexport interface SizingBounds {\n /** Smallest useful input chunk (tokens) — the overlap-derived floor. */\n floor: number;\n /** Largest input chunk (tokens) — the window-fit safety cap; never exceeded. */\n ceiling: number;\n /** The output budget one call is given. Utilization is measured against it. */\n outputBudget: number;\n}\n\n/**\n * The tuning knobs — the numbers that decide how aggressively chunk size chases\n * measured output usage. Grouped and named deliberately, because these are\n * POLICY, not derivation, and the likely future is that they become\n * user/admin-tunable, perhaps per inference provider (a fast, cheap model\n * tolerates more aggressive growth than a slow, dear one).\n *\n * That future changes where a `ChunkSizingPolicy` comes FROM — provider config,\n * a settings surface — not the sizing function, which already takes it as a\n * value, nor the caller's shape. There is exactly ONE today, the default below.\n * No config system, no env var, no per-provider map yet: just a single named\n * home, so when the need is real there is one obvious thing to make configurable\n * and one obvious place to select it (the caller holds the `InferenceClient`,\n * hence the provider).\n */\nexport interface ChunkSizingPolicy {\n /** Below this fraction of the output budget, the chunk left room — grow. */\n growBelow: number;\n /** Above this fraction, the chunk is near the edge — ease off before it\n * truncates and costs a subdivision retry. */\n shrinkAbove: number;\n /** Geometric step when growing (>1). Geometric so a genuinely sparse document\n * reaches its ceiling in a few steps and a bad step costs one correction. */\n growFactor: number;\n /** Geometric step when easing off (<1). Also the response to a truncation. */\n shrinkFactor: number;\n}\n\n/** The one policy in effect today. Coarse on purpose — subdivision forgives a\n * wrong guess — and every value is a candidate for the tunable surface above. */\nexport const DEFAULT_CHUNK_SIZING_POLICY: ChunkSizingPolicy = {\n growBelow: 0.5,\n shrinkAbove: 0.8,\n growFactor: 1.5,\n shrinkFactor: 0.7,\n};\n\nfunction clamp(value: number, lo: number, hi: number): number {\n return Math.max(lo, Math.min(hi, value));\n}\n\n/**\n * Propose the next chunk's input size from what the last chunk produced.\n *\n * A truncated chunk, or one whose output filled more than `shrinkAbove` of the\n * budget, eases the next chunk down; one that used less than `growBelow` grows\n * it; in between — or with nothing measured at all — hold. Always clamped to `[floor, ceiling]`. That is the whole\n * rule — the hard bounds are subdivision's job, not this function's.\n */\nexport function nextChunkSize(\n outcome: CallOutcome,\n current: number,\n bounds: SizingBounds,\n policy: ChunkSizingPolicy = DEFAULT_CHUNK_SIZING_POLICY,\n): number {\n const grow = () => clamp(Math.floor(current * policy.growFactor), bounds.floor, bounds.ceiling);\n const shrink = () => clamp(Math.floor(current * policy.shrinkFactor), bounds.floor, bounds.ceiling);\n const hold = () => clamp(current, bounds.floor, bounds.ceiling);\n\n if (outcome.truncated) return shrink();\n // No measurement, no move. A truncation above is evidence even without a\n // count; an absent count on its own is not.\n if (outcome.outputTokens === undefined) return hold();\n // A zero/absent budget cannot inform utilization; hold rather than divide by\n // zero or grow blindly.\n if (bounds.outputBudget <= 0) return hold();\n\n const utilization = outcome.outputTokens / bounds.outputBudget;\n if (utilization < policy.growBelow) return grow();\n if (utilization > policy.shrinkAbove) return shrink();\n return hold();\n}\n","/**\n * Detection budget derivation — pure window arithmetic over the inference\n * provider's published limits. No hand-tuned chunk constants, no density or\n * yield modeling: document content never enters this arithmetic. The guard\n * for the pathological tail (a chunk whose annotation JSON still overflows\n * the output budget) is `assertNotTruncated` below — invoked per chunk by\n * the callers on every response, not a prediction here.\n *\n * Provider shapes (see `@semiont/inference` interface.ts):\n * - Shared window (Ollama): the provider publishes\n * `maxOutputTokens === contextTokens` — prompt and response share one\n * `num_ctx`. What remains after the prompt scaffold is split\n * input:output = 1:2 (annotation JSON echoes each span plus a fixed\n * key/context envelope, so output needs the larger share). The 1:2 ratio\n * is the plan's one allocation policy — doc-independent, tuned only on\n * live evidence.\n * - Separate ceilings (Anthropic): output takes its full ceiling (duration-\n * capped below), and input follows the same 1:2 allocation — never more\n * than half the output budget. Input does NOT get \"the rest of the\n * window\": measured 2026-09-02, a window-sized chunk of entity-dense text\n * demands more output than any budget holds, so the model grinds toward\n * max_tokens for minutes (killed at the call bound as a \"stall\") or\n * collapses to the degenerate []. Large documents chunk; that is the fix,\n * not a cost.\n */\n\nimport { chunkText, cutChunk, type ChunkingConfig, type Logger, type UnitCursor } from '@semiont/core';\nimport { StructuredReadError, type InferenceLimits, type TokenUsage } from '@semiont/inference';\nimport { recordDetectionCall } from '@semiont/observability';\nimport { DeterministicJobError } from '../../failure-class';\nimport { INFERENCE_TIMEOUT_MS, InferenceTimeoutError } from '../inference-call';\nimport { nextChunkSize, type CallOutcome, type SizingBounds } from './chunk-size-controller';\n\n/**\n * A `max_tokens` stop reason means the model's JSON was cut off mid-stream.\n * Structured output serializes whatever was generated, so that still yields\n * a syntactically-valid but incomplete array — it would parse cleanly and\n * silently under-report. Fail the job loudly instead. With derived budgets\n * this fires only on pathological annotation density.\n *\n * ONE decider for every detection path (entity extraction and the four\n * motivations) — the classification must not diverge between them: same\n * input truncates the same way, so a retry is guaranteed waste and the\n * throw carries the deterministic class (ABANDONED-INFERENCE P3, A4).\n */\nexport function assertNotTruncated(response: { stopReason: string }, label: string, at: number, totalChars: number, outputBudget: number): void {\n if (response.stopReason === 'max_tokens') {\n throw new DeterministicJobError(`${label} response truncated (max_tokens) at character ${at} of ${totalChars} despite the derived output budget of ${outputBudget} tokens — failing the job rather than under-reporting annotations.`);\n }\n}\n\n/**\n * `reconcileSelector` disambiguates a span with up to 64 chars of prefix and\n * 64 of suffix (the annotation-selector schema). Overlap must let a span\n * sitting at a chunk boundary carry that context — plus a span allowance of\n * the same order — into the adjacent chunk. Schema-derived, not tuned.\n */\nconst SELECTOR_CONTEXT_CHARS = 64;\nconst OVERLAP_CHARS =\n SELECTOR_CONTEXT_CHARS + // prefix\n SELECTOR_CONTEXT_CHARS + // suffix\n 2 * SELECTOR_CONTEXT_CHARS; // span allowance\n/** ~4 chars/token — the same heuristic `estimateTokens`/`chunkText` use. */\nconst OVERLAP_TOKENS = Math.ceil(OVERLAP_CHARS / 4);\n\n/**\n * One temperature for every detection call. Detection copies spans verbatim\n * against a closed type vocabulary — a fidelity task, so determinism is the\n * right default (reproducible re-runs). Measured equal to hotter settings\n * on yield and consistency at production call sizes.\n */\nexport const DETECTION_TEMPERATURE = 0;\n\n/**\n * Assumed worst-case output rate for providers that publish none\n * (OLLAMA-DETECTION-TESTING P3b, from F9). Rate-silent providers (Ollama —\n * local hardware, rate unknowable a priori) previously got NO duration bound:\n * capacity-sizing handed a 262K-window model a ~174K-token output budget, and\n * a repetition loop then burned the full 10-minute guillotine as TRANSIENT —\n * retried identically. Under a duration-shaped cap the same loop dies in\n * minutes as max_tokens → deterministic → subdividable: the useful failure.\n *\n * 30 tok/s. P2 measured 36–90 tok/s across three local model families\n * (qwen3.5:9b ~36, gemma4:26b ~50, gemma4:e2b ~90), and the half-bound rule\n * in the cap below means real rates down to 15 tok/s still finish inside the\n * guillotine — anything slower is wedged, not working, which is exactly what\n * the guillotine is for. A conservative floor, deliberately NOT per-model:\n * F8 showed per-model thresholds chase noise. Owned policy constant, same\n * status as the 1:2 split and DETECTION_TEMPERATURE.\n */\nexport const ASSUMED_OUTPUT_TOKENS_PER_HOUR = 108_000;\n\n/**\n * The count-verifier band (OLLAMA-DETECTION-TESTING P3c, user-ratified\n * 2026-09-05): an extraction that found fewer than 1/BAND of the mentions a\n * cheap count call reports is flagged as silent yield collapse (F7). The band\n * is LOAD-BEARING at 2: F12 measured the same model flipping between\n * entity-like and mention-like enumeration on the same corpus, and that\n * legitimate judgment spread must fit INSIDE the band — probe evidence (n=6)\n * put every healthy ratio at 0.68–0.88 and every collapse at 0.18–0.41.\n */\nexport const YIELD_COLLAPSE_BAND = 2;\n\n/**\n * The silent-yield-collapse verdict (F7): a schema-clean, done-reason-clean\n * extraction that found a fraction of what a cheap count call says is present.\n * Extends DeterministicJobError because the collapse is MEASURED deterministic\n * — bit-identical across retries and across budget regimes — so a retry is\n * guaranteed waste; the classification and the size-floored subdivision\n * descent both follow from the base class. It diverges from truncation at the\n * size floor: NO re-roll (a same-size re-roll provably returns the identical\n * collapse), and — ruled 2026-09-05 after P4 attempt 1, amending the original\n * fail-the-job invariant — the floor ACCEPTS the flagged piece's `salvage`\n * loudly rather than failing the unit: one hostile ~530-char stretch had\n * discarded ~20 chunks of good extraction, and at floor sizes the count's\n * evidence sits far below anything the probe validated. The warning and the\n * 'collapsed' telemetry rows are the durable record; re-detection heals.\n */\n/** A floor-accepted piece's evidence: what extraction found against what a\n * count call reported, over a piece of this size. Facts only — never a\n * judgment against an expected yield. */\nexport interface UnderReportedPiece {\n found: number;\n counted: number;\n pieceChars: number;\n}\n\nexport class YieldCollapseError extends DeterministicJobError {\n override readonly name = 'YieldCollapseError';\n /** What the flagged extraction DID find — every span write-time-verified,\n * so discarding it at the floor would add loss on top of the under-report.\n * Carried on the error because the flag site cannot know whether descent\n * remains possible; the floor is the subdivider's knowledge. The verdict\n * rides beside it so an acceptance can report evidence, not a message\n * string. */\n constructor(message: string, readonly salvage: unknown[], readonly verdict: UnderReportedPiece) {\n super(message);\n }\n}\n\nexport interface DetectionBudget {\n /** The chunk size a run OPENS at (tokens), plus the overlap every cut keeps.\n * Opening, not fixed: `runAdaptiveChunks` moves the size within `bounds` as\n * the document reports what it actually costs. */\n chunking: ChunkingConfig;\n /** Pass as `maxTokens` on every per-chunk inference call. */\n outputBudget: number;\n /** How far the sizer may move the chunk size, in either direction. Derived\n * from the same provider arithmetic — never tuning. */\n bounds: SizingBounds;\n}\n\n/**\n * Derive the per-call input/output token budgets for a detection job from the\n * provider's actual limits and the measured prompt scaffold.\n *\n * @param limits - the provider's published ceilings (`client.limits()`)\n * @param scaffoldTokens - tokens of the prompt template around the content,\n * measured from the actually-built template (e.g. `estimateTokens(build(''))`)\n * @param typesPerCall - how many entity types (or equivalent span families)\n * ONE call asks for: output demand scales with it, so the allocation\n * divides by it. The per-type loop passes 1; a future multi-type batch\n * passes its batch size.\n * @throws when the window is too small to hold the scaffold plus a useful\n * chunk — fail-loud, same family as the truncation and window guards.\n */\nexport function deriveDetectionBudget(\n limits: InferenceLimits,\n scaffoldTokens: number,\n typesPerCall: number,\n): DetectionBudget {\n const { contextTokens, maxOutputTokens } = limits;\n const available = contextTokens - scaffoldTokens;\n\n let inputBudget: number;\n let outputBudget: number;\n\n if (maxOutputTokens >= contextTokens) {\n // Shared window: 1:2 input:output split of what the scaffold leaves.\n inputBudget = Math.floor(available / 3);\n outputBudget = available - inputBudget;\n } else {\n outputBudget = maxOutputTokens;\n inputBudget = contextTokens - outputBudget - scaffoldTokens;\n if (inputBudget <= 0) {\n // Degenerate separate-ceilings shape (output ceiling nearly fills the\n // window): fall back to the shared split rather than starving input.\n inputBudget = Math.floor(available / 3);\n outputBudget = available - inputBudget;\n }\n }\n\n // Duration floor (ABANDONED-INFERENCE P4 HD3; universalized by\n // OLLAMA-DETECTION-TESTING P3b): cap each call's output at what the\n // provider's worst-case rate finishes inside our own inference bound — a\n // call projected past the 10-minute guillotine is planned-to-fail — and\n // scale input by the same factor, so the input:output ratio (and with it\n // the per-chunk truncation risk profile) is exactly the capacity\n // solution's. Capacity says what CAN fit in one call; this says what\n // SHOULD. EVERY provider gets the bound: the published rate when there is\n // one, the conservative assumed floor when there is not — a rate-silent\n // provider with no bound turned repetition loops into hour-long transient\n // burns (F9; see ASSUMED_OUTPUT_TOKENS_PER_HOUR). It is a floor on chunk\n // count, never a raise (a tighter capacity budget is left alone). Side\n // effect worth knowing: on Anthropic this lands every detection call at\n // or under the SDK's non-streaming threshold — off the MessageStream path\n // the original `terminated` failure arrived on.\n const outputTokensPerHour = limits.outputTokensPerHour ?? ASSUMED_OUTPUT_TOKENS_PER_HOUR;\n {\n // Half the bound, not all of it: at the full bound the slowest\n // legitimate full-budget generation lands exactly on the guillotine.\n // At half, the guillotine fires only on calls at least 2× slower than\n // the provider's own worst-case model — wedged, not working.\n const durationSafeOutput = Math.floor(outputTokensPerHour * (INFERENCE_TIMEOUT_MS / 2) / 3_600_000);\n if (outputBudget > durationSafeOutput) {\n inputBudget = Math.floor(inputBudget * (durationSafeOutput / outputBudget));\n outputBudget = durationSafeOutput;\n }\n }\n\n // The 1:2 allocation policy, applied to every provider shape (the shared\n // split satisfies it by construction; separate ceilings did not). Output\n // must hold an annotation echo of every span in the chunk plus its\n // key/context envelope, so a chunk larger than half the output budget is\n // a call whose honest answer cannot fit — the failure measured live on\n // 2026-09-02 (silent multi-minute grinds to max_tokens, degenerate []).\n // The demand is PER TYPE ASKED FOR, so a K-type call divides the input\n // share by K. Still no density modeling: same one policy, content never\n // enters.\n // The window fit: the largest input that still leaves the WHOLE output budget\n // room beside it. This is the sizer's ceiling, and it is the only hard bound\n // on input there is — the two derivations above are not.\n //\n // The 1:2 rule below is a GUESS about how much output a chunk will demand,\n // and on a shared window it is baked into the 1:3 split as well, which is why\n // the ceiling cannot be read off either of them (both come back at exactly\n // the guess, leaving no room to grow into on precisely the self-hosted\n // providers the live gate runs). The duration bound is real but bounds\n // GENERATION, so it belongs to `outputBudget`; it reaches input only through\n // the same ratio guess. `nextChunkSize` exists to replace that guess with\n // measurement, so the guess opens the run and the window caps it.\n const capacityInput = contextTokens - scaffoldTokens - outputBudget;\n\n inputBudget = Math.min(inputBudget, Math.floor(outputBudget / (2 * typesPerCall)));\n\n if (inputBudget <= OVERLAP_TOKENS) {\n throw new Error(\n `Inference window too small for detection: context ${contextTokens} tokens minus scaffold ${scaffoldTokens} leaves an input budget of ${inputBudget} (need > ${OVERLAP_TOKENS}). Use a model with a larger context window or reduce the prompt scaffold.`,\n );\n }\n\n return {\n chunking: { chunkSize: inputBudget, overlap: OVERLAP_TOKENS },\n outputBudget,\n bounds: {\n // Two overlaps: at the floor a chunk is still half text it has not seen\n // before, so the cursor keeps making progress rather than re-reading its\n // own tail. Clamped under the opening size, because a cramped window can\n // derive an opening below even that — and a floor above the opening would\n // clamp every proposal upward, silently reversing the sizer.\n floor: Math.min(inputBudget, 2 * OVERLAP_TOKENS),\n // Never below the opening: a degenerate window can make the fit\n // arithmetic smaller than the size already chosen, and a ceiling under\n // the opening would clamp every proposal DOWN on the first call.\n ceiling: Math.max(inputBudget, capacityInput),\n outputBudget,\n },\n };\n}\n\n/** One chunk handed out by `runAdaptiveChunks`, with the cursor either side of\n * it. `at`/`next` over `totalChars` is exact progress — and, once\n * CHUNK-GRAIN-RESUME lands, the checkpoint identity a variable boundary forces\n * (an ordinal cannot name a chunk whose size is decided while the job runs). */\n/**\n * The half of a `UnitCursor` the DETECTION layer can honestly report: where the\n * walk stands and how it is cutting. The tallies belong to the processor, which\n * owns unit identity and unit counts — this loop counts chunks, not annotations,\n * and a zero it invented would be indistinguishable from a real one.\n */\nexport type ChunkCursor = Pick<UnitCursor, 'next' | 'size'>;\n\nexport interface AdaptiveChunk {\n piece: string;\n /** The token size this piece was cut at. Hand it to `callChunkSubdividing`:\n * a descent halves from the size that actually failed, not from the size the\n * run opened at, which adaptivity has long since left behind. */\n size: number;\n /** Characters consumed BEFORE this chunk — the in-flight liveness position. */\n at: number;\n /** Characters consumed once this chunk completes — the boundary position. */\n next: number;\n /** The document's length. */\n totalChars: number;\n}\n\n/**\n * Walk a document in chunks whose size is decided by the chunks before them.\n *\n * `chunkText` fixes every boundary up front from provider limits alone, which\n * is why the sizing rule could exist for a week and change nothing: a\n * measurement taken on chunk N had nowhere to land. Here chunk N+1 is cut only\n * after chunk N has reported, so the run opens at the static density guess and\n * then moves — a sparse document climbing toward the window's real capacity\n * (fewer, bigger calls), a dense one easing off before it pays a subdivision.\n *\n * `onChunk` returns what the chunk cost. It is awaited, so the caller's own\n * durability (committing the chunk's annotations) still gates the next cut, and\n * a throw stops the walk where it stands rather than advancing past unprocessed\n * text.\n *\n * `resume` restarts a unit an earlier attempt left partway (CHUNK-GRAIN-RESUME\n * P3) — the checkpoint P2 made durable, spent. Both halves of it matter and they\n * are spent differently: the position is taken as given, while the size is\n * seeded and then stepped ONCE, as if the last outcome had been a failure. It\n * was: the job died. Opening at the budget instead would throw away the\n * calibration the dead attempt paid for over its earlier chunks, and seeding\n * unchanged would re-cut the identical piece — which at `DETECTION_TEMPERATURE`\n * 0 returns the identical answer, failure included (HD2, option C).\n */\nexport async function runAdaptiveChunks(\n text: string,\n budget: DetectionBudget,\n onChunk: (chunk: AdaptiveChunk) => Promise<CallOutcome>,\n resume?: UnitCursor,\n): Promise<void> {\n let at = resume?.next ?? 0;\n let size = resume\n ? nextChunkSize({ truncated: true }, resume.size, budget.bounds)\n : budget.chunking.chunkSize;\n\n while (at < text.length) {\n const { piece, next } = cutChunk(text, at, { chunkSize: size, overlap: budget.chunking.overlap });\n const outcome = await onChunk({ piece, size, at, next, totalChars: text.length });\n at = next;\n size = nextChunkSize(outcome, size, budget.bounds);\n }\n}\n\n/**\n * Depth cap for TIMEOUTS only: a call still timing out on a quarter-sized\n * chunk is not a size problem, and timeouts classify transient — the\n * job-level retry is their second chance. Truncations descend by size\n * instead (see `callChunkSubdividing`).\n */\nexport const MAX_SUBDIVISION_DEPTH = 2;\n\n/** The failures a smaller chunk can plausibly fix. An unreadable response\n * that stopped naturally is model misbehavior, not size. */\n/** The F3 shape: unreadable output whose stop reason is UNKNOWN (done_reason\n * absent). Held not-subdividable through P3a for lack of evidence; the live\n * gate then reproduced it twice on real text (2026-09-03 at ~21K chars, P4\n * attempt 2 at ~4.5K, mid-descent, adjacent to max_tokens truncations that\n * healed by subdividing) and measured the retry-at-same-size alternative as a\n * deterministic 34 s burn. Size-shaped on the evidence — descends like\n * truncation, but with no floor re-roll (near-deterministic, nothing salvaged)\n * and unchanged retryable classification (a genuinely broken server still\n * deserves its budget). */\nfunction unknownUnreadable(error: unknown): boolean {\n return error instanceof StructuredReadError && error.stopReason === 'unknown';\n}\n\n/**\n * The model FINISHED and still emitted unparseable JSON — not cut off, not\n * stopped for an unknown reason. Subdividable, but DEPTH-capped rather than\n * size-floored, and the two halves of that are separately earned:\n *\n * Subdividable, because one of these killed a 26-minute attempt at chunk 25 of\n * 49 while the preceding 24 chunks parsed cleanly on the same prompt and model\n * — so the malformation was content-triggered drift, which changing the input\n * can fix. A same-size retry cannot: DETECTION_TEMPERATURE is 0, so the\n * identical call returns the identical response (the same reason a collapse\n * verdict gets no re-roll).\n *\n * Depth-capped rather than size-floored, because if the drift is instead\n * systematic — the model answering this prompt shape wrongly everywhere — every\n * extra level re-reads the same content for the same verdict. Measured, the two\n * settings often coincide (the descent aborts at the first failing sub-piece\n * instead of exploring siblings, so cost is linear in depth, not geometric),\n * and where they differ the cap is the cheaper wrong answer. Timeouts are\n * capped on the same reasoning.\n */\nfunction unreadableDespiteFinishing(error: unknown): boolean {\n return error instanceof StructuredReadError && error.stopReason === 'end_turn';\n}\n\nfunction subdividable(error: unknown): boolean {\n return error instanceof InferenceTimeoutError || truncation(error)\n || unknownUnreadable(error) || unreadableDespiteFinishing(error);\n}\n\n/** Truncation in either surface: parsed-but-flagged (`assertNotTruncated`'s\n * `DeterministicJobError`) or cut off mid-JSON (`StructuredReadError` with\n * `max_tokens`). */\nfunction truncation(error: unknown): boolean {\n return (\n error instanceof DeterministicJobError ||\n (error instanceof StructuredReadError && error.stopReason === 'max_tokens')\n );\n}\n\n/**\n * Run one chunk's inference call, subdividing IN PLACE when it fails in a\n * way a smaller chunk can fix — instead of burning the whole attempt to\n * come back at the same size. Sub-pieces re-use the caller's overlap, so\n * spans straddling a split are caught twice and fall to the downstream\n * span-keyed dedupe. On a failure subdivision cannot fix, the ORIGINAL\n * error propagates so classification sees what actually happened.\n */\n/** What `callChunkSubdividing` produced AND what it cost — the second half is\n * what `runAdaptiveChunks` sizes the next chunk from. Accumulated across the\n * whole descent, because the caller's own `call` closure sees one piece at a\n * time and cannot know a subdivision happened at all. */\nexport interface SubdividedCall<T> {\n items: T[];\n outcome: CallOutcome;\n}\n\nexport interface ChunkCallResult<T> {\n items: T[];\n /** The provider's own token counts, when it reported any. Never estimated. */\n usage?: TokenUsage;\n /** The count-verifier's expectation for this piece, when one was priced. */\n counted?: number;\n}\n\n/** Which shape of failure this was — they demand opposite responses, so the\n * history must tell them apart: truncation descends by size, a timeout fails\n * fast, anything else is not size-shaped at all. */\nfunction outcomeOf(error: unknown): 'truncated' | 'timeout' | 'collapsed' | 'error' {\n // Collapse before truncation: it IS a DeterministicJobError (so the\n // truncation check would swallow it), but in the history the two are\n // different facts — one is output overflow, the other silent under-report.\n if (error instanceof YieldCollapseError) return 'collapsed';\n if (truncation(error)) return 'truncated';\n if (error instanceof InferenceTimeoutError) return 'timeout';\n return 'error';\n}\n\nexport async function callChunkSubdividing<T>(\n label: string,\n chunk: string,\n chunking: ChunkingConfig,\n call: (piece: string) => Promise<ChunkCallResult<T>>,\n logger?: Logger,\n /** A floor-accepted piece's evidence, as it is accepted. Only the floor\n * reports: a collapse healed by descent is telemetry, not result. */\n onUnderReport?: (verdict: UnderReportedPiece) => void,\n /** Each ACCEPTED piece's count-verifier expectation — successes, floor\n * re-rolls, floor-accepted salvage. A flagged piece that descends reports\n * nothing: its children's counts replace it, or the same text is priced\n * twice. */\n onCounted?: (counted: number) => void,\n): Promise<SubdividedCall<T>> {\n // The chunk's cost, summed over however many calls its descent takes. Both\n // are recorded where EVERY call passes, so a subdivision cannot hide from\n // them.\n let outputTokens = 0;\n let sizeShaped = false;\n // A descent's calls must ALL report usage for the sum to mean anything: a\n // partial sum under-counts, and under-counting biases toward growth — the\n // one direction that costs a truncation to discover was wrong.\n let callsMade = 0;\n let callsMeasured = 0;\n // One telemetry record per model call, successes AND failures\n // (DETECTION-QUALITY-THROUGHPUT P1). This is the only place `depth` and\n // `reroll` exist, so it is the only place a complete record can be written.\n async function recorded(piece: string, depth: number, reroll: boolean): Promise<ChunkCallResult<T>> {\n const start = performance.now();\n try {\n const result = await call(piece);\n callsMade += 1;\n if (result.usage) {\n callsMeasured += 1;\n outputTokens += result.usage.outputTokens;\n }\n recordDetectionCall({\n label, pieceChars: piece.length, durationMs: performance.now() - start,\n items: result.items.length, depth, reroll, outcome: 'success',\n ...(result.usage ? { inputTokens: result.usage.inputTokens, outputTokens: result.usage.outputTokens } : {}),\n });\n // A success is always accepted — only failures subdivide.\n if (result.counted !== undefined) onCounted?.(result.counted);\n return result;\n } catch (error) {\n // A failed call still cost its input and its wall time — the descent's\n // price is invisible without it.\n recordDetectionCall({\n label, pieceChars: piece.length, durationMs: performance.now() - start,\n items: 0, depth, reroll, outcome: outcomeOf(error),\n });\n throw error;\n }\n }\n\n async function attempt(piece: string, chunkSize: number, depth: number): Promise<T[]> {\n try {\n return (await recorded(piece, depth, false)).items;\n } catch (error) {\n if (!subdividable(error)) throw error;\n // Size-shaped, so the size was wrong — true whether or not the descent\n // below rescues it. The next chunk must not be cut at a size this one\n // already paid to discover was too big.\n sizeShaped = true;\n const half = Math.floor(chunkSize / 2);\n // Truncation descends by SIZE: demand halves with each subdivision,\n // so descent terminates — and list-dense text (registers, indexes)\n // honestly yields several times its input in annotation output, so\n // the descent must go as deep as the content demands. The floor\n // derives from the overlap constant; below it even solid names fit\n // the budget. Timeouts stay depth-capped and fail fast.\n //\n // A descent must also actually CHANGE the input: once a piece fits\n // inside the smaller chunk size, re-chunking returns it unchanged, and\n // at temperature 0 the identical call returns the identical failure —\n // measured live (P4 attempt 1: one 572-char piece \"descended\" through\n // three depths, same verdict each time). A piece that cannot shrink is\n // AT its floor, whatever the arithmetic floor says.\n const pieces = chunkText(piece, { chunkSize: half, overlap: chunking.overlap });\n const shrinks = pieces.length > 1 || pieces[0] !== piece;\n // Size-shaped failures (truncation, collapse, the F3 unknown-unreadable)\n // descend to the SIZE floor; only timeouts are depth-capped — the F3 fix\n // must reach failures that first appear mid-descent (measured at depth 3).\n const canDescend = shrinks && (truncation(error) || unknownUnreadable(error)\n ? half > 2 * OVERLAP_TOKENS\n : depth < MAX_SUBDIVISION_DEPTH);\n if (!canDescend) {\n // A collapse verdict at the floor is ACCEPTED, loudly (ruled\n // 2026-09-05): its salvage flows through with a warning instead of\n // one hostile piece discarding the whole unit's work. No re-roll —\n // the collapse is deterministic, a same-size retry changes nothing.\n if (error instanceof YieldCollapseError) {\n logger?.warn('Floor-size piece still flagged as collapsed — accepting its under-reported salvage and continuing', {\n pieceChars: piece.length,\n salvaged: error.salvage.length,\n error: error.message,\n });\n onUnderReport?.(error.verdict);\n onCounted?.(error.verdict.counted);\n return error.salvage as T[];\n }\n // At the size floor honest overflow is impossible, so truncation\n // here is a degeneration loop — a sampling accident. One same-size\n // re-roll; a second truncation propagates. Timeouts get no re-roll.\n if (!truncation(error)) throw error;\n logger?.warn('Floor-size piece truncated — re-rolling once before giving up', {\n pieceChars: piece.length,\n error: error instanceof Error ? error.message : String(error),\n });\n return (await recorded(piece, depth, true)).items;\n }\n logger?.warn('Chunk call failed at a size-shaped bound — subdividing and retrying smaller', {\n depth: depth + 1,\n pieceChars: piece.length,\n nextChunkSizeTokens: half,\n error: error instanceof Error ? error.message : String(error),\n });\n const collected: T[] = [];\n for (const p of pieces) {\n collected.push(...(await attempt(p, half, depth + 1)));\n }\n return collected;\n }\n }\n const items = await attempt(chunk, chunking.chunkSize, 0);\n const measured = callsMade > 0 && callsMeasured === callsMade;\n return {\n items,\n outcome: { truncated: sizeShaped, ...(measured ? { outputTokens } : {}) },\n };\n}\n","/**\n * Prompt builders for annotation detection motivations\n *\n * Provides static methods to build AI prompts for each Web Annotation motivation type.\n * Extracted from worker implementations to centralize prompt logic.\n *\n * Locale handling: builders take two optional BCP-47 tags. `language` is the\n * locale the *generated body text* should be written in (sourced from the user's\n * UI locale); `sourceLanguage` is the locale of the *text being analyzed*\n * (sourced from `ResourceDescriptor` / its primary representation). They are\n * independent — see `types.ts` \"Locale conventions\" for the full discussion.\n */\n\nimport { getLocaleEnglishName } from '@semiont/core';\n\nfunction languageName(tag: string): string {\n return getLocaleEnglishName(tag) || tag;\n}\n\n/** Returns \"Source text language: <Name>.\" or empty string. */\nfunction sourceLanguageGuidance(sourceLanguage?: string): string {\n if (!sourceLanguage) return '';\n return `\\n\\nSource text language: ${languageName(sourceLanguage)}.`;\n}\n\n/** Returns \"Write your <kind> in <Name>.\" or empty string. */\nfunction bodyLanguageGuidance(language: string | undefined, kind: string): string {\n if (!language || language === 'en') return '';\n return `\\n\\nIMPORTANT: Write your ${kind} in ${languageName(language)}.`;\n}\n\nexport class MotivationPrompts {\n /**\n * Build a prompt for detecting comment-worthy passages\n *\n * @param content - The text content to analyze — a chunk sized by the\n * caller from derived provider limits; NEVER re-truncated here (a\n * builder-level clip is silent input loss — see #738)\n * @param instructions - Optional user-provided instructions\n * @param tone - Optional tone guidance (e.g., \"academic\", \"conversational\")\n * @param density - Optional target number of comments per 2000 words\n * @returns Formatted prompt string\n */\n static buildCommentPrompt(\n content: string,\n instructions?: string,\n tone?: string,\n density?: number,\n language?: string,\n sourceLanguage?: string\n ): string {\n let prompt: string;\n const sourceLang = sourceLanguageGuidance(sourceLanguage);\n const bodyLang = bodyLanguageGuidance(language, 'comments');\n\n if (instructions) {\n // User provided specific instructions - minimal prompt, let instructions drive behavior\n const toneGuidance = tone ? ` Use a ${tone} tone.` : '';\n const densityGuidance = density\n ? `\\n\\nAim for approximately ${density} comments per 2000 words of text.`\n : ''; // Let user instructions determine density\n\n prompt = `Add comments to passages in this text following these instructions:\n\n${instructions}${toneGuidance}${densityGuidance}${sourceLang}${bodyLang}\n\nText to analyze:\n---\n${content}\n---\n\nReturn a JSON array of comments. Each comment must have:\n- \"exact\": the exact text passage being commented on (quoted verbatim from source)\n- \"prefix\": up to 64 characters of text immediately before the passage\n- \"suffix\": up to 64 characters of text immediately after the passage\n- \"comment\": your comment following the instructions above\n\nRespond with a valid JSON array.\n\nExample:\n[\n {\"exact\": \"the quarterly review meeting\", \"prefix\": \"We need to schedule \", \"suffix\": \" for next month.\", \"comment\": \"Who will lead this? Should we invite the external auditors?\"}\n]`;\n } else {\n // No specific instructions - fall back to explanatory/educational mode\n const toneGuidance = tone\n ? `\\n\\nTone: Use a ${tone} style in your comments.`\n : '';\n const densityGuidance = density\n ? `\\n- Aim for approximately ${density} comments per 2000 words`\n : `\\n- Aim for 3-8 comments per 2000 words (not too sparse or dense)`;\n\n prompt = `Identify passages in this text that would benefit from explanatory comments.\nFor each passage, provide contextual information, clarification, or background.${toneGuidance}\n\nGuidelines:\n- Select passages that reference technical terms, historical figures, complex concepts, or unclear references\n- Provide comments that ADD VALUE beyond restating the text\n- Focus on explanation, background, or connections to other ideas\n- Avoid obvious or trivial comments\n- Keep comments concise (1-3 sentences typically)${densityGuidance}${sourceLang}${bodyLang}\n\nText to analyze:\n---\n${content}\n---\n\nReturn a JSON array of comments. Each comment should have:\n- \"exact\": the exact text passage being commented on (quoted verbatim from source)\n- \"prefix\": up to 64 characters of text immediately before the passage\n- \"suffix\": up to 64 characters of text immediately after the passage\n- \"comment\": your explanatory comment (1-3 sentences, provide context/background/clarification)\n\nRespond with a valid JSON array.\n\nExample format:\n[\n {\"exact\": \"Ouranos\", \"prefix\": \"In the beginning, \", \"suffix\": \" ruled the universe\", \"comment\": \"Ouranos (also spelled Uranus) is the primordial Greek deity personifying the sky. In Hesiod's Theogony, he is the son and husband of Gaia (Earth) and father of the Titans.\"}\n]`;\n }\n\n return prompt;\n }\n\n /**\n * Build a prompt for detecting highlight-worthy passages\n *\n * @param content - The text content to analyze — a chunk sized by the\n * caller from derived provider limits; NEVER re-truncated here (a\n * builder-level clip is silent input loss — see #738)\n * @param instructions - Optional user-provided instructions\n * @param density - Optional target number of highlights per 2000 words\n * @returns Formatted prompt string\n */\n static buildHighlightPrompt(\n content: string,\n instructions?: string,\n density?: number,\n sourceLanguage?: string\n ): string {\n let prompt: string;\n const sourceLang = sourceLanguageGuidance(sourceLanguage);\n\n if (instructions) {\n // User provided specific instructions - minimal prompt, let instructions drive behavior\n const densityGuidance = density\n ? `\\n\\nAim for approximately ${density} highlights per 2000 words of text.`\n : ''; // Let user instructions determine density\n\n prompt = `Identify passages in this text to highlight following these instructions:\n\n${instructions}${densityGuidance}${sourceLang}\n\nText to analyze:\n---\n${content}\n---\n\nReturn a JSON array of highlights. Each highlight must have:\n- \"exact\": the exact text passage to highlight (quoted verbatim from source)\n- \"prefix\": up to 64 characters of text immediately before the passage\n- \"suffix\": up to 64 characters of text immediately after the passage\n\nRespond with a valid JSON array.\n\nExample:\n[\n {\"exact\": \"revenue grew 45% year-over-year\", \"prefix\": \"In Q3 2024, \", \"suffix\": \", exceeding all forecasts.\"}\n]`;\n } else {\n // No specific instructions - fall back to importance/salience mode\n const densityGuidance = density\n ? `\\n- Aim for approximately ${density} highlights per 2000 words`\n : `\\n- Aim for 3-8 highlights per 2000 words (be selective)`;\n\n prompt = `Identify passages in this text that merit highlighting for their importance or salience.\nFocus on content that readers should notice and remember.\n\nGuidelines:\n- Highlight key claims, findings, or conclusions\n- Highlight important definitions, terminology, or concepts\n- Highlight notable quotes or particularly striking statements\n- Highlight critical decisions, action items, or turning points\n- Select passages that are SIGNIFICANT, not just interesting\n- Avoid trivial or obvious content${densityGuidance}${sourceLang}\n\nText to analyze:\n---\n${content}\n---\n\nReturn a JSON array of highlights. Each highlight should have:\n- \"exact\": the exact text passage to highlight (quoted verbatim from source)\n- \"prefix\": up to 64 characters of text immediately before the passage\n- \"suffix\": up to 64 characters of text immediately after the passage\n\nRespond with a valid JSON array.\n\nExample format:\n[\n {\"exact\": \"we will discontinue support for legacy systems by March 2025\", \"prefix\": \"After careful consideration, \", \"suffix\": \". This decision affects\"}\n]`;\n }\n\n return prompt;\n }\n\n /**\n * Build a prompt for detecting assessment-worthy passages\n *\n * @param content - The text content to analyze — a chunk sized by the\n * caller from derived provider limits; NEVER re-truncated here (a\n * builder-level clip is silent input loss — see #738)\n * @param instructions - Optional user-provided instructions\n * @param tone - Optional tone guidance (e.g., \"critical\", \"supportive\")\n * @param density - Optional target number of assessments per 2000 words\n * @returns Formatted prompt string\n */\n static buildAssessmentPrompt(\n content: string,\n instructions?: string,\n tone?: string,\n density?: number,\n language?: string,\n sourceLanguage?: string\n ): string {\n let prompt: string;\n const sourceLang = sourceLanguageGuidance(sourceLanguage);\n const bodyLang = bodyLanguageGuidance(language, 'assessments');\n\n if (instructions) {\n // User provided specific instructions - minimal prompt, let instructions drive behavior\n const toneGuidance = tone ? ` Use a ${tone} tone.` : '';\n const densityGuidance = density\n ? `\\n\\nAim for approximately ${density} assessments per 2000 words of text.`\n : ''; // Let user instructions determine density\n\n prompt = `Assess passages in this text following these instructions:\n\n${instructions}${toneGuidance}${densityGuidance}${sourceLang}${bodyLang}\n\nText to analyze:\n---\n${content}\n---\n\nReturn a JSON array of assessments. Each assessment must have:\n- \"exact\": the exact text passage being assessed (quoted verbatim from source)\n- \"prefix\": up to 64 characters of text immediately before the passage\n- \"suffix\": up to 64 characters of text immediately after the passage\n- \"assessment\": your assessment following the instructions above\n\nRespond with a valid JSON array.\n\nExample:\n[\n {\"exact\": \"the quarterly revenue target\", \"prefix\": \"We established \", \"suffix\": \" for Q4 2024.\", \"assessment\": \"This target seems ambitious given market conditions. Consider revising based on recent trends.\"}\n]`;\n } else {\n // No specific instructions - fall back to analytical/evaluation mode\n const toneGuidance = tone\n ? `\\n\\nTone: Use a ${tone} style in your assessments.`\n : '';\n const densityGuidance = density\n ? `\\n- Aim for approximately ${density} assessments per 2000 words`\n : `\\n- Aim for 2-6 assessments per 2000 words (focus on key passages)`;\n\n prompt = `Identify passages in this text that merit critical assessment or evaluation.\nFor each passage, provide analysis of its validity, strength, or implications.${toneGuidance}\n\nGuidelines:\n- Select passages containing claims, arguments, conclusions, or assertions\n- Assess evidence quality, logical soundness, or practical implications\n- Provide assessments that ADD INSIGHT beyond restating the text\n- Focus on passages where evaluation would help readers form judgments\n- Keep assessments concise yet substantive (1-3 sentences typically)${densityGuidance}${sourceLang}${bodyLang}\n\nText to analyze:\n---\n${content}\n---\n\nReturn a JSON array of assessments. Each assessment should have:\n- \"exact\": the exact text passage being assessed (quoted verbatim from source)\n- \"prefix\": up to 64 characters of text immediately before the passage\n- \"suffix\": up to 64 characters of text immediately after the passage\n- \"assessment\": your analytical assessment (1-3 sentences, evaluate validity/strength/implications)\n\nRespond with a valid JSON array.\n\nExample format:\n[\n {\"exact\": \"AI will replace most jobs by 2030\", \"prefix\": \"Many experts predict that \", \"suffix\": \", fundamentally reshaping\", \"assessment\": \"This claim lacks nuance and supporting evidence. Employment patterns historically show job transformation rather than wholesale replacement. The timeline appears speculative without specific sector analysis.\"}\n]`;\n }\n\n return prompt;\n }\n\n /**\n * Build a prompt for detecting structural tags\n *\n * @param content - The full text content to analyze (NOT truncated for structural analysis)\n * @param category - The specific category to detect\n * @param schemaName - Human-readable schema name\n * @param schemaDescription - Schema description\n * @param schemaDomain - Schema domain\n * @param categoryDescription - Category description\n * @param categoryExamples - Example questions/guidance for this category\n * @returns Formatted prompt string\n */\n static buildTagPrompt(\n content: string,\n category: string,\n schemaName: string,\n schemaDescription: string,\n schemaDomain: string,\n categoryDescription: string,\n categoryExamples: string[],\n sourceLanguage?: string\n ): string {\n // Tags are schema-defined identifiers (\"Issue\", \"Rule\") — the LLM doesn't\n // generate the category label, so there's no body-language guidance here.\n // Only `sourceLanguage` is wired into the prompt; `language` (body locale)\n // is consumed at the body-stamp site in `processors.ts`.\n const sourceLang = sourceLanguageGuidance(sourceLanguage);\n\n const prompt = `You are analyzing a text using the ${schemaName} framework.\n\nSchema: ${schemaDescription}\nDomain: ${schemaDomain}\n\nYour task: Identify passages that serve the structural role of \"${category}\".\n\nCategory: ${category}\nDescription: ${categoryDescription}\nKey questions:\n${categoryExamples.map(ex => `- ${ex}`).join('\\n')}\n\nGuidelines:\n- Focus on STRUCTURAL FUNCTION, not semantic content\n- A passage serves the \"${category}\" role if it performs this function in the document's structure\n- Look for passages that explicitly fulfill this role\n- Passages can be sentences, paragraphs, or sections\n- Aim for precision - only tag passages that clearly serve this structural role\n- Typical documents have 1-5 instances of each category (some may have 0)${sourceLang}\n\nText to analyze:\n---\n${content}\n---\n\nReturn a JSON array of tags. Each tag should have:\n- \"exact\": the exact text passage (quoted verbatim from source)\n- \"prefix\": up to 64 characters of text immediately before the passage\n- \"suffix\": up to 64 characters of text immediately after the passage\n\nRespond with a valid JSON array.\n\nExample format:\n[\n {\"exact\": \"What duty did the defendant owe?\", \"prefix\": \"The central question is: \", \"suffix\": \" This question must be\"},\n {\"exact\": \"In tort law, a duty of care is established when...\", \"prefix\": \"Legal framework:\\\\n\", \"suffix\": \"\\\\n\\\\nApplying this standard\"}\n]`;\n\n return prompt;\n }\n}\n","import type { AnchorMethod, Logger } from '@semiont/core';\nimport { recordAnchorOutcome } from '@semiont/observability';\n\n/**\n * The anchoring methods that picked a plausible occurrence rather than a\n * certain one: `first-of-many` had several candidates and no usable context,\n * `fuzzy-match` recovered through case/whitespace/Levenshtein. Neither is an\n * error — the write-time selector invariant still holds — but both are places\n * the span could be the wrong instance.\n *\n * ONE decider, deliberately. This classification lived in two places (the\n * reference path's inline warn and the motivation parsers' logger) that\n * happened to agree; two copies of a risk judgement is one copy too many.\n */\nconst DEGRADED: ReadonlySet<string> = new Set<AnchorMethod>(['first-of-many', 'fuzzy-match']);\n\n/**\n * Audit one anchoring outcome: counted always, warned only when degraded.\n *\n * Counting EVERY outcome is the point (P5). A warning tells an operator that\n * one anchor was uncertain; only a rate over all anchors says whether the\n * detection run was precise, and the rate is what the plan asks to put beside\n * the yield numbers.\n */\nexport function noteAnchor(label: string, exact: string, method: AnchorMethod, logger?: Logger): void {\n recordAnchorOutcome(label, method);\n if (!DEGRADED.has(method)) return;\n const detail = { text: exact, anchorMethod: method };\n if (logger) logger.warn('Annotation anchored via degraded method', { label, ...detail });\n else console.warn(`[${label}] anchored via ${method}: \"${exact}\"`);\n}\n","/**\n * Response parsers for annotation detection motivations\n *\n * Provides static methods to parse and validate AI responses for each motivation type.\n * Includes offset validation and correction logic.\n * Extracted from worker implementations to centralize parsing logic.\n *\n * NOTE: These are static utility methods without logger access.\n * Console statements kept for debugging - consider adding logger parameter in future.\n */\n\nimport { reconcileSelector, isObject, isString, type AnchorMethod } from '@semiont/core';\nimport type { ElementSchema } from '@semiont/inference';\nimport { noteAnchor } from './anchor-audit';\n\n// Parsers receive ALREADY-PARSED elements (`unknown[]`) from the structured\n// inference surface — `generateStructured` returns `T[]` or throws, so\n// \"could not read the model\" never reaches this layer and there is no string\n// to parse here (the former strict `parseJsonArray` moved into that contract,\n// as the earlier tolerant walker moved into it before). What remains here is\n// per-element structural validation (STRUCTURED-INFERENCE D5 — the last line\n// on the Ollama path and the schema/type drift guard) plus reconciliation\n// against the full document.\n//\n// Each element schema is declared ADJACENT to the Match interface it mirrors:\n// the schema constrains the wire, the interface is what the code consumes,\n// and nothing verifies they agree — adjacency is the drift guard.\n// `prefix`/`suffix` stay OUT of `required` deliberately: requiring them turns\n// \"sometimes absent\" into \"always present, sometimes empty\" (measured\n// 2026-08-06), an anchoring-path change Phase 3 examines before anyone\n// relies on it.\n\n/**\n * Represents a detected comment with validated position\n */\nexport interface CommentMatch {\n exact: string;\n start: number;\n end: number;\n prefix?: string;\n suffix?: string;\n comment: string;\n}\n\n/** Wire schema for one comment element — keep in lockstep with `CommentMatch`. */\nexport const COMMENT_ELEMENT_SCHEMA: ElementSchema = {\n type: 'object',\n properties: {\n exact: { type: 'string' },\n prefix: { type: 'string' },\n suffix: { type: 'string' },\n comment: { type: 'string' },\n },\n required: ['exact', 'comment'],\n additionalProperties: false,\n};\n\n/**\n * Represents a detected highlight with validated position\n */\nexport interface HighlightMatch {\n exact: string;\n start: number;\n end: number;\n prefix?: string;\n suffix?: string;\n}\n\n/** Wire schema for one highlight element — keep in lockstep with `HighlightMatch`. */\nexport const HIGHLIGHT_ELEMENT_SCHEMA: ElementSchema = {\n type: 'object',\n properties: {\n exact: { type: 'string' },\n prefix: { type: 'string' },\n suffix: { type: 'string' },\n },\n required: ['exact'],\n additionalProperties: false,\n};\n\n/**\n * Represents a detected assessment with validated position\n */\nexport interface AssessmentMatch {\n exact: string;\n start: number;\n end: number;\n prefix?: string;\n suffix?: string;\n assessment: string;\n}\n\n/** Wire schema for one assessment element — keep in lockstep with `AssessmentMatch`. */\nexport const ASSESSMENT_ELEMENT_SCHEMA: ElementSchema = {\n type: 'object',\n properties: {\n exact: { type: 'string' },\n prefix: { type: 'string' },\n suffix: { type: 'string' },\n assessment: { type: 'string' },\n },\n required: ['exact', 'assessment'],\n additionalProperties: false,\n};\n\n/**\n * Represents a detected tag with validated position\n */\nexport interface TagMatch {\n exact: string;\n start: number;\n end: number;\n prefix?: string;\n suffix?: string;\n category: string;\n}\n\n/**\n * Wire schema for one tag element — keep in lockstep with `RawTagInput`\n * (the category is stamped by the caller, not emitted by the model).\n */\nexport const TAG_ELEMENT_SCHEMA: ElementSchema = {\n type: 'object',\n properties: {\n exact: { type: 'string' },\n prefix: { type: 'string' },\n suffix: { type: 'string' },\n },\n required: ['exact'],\n additionalProperties: false,\n};\n\nexport class MotivationParsers {\n /**\n * Validate and reconcile structured comment elements.\n *\n * @param parsed - Already-parsed elements from the structured surface\n * @param content - Original content to validate offsets against\n * @returns Array of validated comment matches\n */\n static parseComments(parsed: unknown[], content: string): CommentMatch[] {\n\n const valid = parsed.filter((c): c is { exact: string; prefix?: string; suffix?: string; comment: string } =>\n isObject(c) &&\n isString(c.exact) &&\n isString(c.comment) &&\n c.comment.trim().length > 0\n );\n\n console.log(`[MotivationParsers] Parsed ${valid.length} valid comments from ${parsed.length} total`);\n\n const validatedComments: CommentMatch[] = [];\n for (const comment of valid) {\n const reconciled = reconcileSelector(content, {\n exact: comment.exact,\n ...(typeof comment.prefix === 'string' ? { prefix: comment.prefix } : {}),\n ...(typeof comment.suffix === 'string' ? { suffix: comment.suffix } : {}),\n });\n if (!reconciled) {\n console.warn(`[MotivationParsers] Dropped hallucinated comment \"${comment.exact}\"`);\n continue;\n }\n logAnchorMethod('comment', comment.exact, reconciled.anchorMethod);\n validatedComments.push({\n comment: comment.comment,\n exact: reconciled.exact,\n start: reconciled.start,\n end: reconciled.end,\n ...(reconciled.prefix !== undefined ? { prefix: reconciled.prefix } : {}),\n ...(reconciled.suffix !== undefined ? { suffix: reconciled.suffix } : {}),\n });\n }\n\n return validatedComments;\n }\n\n /**\n * Validate and reconcile structured highlight elements.\n *\n * @param parsed - Already-parsed elements from the structured surface\n * @param content - Original content to validate offsets against\n * @returns Array of validated highlight matches\n */\n static parseHighlights(parsed: unknown[], content: string): HighlightMatch[] {\n\n const highlights = parsed.filter((h): h is { exact: string; prefix?: string; suffix?: string } =>\n isObject(h) && isString(h.exact)\n );\n\n const validatedHighlights: HighlightMatch[] = [];\n for (const highlight of highlights) {\n const reconciled = reconcileSelector(content, {\n exact: highlight.exact,\n ...(typeof highlight.prefix === 'string' ? { prefix: highlight.prefix } : {}),\n ...(typeof highlight.suffix === 'string' ? { suffix: highlight.suffix } : {}),\n });\n if (!reconciled) {\n console.warn(`[MotivationParsers] Dropped hallucinated highlight \"${highlight.exact}\"`);\n continue;\n }\n logAnchorMethod('highlight', highlight.exact, reconciled.anchorMethod);\n validatedHighlights.push({\n exact: reconciled.exact,\n start: reconciled.start,\n end: reconciled.end,\n ...(reconciled.prefix !== undefined ? { prefix: reconciled.prefix } : {}),\n ...(reconciled.suffix !== undefined ? { suffix: reconciled.suffix } : {}),\n });\n }\n\n return validatedHighlights;\n }\n\n /**\n * Validate and reconcile structured assessment elements.\n *\n * @param parsed - Already-parsed elements from the structured surface\n * @param content - Original content to validate offsets against\n * @returns Array of validated assessment matches\n */\n static parseAssessments(parsed: unknown[], content: string): AssessmentMatch[] {\n\n const assessments = parsed.filter((a): a is { exact: string; prefix?: string; suffix?: string; assessment: string } =>\n isObject(a) && isString(a.exact) && isString(a.assessment)\n );\n\n const validatedAssessments: AssessmentMatch[] = [];\n for (const assessment of assessments) {\n const reconciled = reconcileSelector(content, {\n exact: assessment.exact,\n ...(typeof assessment.prefix === 'string' ? { prefix: assessment.prefix } : {}),\n ...(typeof assessment.suffix === 'string' ? { suffix: assessment.suffix } : {}),\n });\n if (!reconciled) {\n console.warn(`[MotivationParsers] Dropped hallucinated assessment \"${assessment.exact}\"`);\n continue;\n }\n logAnchorMethod('assessment', assessment.exact, reconciled.anchorMethod);\n validatedAssessments.push({\n assessment: assessment.assessment,\n exact: reconciled.exact,\n start: reconciled.start,\n end: reconciled.end,\n ...(reconciled.prefix !== undefined ? { prefix: reconciled.prefix } : {}),\n ...(reconciled.suffix !== undefined ? { suffix: reconciled.suffix } : {}),\n });\n }\n\n return validatedAssessments;\n }\n\n /**\n * Validate structured tag elements into raw, pre-reconciliation tag inputs.\n * Reconciliation happens in `validateTagOffsets`, which adds `start`/`end`\n * by anchoring `exact` against the source content.\n *\n * @param parsed - Already-parsed elements from the structured surface\n */\n static parseTags(parsed: unknown[]): RawTagInput[] {\n\n const valid = parsed.filter((t): t is RawTagInput =>\n isObject(t) && isString(t.exact) && t.exact.trim().length > 0\n );\n\n console.log(`[MotivationParsers] Parsed ${valid.length} valid tags from ${parsed.length} total`);\n\n return valid;\n }\n\n /**\n * Anchor raw tag inputs against source content and add category.\n */\n static validateTagOffsets(\n tags: RawTagInput[],\n content: string,\n category: string\n ): TagMatch[] {\n const validatedTags: TagMatch[] = [];\n for (const tag of tags) {\n const reconciled = reconcileSelector(content, {\n exact: tag.exact,\n ...(typeof tag.prefix === 'string' ? { prefix: tag.prefix } : {}),\n ...(typeof tag.suffix === 'string' ? { suffix: tag.suffix } : {}),\n });\n if (!reconciled) {\n console.warn(`[MotivationParsers] Dropped hallucinated tag \"${tag.exact}\" for category \"${category}\"`);\n continue;\n }\n logAnchorMethod('tag', tag.exact, reconciled.anchorMethod);\n validatedTags.push({\n category,\n exact: reconciled.exact,\n start: reconciled.start,\n end: reconciled.end,\n ...(reconciled.prefix !== undefined ? { prefix: reconciled.prefix } : {}),\n ...(reconciled.suffix !== undefined ? { suffix: reconciled.suffix } : {}),\n });\n }\n return validatedTags;\n }\n}\n\n/** Raw LLM-emitted tag, pre-reconciliation. */\nexport interface RawTagInput {\n exact: string;\n prefix?: string;\n suffix?: string;\n}\n\n/**\n * Audit one anchor-method classification. Forwards to the single decider\n * shared with the reference path (`anchor-audit.ts`) — which methods count as\n * risky is one judgement, and this file used to hold a second copy of it.\n */\nfunction logAnchorMethod(motivation: string, exact: string, anchorMethod: AnchorMethod): void {\n noteAnchor(motivation, exact, anchorMethod);\n}\n","/**\n * Annotation Detection\n *\n * Orchestrates the full annotation detection pipeline:\n * 1. Build AI prompts using MotivationPrompts\n * 2. Call AI inference\n * 3. Parse and validate results using MotivationParsers\n *\n * All methods take content as a string parameter — the worker process\n * fetches it and hands it in.\n */\n\nimport type { ElementSchema, InferenceClient } from '@semiont/inference';\nimport { estimateTokens, type UnitCursor } from '@semiont/core';\nimport { boundedGenerateStructured } from './inference-call';\nimport { assertNotTruncated, callChunkSubdividing, deriveDetectionBudget, runAdaptiveChunks, type ChunkCursor, DETECTION_TEMPERATURE } from './detection/detection-chunking';\nimport { MotivationPrompts } from './detection/motivation-prompts';\nimport {\n MotivationParsers,\n COMMENT_ELEMENT_SCHEMA,\n HIGHLIGHT_ELEMENT_SCHEMA,\n ASSESSMENT_ELEMENT_SCHEMA,\n TAG_ELEMENT_SCHEMA,\n type CommentMatch,\n type HighlightMatch,\n type AssessmentMatch,\n type TagMatch,\n} from './detection/motivation-parsers';\nimport type { TagSchema } from '@semiont/core';\n\n/**\n * Per-chunk detection loop shared by the four motivations.\n *\n * Budgets derive from the provider's actual limits plus the measured prompt\n * scaffold (`buildPrompt('')`) — no literals. The prompt receives one chunk;\n * `parse` reconciles against the FULL document (the callers close over it),\n * so offsets index into the whole resource with no re-anchoring arithmetic.\n * Overlap duplicates pass through — the processor's span-keyed seen-set is\n * the single dedupe point.\n *\n * `onActivity` fires whenever the detection is demonstrably alive: at each\n * chunk boundary (the count advances) AND periodically while one inference\n * call is in flight (the count repeats — liveness, not progress). Progress is\n * the worker's liveness heartbeat AND the client's inter-emission timeout\n * signal, so a silent single-chunk run kills a healthy job\n * (DETECTION-HEARTBEAT).\n */\nasync function detectInChunks<T>(\n client: InferenceClient,\n content: string,\n buildPrompt: (chunk: string) => string,\n motivation: string,\n elementSchema: ElementSchema,\n parse: (items: unknown[]) => T[],\n onActivity?: (consumedChars: number, totalChars: number) => void,\n /**\n * This chunk's parsed matches, awaited before the loop continues: the\n * caller commits them, and the loop must not run ahead of durability.\n * Unlike `onActivity` (a liveness heartbeat, which may repeat), this fires\n * exactly once per chunk, including the last.\n *\n * `cursor` is where the run stands once this chunk is committed — handed over\n * WITH the results so a caller cannot record a position it has not made\n * durable (CHUNK-GRAIN-RESUME P2).\n */\n /** Where an earlier attempt left this unit (CHUNK-GRAIN-RESUME P3).\n * Before the callback below, which stays last. */\n resume?: UnitCursor,\n onChunkResults?: (parsed: T[], cursor: ChunkCursor) => Promise<void>,\n): Promise<T[]> {\n const limits = await client.limits();\n const scaffoldTokens = estimateTokens(buildPrompt(''));\n // One motivation's spans per call — the single span family this prompt\n // asks for, the motivation-path analogue of one entity type.\n const budget = deriveDetectionBudget(limits, scaffoldTokens, 1);\n const { outputBudget } = budget;\n\n const collected: T[] = [];\n await runAdaptiveChunks(content, budget, async ({ piece: chunk, size, at, next, totalChars }) => {\n // Structured surface: parsed elements or a throw — an unreadable model\n // response fails the job rather than reading as an empty detection. A\n // size-shaped failure (duration bound, truncation) subdivides in place\n // and retries smaller before it is allowed to fail the job.\n const { items, outcome } = await callChunkSubdividing<unknown>(\n motivation, chunk, { chunkSize: size, overlap: budget.chunking.overlap },\n async (piece) => {\n const response = await boundedGenerateStructured<unknown>(\n client, buildPrompt(piece), outputBudget, DETECTION_TEMPERATURE, elementSchema,\n // Still alive, same position (a long single call is otherwise silent).\n () => onActivity?.(at, totalChars),\n );\n assertNotTruncated(response, `${motivation} detection`, at, totalChars, outputBudget);\n // Usage rides back so the telemetry record carries what the call COST\n // beside what it yielded — the provider's own counts, not an estimate.\n return { items: response.items, ...(response.usage ? { usage: response.usage } : {}) };\n },\n );\n const fromChunk = parse(items);\n collected.push(...fromChunk);\n await onChunkResults?.(fromChunk, { next, size });\n // Chunk boundary: the cursor advances (real progress). Only when text\n // remains — the final cut has no boundary after it, and the caller reports\n // the unit's completion itself.\n if (next < totalChars) onActivity?.(next, totalChars);\n return outcome;\n }, resume);\n return collected;\n}\n\nexport class AnnotationDetection {\n\n /**\n * Detect comments in content.\n *\n * `language` is the locale the LLM should write comment text in (annotation\n * body locale). `sourceLanguage` is the locale of the content being analyzed\n * (source-resource locale). See `types.ts` \"Locale conventions\" for the\n * full discussion.\n */\n static async detectComments(\n content: string,\n client: InferenceClient,\n instructions?: string,\n tone?: string,\n density?: number,\n language?: string,\n sourceLanguage?: string,\n onActivity?: (consumedChars: number, totalChars: number) => void,\n /** This chunk's matches, as the chunk completes. */\n /** Where an earlier attempt left this unit (CHUNK-GRAIN-RESUME P3). */\n resume?: UnitCursor,\n /** This chunk's matches, as the chunk completes. Kept LAST. */\n onChunkResults?: (matches: CommentMatch[], cursor: ChunkCursor) => Promise<void>,\n ): Promise<CommentMatch[]> {\n return detectInChunks(\n client, content,\n (chunk) => MotivationPrompts.buildCommentPrompt(chunk, instructions, tone, density, language, sourceLanguage),\n 'comment', COMMENT_ELEMENT_SCHEMA,\n (items) => MotivationParsers.parseComments(items, content),\n onActivity,\n resume,\n onChunkResults,\n );\n }\n\n /**\n * Detect highlights in content.\n *\n * Highlights have no body — only `sourceLanguage` (source-resource locale)\n * applies, used in the prompt so the LLM analyzes non-English source\n * correctly.\n */\n static async detectHighlights(\n content: string,\n client: InferenceClient,\n instructions?: string,\n density?: number,\n sourceLanguage?: string,\n onActivity?: (consumedChars: number, totalChars: number) => void,\n /** This chunk's matches, as the chunk completes. */\n /** Where an earlier attempt left this unit (CHUNK-GRAIN-RESUME P3). */\n resume?: UnitCursor,\n /** This chunk's matches, as the chunk completes. Kept LAST. */\n onChunkResults?: (matches: HighlightMatch[], cursor: ChunkCursor) => Promise<void>,\n ): Promise<HighlightMatch[]> {\n return detectInChunks(\n client, content,\n (chunk) => MotivationPrompts.buildHighlightPrompt(chunk, instructions, density, sourceLanguage),\n 'highlight', HIGHLIGHT_ELEMENT_SCHEMA,\n (items) => MotivationParsers.parseHighlights(items, content),\n onActivity,\n resume,\n onChunkResults,\n );\n }\n\n /**\n * Detect assessments in content.\n *\n * `language` is the locale the LLM should write assessment text in\n * (annotation body locale). `sourceLanguage` is the locale of the content\n * being analyzed (source-resource locale).\n */\n static async detectAssessments(\n content: string,\n client: InferenceClient,\n instructions?: string,\n tone?: string,\n density?: number,\n language?: string,\n sourceLanguage?: string,\n onActivity?: (consumedChars: number, totalChars: number) => void,\n /** This chunk's matches, as the chunk completes. */\n /** Where an earlier attempt left this unit (CHUNK-GRAIN-RESUME P3). */\n resume?: UnitCursor,\n /** This chunk's matches, as the chunk completes. Kept LAST. */\n onChunkResults?: (matches: AssessmentMatch[], cursor: ChunkCursor) => Promise<void>,\n ): Promise<AssessmentMatch[]> {\n return detectInChunks(\n client, content,\n (chunk) => MotivationPrompts.buildAssessmentPrompt(chunk, instructions, tone, density, language, sourceLanguage),\n 'assessment', ASSESSMENT_ELEMENT_SCHEMA,\n (items) => MotivationParsers.parseAssessments(items, content),\n onActivity,\n resume,\n onChunkResults,\n );\n }\n\n /**\n * Detect tags in content for a specific category.\n *\n * The full `TagSchema` is supplied by the dispatcher (resolved against\n * the per-KB tag-schema projection at job-creation time) so the worker\n * is independent of the registry.\n *\n * `sourceLanguage` is the locale of the content being analyzed. Body-locale\n * (`language`) doesn't influence the tag prompt — categories are schema\n * identifiers, not LLM-generated text — so it's consumed at the body-stamp\n * site, not here.\n */\n static async detectTags(\n content: string,\n client: InferenceClient,\n schema: TagSchema,\n category: string,\n sourceLanguage?: string,\n onActivity?: (consumedChars: number, totalChars: number) => void,\n /**\n * This chunk's matches, ANCHORED before they leave: `parse` here yields\n * raw tags, so this path runs `validateTagOffsets` per chunk — a per-item\n * anchor against the full document, so partitioning changes nothing.\n */\n /** Where an earlier attempt left this unit (CHUNK-GRAIN-RESUME P3). */\n resume?: UnitCursor,\n /** This chunk's matches, as the chunk completes. Kept LAST. */\n onChunkResults?: (matches: TagMatch[], cursor: ChunkCursor) => Promise<void>,\n ): Promise<TagMatch[]> {\n const categoryInfo = schema.tags.find((t) => t.name === category);\n if (!categoryInfo) {\n throw new Error(`Invalid category \"${category}\" for schema ${schema.id}`);\n }\n\n // Parse per chunk; anchor once against the full document afterward.\n const parsedTags = await detectInChunks(\n client, content,\n (chunk) => MotivationPrompts.buildTagPrompt(\n chunk,\n category,\n schema.name,\n schema.description,\n schema.domain,\n categoryInfo.description,\n categoryInfo.examples,\n sourceLanguage\n ),\n 'tag', TAG_ELEMENT_SCHEMA,\n (items) => MotivationParsers.parseTags(items),\n onActivity,\n resume,\n onChunkResults\n ? async (raw, cursor) => onChunkResults(MotivationParsers.validateTagOffsets(raw, content, category), cursor)\n : undefined,\n );\n return MotivationParsers.validateTagOffsets(parsedTags, content, category);\n }\n}\n","import type { ElementSchema, InferenceClient } from '@semiont/inference';\nimport { estimateTokens, getLocaleEnglishName, isObject, isString, type Logger, type UnitCursor } from '@semiont/core';\nimport { boundedGenerateStructured, boundedGenerateWithMetadata } from '../inference-call';\nimport { assertNotTruncated, callChunkSubdividing, deriveDetectionBudget, runAdaptiveChunks, type ChunkCursor, DETECTION_TEMPERATURE, YIELD_COLLAPSE_BAND, YieldCollapseError, type UnderReportedPiece } from './detection-chunking';\n\n/**\n * Entity reference extracted from text — pre-reconciliation.\n *\n * The LLM emits `exact` (verbatim text span), `entityType`, and optional\n * `prefix` / `suffix` context for disambiguation. Offsets are not asked\n * for — `reconcileSelector` computes them by anchoring `exact` against\n * the source content in the calling processor.\n */\nexport interface ExtractedEntity {\n exact: string;\n entityType: string;\n prefix?: string;\n suffix?: string;\n}\n\n/**\n * JSON Schema for one extracted entity — the provider-enforced shape.\n *\n * Declared adjacent to `ExtractedEntity` deliberately: the schema is what\n * constrains the wire and the interface is what the code consumes, and\n * nothing verifies they agree — adjacency is the drift guard. The\n * per-element `isObject`/`isString` checks below stay as the structural\n * backstop (STRUCTURED-INFERENCE D5).\n *\n * `prefix`/`suffix` are deliberately NOT in `required`: with all four\n * required, models return `\"prefix\": \"\"` instead of omitting the key\n * (measured 2026-08-06), turning \"sometimes absent\" into \"always present,\n * sometimes empty\" — an anchoring-path change Phase 3 re-examines before\n * anyone relies on it.\n */\nconst ENTITY_ELEMENT_SCHEMA: ElementSchema = {\n type: 'object',\n properties: {\n exact: { type: 'string' },\n entityType: { type: 'string' },\n prefix: { type: 'string' },\n suffix: { type: 'string' },\n },\n required: ['exact', 'entityType'],\n additionalProperties: false,\n};\n\n/**\n * Extract entity references from text using AI.\n *\n * Locale: entity references' bodies are entity-type identifiers (not\n * LLM-generated natural-language text), so only `sourceLanguage` (source-\n * resource locale) is meaningful here — it's used in the prompt so the LLM\n * analyzes non-English source correctly. There's no body-locale parameter.\n *\n * @param text - The text to analyze\n * @param entityTypes - Array of entity types to detect (optionally with examples)\n * @param client - Inference client for AI operations\n * @param includeDescriptiveReferences - Include anaphoric/cataphoric references (default: false)\n * @param logger - Logger for entity-extraction diagnostics (parse failures,\n * anchor decisions, drops). Required so dropped/filtered entities never\n * disappear silently.\n * @param sourceLanguage - BCP-47 tag for the source content's language\n * @param onActivity - Invoked with (consumedChars, totalChars) whenever the\n * extraction is demonstrably alive: at each chunk boundary (the cursor\n * advances) AND periodically while a single inference call is in flight\n * (the position repeats — liveness, not progress). Characters, not chunk\n * ordinals: chunk sizing is decided as the run goes, so there is no chunk\n * total to divide by — and the cursor was always the more honest numerator,\n * since boundary-seeking already made chunks unequal. The caller MUST forward\n * this to its progress channel: progress is the worker's liveness\n * heartbeat for the stall watchdog, and the client's timeout is an\n * INTER-EMISSION one, so a silent single-chunk run kills a healthy job\n * (DETECTION-HEARTBEAT).\n * @returns Array of extracted entities with their character offsets\n */\n/** Output cap for the count call: the answer is one number (≤7 digits), and a\n * tiny cap is itself the safety — a ~5-token output structurally cannot loop\n * or truncate the way the extraction's array can. */\nconst COUNT_MAX_TOKENS = 16;\n\n/** The count answer, read as its leading integer. \"Respond with only the\n * number\" is the prompt's contract, but a model that pads it (\"There are 50\")\n * still yields its verdict; anything with no integer yields none. */\nfunction parseCount(text: string): number | undefined {\n const m = text.trim().match(/\\d+/);\n return m ? Number(m[0]) : undefined;\n}\n\n/**\n * The count-verifier (OLLAMA-DETECTION-TESTING P3c): guard a SUCCESSFUL\n * extraction against silent yield collapse (F7) — a schema-clean response\n * carrying a fraction of the mentions present, measured deterministic and\n * classification-invisible, so nothing else can catch it.\n *\n * A cheap count call (\"respond with only the number\") sets the expectation\n * FROM THE SAME TEXT — corpus-free, per the no-input-assumptions principle.\n * An extraction under 1/BAND of the count throws the collapse verdict, which\n * subdivision treats like truncation (descend by size) except at the floor,\n * where it fails the job loudly (user-ratified).\n *\n * The verifier's own failure — count call errors, or answers with no number —\n * disables it for the chunk (warn, pass through): a safety net's outage must\n * not take down a healthy extraction. Known limit, documented in the bug\n * report: the count saturates past ~8K chars, so margins are cleanest at\n * post-descent sizes; it still flagged every measured collapse at 16K/32K.\n */\nasync function assertYieldNotCollapsed(\n client: InferenceClient,\n piece: string,\n items: readonly unknown[],\n entityTypesDescription: string,\n logger: Logger,\n): Promise<number | undefined> {\n const prompt = `Count every mention of: ${entityTypesDescription} in the following text. Repeated mentions of the same entity count separately. Respond with only the number.\n\nText:\n\"\"\"\n${piece}\n\"\"\"`;\n\n let counted: number | undefined;\n try {\n const response = await boundedGenerateWithMetadata(client, prompt, COUNT_MAX_TOKENS, DETECTION_TEMPERATURE, undefined, logger);\n counted = parseCount(response.text);\n } catch (err) {\n logger.warn('Count-verifier call failed — yield check skipped for this chunk', {\n pieceChars: piece.length,\n error: err instanceof Error ? err.message : String(err),\n });\n return undefined;\n }\n if (counted === undefined) {\n logger.warn('Count-verifier answer carried no number — yield check skipped for this chunk', { pieceChars: piece.length });\n return undefined;\n }\n if (items.length * YIELD_COLLAPSE_BAND < counted) {\n // The salvage rides the error: descent discards it (a smaller re-run does\n // better), the floor accepts it (better than nothing, and every span is\n // write-time-verified).\n throw new YieldCollapseError(\n `Extraction found ${items.length} entities where a count call reports ~${counted} mentions (band ×${YIELD_COLLAPSE_BAND}) on a ${piece.length}-char chunk — silent yield collapse (F7): deterministic — a same-size retry returns the identical under-report.`,\n [...items],\n { found: items.length, counted, pieceChars: piece.length },\n );\n }\n return counted;\n}\n\nexport async function extractEntities(\n exact: string,\n entityTypes: string[] | { type: string; examples?: string[] }[],\n client: InferenceClient,\n includeDescriptiveReferences: boolean,\n logger: Logger,\n sourceLanguage?: string,\n onActivity?: (consumedChars: number, totalChars: number) => void,\n /** A floor-accepted piece's evidence, as it is accepted. */\n onUnderReport?: (verdict: UnderReportedPiece) => void,\n /** Each accepted piece's count-verifier expectation — the denominator. */\n onCounted?: (counted: number) => void,\n /** Where an earlier attempt left this unit (CHUNK-GRAIN-RESUME P3). Absent on\n * a first attempt, and then the run opens at the top exactly as before.\n * Deliberately BEFORE the callback below, which stays last. */\n resume?: UnitCursor,\n /**\n * This chunk's entities, awaited before the loop continues: the caller\n * commits them, and the loop must not run ahead of durability. Unlike\n * `onActivity` (a liveness heartbeat, which may repeat), this fires exactly\n * once per chunk, including the last. Kept LAST on every detection seam —\n * test harnesses read it as the final positional argument, so a parameter\n * appended after it silently starves them of results.\n *\n * `cursor` is where the run stands ONCE THIS CHUNK IS COMMITTED — the pair\n * CHUNK-GRAIN-RESUME checkpoints. It is handed over with the results rather\n * than reported separately so the two cannot drift: a caller that records the\n * position without durably committing the annotations would checkpoint ahead\n * of the log.\n */\n onChunkResults?: (items: ExtractedEntity[], cursor: ChunkCursor) => Promise<void>,\n): Promise<ExtractedEntity[]> {\n\n // Format entity types for the prompt\n const entityTypesDescription = entityTypes.map(et => {\n if (typeof et === 'string') {\n return et;\n }\n return et.examples && et.examples.length > 0\n ? `${et.type} (examples: ${et.examples.slice(0, 3).join(', ')})`\n : et.type;\n }).join(', ');\n\n // Build prompt with optional support for anaphoric/cataphoric references\n // Anaphora: references that point backward (e.g., \"John arrived. He was tired.\")\n // Cataphora: references that point forward (e.g., \"When she arrived, Mary was surprised.\")\n // When enabled, include substantive descriptive references beyond simple pronouns\n const descriptiveReferenceGuidance = includeDescriptiveReferences\n ? `\nInclude both:\n- Direct mentions (names, proper nouns)\n- Descriptive references (substantive phrases that refer to entities)\n\nFor descriptive references, include:\n- Definite descriptions: \"the Nobel laureate\", \"the tech giant\", \"the former president\"\n- Role-based references: \"the CEO\", \"the physicist\", \"the author\", \"the owner\", \"the contractor\"\n- Epithets with context: \"the Cupertino-based company\", \"the iPhone maker\"\n- References to entities even when identity is unknown or unspecified\n\nDo NOT include:\n- Simple pronouns alone: he, she, it, they, him, her, them\n- Generic determiners alone: this, that, these, those\n- Possessives without substance: his, her, their, its\n\nExamples:\n- For \"Marie Curie\", include \"the Nobel laureate\" and \"the physicist\" but NOT \"she\"\n- For an unknown person, include \"the owner\" or \"the contractor\" (role-based references count even when identity is unspecified)\n`\n : `\nFind direct mentions only (names, proper nouns). Do not include pronouns or descriptive references.\n`;\n\n const sourceLangGuidance = sourceLanguage\n ? `\\nSource text language: ${getLocaleEnglishName(sourceLanguage) || sourceLanguage}.\\n`\n : '';\n\n // The LLM is asked for `exact`, `prefix`, and `suffix` — no character\n // offsets. Offsets get computed by `reconcileSelector` against the\n // source content. Asking the model for offsets wastes tokens and\n // encourages it to fabricate where it shouldn't.\n const buildPrompt = (text: string): string => `Identify entity references in the following text. Look for mentions of: ${entityTypesDescription}.\n${descriptiveReferenceGuidance}${sourceLangGuidance}\nText to analyze:\n\"\"\"\n${text}\n\"\"\"\n\nRespond with a JSON array of entities found. Each entity should have:\n- exact: the exact text span from the input (quoted verbatim — character-for-character)\n- entityType: one of the provided entity types\n- prefix: up to 64 characters of text immediately before the entity (used to disambiguate when the same text appears more than once)\n- suffix: up to 64 characters of text immediately after the entity (same purpose)\n\nIf no entities are found, respond with an empty array [].\n\nExample output:\n[{\"exact\":\"Alice\",\"entityType\":\"Person\",\"prefix\":\"\",\"suffix\":\" went to\"},{\"exact\":\"Paris\",\"entityType\":\"Location\",\"prefix\":\"went to \",\"suffix\":\" yesterday\"}]`;\n\n // Budgets derive from the provider's actual limits + the measured scaffold\n // (the template around the content) — no literals. Input is chunked only\n // when the derived budget forces it; small documents make one call, as\n // before.\n const limits = await client.limits();\n // The provider DECLARES whether its extractions get count-verified\n // (universal for real providers since the 2026-09-05 ruling — unverified\n // completeness is not a savings). Jobs does no provider-specific switching:\n // whatever varies by provider is a capability on the InferenceClient\n // contract, read here like `maxConcurrency`.\n const verifyYield = client.verifyDetectionYield;\n const scaffoldTokens = estimateTokens(buildPrompt(''));\n // One call asks for every type in `entityTypes` — the processor's per-type\n // loop passes one, so this is 1 in production today.\n const budget = deriveDetectionBudget(limits, scaffoldTokens, entityTypes.length);\n const { chunking, outputBudget } = budget;\n\n logger.debug('Sending entity extraction request', {\n entityTypes: entityTypesDescription,\n chars: exact.length,\n // The size the run OPENS at, and how far measured yield may move it. The\n // chunk COUNT is deliberately absent: with sizing decided as the run goes,\n // there is no honest total until the cursor reaches the end.\n openingChunkSizeTokens: chunking.chunkSize,\n ceilingChunkSizeTokens: budget.bounds.ceiling,\n outputBudget,\n });\n\n const collected: ExtractedEntity[] = [];\n await runAdaptiveChunks(exact, budget, async ({ piece: chunk, size, at, next, totalChars }) => {\n // The structured surface returns parsed elements or THROWS — an\n // unreadable model response is a job failure, never a silent []. A\n // size-shaped failure (duration bound, truncation) subdivides in place\n // and retries smaller before it is allowed to fail the job.\n const { items, outcome } = await callChunkSubdividing<unknown>(\n 'reference', chunk, { chunkSize: size, overlap: chunking.overlap },\n async (piece) => {\n const response = await boundedGenerateStructured<unknown>(\n client,\n buildPrompt(piece),\n outputBudget,\n DETECTION_TEMPERATURE,\n ENTITY_ELEMENT_SCHEMA,\n // Still alive, same position: a long single call would otherwise emit\n // nothing at all between start and finish.\n () => onActivity?.(at, totalChars),\n logger,\n );\n logger.debug('Got entity extraction response', {\n at,\n totalChars,\n chunkSizeTokens: size,\n pieceChars: piece.length,\n items: response.items.length,\n });\n\n // Truncation is data loss, not \"no entities\" — check it BEFORE\n // consuming: a truncated structured response can still carry a valid\n // partial array, so the items themselves cannot signal the loss.\n assertNotTruncated(response, 'Entity extraction', at, totalChars, outputBudget);\n // And a CLEAN response can still be a silent under-report (F7) — the\n // count-verifier is the only signal for that, and a flag throws the\n // collapse verdict so subdivision changes the input.\n const counted = verifyYield\n ? await assertYieldNotCollapsed(client, piece, response.items, entityTypesDescription, logger)\n : undefined;\n // Usage rides back so the telemetry record carries what the call COST\n // beside what it yielded — the provider's own counts, not an estimate.\n return {\n items: response.items,\n ...(response.usage ? { usage: response.usage } : {}),\n ...(counted !== undefined ? { counted } : {}),\n };\n },\n logger, onUnderReport, onCounted,\n );\n\n const fromChunk: ExtractedEntity[] = [];\n for (const e of items) {\n // No dedupe here: overlap duplicates from adjacent chunks pass through\n // to the caller's decider — the single dedupe point.\n if (isObject(e) && isString(e.exact) && isString(e.entityType)) {\n fromChunk.push({\n exact: e.exact,\n entityType: e.entityType,\n ...(isString(e.prefix) ? { prefix: e.prefix } : {}),\n ...(isString(e.suffix) ? { suffix: e.suffix } : {}),\n });\n } else {\n logger.debug('Dropped malformed LLM entity', { entity: e });\n }\n }\n collected.push(...fromChunk);\n await onChunkResults?.(fromChunk, { next, size });\n\n // Chunk boundary: the cursor advances (real progress). Only when text\n // remains — the final cut has no boundary after it, and the caller reports\n // the unit's completion itself.\n if (next < totalChars) onActivity?.(next, totalChars);\n return outcome;\n }, resume);\n\n return collected;\n}\n","/**\n * Resource Generation\n *\n * Generates markdown resources from topics using AI inference.\n */\n\nimport { getLocaleEnglishName, deriveViews } from '@semiont/core';\nimport type { GatheredContext, Logger, SupportedMediaType } from '@semiont/core';\nimport type { InferenceClient } from '@semiont/inference';\nimport { boundedGenerateWithMetadata } from '../inference-call';\n\n\nfunction getLanguageName(locale: string): string {\n return getLocaleEnglishName(locale) || locale;\n}\n\n// Prompt-embedding caps — bound the context fed to the model (CONTEXT-IDENTIFIERS\n// D4: named constants; promote to caller-tunable options only if a consumer\n// actually hits the wall). A fact past these bounds never reaches the model.\nconst RESOURCE_CONTENT_CAP = 4000;\nconst SEMANTIC_MATCH_LIMIT = 3;\nconst SEMANTIC_MATCH_CHARS = 240;\n\n/**\n * Model-visible identifier handle for an embedded excerpt — the ONE bracket\n * convention across all context sections (CONTEXT-IDENTIFIERS D1/D2): resource id\n * always, annotation id as a suffix when the excerpt is annotation-derived.\n */\nfunction idLabel(resourceId: string, annotationId?: string): string {\n return `[${resourceId}${annotationId ? `/${annotationId}` : ''}]`;\n}\n\n/**\n * The canonical gateway default for maxTokens — a short-definition size. The\n * UI also initialises its field to 500 as a UX convenience, but the\n * authoritative fallback lives here so direct API callers get a sensible\n * limit when they omit the parameter. Exported so error messages elsewhere\n * can name the ceiling that actually applied (one constant, no drift).\n */\nexport const DEFAULT_MAX_TOKENS = 500;\n\n/**\n * Generate resource content using inference.\n *\n * Locale parameters: `locale` is the *body* locale — the language the\n * generated resource should be written in (sourced from the user's UI\n * locale). `sourceLanguage` is the *source* locale — the language of the\n * referenced resource whose context (selected passage, surrounding text)\n * is embedded into the prompt. They're independent: a German user can\n * generate German content from an English source resource. See\n * `types.ts` \"Locale conventions\" for the full discussion.\n */\nexport async function generateResourceFromTopic(\n topic: string,\n entityTypes: string[],\n client: InferenceClient,\n logger: Logger,\n userPrompt?: string,\n locale?: string,\n context?: GatheredContext,\n temperature?: number,\n maxTokens?: number,\n sourceLanguage?: string,\n outputMediaType: SupportedMediaType = 'text/markdown',\n task: string = 'resource',\n structure?: string,\n cite: boolean = false,\n repair?: { source: string; error: string }\n): Promise<{ title: string; content: string; truncated: boolean }> {\n logger.debug('Generating resource from topic', {\n topicPreview: topic.substring(0, 100),\n entityTypes,\n hasUserPrompt: !!userPrompt,\n locale,\n sourceLanguage,\n hasContext: !!context,\n temperature,\n maxTokens,\n outputMediaType,\n task,\n structure\n });\n\n // Use provided values or defaults.\n const finalTemperature = temperature ?? 0.7;\n const finalMaxTokens = maxTokens ?? DEFAULT_MAX_TOKENS;\n\n // Determine language instructions. Body locale (\"write the resource in X\")\n // and source locale (\"the embedded source text is in Y\") are independent —\n // a German user can ask for a German write-up of an English source.\n const languageInstruction = locale && locale !== 'en'\n ? `\\n\\nIMPORTANT: Write the entire resource in ${getLanguageName(locale)}.`\n : '';\n const sourceLanguageInstruction = sourceLanguage\n ? `\\n\\nThe source resource and embedded context are in ${getLanguageName(sourceLanguage)}.`\n : '';\n\n // ── Context sections — switch on the unified GatheredContext focus ─────────\n // Annotation focus drives the annotation + selected-passage sections; resource\n // focus drives a minimal resource anchor (the full grounding — summary,\n // suggestedReferences, content — is YIELD-FROM-RESOURCE Gap B). The graph and\n // semantic sections are shared base, rendered for either focus.\n let annotationSection = '';\n let contextSection = '';\n let resourceSection = '';\n let graphSection = '';\n\n if (context) {\n const { focus } = context;\n // The focal resource's id — equal to the id `buildKnowledgeGraph` (P3) anchored\n // the graph's main node on, so `deriveViews` resolves edges. Read directly off\n // the descriptor (a plain string on the generated type); there is no event id to\n // thread here as the matcher had.\n const mainResourceId =\n focus.kind === 'annotation'\n ? focus.sourceResource['@id']\n : focus.resource['@id'];\n const focalAnnotationId = focus.kind === 'annotation' ? focus.annotation.id : undefined;\n\n if (focus.kind === 'annotation') {\n const parts: string[] = [];\n parts.push(`- Annotation motivation: ${focus.annotation.motivation}`);\n parts.push(`- Source resource: ${focus.sourceResource.name} ${idLabel(mainResourceId)}`);\n // Include body text for commenting/assessing annotations\n const { motivation, body } = focus.annotation;\n if (motivation === 'commenting' || motivation === 'assessing') {\n const bodyItem = Array.isArray(body) ? body[0] : body;\n if (bodyItem && 'value' in bodyItem && bodyItem.value) {\n const label = motivation === 'commenting' ? 'Comment' : 'Assessment';\n parts.push(`- ${label}: ${bodyItem.value}`);\n }\n }\n // The wizard's Hint: supplements or replaces the selected text as the\n // subject signal (GatheredContext.json focus.userHint). The matcher folds\n // it into its search term the same way — the two consumers of one field.\n if (focus.userHint) {\n parts.push(`- User hint (steers what to generate): ${focus.userHint}`);\n }\n annotationSection = `\\n\\nAnnotation context:\\n${parts.join('\\n')}`;\n\n if (focus.selected) {\n const { before, text, after } = focus.selected;\n contextSection = `\\n\\nSource document context:\n---\n${before ? `...${before}` : ''}\n**[${text}]**\n${after ? `${after}...` : ''}\n---\n`;\n }\n } else {\n // Resource focus — ground in the focal resource's summary, suggested\n // references, and content (omit-empty). Content is capped per resource to\n // bound prompt size; the caller already chose breadth via gather.resource.\n const parts = [`- Resource: ${focus.resource.name} ${idLabel(mainResourceId)}`];\n if (focus.summary) parts.push(`- Summary: ${focus.summary}`);\n if (focus.suggestedReferences && focus.suggestedReferences.length > 0) {\n parts.push(`- Suggested references: ${focus.suggestedReferences.join(', ')}`);\n }\n resourceSection = `\\n\\nResource context:\\n${parts.join('\\n')}`;\n\n if (focus.content?.main) {\n resourceSection += `\\n\\nResource content:\\n---\\n${focus.content.main.slice(0, RESOURCE_CONTENT_CAP)}\\n---`;\n }\n const related = Object.entries(focus.content?.related ?? {});\n if (related.length > 0) {\n const blocks = related\n .map(([id, text]) => `[${id}]\\n${text.slice(0, RESOURCE_CONTENT_CAP)}`)\n .join('\\n\\n');\n resourceSection += `\\n\\nRelated resource content:\\n---\\n${blocks}\\n---`;\n }\n }\n\n // Shared base: graph-derived neighborhood (connections / citedBy / siblings)\n // plus the LLM relationship summary, recomputed from the unified graph.\n if (mainResourceId) {\n const views = deriveViews(context.graph, mainResourceId, focalAnnotationId);\n const parts: string[] = [];\n\n if (views.connections.length > 0) {\n const connList = views.connections\n .map(c => `${c.resourceName}${c.entityTypes.length ? ` (${c.entityTypes.join(', ')})` : ''} ${idLabel(c.resourceId)}`)\n .join(', ');\n parts.push(`- Connected resources: ${connList}`);\n }\n\n if (views.citedByCount > 0) {\n const citedNames = views.citedBy.map(c => `${c.resourceName} ${idLabel(c.resourceId)}`).join(', ');\n parts.push(`- This resource is cited by ${views.citedByCount} other resource${views.citedByCount > 1 ? 's' : ''}${citedNames ? `: ${citedNames}` : ''}`);\n }\n\n if (views.siblingEntityTypes.length > 0) {\n parts.push(`- Related entity types in this document: ${views.siblingEntityTypes.join(', ')}`);\n }\n\n if (context.inferredRelationshipSummary) {\n parts.push(`- Relationship summary: ${context.inferredRelationshipSummary}`);\n }\n\n if (parts.length > 0) {\n graphSection = `\\n\\nKnowledge graph context:\\n${parts.join('\\n')}`;\n }\n }\n }\n\n // Build semantic context section if available — the vector matches the gather\n // flow already retrieved for the focal passage, used to ground generation (RAG).\n // Capped and truncated to bound prompt cost (see the named caps above); the\n // gather step pre-filters to ≤10 matches above a 0.5 cosine threshold. Each\n // passage carries its source id so the model can attribute what it uses.\n // See .plans/SEMANTIC-CONTEXT-RAG.md + .plans/CONTEXT-IDENTIFIERS.md.\n let semanticContextSection = '';\n const similar = context?.semanticContext?.similar ?? [];\n if (similar.length > 0) {\n const lines = [...similar]\n .sort((a, b) => b.score - a.score)\n .slice(0, SEMANTIC_MATCH_LIMIT)\n .map(m => `- ${idLabel(m.resourceId, m.annotationId)} (${m.score.toFixed(2)})${m.machineRead ? ' [OCR]' : ''} ${m.text.slice(0, SEMANTIC_MATCH_CHARS)}`);\n // A passage marked [OCR] was recognized from a scanned image, so its\n // wording — and especially its digits — may be misread. Saying so is the\n // whole point of carrying the flag: the model is the only reader of these\n // passages, and it would otherwise quote a scanned figure as confidently\n // as a typed one.\n const ocrNote = similar.some(m => m.machineRead)\n ? '\\nPassages marked [OCR] were read from scanned images by character recognition; treat their exact wording and numbers as uncertain, and say so if you rely on one.'\n : '';\n semanticContextSection = `\\n\\nRelated passages from the knowledge base:\\n${lines.join('\\n')}${ocrNote}`;\n }\n\n // ── Task framing (YIELD-STRUCTURE D1): canonical tasks map to tested framings;\n // an unknown task string is used VERBATIM as the framing instruction (loud\n // degrade — warn, never a silent fallback to 'resource').\n let leadLine: string;\n if (task === 'resource') {\n leadLine = `Generate a concise, informative resource about \"${topic}\".`;\n } else if (task === 'answer') {\n leadLine = `Answer the following question directly and concisely, grounded in the provided context: \"${topic}\"`;\n } else if (task === 'summary') {\n leadLine = `Write a concise summary of \"${topic}\".`;\n } else {\n logger.warn('Unknown task — using it verbatim as the framing instruction', { task });\n leadLine = `${task}\\nTopic: \"${topic}\"`;\n }\n\n // ── Structure directive (YIELD-STRUCTURE D2/D4): emitted ONLY when the caller\n // sets one — unset means no directive at all (the task framing and the model\n // determine shape). Never derived from the token budget: maxTokens is length\n // only. The forced `# Title` heading exists only under canonical 'sections'.\n const isPlainText = outputMediaType === 'text/plain';\n // PDF output means the model authors Typst source (Q1, direct Typst); the\n // worker compiles it. The prompt therefore teaches Typst syntax, and the\n // markdown scaffolding never applies.\n const isPdf = outputMediaType === 'application/pdf';\n let structureRequirement = '';\n let titleRequirement = '';\n if (structure === 'sections') {\n structureRequirement = isPdf\n ? '\\n- Organize the content into titled sections (= Heading) with well-structured paragraphs'\n : isPlainText\n ? '\\n- Organize the content into titled sections with well-structured paragraphs'\n : '\\n- Organize the content into titled sections (## Section) with well-structured paragraphs';\n if (!isPlainText && !isPdf) {\n titleRequirement = '\\n- Start with a clear heading (# Title)';\n }\n } else if (structure === 'prose') {\n structureRequirement = '\\n- Write flowing, well-structured paragraphs with no section headings';\n } else if (structure === 'chat') {\n structureRequirement = '\\n- Structure the content as a conversational chat transcript — a sequence of alternating, speaker-labeled turns (no section headings)';\n } else if (structure) {\n logger.warn('Unknown structure — passing it through as freeform organization guidance', { structure });\n structureRequirement = `\\n- Organize the output as: ${structure}`;\n }\n\n // Citation instruction (INLINE-CITATIONS): ask the model to emit [[<id>]]\n // transport tokens next to each claim, citing only ids the context embedding\n // shows. The worker strips the tokens and reconciles them into linking\n // annotations — they never reach the stored content.\n const citeRequirement = cite\n ? '\\n- Ground every claim in the provided context. Immediately after each claim, cite its source by emitting [[<id>]], where <id> is an id shown in square brackets in the context above (for a passage labeled [abc], emit [[abc]]). Cite only ids that appear in the context.'\n : '';\n\n const formatRequirements = isPdf\n ? `- Write the response as Typst markup (the Typst typesetting language — not markdown, not LaTeX)\n- Headings are written as = Heading (deeper levels == Subheading); everything else is plain prose paragraphs\n- Do not emit markdown syntax or code fences`\n : isPlainText\n ? `- Write the response as plain text — no formatting markup (no #, *, backticks, headings, or links)\n- Begin with the title on its own first line`\n : `- Use markdown formatting\n- Write the response as markdown`;\n\n // Compile-repair context (PDF-GENERATION P3): the previous Typst source\n // failed to compile; the legible compiler diagnostics go back to the model\n // as an authoritative instruction to fix and re-emit the full document.\n const repairSection = repair\n ? `\\n\\nYour previous attempt failed to compile. Fix the error and return the complete corrected document — full source, not a diff.\nCompile error:\n${repair.error}\nPrevious source:\n${repair.source}`\n : '';\n\n // The caller's prompt is an authoritative leading Instruction (YIELD-STRUCTURE D3),\n // not background \"additional context\" — task = what to produce, prompt = how.\n const prompt = `${leadLine}\n${userPrompt ? `Instruction: ${userPrompt}` : ''}${repairSection}\n${entityTypes.length > 0 ? `Focus on these entity types: ${entityTypes.join(', ')}.` : ''}${annotationSection}${contextSection}${resourceSection}${graphSection}${semanticContextSection}${sourceLanguageInstruction}${languageInstruction}\n\nRequirements:\n- Aim for approximately ${finalMaxTokens} tokens of content\n- Be factual and informative${structureRequirement}${titleRequirement}${citeRequirement}\n${formatRequirements}`;\n\n // Simple parser - just use the response directly as markdown\n const parseResponse = (response: string): { title: string; content: string } => {\n // Clean up any markdown code fences if present\n let content = response.trim();\n if (content.startsWith('```markdown') || content.startsWith('```md') || content.startsWith('```typst')) {\n content = content.slice(content.indexOf('\\n') + 1);\n const endIndex = content.lastIndexOf('```');\n if (endIndex !== -1) {\n content = content.slice(0, endIndex);\n }\n } else if (content.startsWith('```')) {\n content = content.slice(3);\n const endIndex = content.lastIndexOf('```');\n if (endIndex !== -1) {\n content = content.slice(0, endIndex);\n }\n }\n\n content = content.trim();\n\n // Title is provided by the caller (topic), not extracted from generated content\n return {\n title: topic,\n content: content\n };\n };\n\n logger.debug('Sending prompt to inference', {\n promptLength: prompt.length,\n temperature: finalTemperature,\n maxTokens: finalMaxTokens\n });\n const response = await boundedGenerateWithMetadata(client, prompt, finalMaxTokens, finalTemperature, undefined, logger);\n logger.debug('Got response from inference', { responseLength: response.text.length, stopReason: response.stopReason });\n\n const result = parseResponse(response.text);\n logger.debug('Parsed response', {\n hasTitle: !!result.title,\n titleLength: result.title?.length,\n hasContent: !!result.content,\n contentLength: result.content?.length\n });\n\n // The provider's stopReason collapses to one protocol-owned bit HERE and\n // nowhere else (GENERATE-FROM-RESOURCE D6): 'max_tokens' means the artifact\n // is cut off, not complete; every other reason is a natural stop.\n return { ...result, truncated: response.stopReason === 'max_tokens' };\n}\n","/**\n * Typst compiler invocation (PDF-GENERATION P3).\n *\n * The model writes Typst source (Q1, settled 2026-08-04: direct Typst); this\n * module turns it into PDF bytes via the pinned binary the worker image ships\n * (P2 — `/usr/local/bin/typst`, v0.15.1, resolved through PATH so tests can\n * substitute a fake). Compile failures return the legible error text — Typst's\n * `file:line:col` + caret diagnostics are the load-bearing input to the bounded\n * repair loop in `processGenerationJob`.\n */\nimport { execFileSync } from 'node:child_process';\nimport { mkdtempSync, writeFileSync, readFileSync, rmSync } from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport { join } from 'node:path';\n\n/**\n * Every compile pins the PDF creation timestamp. Unpinned compiles of\n * identical source produce different bytes every time (CreationDate/ModDate),\n * which churns the content checksum and makes the Smelter re-embed a document\n * that did not change — the spurious-re-embed failure S11/S12 exist to\n * prevent. The value is arbitrary; its fixity is the point.\n */\nexport const PINNED_CREATION_TIMESTAMP = 1700000000;\n\n/**\n * Model-authored source can fail to compile; the error is fed back for repair\n * a bounded number of times, then the job fails loudly with the compiler's\n * diagnostics. Bounded because generation is non-idempotent and paid per\n * attempt — this is a repair loop, not a retry loop.\n */\nexport const MAX_COMPILE_REPAIRS = 2;\n\nexport function compileTypst(source: string): { pdf: Uint8Array } | { error: string } {\n const dir = mkdtempSync(join(tmpdir(), 'typst-'));\n try {\n const inFile = join(dir, 'doc.typ');\n const outFile = join(dir, 'doc.pdf');\n writeFileSync(inFile, source);\n try {\n execFileSync(\n 'typst',\n ['compile', '--creation-timestamp', String(PINNED_CREATION_TIMESTAMP), inFile, outFile],\n { stdio: ['ignore', 'pipe', 'pipe'] },\n );\n } catch (err) {\n const stderr = (err as { stderr?: Buffer }).stderr;\n return { error: stderr?.length ? stderr.toString('utf8') : String(err) };\n }\n return { pdf: new Uint8Array(readFileSync(outFile)) };\n } finally {\n rmSync(dir, { recursive: true, force: true });\n }\n}\n","/**\n * Citation-token resolver (INLINE-CITATIONS P1).\n *\n * Under `cite`, the generation prompt asks the model to emit `[[<resourceId>]]` /\n * `[[<resourceId>/<annotationId>]]` transport tokens after each claim, citing ids\n * the context embedding put in front of it (CONTEXT-IDENTIFIERS). Tokens are\n * TRANSPORT, not content: this resolver parses them, validates each id against the\n * ids actually present in the embedded context (the hallucination guard — an\n * unknown id is dropped loudly, never silently linked), STRIPS them from the\n * content before upload, and returns claim-span citations the worker mints as W3C\n * linking annotations on the derived resource. See .plans/INLINE-CITATIONS.md.\n */\nimport type { GatheredContext, Logger } from '@semiont/core';\n\nexport interface GenerationCitation {\n /** The cited source resource (validated present in the embedded context). */\n resourceId: string;\n /** The contributing annotation, when the cited excerpt was annotation-derived. */\n annotationId?: string;\n /** Claim span in the FINAL (token-stripped) content; substring(start, end) === exact. */\n start: number;\n end: number;\n exact: string;\n}\n\n/** `[[rid]]` or `[[rid/annId]]` — ids are bare (no whitespace, brackets, or slashes). */\nconst CITATION_TOKEN = /\\[\\[([^\\s[\\]/]+)(?:\\/([^\\s[\\]/]+))?\\]\\]/g;\n\n/**\n * The ids the context embedding actually put in front of the model — the\n * hallucination guard's ground truth. Mirrors what the prompt renderers label:\n * the focal resource, graph resource nodes, semantic matches, related content.\n */\nexport function collectContextResourceIds(context: GatheredContext | undefined): Set<string> {\n const ids = new Set<string>();\n if (!context) return ids;\n\n const { focus } = context;\n ids.add(focus.kind === 'annotation' ? focus.sourceResource['@id'] : focus.resource['@id']);\n for (const node of context.graph.nodes) {\n if (node.type === 'resource') ids.add(node.id);\n }\n for (const m of context.semanticContext?.similar ?? []) ids.add(m.resourceId);\n if (focus.kind === 'resource') {\n for (const id of Object.keys(focus.content?.related ?? {})) ids.add(id);\n }\n return ids;\n}\n\nexport function resolveCitationTokens(\n content: string,\n validResourceIds: ReadonlySet<string>,\n logger: Logger,\n): { content: string; citations: GenerationCitation[] } {\n const citations: GenerationCitation[] = [];\n let clean = '';\n let last = 0;\n\n for (const match of content.matchAll(CITATION_TOKEN)) {\n const token = match[0];\n const citedResourceId = match[1]!;\n const annotationId = match[2];\n\n // Append the text before the token, dropping the whitespace run immediately\n // preceding it so stripping never leaves a dangling space.\n clean += content.slice(last, match.index).replace(/[ \\t]+$/, '');\n last = match.index! + token.length;\n\n if (!validResourceIds.has(citedResourceId)) {\n logger.warn('Citation token references an id absent from the provided context — dropped', {\n resourceId: citedResourceId,\n });\n continue;\n }\n\n // Claim span: the sentence preceding the token, computed against the clean\n // content. Offsets are final — later appends never shift earlier positions.\n const end = clean.length;\n let start = 0;\n for (let i = end - 2; i >= 0; i--) {\n const ch = clean[i];\n if (ch === '.' || ch === '!' || ch === '?' || ch === '\\n') {\n start = i + 1;\n break;\n }\n }\n while (start < end && /\\s/.test(clean[start]!)) start++;\n const exact = clean.slice(start, end);\n if (exact.length === 0) {\n logger.warn('Citation token has no preceding claim text — dropped', {\n resourceId: citedResourceId,\n });\n continue;\n }\n\n citations.push({\n resourceId: citedResourceId,\n ...(annotationId ? { annotationId } : {}),\n start,\n end,\n exact,\n });\n }\n\n clean += content.slice(last);\n return { content: clean, citations };\n}\n","/**\n * Run `worker` over `items` with at most `limit` of them in flight at once\n * (DETECTION-QUALITY-THROUGHPUT P6). Hand-rolled rather than a dependency: the\n * need is exactly one bounded map, and the BOUND is the whole point — detection\n * parallelizes independent entity types, but unbounded fan-out just trades\n * sequential waiting for provider 429 thrash, so the cap is the feature.\n *\n * A fixed pool of `min(limit, items.length)` pumps, each pulling the next index\n * until the work is exhausted — so at most `limit` calls are ever awaiting at\n * once, whatever the item count. Results are returned in INPUT order (a caller\n * can zip them back to items); COMPLETION order is not input order — that is the\n * point of concurrency — so a caller that must act \"as each finishes\" does so\n * inside `worker`, not on the returned array.\n *\n * Failure matches the sequential loop it replaces: the first `worker` rejection\n * rejects the whole run. Pumps already awaiting finish (they cannot be\n * cancelled mid-await), but no new item is pulled once a rejection propagates.\n */\nexport async function runBounded<T, R>(\n items: readonly T[],\n limit: number,\n worker: (item: T, index: number) => Promise<R>,\n): Promise<R[]> {\n const results = new Array<R>(items.length);\n let next = 0;\n\n async function pump(): Promise<void> {\n while (true) {\n const i = next++;\n if (i >= items.length) return;\n results[i] = await worker(items[i]!, i);\n }\n }\n\n const poolSize = Math.max(1, Math.min(limit, items.length));\n await Promise.all(Array.from({ length: poolSize }, () => pump()));\n return results;\n}\n","/**\n * Job Processors\n *\n * Pure functions that take content + inference client + params,\n * report progress via callback, and return annotations + results.\n *\n * No EventBus, no JobQueue, no side effects except calling inference.\n * Driven by the remote worker process (worker-process.ts), which claims\n * jobs over SSE and dispatches by jobType to these functions.\n */\n\nimport { AnnotationDetection } from './workers/annotation-detection';\nimport { extractEntities } from './workers/detection/entity-extractor';\nimport { DEFAULT_MAX_TOKENS, generateResourceFromTopic } from './workers/generation/resource-generation';\nimport { compileTypst, MAX_COMPILE_REPAIRS } from './workers/generation/typst-compiler';\nimport { withinByteBudget, MAX_PDF_BYTES } from '@semiont/content';\nimport { resolveCitationTokens, collectContextResourceIds, type GenerationCitation } from './workers/generation/citation-resolver';\nimport { annotationIdFor } from '@semiont/event-sourcing';\nimport { didToAgent, GENERATABLE_MEDIA_TYPES, type Annotation, type GenerationJobParams, type Logger, type ResourceId, type SupportedMediaType, type components, type JobReferenceAnnotationResult, type JobHighlightAnnotationResult, type JobCommentAnnotationResult, type JobAssessmentAnnotationResult, type JobTagAnnotationResult, type UnitCursor } from '@semiont/core';\nimport { reconcileSelector, createFragmentSelector, locate, type ReconciledSelector, type AnchoredText } from '@semiont/core';\nimport type { InferenceClient } from '@semiont/inference';\nimport type {\n HighlightDetectionParams,\n CommentDetectionParams,\n AssessmentDetectionParams,\n DetectionParams,\n TagDetectionParams,\n GenerationResult,\n} from './types';\nimport { noteAnchor } from './workers/detection/anchor-audit';\nimport { runBounded } from './workers/detection/bounded-concurrency';\n\ntype Agent = components['schemas']['Agent'];\n\n/** A detected span — offsets into the extracted `.text`, plus optional context. */\nexport type SpanMatch = { exact: string; start: number; end: number; prefix?: string; suffix?: string };\n\n/**\n * The span half of an annotation's identity (JOB-RESTART-SAFETY P3).\n *\n * Shared by both builders because the span IS the same fact in both — the PDF\n * path additionally persists geometry for it, but that geometry is derived\n * from these offsets, so hashing it too would add no distinguishing power and\n * would make an id depend on a layout the text path cannot reproduce.\n *\n * `exact` is included, not just the offsets: after a content update the same\n * offsets cover different text, and that is a different annotation.\n */\nfunction spanAnchor(match: Pick<SpanMatch, 'start' | 'end' | 'exact'>): string {\n return `${match.start}:${match.end}:${match.exact}`;\n}\n\n/**\n * Turn a detected span into a stored annotation. The media type, resource, and\n * attribution context are closed over by the caller (see `prepareDetection`);\n * the detection processor supplies only the motivation, the span, and any\n * motivation-specific body. This is the single axis that varies by media type,\n * so the detection processors themselves stay media-agnostic.\n */\nexport type BuildAnnotation = (\n motivation: Motivation,\n match: SpanMatch,\n body?: Annotation['body'],\n) => Annotation;\n\n/**\n * Progress callback. The two positional args are the required `JobProgress`\n * fields (`percentage`, `message`). The third optional arg carries the\n * job-type-specific fields (`completedEntityTypes`, `requestParams`, etc.)\n * that the progress UI renders.\n *\n * Anything in `extra` describing the RUN rather than the moment must be passed\n * on EVERY call: the client's `progress$` replaces its value per event, so a\n * field sent once disappears on the next tick.\n *\n * `message` is a CODE plus typed params, never a prose sentence\n * (ASSIST-PROGRESS-CONSOLIDATION A6). The producer reports what happened;\n * each client renders it in the user's language — react-ui from its 29\n * locales, the Go launcher from its English map. The vocabulary is frozen\n * by the census of these call sites: adding a shape means adding a variant\n * to `JobProgressMessage.json` and copy in every client, not composing a\n * new sentence here.\n */\nexport type OnProgress = (\n percentage: number,\n message: JobProgressMessage,\n extra?: Partial<JobProgress>,\n) => void;\n\ntype JobProgress = components['schemas']['JobProgress'];\n/** Derived from the wire, never restated: the per-unit entry's shape is the\n * spec's, so a field added there reaches these accumulators by regeneration\n * rather than by someone remembering two places. */\ntype CompletedItem = NonNullable<JobProgress['completedItems']>[number];\ntype JobProgressMessage = components['schemas']['JobProgressMessage'];\n\n/** The five W3C motivations this system mints — a closed vocabulary, so it is\n * typed as one rather than as `string`. */\nexport type Motivation = Annotation['motivation'];\n\n/**\n * A detection processor returns only its result. Annotations leave through\n * `onChunkComplete`, per chunk — a return that also carried them would be a\n * second path to the same write.\n */\nexport interface ProcessorResult<R> {\n result: R;\n}\n\n/**\n * Strip the audit-only fields (`anchorMethod`, `llmOffsets`, `matchQuality`)\n * off a `ReconciledSelector` so the rest is shaped like a match input for\n * `buildTextAnnotation`. The audit info belongs in logs, not in storage.\n */\nfunction toMatch(r: ReconciledSelector): { exact: string; start: number; end: number; prefix?: string; suffix?: string } {\n return {\n exact: r.exact,\n start: r.start,\n end: r.end,\n ...(r.prefix !== undefined ? { prefix: r.prefix } : {}),\n ...(r.suffix !== undefined ? { suffix: r.suffix } : {}),\n };\n}\n\n/**\n * Identity key for a built annotation: motivation + anchored span + body.\n * Two annotations with the same key are the same event written twice.\n */\nfunction annotationDedupeKey(ann: Record<string, unknown>): string {\n const target = ann.target as\n | { selector?: Array<{ type: string; start?: number; end?: number; value?: string; exact?: string; prefix?: string; suffix?: string }> }\n | undefined;\n const selectors = Array.isArray(target?.selector) ? target.selector : [];\n const pos = selectors.find((s) => s.type === 'TextPositionSelector');\n // Anchor identity is media-specific. Text annotations carry a\n // TextPositionSelector (durable char offsets). PDF annotations have none —\n // their anchor is the per-line FragmentSelector viewrect geometry plus the\n // TextQuoteSelector text. Keying only on TextPositionSelector would collapse\n // every PDF annotation sharing a motivation+body onto one (its offsets fall\n // back to '?'), so e.g. multiple PDF highlights emit as a single annotation.\n let anchor: string;\n if (pos) {\n anchor = `pos:${pos.start ?? '?'}:${pos.end ?? '?'}`;\n } else {\n const frags = selectors.filter((s) => s.type === 'FragmentSelector').map((s) => s.value ?? '').join(',');\n const quote = selectors.find((s) => s.type === 'TextQuoteSelector');\n anchor = `frag:${frags}|quote:${quote?.exact ?? ''}:${quote?.prefix ?? ''}:${quote?.suffix ?? ''}`;\n }\n return [ann.motivation as string, anchor, JSON.stringify(ann.body ?? null)].join('|');\n}\n\n/**\n * Drop annotations that are identical in the fields that define an\n * annotation's meaning: motivation, anchored span, and body.\n *\n * Why this is needed: each LLM-emitted span is reconciled independently\n * (no cross-entry coordination), and `reconcileSelector`'s `first-of-many`\n * fallback anchors every undisambiguated entry at the *same* first\n * occurrence. So a phrase repeated in non-distinctive context can produce\n * several entries that all collapse onto one span — identical events. This\n * collapses them back to one.\n *\n * What it does NOT drop: same span, *different* body (e.g. the same text\n * tagged as two entity types, or two distinct comments on one passage).\n * Those are legitimately distinct annotations.\n *\n * Applied identically by every processor below.\n */\n/**\n * THE dedupe decider — one mechanism for all five detection types, held\n * across a stream of chunk batches: adjacent chunks overlap, so the same\n * span arrives twice, and there is no post-pass to collapse it in. Scope it\n * to one emission stream — per unit for reference detection, per job for the\n * four motivations. Never add a batch post-pass beside it (gated).\n */\nfunction makeSpanDeduper(): (annotations: Annotation[]) => Annotation[] {\n const seen = new Set<string>();\n return (annotations) => {\n const out: Annotation[] = [];\n for (const ann of annotations) {\n const key = annotationDedupeKey(ann);\n if (seen.has(key)) continue;\n seen.add(key);\n out.push(ann);\n }\n return out;\n };\n}\n\nexport function buildTextAnnotation(\n content: string,\n resourceId: ResourceId,\n userId: string,\n generator: Agent,\n motivation: Motivation,\n match: { exact: string; start: number; end: number; prefix?: string; suffix?: string },\n // Body may be a single AnnotationBody object or a non-empty array of\n // them, OR omitted entirely. W3C treats body as optional; annotations\n // whose motivation alone conveys meaning (highlighting) legitimately\n // skip it. Every other motivation currently passes something; the\n // processor that calls this makes the choice per-motivation.\n body?: Annotation['body'],\n) {\n // Write-time invariant. Every selector that reaches storage must be\n // internally consistent with the source content. If a worker bypasses\n // `reconcileSelector` or a future change re-introduces overlap, the\n // throw fires loudly here instead of corrupting the KB.\n if (content.substring(match.start, match.end) !== match.exact) {\n throw new Error(\n `buildTextAnnotation invariant: content.substring(${match.start}, ${match.end}) !== exact ` +\n `for resource ${resourceId}, motivation ${motivation}`,\n );\n }\n if (match.prefix !== undefined) {\n const actualPrefix = content.substring(Math.max(0, match.start - match.prefix.length), match.start);\n if (actualPrefix !== match.prefix) {\n throw new Error(\n `buildTextAnnotation invariant: content prefix-slice !== prefix ` +\n `for resource ${resourceId}, motivation ${motivation}`,\n );\n }\n }\n if (match.suffix !== undefined) {\n const actualSuffix = content.substring(match.end, Math.min(content.length, match.end + match.suffix.length));\n if (actualSuffix !== match.suffix) {\n throw new Error(\n `buildTextAnnotation invariant: content suffix-slice !== suffix ` +\n `for resource ${resourceId}, motivation ${motivation}`,\n );\n }\n }\n\n // `userId` here is the DID of the human who initiated the work. The\n // worker process is acting on their behalf using `generator` to\n // produce content. Per the protocol attribution model:\n // creator = who initiated (the human)\n // generator = what produced (the software peer)\n // wasAttributedTo = both parties (PROV-O)\n // For autonomous-agent work creator and generator collapse to the\n // same Software Agent; the same field assignments still hold.\n const creator = didToAgent(userId);\n const wasAttributedTo: Agent[] =\n creator['@id'] === generator['@id'] ? [generator] : [creator, generator];\n return {\n '@context': 'http://www.w3.org/ns/anno.jsonld' as const,\n 'type': 'Annotation' as const,\n 'id': annotationIdFor({ resourceId: resourceId as string, motivation, anchor: spanAnchor(match), body }),\n motivation,\n creator,\n generator,\n wasAttributedTo,\n created: new Date().toISOString(),\n target: {\n type: 'SpecificResource' as const,\n source: resourceId as string,\n selector: [\n { type: 'TextPositionSelector' as const, start: match.start, end: match.end },\n {\n type: 'TextQuoteSelector' as const,\n exact: match.exact,\n ...(match.prefix && { prefix: match.prefix }),\n ...(match.suffix && { suffix: match.suffix }),\n },\n ],\n },\n ...(body !== undefined ? { body } : {}),\n };\n}\n\n/**\n * PDF sibling of `buildTextAnnotation`. The model returns the same\n * `{ exact, start, end, prefix?, suffix? }` match over the extracted text\n * layer's `text`; geometry comes from the layer, never the model.\n *\n * `target.selector` = one `FragmentSelector` per line (`locate` unions the\n * overlapping text-layer items into per-line viewrects) plus a\n * `TextQuoteSelector` anchor. No `TextPositionSelector`: the extracted text\n * layer is a derived artifact, not the stored content, so its char offsets are\n * not a durable anchor.\n *\n * **And the `id` is hashed over exactly those offsets** — `annotationIdFor` gets\n * `spanAnchor(match)`, i.e. `${start}:${end}:${exact}`. That is worth stating\n * plainly, because it sits in tension with the paragraph above: identity here\n * depends on something the annotation refuses to store, on the grounds that it is\n * not durable. The consequence is not a corrupt write but an invisible one — an\n * extraction that moved would re-identify the same visual span, and the two\n * annotations would agree in every stored field (same rects, same quoted text) and\n * differ only in `id`, so every dedupe layer would correctly let both through.\n *\n * It is left as it is on evidence, not by oversight. The offsets come from a\n * derivation cached per content checksum and gated by a stamp that a release of\n * `@semiont/content`, the PDF engine or its traineddata busts by design; measured\n * across the caret-reachable engine move (pdfjs 6.2.108 → 6.3.289) over 1,192 pages\n * of real documents, they did not move, and `pdf-offset-stability.test.ts` in\n * `@semiont/content` now fails if they ever do. The OCR path is unmeasured, and a\n * scanned document takes its whole text from there. If that gate ever fires, this\n * is the line to revisit: hashing the DURABLE anchor instead (page geometry plus\n * `exact`) makes identity depend only on what the annotation carries — at the cost\n * of one round of new ids for every PDF annotation minted afterwards.\n *\n * Write-time invariant (geometry <-> text): geometry is item-level (word runs),\n * so the covered items' text must *contain* `exact` (whitespace-normalized) —\n * containment, not reconstruction. An empty cover (no overlapping items -> no\n * rects) also fails. Throws loudly, naming the resource + motivation, rather\n * than persisting geometry that doesn't back the quoted text.\n */\nexport function buildPdfAnnotation(\n anchored: AnchoredText,\n resourceId: ResourceId,\n userId: string,\n generator: Agent,\n motivation: Motivation,\n match: { exact: string; start: number; end: number; prefix?: string; suffix?: string },\n body?: Annotation['body'],\n) {\n // `locate` returns both the per-line rects and the overlap items it found;\n // reuse `overlap` for the containment check rather than re-scanning layer.items.\n const { rects, overlap } = locate(anchored, match.start, match.end);\n\n const coveredText = overlap.length\n ? anchored.text.substring(\n Math.min(...overlap.map((i) => i.start)),\n Math.max(...overlap.map((i) => i.end)),\n )\n : '';\n const normalize = (s: string) => s.replace(/\\s+/g, ' ').trim();\n // Two distinct failures, reported distinctly. Merged, both printed \"covered\n // text does not contain exact\" — which sends anyone debugging an empty cover\n // to inspect text matching that never ran. Same class deliberately: both stay\n // plain `Error`, so `classifyFailure` leaves them unrecognized and therefore\n // retryable (ABANDONED-INFERENCE HD2 is one-sided — only KNOWN-deterministic\n // failures skip the budget). No rects LOOKS deterministic, but the stored map\n // is keyed by content checksum, so a retry after the bytes change reads a\n // different map and can legitimately succeed.\n if (rects.length === 0) {\n throw new Error(\n `buildPdfAnnotation invariant: no rects located for offsets ${match.start}-${match.end} ` +\n `for resource ${resourceId}, motivation ${motivation}`,\n );\n }\n if (!normalize(coveredText).includes(normalize(match.exact))) {\n throw new Error(\n `buildPdfAnnotation invariant: covered text does not contain exact ` +\n `for resource ${resourceId}, motivation ${motivation}`,\n );\n }\n\n const creator = didToAgent(userId);\n const wasAttributedTo: Agent[] =\n creator['@id'] === generator['@id'] ? [generator] : [creator, generator];\n\n return {\n '@context': 'http://www.w3.org/ns/anno.jsonld' as const,\n 'type': 'Annotation' as const,\n 'id': annotationIdFor({ resourceId: resourceId as string, motivation, anchor: spanAnchor(match), body }),\n motivation,\n creator,\n generator,\n wasAttributedTo,\n created: new Date().toISOString(),\n target: {\n type: 'SpecificResource' as const,\n source: resourceId as string,\n selector: [\n ...rects.map((coord) => ({\n type: 'FragmentSelector' as const,\n conformsTo: 'http://tools.ietf.org/rfc/rfc3778' as const,\n value: createFragmentSelector(coord),\n })),\n {\n type: 'TextQuoteSelector' as const,\n exact: match.exact,\n ...(match.prefix && { prefix: match.prefix }),\n ...(match.suffix && { suffix: match.suffix }),\n },\n ],\n },\n ...(body !== undefined ? { body } : {}),\n };\n}\n\n/**\n * Where one unit stands once the chunk just handed over is durable\n * (CHUNK-GRAIN-RESUME P2).\n *\n * The unit is named HERE rather than in the detection layer, which knows about\n * chunks and nothing about jobs: for `reference-annotation` a unit is an entity\n * type, for `tag-annotation` a category — both loop over several — and for the\n * other three the job runs exactly one unit, its own motivation.\n */\nexport interface UnitCheckpoint {\n unit: string;\n cursor: UnitCursor;\n}\n\nexport async function processHighlightJob(\n content: string,\n inferenceClient: InferenceClient,\n params: HighlightDetectionParams,\n buildAnnotation: BuildAnnotation,\n onProgress: OnProgress,\n /** This chunk's novel annotations, awaited: the durability write. */\n onChunkComplete: (annotations: Annotation[], checkpoint: UnitCheckpoint) => Promise<void>,\n /** Where earlier attempts left each unit (CHUNK-GRAIN-RESUME P3), keyed the\n * same way the checkpoint is. A unit absent here starts at the top, which is\n * every unit of a first attempt. */\n resumeCursors?: Record<string, UnitCursor>,\n): Promise<ProcessorResult<JobHighlightAnnotationResult>> {\n const echo = detectionEcho(params);\n\n onProgress(10, { code: 'loading' }, echo);\n onProgress(30, { code: 'analyzing' }, echo);\n\n const dedupe = makeSpanDeduper();\n // Seeded from what an earlier attempt already counted for this unit, so a\n // resumed job's terminal record describes the document rather than the\n // remainder it happened to run (CHUNK-GRAIN-RESUME HD3).\n const prior = resumeCursors?.['highlighting'];\n let found = prior?.found ?? 0;\n let created = prior?.emitted ?? 0;\n await AnnotationDetection.detectHighlights(\n content, inferenceClient, params.instructions, params.density, params.sourceLanguage,\n // Liveness (chunk boundaries + in-flight heartbeat): 30–60 band.\n (consumedChars, totalChars) => onProgress(30 + Math.round((consumedChars / totalChars) * 30), { code: 'analyzing' }, echo),\n resumeCursors?.['highlighting'],\n async (matches, cursor) => {\n found += matches.length;\n // Highlights carry no body — motivation:'highlighting' on a target\n // is a complete annotation per the W3C Web Annotation Model.\n const fresh = dedupe(matches.map((h) => buildAnnotation('highlighting', h)));\n created += fresh.length;\n onProgress(60, { code: 'creating-annotations', count: created }, echo);\n // One motivation per job, so exactly one unit — and with only one, a\n // unit-grain checkpoint could record nothing until the whole document\n // was done. The cursor is the entire resume story for these three types.\n await onChunkComplete(fresh, { unit: 'highlighting', cursor: { ...cursor, found, emitted: created } });\n },\n );\n\n onProgress(100, { code: 'complete-created', count: created, kind: 'highlight' }, echo);\n\n return {\n result: { kind: 'highlight-annotation', highlightsFound: found, highlightsCreated: created },\n };\n}\n\n/**\n * The user's own inputs, echoed back for the progress widget. Labels are CODES\n * (the client localizes them); values are the user's words and are shown\n * verbatim. Absent or blank inputs are omitted rather than rendered as empty\n * rows — \"Instructions:\" with nothing after it is noise, not information.\n *\n * Returned as the `extra` object rather than a bare array because every\n * `onProgress` in the run passes it: `progress$` REPLACES its value per event\n * (mark-state-unit), so a field sent once would flash at 10% and disappear.\n * The parameters describe the whole run, so every event carries them — the\n * same convention `processReferenceJob` already follows for its entity types.\n */\nfunction detectionEcho(p: {\n instructions?: string;\n tone?: string;\n density?: number;\n}): Partial<JobProgress> {\n const requestParams: Array<{ label: 'instructions' | 'tone' | 'density'; value: string }> = [];\n if (p.instructions?.trim()) requestParams.push({ label: 'instructions', value: p.instructions.trim() });\n if (p.tone?.trim()) requestParams.push({ label: 'tone', value: p.tone.trim() });\n if (p.density !== undefined) requestParams.push({ label: 'density', value: String(p.density) });\n return requestParams.length > 0 ? { requestParams } : {};\n}\n\nexport async function processCommentJob(\n content: string,\n inferenceClient: InferenceClient,\n params: CommentDetectionParams,\n buildAnnotation: BuildAnnotation,\n onProgress: OnProgress,\n /** This chunk's novel annotations, awaited: the durability write. */\n onChunkComplete: (annotations: Annotation[], checkpoint: UnitCheckpoint) => Promise<void>,\n /** Where earlier attempts left each unit (CHUNK-GRAIN-RESUME P3), keyed the\n * same way the checkpoint is. A unit absent here starts at the top, which is\n * every unit of a first attempt. */\n resumeCursors?: Record<string, UnitCursor>,\n): Promise<ProcessorResult<JobCommentAnnotationResult>> {\n const echo = detectionEcho(params);\n\n onProgress(10, { code: 'loading' }, echo);\n onProgress(30, { code: 'analyzing' }, echo);\n\n // The body's `language` reflects the locale the LLM was asked to write in\n // (`params.language` — the user's UI locale). Defaults to 'en' when the\n // caller didn't specify, matching what the LLM produces by default.\n const bodyLanguage = params.language ?? 'en';\n const dedupe = makeSpanDeduper();\n // Seeded from what an earlier attempt already counted for this unit, so a\n // resumed job's terminal record describes the document rather than the\n // remainder it happened to run (CHUNK-GRAIN-RESUME HD3).\n const prior = resumeCursors?.['commenting'];\n let found = prior?.found ?? 0;\n let created = prior?.emitted ?? 0;\n await AnnotationDetection.detectComments(\n content, inferenceClient, params.instructions, params.tone, params.density,\n params.language, params.sourceLanguage,\n // Liveness (chunk boundaries + in-flight heartbeat): 30–60 band.\n (consumedChars, totalChars) => onProgress(30 + Math.round((consumedChars / totalChars) * 30), { code: 'analyzing' }, echo),\n resumeCursors?.['commenting'],\n async (comments, cursor) => {\n found += comments.length;\n const fresh = dedupe(comments.map((c) =>\n // Match the pre-#651 CommentAnnotationWorker: include format and\n // language on the body TextualBody. Optional in the schema, but\n // consumers that do language-aware rendering rely on them.\n buildAnnotation('commenting', c, [\n { type: 'TextualBody', value: c.comment, purpose: 'commenting', format: 'text/plain' satisfies SupportedMediaType, language: bodyLanguage },\n ]),\n ));\n created += fresh.length;\n onProgress(60, { code: 'creating-annotations', count: created }, echo);\n await onChunkComplete(fresh, { unit: 'commenting', cursor: { ...cursor, found, emitted: created } });\n },\n );\n\n onProgress(100, { code: 'complete-created', count: created, kind: 'comment' }, echo);\n\n return {\n result: { kind: 'comment-annotation', commentsFound: found, commentsCreated: created },\n };\n}\n\nexport async function processAssessmentJob(\n content: string,\n inferenceClient: InferenceClient,\n params: AssessmentDetectionParams,\n buildAnnotation: BuildAnnotation,\n onProgress: OnProgress,\n /** This chunk's novel annotations, awaited: the durability write. */\n onChunkComplete: (annotations: Annotation[], checkpoint: UnitCheckpoint) => Promise<void>,\n /** Where earlier attempts left each unit (CHUNK-GRAIN-RESUME P3), keyed the\n * same way the checkpoint is. A unit absent here starts at the top, which is\n * every unit of a first attempt. */\n resumeCursors?: Record<string, UnitCursor>,\n): Promise<ProcessorResult<JobAssessmentAnnotationResult>> {\n const echo = detectionEcho(params);\n\n onProgress(10, { code: 'loading' }, echo);\n onProgress(30, { code: 'analyzing' }, echo);\n\n const bodyLanguage = params.language ?? 'en';\n const dedupe = makeSpanDeduper();\n // Seeded from what an earlier attempt already counted for this unit, so a\n // resumed job's terminal record describes the document rather than the\n // remainder it happened to run (CHUNK-GRAIN-RESUME HD3).\n const prior = resumeCursors?.['assessing'];\n let found = prior?.found ?? 0;\n let created = prior?.emitted ?? 0;\n await AnnotationDetection.detectAssessments(\n content, inferenceClient, params.instructions, params.tone, params.density,\n params.language, params.sourceLanguage,\n // Liveness (chunk boundaries + in-flight heartbeat): 30–60 band.\n (consumedChars, totalChars) => onProgress(30 + Math.round((consumedChars / totalChars) * 30), { code: 'analyzing' }, echo),\n resumeCursors?.['assessing'],\n async (assessments, cursor) => {\n found += assessments.length;\n const fresh = dedupe(assessments.map((a) =>\n // Single-object body with purpose aligned to motivation, matching the\n // pre-#651 AssessmentAnnotationWorker's shape and the majority of\n // persisted assessments. Do not switch to an array or to\n // purpose='describing' — that loses the \"this is an assessment, not\n // a description\" signal and breaks existing readers that access\n // `body.value` directly on the object.\n buildAnnotation('assessing', a, {\n type: 'TextualBody', value: a.assessment, purpose: 'assessing', format: 'text/plain' satisfies SupportedMediaType, language: bodyLanguage,\n }),\n ));\n created += fresh.length;\n onProgress(60, { code: 'creating-annotations', count: created }, echo);\n await onChunkComplete(fresh, { unit: 'assessing', cursor: { ...cursor, found, emitted: created } });\n },\n );\n\n onProgress(100, { code: 'complete-created', count: created, kind: 'assessment' }, echo);\n\n return {\n result: { kind: 'assessment-annotation', assessmentsFound: found, assessmentsCreated: created },\n };\n}\n\n/**\n * Reference detection commits per UNIT — one entity type — through\n * `onUnitComplete` (ABANDONED-INFERENCE P2, checkpointed resume): the\n * callback receives the unit's deduped annotations, and only after it\n * resolves does the unit count as complete. Emission belongs to the\n * callback alone; the processor returns only the result — returning the\n * annotations as well would recreate the post-run batch that N2 showed\n * discards completed work wholesale.\n */\nexport async function processReferenceJob(\n content: string,\n inferenceClient: InferenceClient,\n params: DetectionParams,\n buildAnnotation: BuildAnnotation,\n onProgress: OnProgress,\n logger: Logger,\n /**\n * The CHECKPOINT, fired once per unit after every one of its chunks has\n * committed. It carries no annotations — the effect already happened per\n * chunk through `onChunkComplete`, and a unit callback that also carried\n * them would be a second commit path.\n */\n onUnitComplete: (entityType: string) => Promise<void>,\n signal?: AbortSignal,\n /** This chunk's novel annotations, awaited: the durability write. */\n onChunkComplete?: (annotations: Annotation[], checkpoint: UnitCheckpoint) => Promise<void>,\n /** Where earlier attempts left each entity-type unit (CHUNK-GRAIN-RESUME P3).\n * A unit absent here starts at the top; units already COMPLETE never reach\n * this function at all, the caller having filtered them out. */\n resumeCursors?: Record<string, UnitCursor>,\n): Promise<{ result: JobReferenceAnnotationResult }> {\n const entityTypeNames = params.entityTypes.map(String);\n const requestParams = [{ label: 'entity-types' as const, value: entityTypeNames.join(', ') }];\n const completedItems: CompletedItem[] = [];\n // Seeded with what earlier attempts already counted for the units this\n // attempt is RESUMING. Units that completed earlier carry no cursor — they\n // are filtered out before this function sees them — so their share is still\n // missing from the job total. That is the pre-existing unit-grain gap, named\n // in CHUNK-GRAIN-RESUME rather than silently half-fixed here.\n let totalFound = Object.values(resumeCursors ?? {}).reduce((n, c) => n + c.found, 0);\n let totalEmitted = Object.values(resumeCursors ?? {}).reduce((n, c) => n + c.emitted, 0);\n let errors = 0;\n let totalUnderReportedPieces = 0;\n // The denominator: cumulative count-verifier expectations over accepted\n // pieces. Zero means no piece was priced — the frame then carries nothing.\n let totalExpected = 0;\n\n onProgress(10, { code: 'loading' }, { requestParams });\n\n const bodyLanguage = params.language ?? 'en';\n\n // Entity types run BOUNDED-CONCURRENT (DETECTION-QUALITY-THROUGHPUT P6).\n // They are independent units — own extraction, own commit, own checkpoint —\n // and a single sequential job used a sliver of the provider's rate limit, so\n // the old `for … await` was the 9×-sequential ≈ 2.5 h/document. The bound is\n // the point: unbounded fan-out just trades sequential waiting for 429 thrash.\n //\n // Shared counters and `completedItems` are mutated SYNCHRONOUSLY between\n // awaits inside the worker — safe under the single-threaded event loop (no\n // read-modify-write straddles an await), so no locking is needed. Progress is\n // now \"M of N done\" rather than \"on type i\": concurrent types finish out of\n // order, and `completedItems` already tolerates that.\n let completed = 0;\n const total = entityTypeNames.length;\n\n const emitTypeProgress = (entityTypeName: string): void => {\n const pct = 20 + Math.round((completed / total) * 60);\n onProgress(pct, { code: 'detecting-entities', entityType: entityTypeName }, {\n current: { kind: 'entity-type', value: entityTypeName },\n processed: completed,\n total,\n entitiesFound: totalFound,\n ...(totalExpected > 0 ? { entitiesExpected: totalExpected } : {}),\n entitiesEmitted: totalEmitted,\n completedItems: [...completedItems],\n requestParams,\n });\n };\n\n // Concurrency is the PROVIDER's capability, not a flat constant: a hosted\n // API parallelizes, a local single-model server does not (P6). Reading it\n // from the client is what makes the same code correct on both.\n await runBounded(entityTypeNames, inferenceClient.maxConcurrency, async (entityTypeName) => {\n if (!entityTypeName) return;\n // Cooperative cancellation (JOB-RESTART-SAFETY P4): once aborted, a pending\n // type is skipped when its turn comes; types already in flight finish and\n // commit, so committed units stay checkpointed. The caller reads\n // `signal.aborted` to move the job to cancelled/ rather than complete/.\n if (signal?.aborted) return;\n\n emitTypeProgress(entityTypeName);\n\n // Unresolved reference body: the entity type as a tagging TextualBody,\n // stamped with the body locale to match the comment/assess/tag pattern.\n // The bind flow later appends a SpecificResource (purpose: 'linking') via\n // mark:body-updated to produce the resolved shape. Emitting an empty body\n // would break the append contract.\n const unresolvedBody: Annotation['body'] = [\n { type: 'TextualBody' as const, value: entityTypeName, purpose: 'tagging' as const, format: 'text/plain' satisfies SupportedMediaType, language: bodyLanguage },\n ];\n\n // One deduper per unit, held across its chunks.\n const dedupe = makeSpanDeduper();\n // Seeded from what earlier attempts already counted for THIS unit, so the\n // terminal record describes the document rather than the remainder this\n // attempt happened to run. Absent (a first attempt) means zero, which is\n // the truth rather than a default.\n const priorUnit = resumeCursors?.[entityTypeName];\n let unitFound = priorUnit?.found ?? 0;\n let unitPersisted = priorUnit?.emitted ?? 0;\n // What remains unknown at the unit's end: floor-accepted pieces, folded.\n let underReported: { pieces: number; found: number; counted: number } | undefined;\n await extractEntities(\n content, [entityTypeName], inferenceClient, params.includeDescriptiveReferences ?? false, logger,\n params.sourceLanguage,\n // Liveness heartbeat (DETECTION-HEARTBEAT): fires at chunk boundaries and\n // every ~15 s while a call is in flight, so a long single-chunk call is\n // not silent. It repeats the current position rather than inventing an\n // advance — the stall watchdog, janitor and client timeout need a signal,\n // not a monotone.\n () => emitTypeProgress(entityTypeName),\n (verdict) => {\n underReported = {\n pieces: (underReported?.pieces ?? 0) + 1,\n found: (underReported?.found ?? 0) + verdict.found,\n counted: (underReported?.counted ?? 0) + verdict.counted,\n };\n },\n (counted) => {\n totalExpected += counted;\n emitTypeProgress(entityTypeName);\n },\n resumeCursors?.[entityTypeName],\n async (chunkEntities, cursor) => {\n const built: Annotation[] = [];\n for (const entity of chunkEntities) {\n const reconciled = reconcileSelector(content, {\n exact: entity.exact,\n ...(entity.prefix !== undefined ? { prefix: entity.prefix } : {}),\n ...(entity.suffix !== undefined ? { suffix: entity.suffix } : {}),\n });\n if (!reconciled) {\n logger.error('Entity dropped — text not found in source', {\n text: entity.exact,\n entityType: entity.entityType,\n });\n errors++;\n continue;\n }\n noteAnchor('reference', entity.exact, reconciled.anchorMethod, logger);\n built.push(buildAnnotation('linking', toMatch(reconciled), unresolvedBody));\n }\n const fresh = dedupe(built);\n // What this chunk makes true ONCE IT IS DURABLE. Computed before the\n // commit because the checkpoint is written inside it and has to carry\n // the running totals — a checkpoint reporting only this attempt's share\n // would reset the count on every death — but assigned after, so the\n // invariant below still holds.\n const nextFound = unitFound + chunkEntities.length;\n const nextEmitted = unitPersisted + fresh.length;\n // Awaited: a failed commit fails the unit before it can checkpoint;\n // the cursor rides with it so the checkpoint trails the log by\n // construction rather than by the caller remembering to order them.\n await onChunkComplete?.(fresh, {\n unit: entityTypeName,\n cursor: { ...cursor, found: nextFound, emitted: nextEmitted },\n });\n // Tallies move only PAST the awaited commit — a chunk that fails to\n // commit contributes nothing anywhere — and the numerator advances at\n // the same grain as the denominator: per chunk, in the same frame\n // family the viewer's found-of-~expected tally reads.\n unitFound = nextFound;\n unitPersisted = nextEmitted;\n totalFound += chunkEntities.length;\n totalEmitted += fresh.length;\n emitTypeProgress(entityTypeName);\n },\n );\n\n // Every chunk of this unit is durable; only now may it checkpoint.\n await onUnitComplete(entityTypeName);\n // Found vs persisted, per unit — the gap between them is this flow's yield.\n completedItems.push({\n value: entityTypeName,\n foundCount: unitFound,\n persistedCount: unitPersisted,\n ...(underReported ? { underReported } : {}),\n });\n if (underReported) totalUnderReportedPieces += underReported.pieces;\n completed++;\n emitTypeProgress(entityTypeName);\n });\n\n // The terminal frame carries the completed set, and it is the only frame\n // that can: the per-unit entries ride the progress emitted at the START of\n // each unit, so the LAST unit's entry — and on a single-type job, every\n // entry — was never reported anywhere (DETECTION-QUALITY-THROUGHPUT P1).\n onProgress(100, { code: 'complete-created', count: totalEmitted, kind: 'reference' }, {\n ...(totalExpected > 0 ? { entitiesExpected: totalExpected } : {}),\n completedItems: [...completedItems],\n requestParams,\n });\n\n return {\n result: {\n kind: 'reference-annotation', totalFound, totalEmitted, errors,\n ...(totalUnderReportedPieces > 0 ? { underReportedPieces: totalUnderReportedPieces } : {}),\n },\n };\n}\n\nexport async function processTagJob(\n content: string,\n inferenceClient: InferenceClient,\n params: TagDetectionParams,\n buildAnnotation: BuildAnnotation,\n onProgress: OnProgress,\n /** This chunk's novel annotations, awaited: the durability write. */\n onChunkComplete: (annotations: Annotation[], checkpoint: UnitCheckpoint) => Promise<void>,\n /** Where earlier attempts left each CATEGORY (CHUNK-GRAIN-RESUME P3) — a tag\n * job's units are its categories, not its motivation: each walks the whole\n * document, so one shared cursor would skip text for all but one of them. */\n resumeCursors?: Record<string, UnitCursor>,\n): Promise<ProcessorResult<JobTagAnnotationResult>> {\n onProgress(10, { code: 'loading' });\n onProgress(30, { code: 'analyzing-tags' });\n\n const bodyLanguage = params.language ?? 'en';\n // One deduper across every category: they share an emission stream, and\n // the key includes the body, so only true repeats collapse.\n const dedupe = makeSpanDeduper();\n // Resumed tallies (CHUNK-GRAIN-RESUME HD3), and the two are seeded\n // DIFFERENTLY because they accumulate differently. `found` sums each\n // category's own running total at the end of that category, and those totals\n // are already seeded — seeding here too would count the earlier attempt\n // twice. `created` accumulates per chunk from this attempt only, so it must\n // start at what earlier attempts committed.\n let found = 0;\n let created = Object.values(resumeCursors ?? {}).reduce((n, c) => n + c.emitted, 0);\n // byCategory counts the DEDUPED set so the per-category counts match what\n // is actually stored. The category is the first (tagging) TextualBody.\n const byCategory: Record<string, number> = {};\n const completedItems: CompletedItem[] = [];\n\n for (let c = 0; c < params.categories.length; c++) {\n const category = params.categories[c]!;\n // The loop always existed; it just never reported itself, so the tag flow\n // was the one counting flow with no subject line (CLEAN-PROGRESS A5).\n const position = () => ({\n current: { kind: 'category' as const, value: category },\n processed: c,\n total: params.categories.length,\n completedItems: [...completedItems],\n });\n onProgress(\n 30 + Math.round((c / params.categories.length) * 30),\n { code: 'analyzing-tags' },\n position(),\n );\n // This category's own running tallies, seeded from its cursor so the\n // checkpoint it writes stays continuable for a third attempt.\n const priorCategory = resumeCursors?.[category];\n let categoryFound = priorCategory?.found ?? 0;\n let categoryCreated = priorCategory?.emitted ?? 0;\n await AnnotationDetection.detectTags(\n content, inferenceClient, params.schema, category, params.sourceLanguage,\n // Liveness (chunk boundaries + in-flight heartbeat): this category's\n // slice of the 30–60 band.\n (consumedChars, totalChars) => onProgress(\n 30 + Math.round(((c + consumedChars / totalChars) / params.categories.length) * 30),\n { code: 'analyzing-tags' },\n position(),\n ),\n resumeCursors?.[category],\n async (matches, cursor) => {\n categoryFound += matches.length;\n const fresh = dedupe(matches.map((t) => {\n const cat = t.category ?? 'unknown';\n // Two-body shape matches the pre-#651 TagAnnotationWorker and every\n // persisted tag annotation: the category as a tagging TextualBody,\n // plus the tagging-schema id as a classifying TextualBody. The\n // classifying body is the only trace of schema provenance in the\n // event log — do not drop it.\n return buildAnnotation('tagging', t, [\n { type: 'TextualBody', value: cat, purpose: 'tagging', format: 'text/plain' satisfies SupportedMediaType, language: bodyLanguage },\n { type: 'TextualBody', value: params.schema.id, purpose: 'classifying', format: 'text/plain' satisfies SupportedMediaType },\n ]);\n }));\n created += fresh.length;\n for (const ann of fresh) {\n const body = (ann as { body?: Array<{ value?: unknown }> }).body;\n const cat = Array.isArray(body) && typeof body[0]?.value === 'string' ? body[0].value : 'unknown';\n byCategory[cat] = (byCategory[cat] ?? 0) + 1;\n }\n onProgress(60, { code: 'creating-tag-annotations', count: created });\n // The unit is the CATEGORY, not the motivation. `tag-annotation` loops\n // over `params.categories`, each walking the whole document from zero,\n // so a single 'tagging' key would have every category overwriting one\n // cursor — and the monotone merge would keep the furthest, which is the\n // right answer for at most one of them and silently skips text for the\n // rest. Same shape as reference's entity types, for the same reason.\n categoryCreated += fresh.length;\n await onChunkComplete(fresh, {\n unit: category,\n cursor: { ...cursor, found: categoryFound, emitted: categoryCreated },\n });\n },\n );\n found += categoryFound;\n completedItems.push({ value: category, foundCount: categoryFound });\n }\n\n onProgress(100, { code: 'complete-created', count: created, kind: 'tag' });\n\n return {\n result: { kind: 'tag-annotation', tagsFound: found, tagsCreated: created, byCategory },\n };\n}\n\n/**\n * Output bound (PDF-GENERATION P5), symmetric with #1124's extraction budget\n * and deliberately the SAME threshold: an artifact larger than what extraction\n * accepts would be a resource our own Smelter declines as 'too-large'. One\n * judgment, two enforcement points. A runaway generation fails loudly\n * (job:fail); it never uploads.\n */\nexport function assertWithinOutputBudget(byteLength: number): void {\n if (!withinByteBudget(byteLength)) {\n throw new Error(\n `Generated artifact exceeds the output byte budget: ${byteLength} bytes > ${MAX_PDF_BYTES}. Refusing a runaway generation.`,\n );\n }\n}\n\nexport async function processGenerationJob(\n inferenceClient: InferenceClient,\n params: GenerationJobParams,\n onProgress: OnProgress,\n logger: Logger,\n): Promise<{ content: Uint8Array; title: string; format: SupportedMediaType; citations: GenerationCitation[]; result: GenerationResult }> {\n // Refuse any requested media type the registry doesn't mark `generatable` —\n // loudly (the throw propagates as job:fail), never a silent markdown fallback\n // under a mislabeled format. The gate reads the registry capability\n // (PDF-GENERATION P1), not a local table. Validate before the LLM call.\n const outputMediaType: SupportedMediaType = params.outputMediaType ?? 'text/markdown';\n if (!GENERATABLE_MEDIA_TYPES.includes(outputMediaType)) {\n throw new Error(\n `Unsupported outputMediaType for generation: ${outputMediaType}. Generation produces ${GENERATABLE_MEDIA_TYPES.join(' or ')}.`,\n );\n }\n\n const title = params.title ?? 'Untitled';\n const entityTypes = (params.entityTypes ?? []).map(String);\n\n // PDF path (PDF-GENERATION P3): the model authors Typst; the worker's pinned\n // binary compiles it, with the legible compile errors fed back for a bounded\n // number of repairs. Citations on PDFs need page geometry, not text offsets —\n // until the citation branch (P4) provides it, `cite` fails fast and loudly\n // rather than minting selectors that would render nothing.\n if (outputMediaType === 'application/pdf') {\n onProgress(5, { code: 'generating-resource' });\n\n // Under `cite`, [[<id>]] tokens are stripped from the SOURCE before every\n // compile — they must never render into the artifact. The citations carry\n // the claim text; the worker re-anchors it by page geometry after\n // extraction (P4). Offsets in these citations index the Typst source and\n // are NOT used for PDF anchoring.\n const validIds = params.cite === true ? collectContextResourceIds(params.context) : null;\n\n let generated = await generateResourceFromTopic(\n title, entityTypes, inferenceClient, logger,\n params.prompt, params.language, params.context, params.temperature,\n params.maxTokens, params.sourceLanguage, outputMediaType,\n params.task, params.structure, params.cite,\n );\n let source = generated.content;\n let citations: GenerationCitation[] = [];\n if (validIds) {\n const resolved = resolveCitationTokens(generated.content, validIds, logger);\n source = resolved.content;\n citations = resolved.citations;\n }\n let compiled = compileTypst(source);\n let repairs = 0;\n while ('error' in compiled) {\n // A truncated source that fails to compile is cut off, not wrong —\n // every repair regenerates under the same ceiling and cannot restore\n // content that was never generated. Fail immediately with the actual\n // cause instead of burning the repair budget and blaming the compiler\n // (GENERATE-FROM-RESOURCE P3a).\n if (generated.truncated) {\n throw new Error(\n `Generation stopped at the maxTokens ceiling (${params.maxTokens ?? DEFAULT_MAX_TOKENS} tokens) and the cut-off Typst source does not compile — repair cannot help; raise maxTokens. Compile error: ${compiled.error}`,\n );\n }\n if (repairs >= MAX_COMPILE_REPAIRS) {\n throw new Error(\n `Typst compilation failed after ${MAX_COMPILE_REPAIRS} repair attempts: ${compiled.error}`,\n );\n }\n repairs++;\n logger.warn('Typst compile failed — feeding the error back for repair', {\n attempt: repairs,\n error: compiled.error.slice(0, 500),\n });\n generated = await generateResourceFromTopic(\n title, entityTypes, inferenceClient, logger,\n params.prompt, params.language, params.context, params.temperature,\n params.maxTokens, params.sourceLanguage, outputMediaType,\n params.task, params.structure, params.cite,\n { source, error: compiled.error },\n );\n if (validIds) {\n const resolved = resolveCitationTokens(generated.content, validIds, logger);\n source = resolved.content;\n citations = resolved.citations;\n } else {\n source = generated.content;\n }\n compiled = compileTypst(source);\n }\n\n assertWithinOutputBudget(compiled.pdf.byteLength);\n onProgress(95, { code: 'creating-resource' });\n // The producer owns terminality (GENERATE-FROM-RESOURCE P1/D1): without\n // this, the client's last frame is forever the 95% payload. Generic by\n // design — the outcome (name + link) travels on job:complete (D8).\n // `truncated` rides both surfaces (P3a/D6): truncation that lands at a\n // syntactic boundary still compiles, and the artifact is still cut off.\n onProgress(100, { code: 'complete-generated', truncated: generated.truncated });\n\n return {\n content: compiled.pdf,\n title,\n format: outputMediaType,\n citations,\n result: {\n kind: 'generation',\n resourceId: '' as ResourceId,\n resourceName: title,\n truncated: generated.truncated,\n },\n };\n }\n\n // Generation has exactly two observable transitions: the LLM call starting\n // ('generating') and content finalized / creation beginning ('creating').\n // There is no fetch — context arrives pre-gathered in params. Percentages\n // approximate the share of expected wall-clock complete at each transition\n // (a single atomic LLM call has no measurable progress, and inference\n // dominates the job): its start is ~5, its end ~95.\n onProgress(5, { code: 'generating-resource' });\n\n const generated = await generateResourceFromTopic(\n title,\n entityTypes,\n inferenceClient,\n logger,\n params.prompt,\n params.language,\n params.context,\n params.temperature,\n params.maxTokens,\n params.sourceLanguage,\n outputMediaType,\n params.task,\n params.structure,\n params.cite,\n );\n\n // Under `cite`, the model emitted [[<id>]] transport tokens — resolve them:\n // validate against the ids the context actually contained, strip from the\n // stored content, and carry claim-span citations for the worker to mint.\n // When cite is off, bracketed text is legitimate content — leave it alone.\n let content = generated.content;\n let citations: GenerationCitation[] = [];\n if (params.cite === true) {\n const resolved = resolveCitationTokens(content, collectContextResourceIds(params.context), logger);\n content = resolved.content;\n citations = resolved.citations;\n }\n\n onProgress(95, { code: 'creating-resource' });\n\n // The artifact is bytes; text is an encoding of them. One shape for every\n // output media type, so a string can never travel mislabeled as a binary\n // format (PDF-GENERATION P1). Citation offsets index the decoded text.\n const artifact = new TextEncoder().encode(content);\n assertWithinOutputBudget(artifact.byteLength);\n\n // The producer owns terminality (GENERATE-FROM-RESOURCE P1/D1): without\n // this, the client's last frame is forever the 95% payload. Generic by\n // design — the outcome (name + link) travels on job:complete (D8).\n // `truncated` rides both surfaces (P3a/D6): the event is the frame the\n // client renders, the result is the record.\n onProgress(100, { code: 'complete-generated', truncated: generated.truncated });\n\n return {\n content: artifact,\n title,\n format: outputMediaType,\n citations,\n result: {\n kind: 'generation',\n resourceId: '' as ResourceId,\n resourceName: title,\n truncated: generated.truncated,\n },\n };\n}\n","/**\n * Worker Runtime — the importable half of the worker host.\n *\n * `worker-main.ts` is a process entrypoint (config at module scope,\n * `main()` at import) and therefore untestable by construction; everything\n * a unit test needs to reach lives here instead, fully parameterized — no\n * module-scope env reads, no side effects at import.\n *\n * The load-bearing contract this module owns (and the reason it was\n * extracted): a worker's stamped identity is the DID the\n * `/api/tokens/agent` exchange MINTED for it, carried verbatim — never\n * re-derived from the URL the worker happens to dial. One logical agent\n * previously got two DIDs that way (.plans/bugs/agent-did-host-skew.md).\n */\n\nimport type { EventMap } from '@semiont/core';\nimport { startWorkerProcess } from './worker-process';\nimport type { MarkCommitAwaits, DescriptorReadAwaits, DurabilityProbeAwaits } from './worker-process';\nimport type { WorkerVitals, JobClaimAwaits } from './job-claim-adapter';\nimport type { ConsultAnchoredTextAwaits } from './workers/detection/prepare-detection';\nimport type { InferenceClient } from '@semiont/inference';\nimport { hostname } from 'os';\nimport {\n replyChannelsFor,\n didToAgent,\n baseUrl,\n retryWithBackoff,\n isTransientFetchError,\n STARTUP_FETCH_RETRY,\n type BusOperationKey,\n type RetryPolicy,\n type components,\n type Logger,\n type AccessToken,\n} from '@semiont/core';\nimport {\n InMemorySessionStorage,\n SemiontClient,\n SemiontSession,\n kbGatewayUrl,\n setStoredSession,\n type HttpEndpoint,\n type KbTarget,\n} from '@semiont/sdk';\nimport { HttpContentTransport, HttpTransport } from '@semiont/http-transport';\nimport type { ContentReads } from '@semiont/content';\nimport { BehaviorSubject } from 'rxjs';\n\ntype Agent = components['schemas']['Agent'];\n\n/** Shape of each resolved worker inference entry under `_metadata.workers`. */\nexport type ResolvedInference = {\n type: 'anthropic' | 'ollama';\n model: string;\n apiKey?: string;\n endpoint?: string;\n baseURL?: string;\n};\n\n/** One agent identity: an inference engine and the job types it serves. */\nexport interface AgentGroup {\n inference: ResolvedInference;\n jobTypes: string[];\n client: InferenceClient;\n}\n\nexport interface WorkerRuntimeOptions {\n group: AgentGroup;\n /** The gateway URL this worker dials — connection topology ONLY, never identity. */\n gatewayBaseUrl: string;\n /** Shared secret for `/api/tokens/agent`, and the bearer the byte reads\n * below show the Archivist. */\n workerSecret: string;\n /**\n * The resource's bytes, for detection's extraction seam. Built by the\n * entrypoint (`worker-main`) rather than here, so a worker with no\n * Archivist configured refuses at boot instead of failing every job\n * (SINGLE-KB-MOUNT P4).\n */\n contentReads: ContentReads;\n logger: Logger;\n}\n\n/** Per-agent liveness: the adapter's snapshot plus this agent's identity. */\nexport interface AgentVitals extends WorkerVitals {\n provider: string;\n model: string;\n did: string;\n jobTypes: string[];\n}\n\nexport interface AgentWorkerHandle {\n session: SemiontSession;\n vitals(): AgentVitals;\n dispose(): Promise<void>;\n}\n\nexport interface WorkerHealthPayload {\n status: 'ok';\n agents: number;\n workers: AgentVitals[];\n}\n\n/**\n * The `/health` body (WORKER-LIVENESS.md P1). Additive: existing\n * consumers (image HEALTHCHECK, compose `service_healthy`, `semiont start`)\n * keep reading `status`/`agents`; the per-agent vitals expose\n * claim-loop progress so a stalled worker is *visible*, not just alive.\n */\nexport function buildHealthPayload(workers: ReadonlyArray<{ vitals(): AgentVitals }>): WorkerHealthPayload {\n return {\n status: 'ok',\n agents: workers.length,\n workers: workers.map((w) => w.vitals()),\n };\n}\n\n/**\n * Stall watchdog (WORKER-LIVENESS.md P3) — the fail-fast line behind the\n * inference timeout. There is no poll loop to heartbeat; the honest\n * stall signal in this push-driven architecture is *processing without\n * activity*: an agent holding a claimed job whose `lastActivityAt`\n * (claim / progress / finish) has stopped advancing is wedged — the\n * adapter ignores every announcement while `isProcessing`, so a wedged\n * agent never recovers on its own. Silent hang → loud crash → whatever\n * restart policy the deployment chose.\n *\n * Thresholds are fixed by design (no env knobs) and deliberately\n * layered: inference timeout (10 min, P2) fires first; this watchdog\n * (15 min) catches wedges where the loop still turns but activity has\n * stopped; the gateway's dead-worker janitor (30 min) re-queues the job\n * regardless.\n *\n * The layering matters because this watchdog has a hard limit: it is an\n * IN-PROCESS timer, so it cannot fire while the event loop itself is\n * blocked — the exact condition a blocked loop creates\n * (JOB-RESTART-SAFETY P7, the 2026-09-03 finalization hang: 18 min silent,\n * this watchdog never fired, an empty /health confirming the loop was\n * wedged). The unbounded emit that caused that specific hang is now bounded\n * at the transport (`EMIT_TIMEOUT_MS`), so the loop errors instead of\n * blocking; but for any future blocked-loop bug the ONLY backstop is the\n * out-of-process one — the gateway's janitor sweeping this worker's job\n * files by mtime (`fs-job-queue.ts` `recoverStaleRunningJobs`). A liveness\n * guarantee a blocked loop defeats is not one; the janitor is the guarantee.\n */\nexport const STALL_THRESHOLD_MS = 15 * 60_000;\nexport const STALL_CHECK_INTERVAL_MS = 60_000;\n\nexport interface StallWatchdogOptions {\n workers: ReadonlyArray<{ vitals(): AgentVitals }>;\n logger: Logger;\n /** Test seam; defaults to process.exit. */\n exit?: (code: number) => void;\n}\n\nexport function startStallWatchdog(opts: StallWatchdogOptions): { dispose(): void } {\n const { workers, logger, exit = (code: number) => process.exit(code) } = opts;\n\n const timer = setInterval(() => {\n const now = Date.now();\n for (const worker of workers) {\n const v = worker.vitals();\n if (!v.activeJob || !v.lastActivityAt) continue;\n\n const silentForMs = now - Date.parse(v.lastActivityAt);\n if (silentForMs <= STALL_THRESHOLD_MS) continue;\n\n logger.error('Worker stalled — exiting for restart', {\n provider: v.provider,\n model: v.model,\n did: v.did,\n jobId: v.activeJob.jobId,\n jobType: v.activeJob.type,\n processingSince: v.activeJob.since,\n lastActivityAt: v.lastActivityAt,\n silentForMs,\n thresholdMs: STALL_THRESHOLD_MS,\n });\n clearInterval(timer);\n exit(1);\n return;\n }\n }, STALL_CHECK_INTERVAL_MS);\n timer.unref?.();\n\n return { dispose: () => clearInterval(timer) };\n}\n\n/**\n * The bus operations a worker process ever AWAITS a reply to. Reply channels\n * are global fan-out on the gateway, so a full `BRIDGED_CHANNELS`\n * subscription made every worker receive every other client's reply traffic\n * — measured at ~85 multi-MB `browse:annotations-result` frames/min during\n * the 2026-09-03 worker OOM, all parsed and dropped by cid filtering. The\n * worker subscribes exactly its own operations' reply channels instead.\n *\n * This list restates a fact the code owns (which operations worker code\n * paths call `busRequest` on); its gate is the build-time census below\n * (`workerAwaitCensus`): every awaiting site declares its operation next to\n * the call, and an operation awaited but missing here fails COMPILATION with\n * the operation named. `busRequest`'s `isSubscribed` probe remains the\n * runtime backstop for an await nobody declared — a loud `bus.unsubscribed`\n * at first use, never a silent 30 s timeout.\n * (`job:queued` is not here: it is a broadcast, not an awaited reply. The\n * broadcasts a worker consumes are declared in `WORKER_CONSUMED_BROADCASTS`\n * below, and `WORKER_CHANNELS` is the union.)\n */\nexport const WORKER_AWAITED_OPERATIONS = [\n 'job:claim',\n 'browse:resource-requested',\n // Canonical geometry for a geometry-bearing detection: the consult behind\n // `ConsultAnchoredText` (SMELTER-OWNS-OCR P2). Its omission broke every\n // PDF detection job at the transport probe\n // (.plans/WORKER-ANCHORED-TEXT-CHANNEL.md); the census below now fails the\n // BUILD when this list and the declared awaits drift.\n 'browse:anchored-text-requested',\n // Durability acknowledgement for a unit's annotations (JOB-RESTART-SAFETY\n // P6). The worker AWAITS this one — a unit may not advance until its\n // annotations are in the event log — so its replies must be in the narrow\n // channel set or every commit fails fast with `bus.unsubscribed`.\n 'mark:commit',\n // The durability probe for a commit whose acknowledgement never routed\n // (COMMIT-ACK-FALSE-FAILURE F1). SINGULAR by design: the annotation LIST\n // channel is the multi-MB fan-out this narrowing exists to keep out, and a\n // rare error path is no reason to let it back in.\n 'browse:annotation-requested',\n] as const satisfies readonly BusOperationKey[];\n\n/**\n * The broadcasts a worker CONSUMES — announcements nobody replies to, which\n * therefore derive from no operation.\n *\n * One home, because the alternative was proven: these lived as `addChannels`\n * calls beside their consumers, so the worker's complete set existed nowhere\n * and a widening could be deleted without any list getting shorter. That is\n * the 2026-09-16 outage — every worker idle on a `job:queued` its transport\n * would never carry, `lastQueuedEventAt: null`, nothing thrown and nothing\n * logged.\n *\n * An entry here must have a consumer and a consumer must have an entry; the\n * census beside this file asserts both directions.\n */\nexport const WORKER_CONSUMED_BROADCASTS = [\n // The queue announcement the claim adapter races for.\n 'job:queued',\n // Cooperative cancellation of the ACTIVE job (JOB-RESTART-SAFETY P4).\n 'job:cancel-requested',\n] as const satisfies readonly (keyof EventMap)[];\n\n/**\n * The global SSE channel set for a worker's transport: the whole manifest,\n * stated once and passed at construction. Nothing widens it afterwards.\n */\n// `replyChannelsFor` already returns `EventName[]`; annotating this\n// `readonly string[]` threw that proof away and was the only reason a\n// transport's channel roster was ever wider than the registry.\nexport const WORKER_CHANNELS: readonly (keyof EventMap)[] = [\n ...replyChannelsFor(WORKER_AWAITED_OPERATIONS),\n ...WORKER_CONSUMED_BROADCASTS,\n];\n\n/**\n * The build-time census gate (WORKER-ANCHORED-TEXT-CHANNEL F2).\n *\n * `WORKER_AWAITED_OPERATIONS` restates a fact the code owns — which\n * operations worker paths call `busRequest` on — and one of those calls\n * hides behind an injected seam (`ConsultAnchoredText` → the SDK), where no\n * grep and no runtime probe-before-shipping can see it. So every awaiting\n * site DECLARES its operation next to the call (the `*Awaits` aliases), the\n * declarations are assembled here, and this constant compiles ONLY when the\n * list and the declarations agree in BOTH directions. Drift fails the build\n * with the drifted operation named in the type error — at build time, not at\n * the first live job. `busRequest`'s `bus.unsubscribed` probe remains the\n * runtime backstop for an await nobody declared.\n */\ntype DeclaredWorkerAwaits =\n | JobClaimAwaits // job-claim-adapter.ts — claiming an announced job\n | DescriptorReadAwaits // worker-process.ts — the resource descriptor read\n | MarkCommitAwaits // worker-process.ts — the durability ack (JOB-RESTART-SAFETY P6)\n | DurabilityProbeAwaits // worker-process.ts — did the batch land? (COMMIT-ACK-FALSE-FAILURE F1)\n | ConsultAnchoredTextAwaits; // prepare-detection.ts — canonical geometry (SMELTER-OWNS-OCR P2)\n\ntype WorkerAwaitCensusDrift =\n | Exclude<DeclaredWorkerAwaits, (typeof WORKER_AWAITED_OPERATIONS)[number]>\n | Exclude<(typeof WORKER_AWAITED_OPERATIONS)[number], DeclaredWorkerAwaits>;\n\nexport const workerAwaitCensus: [WorkerAwaitCensusDrift] extends [never]\n ? 'in-census'\n : WorkerAwaitCensusDrift = 'in-census';\n\nexport function parseGatewayUrl(url: string): { protocol: 'http' | 'https'; host: string; port: number } {\n const parsed = new URL(url);\n const protocol = (parsed.protocol.replace(':', '') === 'https' ? 'https' : 'http') as 'http' | 'https';\n const host = parsed.hostname;\n const port = parsed.port\n ? Number(parsed.port)\n : protocol === 'https' ? 443 : 80;\n return { protocol, host, port };\n}\n\n/**\n * Exchange the worker secret for this agent's JWT and its canonical DID.\n * The DID is minted by the gateway (from its `site.domain`) — the caller\n * carries it verbatim.\n *\n * Connection-level failures (`TypeError: fetch failed`) are retried with\n * exponential backoff: the gateway may be mid-restart or the container\n * network still warming up when this process starts, and orchestration\n * runs workers with `--rm` and no restart policy — exiting on the first\n * failed fetch is permanent death. HTTP-level rejections (401 on a bad\n * secret) are NOT retried; the gateway is up and said no.\n */\nexport async function authenticateAgent(opts: {\n gatewayBaseUrl: string;\n workerSecret: string;\n provider: string;\n model: string;\n logger?: Logger;\n retry?: RetryPolicy;\n}): Promise<{ token: string; did: string }> {\n const { gatewayBaseUrl, workerSecret, provider, model, logger, retry = STARTUP_FETCH_RETRY } = opts;\n if (!workerSecret) {\n throw new Error('SEMIONT_WORKER_SECRET is required to authenticate worker agents');\n }\n\n return retryWithBackoff(\n async () => {\n const response = await fetch(`${gatewayBaseUrl}/api/tokens/agent`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ secret: workerSecret, provider, model }),\n });\n\n if (!response.ok) {\n throw new Error(`Agent authentication failed for ${provider}:${model}: ${response.status} ${response.statusText}`);\n }\n\n return await response.json() as { token: string; did: string };\n },\n isTransientFetchError,\n retry,\n ({ attempt, attempts, delayMs, error }) => {\n logger?.warn('Gateway unreachable, retrying agent authentication', {\n agent: `${provider}:${model}`,\n attempt,\n attempts,\n retryInMs: delayMs,\n error: error instanceof Error ? error.message : String(error),\n });\n },\n );\n}\n\nexport async function startAgentWorker(\n opts: WorkerRuntimeOptions,\n): Promise<AgentWorkerHandle> {\n const { group, gatewayBaseUrl, workerSecret, contentReads, logger } = opts;\n const { inference } = group;\n\n const { protocol, host, port } = parseGatewayUrl(gatewayBaseUrl);\n const { token: initialToken, did } = await authenticateAgent({\n gatewayBaseUrl,\n workerSecret,\n provider: inference.type,\n model: inference.model,\n logger,\n });\n\n // The exchange minted this worker's canonical DID (from the gateway's\n // site.domain) and we carry it VERBATIM — never re-derive identity from\n // the URL we happen to dial (`host` is connection topology only). One\n // logical agent previously got two DIDs this way:\n // .plans/bugs/agent-did-host-skew.md.\n const generator: Agent = didToAgent(did);\n\n const kbId = `agent-${inference.type}-${inference.model}-${hostname()}`;\n const endpoint: HttpEndpoint = { kind: 'http', host, port, protocol };\n const kb: KbTarget = {\n id: kbId,\n label: `${inference.type} / ${inference.model} @ ${host}`,\n email: `agent@${host}`,\n endpoint,\n };\n const storage = new InMemorySessionStorage();\n setStoredSession(storage, kbId, { access: initialToken, refresh: '' });\n\n const token$ = new BehaviorSubject<AccessToken | null>(null);\n let session!: SemiontSession;\n const transport = new HttpTransport({\n baseUrl: baseUrl(kbGatewayUrl(endpoint)),\n token$,\n tokenRefresher: () => session.refresh().then((t) => t ?? null),\n // Only the reply channels this process awaits — not the full bridged\n // set. See WORKER_AWAITED_OPERATIONS.\n channels: WORKER_CHANNELS,\n });\n const content = new HttpContentTransport(transport);\n const client = new SemiontClient(transport, content, transport);\n session = new SemiontSession({\n kb,\n storage,\n client,\n token$,\n refresh: async () => {\n try {\n const { token } = await authenticateAgent({\n gatewayBaseUrl,\n workerSecret,\n provider: inference.type,\n model: inference.model,\n logger,\n });\n return token;\n } catch (err) {\n logger.error('Agent token refresh failed', {\n error: err instanceof Error ? err.message : String(err),\n agent: did,\n });\n return null;\n }\n },\n onError: (err) => {\n logger.error('Session error', { code: err.code, message: err.message, agent: did });\n },\n });\n await session.ready;\n\n const adapter = startWorkerProcess({\n session,\n jobTypes: group.jobTypes,\n inferenceClient: group.client,\n generator,\n // Byte reads for decode-path media only. A geometry-bearing type's text\n // never comes from bytes here — it is CONSULTED from the Smelter's\n // canonical anchored text over the bus (SMELTER-OWNS-OCR P2), and this\n // worker cannot derive even by mistake (READ-VS-EXTRACT P2).\n contentReads,\n logger,\n });\n\n logger.info('Agent ready', {\n did,\n provider: inference.type,\n model: inference.model,\n jobTypes: group.jobTypes,\n });\n\n return {\n session,\n vitals: () => ({\n provider: inference.type,\n model: inference.model,\n did,\n jobTypes: group.jobTypes,\n ...adapter.vitals(),\n }),\n dispose: async () => {\n adapter.dispose();\n await session.dispose();\n },\n };\n}\n"]}
|