@tangle-network/agent-knowledge 1.7.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +77 -1
- package/dist/chunk-MU5CEOGE.js +387 -0
- package/dist/chunk-MU5CEOGE.js.map +1 -0
- package/dist/{chunk-HKYD765Q.js → chunk-WWY5FTKQ.js} +11 -14
- package/dist/chunk-WWY5FTKQ.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +264 -2
- package/dist/index.js +273 -9
- package/dist/index.js.map +1 -1
- package/dist/profiles/index.d.ts +1 -1
- package/dist/profiles/index.js +4 -361
- package/dist/profiles/index.js.map +1 -1
- package/docs/recursive-research-leaf.md +86 -0
- package/package.json +4 -4
- package/dist/chunk-HKYD765Q.js.map +0 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/changes.ts","../src/chunking.ts","../src/discovery.ts","../src/eval-readiness.ts","../src/events.ts","../src/freshness.ts","../src/kb-store.ts","../src/propose-from-finding.ts","../src/release.ts","../src/research-loop.ts"],"sourcesContent":["import type { KnowledgeFragment } from './sources/types'\n\n/**\n * Change detection across snapshots of one source's fragments.\n *\n * The output drives the continuous-ingestion loop: each `KnowledgeChange`\n * carries the eval dimensions affected (`affectedDimensions`), which an\n * agent-eval campaign scheduler consumes to decide which campaigns to\n * re-run. Three change kinds:\n *\n * - `added` — fragment id appears in `next` but not `prev`.\n * - `removed` — fragment id appears in `prev` but not `next`. Typical\n * trigger: an authority retires a Wex slug, or a state SOS reorganises\n * its forms catalogue.\n * - `modified` — fragment id appears in both, body hash differs. This\n * is the dominant change kind in practice — the Ryan-LLC v. FTC\n * vacatur case manifests as a `modified` on the Wex non-compete\n * fragment, NOT as a removed one.\n *\n * Unverifiable fragments are filtered out before diffing — comparing a\n * captcha-blocked snapshot against a real one would falsely fire every\n * fragment as removed. The caller can inspect the raw lists if they want\n * to surface block-page failures.\n *\n * Within-snapshot duplicate ids are an upstream bug; this function picks\n * the LAST one and emits a diagnostic via the `warnings` field.\n *\n * @stable\n */\n\nexport type KnowledgeChangeKind = 'added' | 'removed' | 'modified'\n\nexport interface KnowledgeChange {\n /** Source-scoped id (matches `KnowledgeFragment.id`). */\n fragmentId: string\n kind: KnowledgeChangeKind\n /**\n * For `added`: full body of the new fragment.\n * For `removed`: full body of the prior fragment.\n * For `modified`: unified-diff-style payload `{ before, after }` body strings.\n */\n diff?: { before?: string; after?: string }\n /**\n * Eval dimensions to re-score. Computed as the union of both fragments'\n * `dimensionHints`. The eval cron treats this as a set of campaign tags.\n */\n affectedDimensions: string[]\n /** URL of the affected authority page (from whichever side has it). */\n url?: string\n /**\n * Source-attested change time. For `modified`, takes the NEXT fragment's\n * `sourceUpdatedAt`. For `removed`, takes the PRIOR fragment's\n * `sourceUpdatedAt`. For `added`, takes the NEXT fragment's\n * `sourceUpdatedAt`. Consumers index changes by this date.\n */\n detectedAt: string\n}\n\nexport interface DetectChangesResult {\n changes: KnowledgeChange[]\n /** Counts by kind — handy for dashboards. */\n summary: { added: number; removed: number; modified: number }\n /** Non-fatal diagnostics (duplicate ids, dropped unverifiable fragments). */\n warnings: string[]\n}\n\nexport interface DetectChangesOptions {\n /**\n * When true (default), unverifiable fragments are dropped from both\n * sides before comparison. Set false ONLY when debugging block-page\n * issues — comparing against unverifiable content emits false\n * `removed`/`modified` changes.\n */\n skipUnverifiable?: boolean\n /**\n * When provided, only changes whose `affectedDimensions` intersect this\n * set are returned. Useful for cron loops that schedule per-dimension\n * eval campaigns and only care about a subset.\n */\n filterDimensions?: string[]\n}\n\nexport function detectChanges(\n prev: KnowledgeFragment[],\n next: KnowledgeFragment[],\n options: DetectChangesOptions = {},\n): DetectChangesResult {\n const skipUnverifiable = options.skipUnverifiable ?? true\n const warnings: string[] = []\n\n const { map: prevMap, warnings: prevWarn } = indexFragments(prev, skipUnverifiable, 'prev')\n const { map: nextMap, warnings: nextWarn } = indexFragments(next, skipUnverifiable, 'next')\n warnings.push(...prevWarn, ...nextWarn)\n\n const changes: KnowledgeChange[] = []\n const seen = new Set<string>()\n\n for (const [id, nextFragment] of nextMap) {\n seen.add(id)\n const prevFragment = prevMap.get(id)\n if (!prevFragment) {\n changes.push({\n fragmentId: id,\n kind: 'added',\n diff: { after: nextFragment.body },\n affectedDimensions: dedup(nextFragment.dimensionHints),\n url: nextFragment.provenance.url,\n detectedAt: nextFragment.provenance.sourceUpdatedAt,\n })\n continue\n }\n if (prevFragment.bodyHash !== nextFragment.bodyHash) {\n changes.push({\n fragmentId: id,\n kind: 'modified',\n diff: { before: prevFragment.body, after: nextFragment.body },\n affectedDimensions: dedup([...prevFragment.dimensionHints, ...nextFragment.dimensionHints]),\n url: nextFragment.provenance.url,\n detectedAt: nextFragment.provenance.sourceUpdatedAt,\n })\n }\n }\n\n for (const [id, prevFragment] of prevMap) {\n if (seen.has(id)) continue\n changes.push({\n fragmentId: id,\n kind: 'removed',\n diff: { before: prevFragment.body },\n affectedDimensions: dedup(prevFragment.dimensionHints),\n url: prevFragment.provenance.url,\n detectedAt: prevFragment.provenance.sourceUpdatedAt,\n })\n }\n\n const filtered = options.filterDimensions\n ? changes.filter((c) => c.affectedDimensions.some((d) => options.filterDimensions?.includes(d)))\n : changes\n\n return {\n changes: filtered,\n summary: {\n added: filtered.filter((c) => c.kind === 'added').length,\n removed: filtered.filter((c) => c.kind === 'removed').length,\n modified: filtered.filter((c) => c.kind === 'modified').length,\n },\n warnings,\n }\n}\n\nfunction indexFragments(\n fragments: KnowledgeFragment[],\n skipUnverifiable: boolean,\n side: string,\n): { map: Map<string, KnowledgeFragment>; warnings: string[] } {\n const map = new Map<string, KnowledgeFragment>()\n const warnings: string[] = []\n let dropped = 0\n for (const fragment of fragments) {\n if (skipUnverifiable && !fragment.provenance.verifiable) {\n dropped += 1\n continue\n }\n if (map.has(fragment.id)) {\n warnings.push(`${side}: duplicate fragment id ${fragment.id} — keeping last`)\n }\n map.set(fragment.id, fragment)\n }\n if (dropped > 0) {\n warnings.push(`${side}: dropped ${dropped} unverifiable fragment(s) before diff`)\n }\n return { map, warnings }\n}\n\nfunction dedup<T>(items: T[]): T[] {\n return [...new Set(items)]\n}\n","export interface ChunkingOptions {\n targetChars: number\n maxChars: number\n minChars: number\n overlapChars: number\n}\n\nexport interface KnowledgeChunk {\n index: number\n text: string\n headingPath: string\n charStart: number\n charEnd: number\n oversized: boolean\n}\n\nconst DEFAULT_OPTIONS: ChunkingOptions = {\n targetChars: 1000,\n maxChars: 1500,\n minChars: 160,\n overlapChars: 180,\n}\n\nexport function chunkMarkdown(\n content: string,\n options?: Partial<ChunkingOptions>,\n): KnowledgeChunk[] {\n const opts = normalizeOptions({ ...DEFAULT_OPTIONS, ...(options ?? {}) })\n const { body, bodyOffset } = stripFrontmatter(content)\n if (body.trim() === '') return []\n\n const sections = splitSections(body, bodyOffset)\n const chunks: KnowledgeChunk[] = []\n for (const section of sections) {\n for (const part of chunkText(section.text, opts)) {\n const start = section.start + part.start\n chunks.push({\n index: chunks.length,\n text: part.text,\n headingPath: section.headingPath,\n charStart: start,\n charEnd: start + part.text.length,\n oversized: part.text.length > opts.maxChars,\n })\n }\n }\n return chunks\n}\n\nexport function stripFrontmatter(content: string): { body: string; bodyOffset: number } {\n const normalized = content.replace(/\\r\\n/g, '\\n')\n if (!normalized.startsWith('---\\n')) return { body: normalized, bodyOffset: 0 }\n const match = /^---\\n[\\s\\S]*?\\n---\\s*\\n?/.exec(normalized)\n if (!match) return { body: normalized, bodyOffset: 0 }\n return { body: normalized.slice(match[0].length), bodyOffset: match[0].length }\n}\n\nfunction normalizeOptions(opts: ChunkingOptions): ChunkingOptions {\n if (opts.maxChars < opts.targetChars) opts.maxChars = opts.targetChars\n if (opts.overlapChars >= opts.targetChars) opts.overlapChars = Math.floor(opts.targetChars / 2)\n return opts\n}\n\ninterface Section {\n text: string\n start: number\n headingPath: string\n}\n\nfunction splitSections(body: string, bodyOffset: number): Section[] {\n const lines = body.split('\\n')\n const sections: Section[] = []\n const headings: Record<number, string> = {}\n let current: { lines: string[]; start: number; headingPath: string } = {\n lines: [],\n start: bodyOffset,\n headingPath: '',\n }\n let cursor = bodyOffset\n let fence: string | null = null\n\n const flush = () => {\n const text = current.lines.join('\\n')\n if (text.trim() !== '')\n sections.push({ text, start: current.start, headingPath: current.headingPath })\n }\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]!\n const lineLen = line.length + (i < lines.length - 1 ? 1 : 0)\n const fenceMatch = /^(`{3,}|~{3,})/.exec(line)\n if (fenceMatch) {\n const marker = fenceMatch[1]!\n fence = fence === null ? marker : line.startsWith(fence) ? null : fence\n current.lines.push(line)\n cursor += lineLen\n continue\n }\n const heading = fence === null ? /^(#{1,6})\\s+(.+?)\\s*$/.exec(line) : null\n if (heading) {\n flush()\n const level = heading[1]!.length\n headings[level] = heading[2]!.trim()\n for (let next = level + 1; next <= 6; next++) delete headings[next]\n current = {\n lines: [line],\n start: cursor,\n headingPath: Object.entries(headings)\n .sort(([a], [b]) => Number(a) - Number(b))\n .map(([levelText, title]) => `${'#'.repeat(Number(levelText))} ${title}`)\n .join(' > '),\n }\n cursor += lineLen\n continue\n }\n current.lines.push(line)\n cursor += lineLen\n }\n flush()\n return sections\n}\n\nfunction chunkText(text: string, opts: ChunkingOptions): Array<{ text: string; start: number }> {\n if (text.length <= opts.targetChars) return [{ text, start: 0 }]\n const atoms = splitAtoms(text)\n const chunks: Array<{ text: string; start: number }> = []\n let buffer = ''\n let bufferStart = 0\n for (const atom of atoms) {\n if (buffer === '') bufferStart = atom.start\n if (buffer.length > 0 && buffer.length + atom.text.length > opts.targetChars) {\n chunks.push({ text: buffer.trimEnd(), start: bufferStart })\n const overlap = buffer.slice(Math.max(0, buffer.length - opts.overlapChars))\n buffer = overlap + atom.text\n bufferStart = Math.max(bufferStart, atom.start - overlap.length)\n } else {\n buffer += atom.text\n }\n }\n if (buffer.trim() !== '') chunks.push({ text: buffer.trimEnd(), start: bufferStart })\n return mergeTinyChunks(chunks, opts)\n}\n\nfunction splitAtoms(text: string): Array<{ text: string; start: number }> {\n const parts: Array<{ text: string; start: number }> = []\n const paragraphs = text.split(/(\\n\\s*\\n)/)\n let cursor = 0\n for (let i = 0; i < paragraphs.length; i += 2) {\n const paragraph = (paragraphs[i] ?? '') + (paragraphs[i + 1] ?? '')\n if (paragraph === '') continue\n parts.push({ text: paragraph, start: cursor })\n cursor += paragraph.length\n }\n return parts\n}\n\nfunction mergeTinyChunks(\n chunks: Array<{ text: string; start: number }>,\n opts: ChunkingOptions,\n): Array<{ text: string; start: number }> {\n const out: Array<{ text: string; start: number }> = []\n for (const chunk of chunks) {\n const prev = out[out.length - 1]\n if (\n prev &&\n chunk.text.length < opts.minChars &&\n prev.text.length + chunk.text.length <= opts.maxChars\n ) {\n prev.text = `${prev.text}\\n\\n${chunk.text}`\n } else {\n out.push({ ...chunk })\n }\n }\n return out\n}\n","export interface DiscoveryTask {\n id: string\n goal: string\n query?: string\n sourceHints?: string[]\n metadata?: Record<string, unknown>\n}\n\nexport interface DiscoveryResult {\n taskId: string\n summary: string\n sourceUris?: string[]\n claims?: Array<{ text: string; sourceUri?: string; confidence?: number }>\n followUpTasks?: DiscoveryTask[]\n metadata?: Record<string, unknown>\n}\n\nexport interface KnowledgeDiscoveryWorker {\n run(task: DiscoveryTask, signal?: AbortSignal): Promise<DiscoveryResult> | DiscoveryResult\n}\n\nexport interface KnowledgeDiscoveryDispatcher {\n dispatch(\n tasks: DiscoveryTask[],\n options?: {\n concurrency?: number\n signal?: AbortSignal\n },\n ): Promise<DiscoveryResult[]>\n}\n\nexport function createLocalDiscoveryDispatcher(\n worker: KnowledgeDiscoveryWorker,\n): KnowledgeDiscoveryDispatcher {\n return {\n async dispatch(tasks, options = {}) {\n const concurrency = Math.max(1, options.concurrency ?? 4)\n const results: DiscoveryResult[] = []\n let cursor = 0\n async function runNext(): Promise<void> {\n while (cursor < tasks.length) {\n if (options.signal?.aborted) throw new Error('Discovery dispatch aborted')\n const task = tasks[cursor++]!\n results.push(await worker.run(task, options.signal))\n }\n }\n await Promise.all(Array.from({ length: Math.min(concurrency, tasks.length) }, runNext))\n return results.sort(\n (a, b) =>\n tasks.findIndex((task) => task.id === a.taskId) -\n tasks.findIndex((task) => task.id === b.taskId),\n )\n },\n }\n}\n","import {\n acquisitionPlansForKnowledgeGaps,\n type DataAcquisitionPlan,\n type KnowledgeAcquisitionMode,\n type KnowledgeBundle,\n type KnowledgeFreshness,\n type KnowledgeImportance,\n type KnowledgeReadinessReport,\n type KnowledgeRequirement,\n type KnowledgeRequirementCategory,\n type KnowledgeSensitivity,\n scoreKnowledgeReadiness,\n type UserQuestion,\n userQuestionsForKnowledgeGaps,\n} from '@tangle-network/agent-eval'\nimport { searchKnowledge } from './search'\nimport type { KnowledgeIndex, KnowledgeSearchResult } from './types'\n\nexport interface KnowledgeReadinessSpec {\n id: string\n description: string\n query: string\n requiredFor: string[]\n category: KnowledgeRequirementCategory\n acquisitionMode: KnowledgeAcquisitionMode\n importance: KnowledgeImportance\n freshness: KnowledgeFreshness\n sensitivity: KnowledgeSensitivity\n confidenceNeeded: number\n fallbackPolicy?: KnowledgeRequirement['fallbackPolicy']\n minSources?: number\n minHits?: number\n metadata?: Record<string, unknown>\n}\n\n/**\n * Defaults applied by `defineReadinessSpec` when the caller omits the field.\n *\n * These are deliberately conservative: a domain-specific topic that an agent\n * needs grounded to a high-confidence threshold from at least one authoritative\n * source. Override any field to specialise (e.g. `freshness: 'daily'` for a\n * topic that must reflect today's regulatory state).\n */\nexport const READINESS_SPEC_DEFAULTS = {\n category: 'domain_specific',\n acquisitionMode: 'search_web',\n importance: 'high',\n freshness: 'monthly',\n sensitivity: 'public',\n confidenceNeeded: 0.7,\n minSources: 1,\n minHits: 2,\n} as const satisfies Pick<\n KnowledgeReadinessSpec,\n | 'category'\n | 'acquisitionMode'\n | 'importance'\n | 'freshness'\n | 'sensitivity'\n | 'confidenceNeeded'\n | 'minSources'\n | 'minHits'\n>\n\n/**\n * Inputs accepted by `defineReadinessSpec`. The four fields the caller cannot\n * sanely default (id, description, query, requiredFor) are required; everything\n * else is optional and pulls from `READINESS_SPEC_DEFAULTS`.\n */\nexport type DefineReadinessSpecInput = Pick<\n KnowledgeReadinessSpec,\n 'id' | 'description' | 'query' | 'requiredFor'\n> &\n Partial<Omit<KnowledgeReadinessSpec, 'id' | 'description' | 'query' | 'requiredFor'>>\n\n/**\n * Builder that returns a fully-typed `KnowledgeReadinessSpec` from a slim input.\n *\n * Motivation: `KnowledgeReadinessSpec` has eleven required fields. Most of them\n * (category, acquisition mode, importance, freshness, sensitivity, confidence\n * threshold, source/hit minima) have a clear default for domain-specific KB\n * topics — agents end up copy-pasting the same boilerplate across every spec.\n * This helper collapses that boilerplate while keeping every field overridable.\n *\n * @example\n * defineReadinessSpec({\n * id: 'coop/intent-grounding',\n * description: 'Required grounding for coop pack intent stage',\n * query: 'flock size space ventilation predator',\n * requiredFor: ['IntentIntakeAgent', 'requirements'],\n * })\n *\n * @example // override defaults where the topic demands it\n * defineReadinessSpec({\n * id: 'medical/dosing',\n * description: 'Dosing guidance for compounding pharmacy',\n * query: 'compounding dose schedule',\n * requiredFor: ['DosingAgent'],\n * importance: 'blocking',\n * freshness: 'daily',\n * sensitivity: 'private',\n * confidenceNeeded: 0.95,\n * minSources: 3,\n * })\n */\nexport function defineReadinessSpec(input: DefineReadinessSpecInput): KnowledgeReadinessSpec {\n return {\n ...READINESS_SPEC_DEFAULTS,\n ...input,\n }\n}\n\nexport interface BuildEvalKnowledgeBundleOptions {\n taskId: string\n index: KnowledgeIndex\n specs: KnowledgeReadinessSpec[]\n userAnswers?: Record<string, string>\n searchLimit?: number\n metadata?: Record<string, unknown>\n now?: Date\n}\n\nexport interface EvalKnowledgeBundleBuildResult {\n bundle: KnowledgeBundle\n report: KnowledgeReadinessReport\n requirements: KnowledgeRequirement[]\n searchResultsByRequirement: Record<string, KnowledgeSearchResult[]>\n questions: UserQuestion[]\n acquisitionPlans: DataAcquisitionPlan[]\n}\n\nexport function buildEvalKnowledgeBundle(\n options: BuildEvalKnowledgeBundleOptions,\n): EvalKnowledgeBundleBuildResult {\n const searchLimit = options.searchLimit ?? 5\n const now = options.now ?? new Date()\n const searchResultsByRequirement: Record<string, KnowledgeSearchResult[]> = {}\n const requirements = options.specs.map((spec) => {\n const results = searchKnowledge(options.index, spec.query, searchLimit)\n searchResultsByRequirement[spec.id] = results\n return requirementFromSearch(options.index, spec, results, now)\n })\n const report = scoreKnowledgeReadiness({\n taskId: options.taskId,\n requirements,\n userAnswers: options.userAnswers,\n evidenceIds: requirements.flatMap((requirement) => requirement.evidenceIds),\n claimIds: [],\n wikiPageIds: unique(\n requirements.flatMap((requirement) =>\n pageIdsFromResults(searchResultsByRequirement[requirement.id] ?? []),\n ),\n ),\n metadata: options.metadata,\n })\n const questions = userQuestionsForKnowledgeGaps(report.blockingMissingRequirements)\n const acquisitionPlans = acquisitionPlansForKnowledgeGaps([\n ...report.blockingMissingRequirements,\n ...report.nonBlockingGaps,\n ])\n\n return {\n bundle: report.bundle,\n report,\n requirements,\n searchResultsByRequirement,\n questions,\n acquisitionPlans,\n }\n}\n\nfunction requirementFromSearch(\n index: KnowledgeIndex,\n spec: KnowledgeReadinessSpec,\n results: KnowledgeSearchResult[],\n now: Date,\n): KnowledgeRequirement {\n const hitCount = results.length\n const sourceIds = unique(results.flatMap((result) => result.page.sourceIds))\n const sources = index.sources.filter((source) => sourceIds.includes(source.id))\n const bestScore = results[0]?.normalizedScore ?? 0\n const sourceCoverage = spec.minSources\n ? Math.min(1, sourceIds.length / spec.minSources)\n : sourceIds.length > 0\n ? 1\n : 0\n const hitCoverage = spec.minHits ? Math.min(1, hitCount / spec.minHits) : hitCount > 0 ? 1 : 0\n const freshness = sourceFreshness(sources, now)\n const currentConfidence = round(Math.min(bestScore, sourceCoverage, hitCoverage, freshness.score))\n\n return {\n id: spec.id,\n description: spec.description,\n requiredFor: spec.requiredFor,\n category: spec.category,\n acquisitionMode: spec.acquisitionMode,\n importance: spec.importance,\n freshness: spec.freshness,\n sensitivity: spec.sensitivity,\n confidenceNeeded: spec.confidenceNeeded,\n currentConfidence,\n evidenceIds: unique([\n ...sourceIds.map((sourceId) => `source:${sourceId}`),\n ...results.map((result) => `page:${result.page.id}`),\n ]),\n fallbackPolicy:\n spec.fallbackPolicy ?? (spec.importance === 'blocking' ? 'block' : 'continue_with_caveat'),\n metadata: {\n ...spec.metadata,\n query: spec.query,\n hitCount,\n sourceCount: sourceIds.length,\n bestNormalizedScore: bestScore,\n expiredSourceIds: freshness.expiredSourceIds,\n freshnessScore: freshness.score,\n validUntil: freshness.validUntil,\n lastVerifiedAt: freshness.lastVerifiedAt,\n },\n }\n}\n\nfunction sourceFreshness(\n sources: KnowledgeIndex['sources'],\n now: Date,\n): { score: number; validUntil?: string; lastVerifiedAt?: string; expiredSourceIds: string[] } {\n if (sources.length === 0) return { score: 0, expiredSourceIds: [] }\n const validUntilValues = sources\n .map(\n (source) =>\n source.validUntil ??\n stringMetadata(source.metadata, 'validUntil') ??\n stringMetadata(source.metadata, 'expiresAt'),\n )\n .filter(isIsoDate)\n const lastVerifiedValues = sources\n .map((source) => source.lastVerifiedAt ?? stringMetadata(source.metadata, 'lastVerifiedAt'))\n .filter(isIsoDate)\n const expiredSourceIds = sources\n .filter((source) => {\n const validUntil =\n source.validUntil ??\n stringMetadata(source.metadata, 'validUntil') ??\n stringMetadata(source.metadata, 'expiresAt')\n return validUntil ? Date.parse(validUntil) <= now.getTime() : false\n })\n .map((source) => source.id)\n return {\n score: expiredSourceIds.length > 0 ? 0 : 1,\n validUntil: earliestIso(validUntilValues),\n lastVerifiedAt: latestIso(lastVerifiedValues),\n expiredSourceIds,\n }\n}\n\nfunction stringMetadata(\n metadata: Record<string, unknown> | undefined,\n key: string,\n): string | undefined {\n const value = metadata?.[key]\n return typeof value === 'string' ? value : undefined\n}\n\nfunction isIsoDate(value: string | undefined): value is string {\n return Boolean(value && Number.isFinite(Date.parse(value)))\n}\n\nfunction earliestIso(values: string[]): string | undefined {\n return values.sort((a, b) => Date.parse(a) - Date.parse(b))[0]\n}\n\nfunction latestIso(values: string[]): string | undefined {\n return values.sort((a, b) => Date.parse(b) - Date.parse(a))[0]\n}\n\nfunction pageIdsFromResults(results: KnowledgeSearchResult[]): string[] {\n return results.map((result) => result.page.id)\n}\n\nfunction unique<T>(items: T[]): T[] {\n return [...new Set(items)]\n}\n\nfunction round(value: number): number {\n return Math.round(value * 1000) / 1000\n}\n","import { stableId } from './ids'\nimport type { KnowledgeEvent, KnowledgeEventType } from './types'\n\nexport interface KnowledgeEventQuery {\n type?: KnowledgeEventType\n target?: string\n limit?: number\n}\n\nexport function createKnowledgeEvent(input: {\n type: KnowledgeEventType\n actor?: string\n target?: string\n metadata?: Record<string, unknown>\n now?: () => Date\n}): KnowledgeEvent {\n const createdAt = (input.now ?? (() => new Date()))().toISOString()\n return {\n id: stableId(\n 'evt',\n `${input.type}:${input.target ?? ''}:${createdAt}:${JSON.stringify(input.metadata ?? {})}`,\n ),\n type: input.type,\n createdAt,\n actor: input.actor,\n target: input.target,\n metadata: input.metadata,\n }\n}\n","import { mkdir, readFile, writeFile } from 'node:fs/promises'\nimport { dirname, join } from 'node:path'\n\n/**\n * Knowledge freshness store: tracks when each `(workspaceId, sourceId)` pair\n * was last successfully refreshed, and reports staleness against a TTL.\n *\n * The contract is intentionally minimal — just enough to drive a cron loop:\n *\n * ```ts\n * const store = createFileSystemFreshnessStore({ root: '.agent-knowledge' })\n * for (const source of sources) {\n * if (await store.stale({ workspaceId, sourceId: source.id, ttlMs: DAY })) {\n * const fragments = await source.fetch({ cacheDir })\n * await persistFragments(fragments)\n * await store.mark({ workspaceId, sourceId: source.id, when: new Date() })\n * }\n * }\n * ```\n *\n * Per-tenant isolation is enforced by `workspaceId` keying — there is no\n * global mutable state across workspaces.\n *\n * Two adapters ship in-package:\n *\n * - `createFileSystemFreshnessStore` — JSON file under the knowledge root,\n * mirrors the layout convention already used by `sources.json`.\n * - `createD1FreshnessStoreStub` — adapter scaffold for Cloudflare D1 /\n * Postgres. Production consumers should implement the `D1Adapter`\n * interface inside their own app; this stub exists to anchor the shape.\n *\n * @stable contract — interface is frozen at 0.x within this major.\n * @stable filesystem adapter\n * @experimental D1 stub — interface will evolve as real consumers wire it.\n */\n\n/** Identity for one freshness record. */\nexport interface FreshnessKey {\n workspaceId: string\n sourceId: string\n}\n\n/** TTL bound for staleness checks. */\nexport interface FreshnessTtl extends FreshnessKey {\n /** Milliseconds — `Date.now() - last() > ttlMs` ⇒ stale. */\n ttlMs: number\n /** Injected clock for deterministic tests; defaults to system time. */\n now?: Date\n}\n\n/** Mark argument. */\nexport interface FreshnessMark extends FreshnessKey {\n when: Date\n /** Optional content hash captured at refresh time; aids debugging. */\n contentHash?: string\n}\n\nexport interface KnowledgeFreshnessStore {\n /** Last refresh time, or null if never refreshed. */\n last(key: FreshnessKey): Promise<Date | null>\n /** Record a successful refresh. */\n mark(input: FreshnessMark): Promise<void>\n /** True iff `last(key)` is null or older than `ttlMs`. */\n stale(input: FreshnessTtl): Promise<boolean>\n /** All records for a workspace — useful for dashboards / debugging. */\n list(workspaceId: string): Promise<FreshnessRecord[]>\n}\n\nexport interface FreshnessRecord {\n workspaceId: string\n sourceId: string\n lastRefreshedAt: string\n contentHash?: string\n}\n\nexport interface FileSystemFreshnessStoreOptions {\n /**\n * Knowledge root. The store writes to `<root>/.agent-knowledge/freshness.json`,\n * mirroring the convention used by `sources.json`.\n */\n root: string\n}\n\n/**\n * Filesystem-backed implementation. Single JSON file per knowledge root,\n * indexed by `${workspaceId}::${sourceId}`. Reads parse on every call —\n * cron tick rate is well below the cost of one JSON parse.\n *\n * Concurrent writes from a single process serialize through `writeQueue`.\n * Cross-process concurrency is undefined; the consuming app should run the\n * cron in a single worker.\n */\nexport function createFileSystemFreshnessStore(\n options: FileSystemFreshnessStoreOptions,\n): KnowledgeFreshnessStore {\n const path = join(options.root, '.agent-knowledge', 'freshness.json')\n let writeQueue: Promise<unknown> = Promise.resolve()\n\n const read = async (): Promise<Record<string, FreshnessRecord>> => {\n try {\n const text = await readFile(path, 'utf8')\n const parsed = JSON.parse(text) as { records?: Record<string, FreshnessRecord> }\n return parsed.records ?? {}\n } catch {\n return {}\n }\n }\n\n const write = async (records: Record<string, FreshnessRecord>): Promise<void> => {\n await mkdir(dirname(path), { recursive: true })\n await writeFile(path, `${JSON.stringify({ records }, null, 2)}\\n`, 'utf8')\n }\n\n return {\n async last(key) {\n const records = await read()\n const record = records[buildKey(key)]\n return record ? new Date(record.lastRefreshedAt) : null\n },\n async mark(input) {\n writeQueue = writeQueue.then(async () => {\n const records = await read()\n records[buildKey(input)] = {\n workspaceId: input.workspaceId,\n sourceId: input.sourceId,\n lastRefreshedAt: input.when.toISOString(),\n contentHash: input.contentHash,\n }\n await write(records)\n })\n await writeQueue\n },\n async stale(input) {\n const last = await this.last(input)\n if (!last) return true\n const now = input.now ?? new Date()\n return now.getTime() - last.getTime() > input.ttlMs\n },\n async list(workspaceId) {\n const records = await read()\n return Object.values(records).filter((r) => r.workspaceId === workspaceId)\n },\n }\n}\n\n/**\n * D1 / Postgres adapter scaffold. Production consumers implement\n * `D1Adapter` against their own driver (better-sqlite3, postgres,\n * Cloudflare D1 binding, ...). This factory wires the adapter to the\n * `KnowledgeFreshnessStore` interface.\n *\n * The expected schema:\n *\n * ```sql\n * CREATE TABLE knowledge_freshness (\n * workspace_id TEXT NOT NULL,\n * source_id TEXT NOT NULL,\n * last_refreshed_at TEXT NOT NULL,\n * content_hash TEXT,\n * PRIMARY KEY (workspace_id, source_id)\n * );\n * ```\n */\nexport interface D1Adapter {\n get(workspaceId: string, sourceId: string): Promise<FreshnessRecord | null>\n upsert(record: FreshnessRecord): Promise<void>\n listByWorkspace(workspaceId: string): Promise<FreshnessRecord[]>\n}\n\nexport function createD1FreshnessStoreStub(adapter: D1Adapter): KnowledgeFreshnessStore {\n return {\n async last(key) {\n const record = await adapter.get(key.workspaceId, key.sourceId)\n return record ? new Date(record.lastRefreshedAt) : null\n },\n async mark(input) {\n await adapter.upsert({\n workspaceId: input.workspaceId,\n sourceId: input.sourceId,\n lastRefreshedAt: input.when.toISOString(),\n contentHash: input.contentHash,\n })\n },\n async stale(input) {\n const last = await this.last(input)\n if (!last) return true\n const now = input.now ?? new Date()\n return now.getTime() - last.getTime() > input.ttlMs\n },\n async list(workspaceId) {\n return adapter.listByWorkspace(workspaceId)\n },\n }\n}\n\nfunction buildKey(key: FreshnessKey): string {\n return `${key.workspaceId}::${key.sourceId}`\n}\n","import { mkdir, readFile, writeFile } from 'node:fs/promises'\nimport { dirname, join } from 'node:path'\nimport type { KnowledgeEventQuery } from './events'\nimport { buildKnowledgeGraph } from './graph'\nimport type { KnowledgeEvent, KnowledgeIndex, KnowledgePage, SourceRecord } from './types'\n\nexport interface KbStore {\n putSource(source: SourceRecord): Promise<void>\n getSource(id: string): Promise<SourceRecord | null>\n listSources(): Promise<SourceRecord[]>\n putPage(page: KnowledgePage): Promise<void>\n getPage(idOrPath: string): Promise<KnowledgePage | null>\n listPages(): Promise<KnowledgePage[]>\n putIndex(index: KnowledgeIndex): Promise<void>\n getIndex(): Promise<KnowledgeIndex | null>\n putEvent(event: KnowledgeEvent): Promise<void>\n listEvents(query?: KnowledgeEventQuery): Promise<KnowledgeEvent[]>\n}\n\nexport class MemoryKbStore implements KbStore {\n private readonly sources = new Map<string, SourceRecord>()\n private readonly pages = new Map<string, KnowledgePage>()\n private readonly events: KnowledgeEvent[] = []\n private index: KnowledgeIndex | null = null\n\n async putSource(source: SourceRecord): Promise<void> {\n this.sources.set(source.id, clone(source))\n }\n\n async getSource(id: string): Promise<SourceRecord | null> {\n return clone(this.sources.get(id) ?? null)\n }\n\n async listSources(): Promise<SourceRecord[]> {\n return [...this.sources.values()].map(clone)\n }\n\n async putPage(page: KnowledgePage): Promise<void> {\n this.pages.set(page.id, clone(page))\n }\n\n async getPage(idOrPath: string): Promise<KnowledgePage | null> {\n return clone(\n this.pages.get(idOrPath) ??\n [...this.pages.values()].find((page) => page.path === idOrPath) ??\n null,\n )\n }\n\n async listPages(): Promise<KnowledgePage[]> {\n return [...this.pages.values()].map(clone)\n }\n\n async putIndex(index: KnowledgeIndex): Promise<void> {\n this.index = clone(index)\n }\n\n async getIndex(): Promise<KnowledgeIndex | null> {\n if (this.index) return clone(this.index)\n const pages = await this.listPages()\n const sources = await this.listSources()\n return {\n root: 'memory',\n generatedAt: new Date().toISOString(),\n sources,\n pages,\n graph: buildKnowledgeGraph(pages),\n }\n }\n\n async putEvent(event: KnowledgeEvent): Promise<void> {\n this.events.push(clone(event))\n }\n\n async listEvents(query: KnowledgeEventQuery = {}): Promise<KnowledgeEvent[]> {\n let out = this.events\n if (query.type) out = out.filter((event) => event.type === query.type)\n if (query.target) out = out.filter((event) => event.target === query.target)\n out = [...out].sort((a, b) => a.createdAt.localeCompare(b.createdAt))\n return out.slice(-(query.limit ?? out.length)).map(clone)\n }\n}\n\nexport class FileSystemKbStore extends MemoryKbStore {\n constructor(private readonly dir: string) {\n super()\n }\n\n async putIndex(index: KnowledgeIndex): Promise<void> {\n await super.putIndex(index)\n await writeJson(join(this.dir, 'index.json'), index)\n }\n\n async getIndex(): Promise<KnowledgeIndex | null> {\n try {\n return JSON.parse(await readFile(join(this.dir, 'index.json'), 'utf8')) as KnowledgeIndex\n } catch {\n return await super.getIndex()\n }\n }\n\n async putEvent(event: KnowledgeEvent): Promise<void> {\n await super.putEvent(event)\n const events = await this.listEvents()\n await writeJson(join(this.dir, 'events.json'), events)\n }\n}\n\nasync function writeJson(path: string, value: unknown): Promise<void> {\n await mkdir(dirname(path), { recursive: true })\n await writeFile(path, `${JSON.stringify(value, null, 2)}\\n`, 'utf8')\n}\n\nfunction clone<T>(value: T): T {\n return value == null ? value : (JSON.parse(JSON.stringify(value)) as T)\n}\n","/**\n * Bridge from `AnalystFinding` (agent-eval) to knowledge proposals.\n *\n * Closes the failure → wiki side of the recursive-self-improvement\n * loop: a knowledge-gap or knowledge-poisoning finding produced by an\n * analyst becomes a concrete proposal an operator (or auto-merge bot)\n * can review and apply. The bridge is intentionally lossless on the\n * fail-loud side — a finding the parser can't classify returns a\n * `KnowledgeProposalParseError` rather than a silent skip, so the\n * loop never accepts an underspecified edit.\n *\n * Subject grammar this bridge understands (analyst-side convention,\n * stamped in the kind prompts):\n *\n * agent-knowledge:wiki:<page-slug> create / update page\n * agent-knowledge:wiki:<page-slug>#<heading> insert section under page\n * agent-knowledge:claim:<topic> draft claim row\n * agent-knowledge:raw:<source-id> lift raw → curated\n * agent-knowledge:stale:<page-slug> mark page superseded\n *\n * Anything else (websearch:outdated:*, tool-doc:*, system-prompt:*,\n * memory:*) is NOT a knowledge-base concern and returns `null` so the\n * loop's improvement-applier handles it.\n */\n\nimport type { AnalystFinding, AnalystSeverity } from '@tangle-network/agent-eval'\nimport type { ClaimRef, KnowledgeClaim, KnowledgeWriteBlock } from './types'\n\nexport interface KnowledgeProposal {\n /**\n * Stable id derived from the finding so cross-run diffs share an\n * identity. Re-proposing the same finding produces the same id.\n */\n id: string\n /** The finding that generated this proposal — useful for audit + revert. */\n sourceFindingId: string\n /** What the proposal does. */\n kind:\n | 'create-page'\n | 'update-page'\n | 'append-section'\n | 'create-claim'\n | 'lift-raw'\n | 'mark-stale'\n /** Locus on disk (page slug or claim topic). */\n locus: string\n /**\n * Page write blocks the standard `applyKnowledgeWriteBlocks` consumer\n * accepts. Empty for proposals that don't change page text (e.g.\n * `create-claim` produces a `claim` field instead).\n */\n writeBlocks: KnowledgeWriteBlock[]\n /**\n * Granular claim draft for proposals whose unit-of-change is a claim\n * row rather than a whole page. `status: 'draft'` until reviewed.\n */\n claim?: KnowledgeClaim\n /** Per-proposal metadata: severity, confidence, source span. */\n metadata: {\n severity: AnalystSeverity\n confidence: number\n evidence_uri?: string\n analyst_id: string\n }\n}\n\nexport class KnowledgeProposalParseError extends Error {\n constructor(\n public readonly findingId: string,\n public readonly subject: string,\n message: string,\n ) {\n super(`proposeFromFinding(${findingId}, subject=${subject}): ${message}`)\n this.name = 'KnowledgeProposalParseError'\n }\n}\n\n/**\n * Convert one `AnalystFinding` into a knowledge proposal. Returns\n * `null` when the finding's locus isn't a knowledge-base concern\n * (`websearch:outdated:*`, `tool-doc:*`, `system-prompt:*`,\n * `memory:*`, missing subject). Throws when the locus IS a\n * knowledge-base concern but is malformed — that's a bug in the\n * analyst prompt and should fail loud.\n *\n * Caller convention: feed the function the analyst's full findings\n * list and filter out the `null`s; the orchestrator passes the\n * remaining proposals to the existing review / apply pipeline.\n */\nexport function proposeFromFinding(finding: AnalystFinding): KnowledgeProposal | null {\n if (!finding.subject) return null\n if (!finding.subject.startsWith('agent-knowledge:')) return null\n\n const rest = finding.subject.slice('agent-knowledge:'.length)\n const [kindPart, ...locusParts] = rest.split(':')\n const locus = locusParts.join(':')\n if (!kindPart || !locus) {\n throw new KnowledgeProposalParseError(\n finding.finding_id,\n finding.subject,\n 'expected `agent-knowledge:<kind>:<locus>` shape',\n )\n }\n\n const baseMeta: KnowledgeProposal['metadata'] = {\n severity: finding.severity,\n confidence: finding.confidence,\n evidence_uri: finding.evidence_refs[0]?.uri,\n analyst_id: finding.analyst_id,\n }\n\n switch (kindPart) {\n case 'wiki':\n return wikiProposal(finding, locus, baseMeta)\n case 'claim':\n return claimProposal(finding, locus, baseMeta)\n case 'raw':\n return liftRawProposal(finding, locus, baseMeta)\n case 'stale':\n return markStaleProposal(finding, locus, baseMeta)\n default:\n throw new KnowledgeProposalParseError(\n finding.finding_id,\n finding.subject,\n `unknown kind \"${kindPart}\" (expected one of: wiki | claim | raw | stale)`,\n )\n }\n}\n\nfunction wikiProposal(\n finding: AnalystFinding,\n locus: string,\n metadata: KnowledgeProposal['metadata'],\n): KnowledgeProposal {\n const hashIdx = locus.indexOf('#')\n const pageSlug = hashIdx >= 0 ? locus.slice(0, hashIdx) : locus\n const heading = hashIdx >= 0 ? locus.slice(hashIdx + 1) : null\n const path = `knowledge/${ensureSlug(pageSlug)}.md`\n\n const body = renderWikiBody(finding, pageSlug, heading)\n return {\n id: `prop-${finding.finding_id}`,\n sourceFindingId: finding.finding_id,\n kind: heading ? 'append-section' : 'create-page',\n locus: pageSlug,\n writeBlocks: [{ path, content: body }],\n metadata,\n }\n}\n\nfunction claimProposal(\n finding: AnalystFinding,\n locus: string,\n metadata: KnowledgeProposal['metadata'],\n): KnowledgeProposal {\n const refs: ClaimRef[] = finding.evidence_refs\n .filter((r) => r.uri)\n .map((r) => ({\n sourceId: `analyst-finding:${finding.finding_id}`,\n anchorId: r.uri,\n quote: r.excerpt,\n }))\n const claim: KnowledgeClaim = {\n id: `claim-${finding.finding_id}`,\n text: finding.recommended_action ?? finding.claim,\n refs,\n confidence: finding.confidence,\n status: 'draft',\n metadata: {\n analyst_id: finding.analyst_id,\n source_finding_id: finding.finding_id,\n topic: locus,\n },\n }\n return {\n id: `prop-${finding.finding_id}`,\n sourceFindingId: finding.finding_id,\n kind: 'create-claim',\n locus,\n writeBlocks: [],\n claim,\n metadata,\n }\n}\n\nfunction liftRawProposal(\n finding: AnalystFinding,\n sourceId: string,\n metadata: KnowledgeProposal['metadata'],\n): KnowledgeProposal {\n const path = `knowledge/${ensureSlug(sourceId)}.md`\n const body = [\n '---',\n `title: ${sourceId}`,\n `source: ${sourceId}`,\n `status: draft`,\n `lifted_from_finding: ${finding.finding_id}`,\n '---',\n '',\n '## Why this page exists',\n '',\n finding.claim,\n '',\n ...(finding.rationale ? ['## Rationale', '', finding.rationale, ''] : []),\n ...(finding.recommended_action\n ? ['## Recommended action', '', finding.recommended_action, '']\n : []),\n ].join('\\n')\n return {\n id: `prop-${finding.finding_id}`,\n sourceFindingId: finding.finding_id,\n kind: 'lift-raw',\n locus: sourceId,\n writeBlocks: [{ path, content: body }],\n metadata,\n }\n}\n\nfunction markStaleProposal(\n finding: AnalystFinding,\n pageSlug: string,\n metadata: KnowledgeProposal['metadata'],\n): KnowledgeProposal {\n const path = `knowledge/${ensureSlug(pageSlug)}.stale.md`\n const body = [\n '---',\n `title: ${pageSlug} (marked stale)`,\n `status: superseded`,\n `superseded_by_finding: ${finding.finding_id}`,\n `confidence: ${finding.confidence}`,\n '---',\n '',\n '## Why marked stale',\n '',\n finding.claim,\n '',\n ...(finding.rationale ? ['## Evidence', '', finding.rationale, ''] : []),\n ...(finding.recommended_action ? ['## Action', '', finding.recommended_action, ''] : []),\n ].join('\\n')\n return {\n id: `prop-${finding.finding_id}`,\n sourceFindingId: finding.finding_id,\n kind: 'mark-stale',\n locus: pageSlug,\n writeBlocks: [{ path, content: body }],\n metadata,\n }\n}\n\n/**\n * Plural convenience: filter + map across an entire findings batch\n * with one call. Parse errors collect into `errors[]`; the loop\n * decides per-error whether to abort or continue.\n */\nexport interface ProposeFromFindingsResult {\n proposals: KnowledgeProposal[]\n skipped: number\n errors: KnowledgeProposalParseError[]\n}\n\nexport function proposeFromFindings(\n findings: ReadonlyArray<AnalystFinding>,\n): ProposeFromFindingsResult {\n const proposals: KnowledgeProposal[] = []\n const errors: KnowledgeProposalParseError[] = []\n let skipped = 0\n for (const f of findings) {\n try {\n const p = proposeFromFinding(f)\n if (p) proposals.push(p)\n else skipped += 1\n } catch (err) {\n if (err instanceof KnowledgeProposalParseError) errors.push(err)\n else throw err\n }\n }\n return { proposals, skipped, errors }\n}\n\nfunction ensureSlug(s: string): string {\n return (\n s\n .toLowerCase()\n .replace(/[^a-z0-9-]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 200) || 'untitled'\n )\n}\n\nfunction renderWikiBody(finding: AnalystFinding, slug: string, heading: string | null): string {\n const title = humanize(slug)\n if (heading) {\n return [\n `## ${heading}`,\n '',\n finding.claim,\n '',\n ...(finding.rationale ? ['### Rationale', '', finding.rationale, ''] : []),\n ...(finding.recommended_action ? ['### Action', '', finding.recommended_action, ''] : []),\n `_Drafted from finding ${finding.finding_id} (confidence ${finding.confidence.toFixed(2)})._`,\n ].join('\\n')\n }\n return [\n '---',\n `title: ${title}`,\n `status: draft`,\n `drafted_from_finding: ${finding.finding_id}`,\n `confidence: ${finding.confidence}`,\n '---',\n '',\n `# ${title}`,\n '',\n finding.claim,\n '',\n ...(finding.rationale ? ['## Rationale', '', finding.rationale, ''] : []),\n ...(finding.recommended_action\n ? ['## Recommended action', '', finding.recommended_action, '']\n : []),\n ].join('\\n')\n}\n\nfunction humanize(slug: string): string {\n return (\n slug\n .split('-')\n .filter(Boolean)\n .map((w) => w[0]?.toUpperCase() + w.slice(1))\n .join(' ') || slug\n )\n}\n","import {\n evaluateReleaseConfidence,\n type GateDecision,\n type ReleaseConfidenceScorecard,\n type ReleaseTraceEvidence,\n type RunRecord,\n validateRunRecord,\n} from '@tangle-network/agent-eval'\nimport { stableId } from './ids'\nimport type { KnowledgeRelease } from './types'\n\nexport interface KnowledgeReleaseReport {\n release: KnowledgeRelease\n scorecard: ReleaseConfidenceScorecard\n candidateRuns: RunRecord[]\n baselineRuns: RunRecord[]\n}\n\n/**\n * Campaign-native release report. The caller (a consumer's KB self-improvement\n * loop) supplies the candidate/baseline `RunRecord[]` (e.g. via\n * `campaignToRunRecords`) + optional per-instance `ReleaseTraceEvidence` + the\n * gate decision; this folds them into a `ReleaseConfidenceScorecard` + a\n * `KnowledgeRelease`. Release confidence is computed from run records + traces,\n * independent of any optimizer result shape.\n */\nexport interface KnowledgeReleaseInput {\n candidateId: string\n baselineId?: string\n candidateRuns: RunRecord[]\n baselineRuns?: RunRecord[]\n traces?: ReleaseTraceEvidence[]\n gateDecision?: GateDecision | null\n /** True when a held-out split was evaluated (drives the holdout threshold). */\n hasHoldout?: boolean\n /** Candidate is the search-best variant — a promotion precondition. Default true. */\n promotedIsBest?: boolean\n createdAt?: string\n minScore?: number\n}\n\nexport function knowledgeReleaseReport(input: KnowledgeReleaseInput): KnowledgeReleaseReport {\n const baselineRuns = input.baselineRuns ?? []\n const runRecords = [...input.candidateRuns, ...baselineRuns].map(validateRunRecord)\n const scorecard = evaluateReleaseConfidence({\n target: 'agent-knowledge-base',\n candidateId: input.candidateId,\n baselineId: input.baselineId ?? 'baseline',\n traces: input.traces ?? [],\n runs: runRecords,\n gateDecision: input.gateDecision ?? null,\n thresholds: {\n requireCorpus: false,\n requireHoldout: input.hasHoldout ?? false,\n minHoldoutRuns: input.hasHoldout ? 1 : 0,\n minSearchRuns: 1,\n minMeanScore: input.minScore ?? 0.7,\n },\n })\n const release: KnowledgeRelease = {\n id: stableId('krel', `${input.candidateId}:${input.createdAt ?? new Date().toISOString()}`),\n candidateId: input.candidateId,\n createdAt: input.createdAt ?? new Date().toISOString(),\n promoted: scorecard.status !== 'fail' && (input.promotedIsBest ?? true),\n scorecard,\n runRecordIds: runRecords.map((record) => record.runId),\n }\n return { release, scorecard, candidateRuns: input.candidateRuns, baselineRuns }\n}\n","import {\n blockingKnowledgeEval,\n type ControlEvalResult,\n type ControlRuntimeConfig,\n objectiveEval,\n} from '@tangle-network/agent-eval'\nimport {\n type BuildEvalKnowledgeBundleOptions,\n buildEvalKnowledgeBundle,\n type EvalKnowledgeBundleBuildResult,\n type KnowledgeReadinessSpec,\n} from './eval-readiness'\nimport { createKnowledgeEvent } from './events'\nimport { buildKnowledgeIndex } from './indexer'\nimport { lintKnowledgeIndex } from './lint'\nimport { type ApplyWriteBlocksResult, applyKnowledgeWriteBlocks } from './proposals'\nimport {\n type AddSourceOptions,\n type AddSourceTextInput,\n addSourcePath,\n addSourceText,\n} from './sources'\nimport { initKnowledgeBase } from './store'\nimport type { KnowledgeEvent, KnowledgeIndex, KnowledgeLintFinding, SourceRecord } from './types'\nimport {\n type ValidateKnowledgeOptions,\n type ValidateKnowledgeResult,\n validateKnowledgeIndex,\n} from './validate'\n\nexport interface KnowledgeResearchLoopContext {\n root: string\n goal: string\n iteration: number\n index: KnowledgeIndex\n lintFindings: KnowledgeLintFinding[]\n validation: ValidateKnowledgeResult\n readiness?: EvalKnowledgeBundleBuildResult\n previousSteps: KnowledgeResearchLoopStep[]\n signal?: AbortSignal\n}\n\nexport interface KnowledgeResearchLoopDecision {\n /**\n * Free-form notes from the researcher. Keep this human-readable; products can\n * store it as the research transcript.\n */\n notes?: string\n /**\n * Local files to register as immutable sources before applying proposals.\n */\n sourcePaths?: string[]\n /**\n * Textual source artifacts discovered by an agent, browser worker, connector,\n * or deep-research process.\n */\n sourceTexts?: AddSourceTextInput[]\n /**\n * Safe write protocol text. The loop parses and applies only accepted\n * `---FILE: knowledge/...---` blocks.\n */\n proposalText?: string\n /**\n * The researcher decides when the wiki is good enough. The loop deliberately\n * does not encode a domain-specific definition of \"done\".\n */\n done?: boolean\n metadata?: Record<string, unknown>\n}\n\nexport interface KnowledgeResearchLoopStep {\n iteration: number\n notes?: string\n addedSources: SourceRecord[]\n applied?: ApplyWriteBlocksResult\n lintFindings: KnowledgeLintFinding[]\n validation: ValidateKnowledgeResult\n readiness?: EvalKnowledgeBundleBuildResult\n event: KnowledgeEvent\n done: boolean\n metadata?: Record<string, unknown>\n}\n\nexport interface RunKnowledgeResearchLoopOptions {\n root: string\n goal: string\n maxIterations?: number\n actor?: string\n strict?: ValidateKnowledgeOptions['strict']\n readinessSpecs?: KnowledgeReadinessSpec[]\n readinessTaskId?: string\n readiness?: Omit<BuildEvalKnowledgeBundleOptions, 'taskId' | 'index' | 'specs'>\n sourceOptions?: Pick<AddSourceOptions, 'adapters' | 'now'>\n signal?: AbortSignal\n step(\n context: KnowledgeResearchLoopContext,\n ): Promise<KnowledgeResearchLoopDecision> | KnowledgeResearchLoopDecision\n onStep?: (step: KnowledgeResearchLoopStep) => Promise<void> | void\n}\n\nexport interface KnowledgeResearchLoopResult {\n root: string\n goal: string\n iterations: number\n done: boolean\n index: KnowledgeIndex\n lintFindings: KnowledgeLintFinding[]\n validation: ValidateKnowledgeResult\n readiness?: EvalKnowledgeBundleBuildResult\n steps: KnowledgeResearchLoopStep[]\n}\n\nexport type KnowledgeControlLoopState = KnowledgeResearchLoopContext\nexport type KnowledgeControlLoopAction = KnowledgeResearchLoopDecision\nexport type KnowledgeControlLoopActionResult = KnowledgeResearchLoopStep\n\nexport interface KnowledgeControlLoopAdapterOptions {\n root: string\n goal: string\n actor?: string\n strict?: ValidateKnowledgeOptions['strict']\n readinessSpecs?: KnowledgeReadinessSpec[]\n readinessTaskId?: string\n readiness?: Omit<BuildEvalKnowledgeBundleOptions, 'taskId' | 'index' | 'specs'>\n sourceOptions?: Pick<AddSourceOptions, 'adapters' | 'now'>\n}\n\nexport type KnowledgeControlLoopAdapter = Pick<\n ControlRuntimeConfig<\n KnowledgeControlLoopState,\n KnowledgeControlLoopAction,\n KnowledgeControlLoopActionResult,\n ControlEvalResult\n >,\n 'intent' | 'observe' | 'validate' | 'act' | 'shouldStop'\n>\n\n/**\n * Adapter for running knowledge growth through `agent-eval`'s generic control\n * runtime. The caller still owns `decide`: that can be a proposer agent,\n * reviewer agent, deterministic policy, or a composition of all three.\n */\nexport function createKnowledgeControlLoopAdapter(\n options: KnowledgeControlLoopAdapterOptions,\n): KnowledgeControlLoopAdapter {\n let initialized = false\n const appliedSteps: KnowledgeResearchLoopStep[] = []\n\n return {\n intent: options.goal,\n async observe({ history, abortSignal }) {\n if (abortSignal.aborted) throw new Error('Knowledge control loop aborted')\n if (!initialized) {\n await initKnowledgeBase(options.root)\n initialized = true\n }\n const index = await buildKnowledgeIndex(options.root)\n const validation = validateKnowledgeIndex(index, { strict: options.strict })\n const lintFindings = lintKnowledgeIndex(index)\n const readiness = readinessFor(options, index)\n return {\n root: options.root,\n goal: options.goal,\n iteration: history.length + 1,\n index,\n lintFindings,\n validation,\n readiness,\n previousSteps: [...appliedSteps],\n signal: abortSignal,\n }\n },\n validate({ state }) {\n const errorFindings = state.validation.findings.filter(\n (finding) => finding.severity === 'error',\n )\n const evals: ControlEvalResult[] = [\n objectiveEval({\n id: 'knowledge-valid',\n passed: state.validation.ok,\n severity: 'critical',\n detail: state.validation.ok\n ? 'Knowledge index is valid.'\n : 'Knowledge index has validation errors.',\n metadata: { findings: state.validation.findings },\n }),\n objectiveEval({\n id: 'knowledge-lint-errors',\n passed: errorFindings.length === 0,\n severity: 'error',\n detail:\n errorFindings.length === 0\n ? 'No lint errors.'\n : `${errorFindings.length} lint error(s).`,\n metadata: { findings: errorFindings },\n }),\n ]\n if (state.readiness) evals.push(blockingKnowledgeEval(state.readiness.report))\n return evals\n },\n shouldStop() {\n return { stop: false, pass: false, reason: 'knowledge driver owns stop decisions' }\n },\n async act(action, ctx) {\n const step = await applyKnowledgeResearchDecision(options, action, ctx.state.iteration)\n appliedSteps.push(step)\n return step\n },\n }\n}\n\nexport async function runKnowledgeResearchLoop(\n options: RunKnowledgeResearchLoopOptions,\n): Promise<KnowledgeResearchLoopResult> {\n const maxIterations = Math.max(1, options.maxIterations ?? 3)\n await initKnowledgeBase(options.root)\n const steps: KnowledgeResearchLoopStep[] = []\n let index = await buildKnowledgeIndex(options.root)\n let validation = validateKnowledgeIndex(index, { strict: options.strict })\n let lintFindings = lintKnowledgeIndex(index)\n let readiness = readinessFor(options, index)\n let done = false\n\n for (let iteration = 1; iteration <= maxIterations; iteration++) {\n if (options.signal?.aborted) throw new Error('Knowledge research loop aborted')\n const decision = await options.step({\n root: options.root,\n goal: options.goal,\n iteration,\n index,\n lintFindings,\n validation,\n readiness,\n previousSteps: steps,\n signal: options.signal,\n })\n\n done = Boolean(decision.done)\n const step = await applyKnowledgeResearchDecision(options, decision, iteration)\n index = await buildKnowledgeIndex(options.root)\n validation = step.validation\n lintFindings = step.lintFindings\n readiness = step.readiness\n steps.push(step)\n await options.onStep?.(step)\n\n if (done) break\n if (!step.applied && step.addedSources.length === 0) break\n }\n\n return {\n root: options.root,\n goal: options.goal,\n iterations: steps.length,\n done,\n index,\n lintFindings,\n validation,\n readiness,\n steps,\n }\n}\n\nasync function applyKnowledgeResearchDecision(\n options: KnowledgeControlLoopAdapterOptions,\n decision: KnowledgeResearchLoopDecision,\n iteration = 1,\n): Promise<KnowledgeResearchLoopStep> {\n const addedSources: SourceRecord[] = []\n for (const sourcePath of decision.sourcePaths ?? []) {\n addedSources.push(...(await addSourcePath(options.root, sourcePath, options.sourceOptions)))\n }\n for (const sourceText of decision.sourceTexts ?? []) {\n addedSources.push(await addSourceText(options.root, sourceText, options.sourceOptions))\n }\n\n const applied = decision.proposalText\n ? await applyKnowledgeWriteBlocks(options.root, decision.proposalText)\n : undefined\n\n const index = await buildKnowledgeIndex(options.root)\n const validation = validateKnowledgeIndex(index, { strict: options.strict })\n const lintFindings = lintKnowledgeIndex(index)\n const readiness = readinessFor(options, index)\n const done = Boolean(decision.done)\n const event = createKnowledgeEvent({\n type: 'research.iteration',\n actor: options.actor,\n target: options.root,\n metadata: {\n goal: options.goal,\n iteration,\n done,\n addedSourceCount: addedSources.length,\n written: applied?.written,\n warningCount: applied?.warnings.length ?? 0,\n errorCount: validation.findings.filter((finding) => finding.severity === 'error').length,\n },\n })\n\n return {\n iteration,\n notes: decision.notes,\n addedSources,\n applied,\n lintFindings,\n validation,\n readiness,\n event,\n done,\n metadata: decision.metadata,\n }\n}\n\nfunction readinessFor(\n options: KnowledgeControlLoopAdapterOptions,\n index: KnowledgeIndex,\n): EvalKnowledgeBundleBuildResult | undefined {\n if (!options.readinessSpecs?.length) return undefined\n return buildEvalKnowledgeBundle({\n ...(options.readiness ?? {}),\n taskId: options.readinessTaskId ?? options.goal,\n index,\n specs: options.readinessSpecs,\n })\n}\n"],"mappings":"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","names":["mkdir","readFile","writeFile","dirname","join","writeJson","join","readFile","mkdir","dirname","writeFile"]}
|
|
1
|
+
{"version":3,"sources":["../src/changes.ts","../src/chunking.ts","../src/discovery.ts","../src/eval-readiness.ts","../src/events.ts","../src/freshness.ts","../src/kb-store.ts","../src/propose-from-finding.ts","../src/release.ts","../src/research-loop.ts","../src/readiness-helpers.ts","../src/research-supervisor.ts","../src/two-agent-research-loop.ts"],"sourcesContent":["import type { KnowledgeFragment } from './sources/types'\n\n/**\n * Change detection across snapshots of one source's fragments.\n *\n * The output drives the continuous-ingestion loop: each `KnowledgeChange`\n * carries the eval dimensions affected (`affectedDimensions`), which an\n * agent-eval campaign scheduler consumes to decide which campaigns to\n * re-run. Three change kinds:\n *\n * - `added` — fragment id appears in `next` but not `prev`.\n * - `removed` — fragment id appears in `prev` but not `next`. Typical\n * trigger: an authority retires a Wex slug, or a state SOS reorganises\n * its forms catalogue.\n * - `modified` — fragment id appears in both, body hash differs. This\n * is the dominant change kind in practice — the Ryan-LLC v. FTC\n * vacatur case manifests as a `modified` on the Wex non-compete\n * fragment, NOT as a removed one.\n *\n * Unverifiable fragments are filtered out before diffing — comparing a\n * captcha-blocked snapshot against a real one would falsely fire every\n * fragment as removed. The caller can inspect the raw lists if they want\n * to surface block-page failures.\n *\n * Within-snapshot duplicate ids are an upstream bug; this function picks\n * the LAST one and emits a diagnostic via the `warnings` field.\n *\n * @stable\n */\n\nexport type KnowledgeChangeKind = 'added' | 'removed' | 'modified'\n\nexport interface KnowledgeChange {\n /** Source-scoped id (matches `KnowledgeFragment.id`). */\n fragmentId: string\n kind: KnowledgeChangeKind\n /**\n * For `added`: full body of the new fragment.\n * For `removed`: full body of the prior fragment.\n * For `modified`: unified-diff-style payload `{ before, after }` body strings.\n */\n diff?: { before?: string; after?: string }\n /**\n * Eval dimensions to re-score. Computed as the union of both fragments'\n * `dimensionHints`. The eval cron treats this as a set of campaign tags.\n */\n affectedDimensions: string[]\n /** URL of the affected authority page (from whichever side has it). */\n url?: string\n /**\n * Source-attested change time. For `modified`, takes the NEXT fragment's\n * `sourceUpdatedAt`. For `removed`, takes the PRIOR fragment's\n * `sourceUpdatedAt`. For `added`, takes the NEXT fragment's\n * `sourceUpdatedAt`. Consumers index changes by this date.\n */\n detectedAt: string\n}\n\nexport interface DetectChangesResult {\n changes: KnowledgeChange[]\n /** Counts by kind — handy for dashboards. */\n summary: { added: number; removed: number; modified: number }\n /** Non-fatal diagnostics (duplicate ids, dropped unverifiable fragments). */\n warnings: string[]\n}\n\nexport interface DetectChangesOptions {\n /**\n * When true (default), unverifiable fragments are dropped from both\n * sides before comparison. Set false ONLY when debugging block-page\n * issues — comparing against unverifiable content emits false\n * `removed`/`modified` changes.\n */\n skipUnverifiable?: boolean\n /**\n * When provided, only changes whose `affectedDimensions` intersect this\n * set are returned. Useful for cron loops that schedule per-dimension\n * eval campaigns and only care about a subset.\n */\n filterDimensions?: string[]\n}\n\nexport function detectChanges(\n prev: KnowledgeFragment[],\n next: KnowledgeFragment[],\n options: DetectChangesOptions = {},\n): DetectChangesResult {\n const skipUnverifiable = options.skipUnverifiable ?? true\n const warnings: string[] = []\n\n const { map: prevMap, warnings: prevWarn } = indexFragments(prev, skipUnverifiable, 'prev')\n const { map: nextMap, warnings: nextWarn } = indexFragments(next, skipUnverifiable, 'next')\n warnings.push(...prevWarn, ...nextWarn)\n\n const changes: KnowledgeChange[] = []\n const seen = new Set<string>()\n\n for (const [id, nextFragment] of nextMap) {\n seen.add(id)\n const prevFragment = prevMap.get(id)\n if (!prevFragment) {\n changes.push({\n fragmentId: id,\n kind: 'added',\n diff: { after: nextFragment.body },\n affectedDimensions: dedup(nextFragment.dimensionHints),\n url: nextFragment.provenance.url,\n detectedAt: nextFragment.provenance.sourceUpdatedAt,\n })\n continue\n }\n if (prevFragment.bodyHash !== nextFragment.bodyHash) {\n changes.push({\n fragmentId: id,\n kind: 'modified',\n diff: { before: prevFragment.body, after: nextFragment.body },\n affectedDimensions: dedup([...prevFragment.dimensionHints, ...nextFragment.dimensionHints]),\n url: nextFragment.provenance.url,\n detectedAt: nextFragment.provenance.sourceUpdatedAt,\n })\n }\n }\n\n for (const [id, prevFragment] of prevMap) {\n if (seen.has(id)) continue\n changes.push({\n fragmentId: id,\n kind: 'removed',\n diff: { before: prevFragment.body },\n affectedDimensions: dedup(prevFragment.dimensionHints),\n url: prevFragment.provenance.url,\n detectedAt: prevFragment.provenance.sourceUpdatedAt,\n })\n }\n\n const filtered = options.filterDimensions\n ? changes.filter((c) => c.affectedDimensions.some((d) => options.filterDimensions?.includes(d)))\n : changes\n\n return {\n changes: filtered,\n summary: {\n added: filtered.filter((c) => c.kind === 'added').length,\n removed: filtered.filter((c) => c.kind === 'removed').length,\n modified: filtered.filter((c) => c.kind === 'modified').length,\n },\n warnings,\n }\n}\n\nfunction indexFragments(\n fragments: KnowledgeFragment[],\n skipUnverifiable: boolean,\n side: string,\n): { map: Map<string, KnowledgeFragment>; warnings: string[] } {\n const map = new Map<string, KnowledgeFragment>()\n const warnings: string[] = []\n let dropped = 0\n for (const fragment of fragments) {\n if (skipUnverifiable && !fragment.provenance.verifiable) {\n dropped += 1\n continue\n }\n if (map.has(fragment.id)) {\n warnings.push(`${side}: duplicate fragment id ${fragment.id} — keeping last`)\n }\n map.set(fragment.id, fragment)\n }\n if (dropped > 0) {\n warnings.push(`${side}: dropped ${dropped} unverifiable fragment(s) before diff`)\n }\n return { map, warnings }\n}\n\nfunction dedup<T>(items: T[]): T[] {\n return [...new Set(items)]\n}\n","export interface ChunkingOptions {\n targetChars: number\n maxChars: number\n minChars: number\n overlapChars: number\n}\n\nexport interface KnowledgeChunk {\n index: number\n text: string\n headingPath: string\n charStart: number\n charEnd: number\n oversized: boolean\n}\n\nconst DEFAULT_OPTIONS: ChunkingOptions = {\n targetChars: 1000,\n maxChars: 1500,\n minChars: 160,\n overlapChars: 180,\n}\n\nexport function chunkMarkdown(\n content: string,\n options?: Partial<ChunkingOptions>,\n): KnowledgeChunk[] {\n const opts = normalizeOptions({ ...DEFAULT_OPTIONS, ...(options ?? {}) })\n const { body, bodyOffset } = stripFrontmatter(content)\n if (body.trim() === '') return []\n\n const sections = splitSections(body, bodyOffset)\n const chunks: KnowledgeChunk[] = []\n for (const section of sections) {\n for (const part of chunkText(section.text, opts)) {\n const start = section.start + part.start\n chunks.push({\n index: chunks.length,\n text: part.text,\n headingPath: section.headingPath,\n charStart: start,\n charEnd: start + part.text.length,\n oversized: part.text.length > opts.maxChars,\n })\n }\n }\n return chunks\n}\n\nexport function stripFrontmatter(content: string): { body: string; bodyOffset: number } {\n const normalized = content.replace(/\\r\\n/g, '\\n')\n if (!normalized.startsWith('---\\n')) return { body: normalized, bodyOffset: 0 }\n const match = /^---\\n[\\s\\S]*?\\n---\\s*\\n?/.exec(normalized)\n if (!match) return { body: normalized, bodyOffset: 0 }\n return { body: normalized.slice(match[0].length), bodyOffset: match[0].length }\n}\n\nfunction normalizeOptions(opts: ChunkingOptions): ChunkingOptions {\n if (opts.maxChars < opts.targetChars) opts.maxChars = opts.targetChars\n if (opts.overlapChars >= opts.targetChars) opts.overlapChars = Math.floor(opts.targetChars / 2)\n return opts\n}\n\ninterface Section {\n text: string\n start: number\n headingPath: string\n}\n\nfunction splitSections(body: string, bodyOffset: number): Section[] {\n const lines = body.split('\\n')\n const sections: Section[] = []\n const headings: Record<number, string> = {}\n let current: { lines: string[]; start: number; headingPath: string } = {\n lines: [],\n start: bodyOffset,\n headingPath: '',\n }\n let cursor = bodyOffset\n let fence: string | null = null\n\n const flush = () => {\n const text = current.lines.join('\\n')\n if (text.trim() !== '')\n sections.push({ text, start: current.start, headingPath: current.headingPath })\n }\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]!\n const lineLen = line.length + (i < lines.length - 1 ? 1 : 0)\n const fenceMatch = /^(`{3,}|~{3,})/.exec(line)\n if (fenceMatch) {\n const marker = fenceMatch[1]!\n fence = fence === null ? marker : line.startsWith(fence) ? null : fence\n current.lines.push(line)\n cursor += lineLen\n continue\n }\n const heading = fence === null ? /^(#{1,6})\\s+(.+?)\\s*$/.exec(line) : null\n if (heading) {\n flush()\n const level = heading[1]!.length\n headings[level] = heading[2]!.trim()\n for (let next = level + 1; next <= 6; next++) delete headings[next]\n current = {\n lines: [line],\n start: cursor,\n headingPath: Object.entries(headings)\n .sort(([a], [b]) => Number(a) - Number(b))\n .map(([levelText, title]) => `${'#'.repeat(Number(levelText))} ${title}`)\n .join(' > '),\n }\n cursor += lineLen\n continue\n }\n current.lines.push(line)\n cursor += lineLen\n }\n flush()\n return sections\n}\n\nfunction chunkText(text: string, opts: ChunkingOptions): Array<{ text: string; start: number }> {\n if (text.length <= opts.targetChars) return [{ text, start: 0 }]\n const atoms = splitAtoms(text)\n const chunks: Array<{ text: string; start: number }> = []\n let buffer = ''\n let bufferStart = 0\n for (const atom of atoms) {\n if (buffer === '') bufferStart = atom.start\n if (buffer.length > 0 && buffer.length + atom.text.length > opts.targetChars) {\n chunks.push({ text: buffer.trimEnd(), start: bufferStart })\n const overlap = buffer.slice(Math.max(0, buffer.length - opts.overlapChars))\n buffer = overlap + atom.text\n bufferStart = Math.max(bufferStart, atom.start - overlap.length)\n } else {\n buffer += atom.text\n }\n }\n if (buffer.trim() !== '') chunks.push({ text: buffer.trimEnd(), start: bufferStart })\n return mergeTinyChunks(chunks, opts)\n}\n\nfunction splitAtoms(text: string): Array<{ text: string; start: number }> {\n const parts: Array<{ text: string; start: number }> = []\n const paragraphs = text.split(/(\\n\\s*\\n)/)\n let cursor = 0\n for (let i = 0; i < paragraphs.length; i += 2) {\n const paragraph = (paragraphs[i] ?? '') + (paragraphs[i + 1] ?? '')\n if (paragraph === '') continue\n parts.push({ text: paragraph, start: cursor })\n cursor += paragraph.length\n }\n return parts\n}\n\nfunction mergeTinyChunks(\n chunks: Array<{ text: string; start: number }>,\n opts: ChunkingOptions,\n): Array<{ text: string; start: number }> {\n const out: Array<{ text: string; start: number }> = []\n for (const chunk of chunks) {\n const prev = out[out.length - 1]\n if (\n prev &&\n chunk.text.length < opts.minChars &&\n prev.text.length + chunk.text.length <= opts.maxChars\n ) {\n prev.text = `${prev.text}\\n\\n${chunk.text}`\n } else {\n out.push({ ...chunk })\n }\n }\n return out\n}\n","export interface DiscoveryTask {\n id: string\n goal: string\n query?: string\n sourceHints?: string[]\n metadata?: Record<string, unknown>\n}\n\nexport interface DiscoveryResult {\n taskId: string\n summary: string\n sourceUris?: string[]\n claims?: Array<{ text: string; sourceUri?: string; confidence?: number }>\n followUpTasks?: DiscoveryTask[]\n metadata?: Record<string, unknown>\n}\n\nexport interface KnowledgeDiscoveryWorker {\n run(task: DiscoveryTask, signal?: AbortSignal): Promise<DiscoveryResult> | DiscoveryResult\n}\n\nexport interface KnowledgeDiscoveryDispatcher {\n dispatch(\n tasks: DiscoveryTask[],\n options?: {\n concurrency?: number\n signal?: AbortSignal\n },\n ): Promise<DiscoveryResult[]>\n}\n\nexport function createLocalDiscoveryDispatcher(\n worker: KnowledgeDiscoveryWorker,\n): KnowledgeDiscoveryDispatcher {\n return {\n async dispatch(tasks, options = {}) {\n const concurrency = Math.max(1, options.concurrency ?? 4)\n const results: DiscoveryResult[] = []\n let cursor = 0\n async function runNext(): Promise<void> {\n while (cursor < tasks.length) {\n if (options.signal?.aborted) throw new Error('Discovery dispatch aborted')\n const task = tasks[cursor++]!\n results.push(await worker.run(task, options.signal))\n }\n }\n await Promise.all(Array.from({ length: Math.min(concurrency, tasks.length) }, runNext))\n return results.sort(\n (a, b) =>\n tasks.findIndex((task) => task.id === a.taskId) -\n tasks.findIndex((task) => task.id === b.taskId),\n )\n },\n }\n}\n","import {\n acquisitionPlansForKnowledgeGaps,\n type DataAcquisitionPlan,\n type KnowledgeAcquisitionMode,\n type KnowledgeBundle,\n type KnowledgeFreshness,\n type KnowledgeImportance,\n type KnowledgeReadinessReport,\n type KnowledgeRequirement,\n type KnowledgeRequirementCategory,\n type KnowledgeSensitivity,\n scoreKnowledgeReadiness,\n type UserQuestion,\n userQuestionsForKnowledgeGaps,\n} from '@tangle-network/agent-eval'\nimport { stringMetadata } from './metadata'\nimport { searchKnowledge } from './search'\nimport type { KnowledgeIndex, KnowledgeSearchResult } from './types'\n\nexport interface KnowledgeReadinessSpec {\n id: string\n description: string\n query: string\n requiredFor: string[]\n category: KnowledgeRequirementCategory\n acquisitionMode: KnowledgeAcquisitionMode\n importance: KnowledgeImportance\n freshness: KnowledgeFreshness\n sensitivity: KnowledgeSensitivity\n confidenceNeeded: number\n fallbackPolicy?: KnowledgeRequirement['fallbackPolicy']\n minSources?: number\n minHits?: number\n metadata?: Record<string, unknown>\n}\n\n/**\n * Defaults applied by `defineReadinessSpec` when the caller omits the field.\n *\n * These are deliberately conservative: a domain-specific topic that an agent\n * needs grounded to a high-confidence threshold from at least one authoritative\n * source. Override any field to specialise (e.g. `freshness: 'daily'` for a\n * topic that must reflect today's regulatory state).\n */\nexport const READINESS_SPEC_DEFAULTS = {\n category: 'domain_specific',\n acquisitionMode: 'search_web',\n importance: 'high',\n freshness: 'monthly',\n sensitivity: 'public',\n confidenceNeeded: 0.7,\n minSources: 1,\n minHits: 2,\n} as const satisfies Pick<\n KnowledgeReadinessSpec,\n | 'category'\n | 'acquisitionMode'\n | 'importance'\n | 'freshness'\n | 'sensitivity'\n | 'confidenceNeeded'\n | 'minSources'\n | 'minHits'\n>\n\n/**\n * Inputs accepted by `defineReadinessSpec`. The four fields the caller cannot\n * sanely default (id, description, query, requiredFor) are required; everything\n * else is optional and pulls from `READINESS_SPEC_DEFAULTS`.\n */\nexport type DefineReadinessSpecInput = Pick<\n KnowledgeReadinessSpec,\n 'id' | 'description' | 'query' | 'requiredFor'\n> &\n Partial<Omit<KnowledgeReadinessSpec, 'id' | 'description' | 'query' | 'requiredFor'>>\n\n/**\n * Builder that returns a fully-typed `KnowledgeReadinessSpec` from a slim input.\n *\n * Motivation: `KnowledgeReadinessSpec` has eleven required fields. Most of them\n * (category, acquisition mode, importance, freshness, sensitivity, confidence\n * threshold, source/hit minima) have a clear default for domain-specific KB\n * topics — agents end up copy-pasting the same boilerplate across every spec.\n * This helper collapses that boilerplate while keeping every field overridable.\n *\n * @example\n * defineReadinessSpec({\n * id: 'coop/intent-grounding',\n * description: 'Required grounding for coop pack intent stage',\n * query: 'flock size space ventilation predator',\n * requiredFor: ['IntentIntakeAgent', 'requirements'],\n * })\n *\n * @example // override defaults where the topic demands it\n * defineReadinessSpec({\n * id: 'medical/dosing',\n * description: 'Dosing guidance for compounding pharmacy',\n * query: 'compounding dose schedule',\n * requiredFor: ['DosingAgent'],\n * importance: 'blocking',\n * freshness: 'daily',\n * sensitivity: 'private',\n * confidenceNeeded: 0.95,\n * minSources: 3,\n * })\n */\nexport function defineReadinessSpec(input: DefineReadinessSpecInput): KnowledgeReadinessSpec {\n return {\n ...READINESS_SPEC_DEFAULTS,\n ...input,\n }\n}\n\nexport interface BuildEvalKnowledgeBundleOptions {\n taskId: string\n index: KnowledgeIndex\n specs: KnowledgeReadinessSpec[]\n userAnswers?: Record<string, string>\n searchLimit?: number\n metadata?: Record<string, unknown>\n now?: Date\n}\n\nexport interface EvalKnowledgeBundleBuildResult {\n bundle: KnowledgeBundle\n report: KnowledgeReadinessReport\n requirements: KnowledgeRequirement[]\n searchResultsByRequirement: Record<string, KnowledgeSearchResult[]>\n questions: UserQuestion[]\n acquisitionPlans: DataAcquisitionPlan[]\n}\n\nexport function buildEvalKnowledgeBundle(\n options: BuildEvalKnowledgeBundleOptions,\n): EvalKnowledgeBundleBuildResult {\n const searchLimit = options.searchLimit ?? 5\n const now = options.now ?? new Date()\n const searchResultsByRequirement: Record<string, KnowledgeSearchResult[]> = {}\n const requirements = options.specs.map((spec) => {\n const results = searchKnowledge(options.index, spec.query, searchLimit)\n searchResultsByRequirement[spec.id] = results\n return requirementFromSearch(options.index, spec, results, now)\n })\n const report = scoreKnowledgeReadiness({\n taskId: options.taskId,\n requirements,\n userAnswers: options.userAnswers,\n evidenceIds: requirements.flatMap((requirement) => requirement.evidenceIds),\n claimIds: [],\n wikiPageIds: unique(\n requirements.flatMap((requirement) =>\n pageIdsFromResults(searchResultsByRequirement[requirement.id] ?? []),\n ),\n ),\n metadata: options.metadata,\n })\n const questions = userQuestionsForKnowledgeGaps(report.blockingMissingRequirements)\n const acquisitionPlans = acquisitionPlansForKnowledgeGaps([\n ...report.blockingMissingRequirements,\n ...report.nonBlockingGaps,\n ])\n\n return {\n bundle: report.bundle,\n report,\n requirements,\n searchResultsByRequirement,\n questions,\n acquisitionPlans,\n }\n}\n\nfunction requirementFromSearch(\n index: KnowledgeIndex,\n spec: KnowledgeReadinessSpec,\n results: KnowledgeSearchResult[],\n now: Date,\n): KnowledgeRequirement {\n const hitCount = results.length\n const sourceIds = unique(results.flatMap((result) => result.page.sourceIds))\n const sources = index.sources.filter((source) => sourceIds.includes(source.id))\n const bestScore = results[0]?.normalizedScore ?? 0\n const sourceCoverage = spec.minSources\n ? Math.min(1, sourceIds.length / spec.minSources)\n : sourceIds.length > 0\n ? 1\n : 0\n const hitCoverage = spec.minHits ? Math.min(1, hitCount / spec.minHits) : hitCount > 0 ? 1 : 0\n const freshness = sourceFreshness(sources, now)\n const currentConfidence = round(Math.min(bestScore, sourceCoverage, hitCoverage, freshness.score))\n\n return {\n id: spec.id,\n description: spec.description,\n requiredFor: spec.requiredFor,\n category: spec.category,\n acquisitionMode: spec.acquisitionMode,\n importance: spec.importance,\n freshness: spec.freshness,\n sensitivity: spec.sensitivity,\n confidenceNeeded: spec.confidenceNeeded,\n currentConfidence,\n evidenceIds: unique([\n ...sourceIds.map((sourceId) => `source:${sourceId}`),\n ...results.map((result) => `page:${result.page.id}`),\n ]),\n fallbackPolicy:\n spec.fallbackPolicy ?? (spec.importance === 'blocking' ? 'block' : 'continue_with_caveat'),\n metadata: {\n ...spec.metadata,\n query: spec.query,\n hitCount,\n sourceCount: sourceIds.length,\n bestNormalizedScore: bestScore,\n expiredSourceIds: freshness.expiredSourceIds,\n freshnessScore: freshness.score,\n validUntil: freshness.validUntil,\n lastVerifiedAt: freshness.lastVerifiedAt,\n },\n }\n}\n\nfunction sourceFreshness(\n sources: KnowledgeIndex['sources'],\n now: Date,\n): { score: number; validUntil?: string; lastVerifiedAt?: string; expiredSourceIds: string[] } {\n if (sources.length === 0) return { score: 0, expiredSourceIds: [] }\n const validUntilValues = sources\n .map(\n (source) =>\n source.validUntil ??\n stringMetadata(source.metadata, 'validUntil') ??\n stringMetadata(source.metadata, 'expiresAt'),\n )\n .filter(isIsoDate)\n const lastVerifiedValues = sources\n .map((source) => source.lastVerifiedAt ?? stringMetadata(source.metadata, 'lastVerifiedAt'))\n .filter(isIsoDate)\n const expiredSourceIds = sources\n .filter((source) => {\n const validUntil =\n source.validUntil ??\n stringMetadata(source.metadata, 'validUntil') ??\n stringMetadata(source.metadata, 'expiresAt')\n return validUntil ? Date.parse(validUntil) <= now.getTime() : false\n })\n .map((source) => source.id)\n return {\n score: expiredSourceIds.length > 0 ? 0 : 1,\n validUntil: earliestIso(validUntilValues),\n lastVerifiedAt: latestIso(lastVerifiedValues),\n expiredSourceIds,\n }\n}\n\nfunction isIsoDate(value: string | undefined): value is string {\n return Boolean(value && Number.isFinite(Date.parse(value)))\n}\n\nfunction earliestIso(values: string[]): string | undefined {\n return values.sort((a, b) => Date.parse(a) - Date.parse(b))[0]\n}\n\nfunction latestIso(values: string[]): string | undefined {\n return values.sort((a, b) => Date.parse(b) - Date.parse(a))[0]\n}\n\nfunction pageIdsFromResults(results: KnowledgeSearchResult[]): string[] {\n return results.map((result) => result.page.id)\n}\n\nfunction unique<T>(items: T[]): T[] {\n return [...new Set(items)]\n}\n\nfunction round(value: number): number {\n return Math.round(value * 1000) / 1000\n}\n","import { stableId } from './ids'\nimport type { KnowledgeEvent, KnowledgeEventType } from './types'\n\nexport interface KnowledgeEventQuery {\n type?: KnowledgeEventType\n target?: string\n limit?: number\n}\n\nexport function createKnowledgeEvent(input: {\n type: KnowledgeEventType\n actor?: string\n target?: string\n metadata?: Record<string, unknown>\n now?: () => Date\n}): KnowledgeEvent {\n const createdAt = (input.now ?? (() => new Date()))().toISOString()\n return {\n id: stableId(\n 'evt',\n `${input.type}:${input.target ?? ''}:${createdAt}:${JSON.stringify(input.metadata ?? {})}`,\n ),\n type: input.type,\n createdAt,\n actor: input.actor,\n target: input.target,\n metadata: input.metadata,\n }\n}\n","import { mkdir, readFile, writeFile } from 'node:fs/promises'\nimport { dirname, join } from 'node:path'\n\n/**\n * Knowledge freshness store: tracks when each `(workspaceId, sourceId)` pair\n * was last successfully refreshed, and reports staleness against a TTL.\n *\n * The contract is intentionally minimal — just enough to drive a cron loop:\n *\n * ```ts\n * const store = createFileSystemFreshnessStore({ root: '.agent-knowledge' })\n * for (const source of sources) {\n * if (await store.stale({ workspaceId, sourceId: source.id, ttlMs: DAY })) {\n * const fragments = await source.fetch({ cacheDir })\n * await persistFragments(fragments)\n * await store.mark({ workspaceId, sourceId: source.id, when: new Date() })\n * }\n * }\n * ```\n *\n * Per-tenant isolation is enforced by `workspaceId` keying — there is no\n * global mutable state across workspaces.\n *\n * Two adapters ship in-package:\n *\n * - `createFileSystemFreshnessStore` — JSON file under the knowledge root,\n * mirrors the layout convention already used by `sources.json`.\n * - `createD1FreshnessStoreStub` — adapter scaffold for Cloudflare D1 /\n * Postgres. Production consumers should implement the `D1Adapter`\n * interface inside their own app; this stub exists to anchor the shape.\n *\n * @stable contract — interface is frozen at 0.x within this major.\n * @stable filesystem adapter\n * @experimental D1 stub — interface will evolve as real consumers wire it.\n */\n\n/** Identity for one freshness record. */\nexport interface FreshnessKey {\n workspaceId: string\n sourceId: string\n}\n\n/** TTL bound for staleness checks. */\nexport interface FreshnessTtl extends FreshnessKey {\n /** Milliseconds — `Date.now() - last() > ttlMs` ⇒ stale. */\n ttlMs: number\n /** Injected clock for deterministic tests; defaults to system time. */\n now?: Date\n}\n\n/** Mark argument. */\nexport interface FreshnessMark extends FreshnessKey {\n when: Date\n /** Optional content hash captured at refresh time; aids debugging. */\n contentHash?: string\n}\n\nexport interface KnowledgeFreshnessStore {\n /** Last refresh time, or null if never refreshed. */\n last(key: FreshnessKey): Promise<Date | null>\n /** Record a successful refresh. */\n mark(input: FreshnessMark): Promise<void>\n /** True iff `last(key)` is null or older than `ttlMs`. */\n stale(input: FreshnessTtl): Promise<boolean>\n /** All records for a workspace — useful for dashboards / debugging. */\n list(workspaceId: string): Promise<FreshnessRecord[]>\n}\n\nexport interface FreshnessRecord {\n workspaceId: string\n sourceId: string\n lastRefreshedAt: string\n contentHash?: string\n}\n\nexport interface FileSystemFreshnessStoreOptions {\n /**\n * Knowledge root. The store writes to `<root>/.agent-knowledge/freshness.json`,\n * mirroring the convention used by `sources.json`.\n */\n root: string\n}\n\n/**\n * Filesystem-backed implementation. Single JSON file per knowledge root,\n * indexed by `${workspaceId}::${sourceId}`. Reads parse on every call —\n * cron tick rate is well below the cost of one JSON parse.\n *\n * Concurrent writes from a single process serialize through `writeQueue`.\n * Cross-process concurrency is undefined; the consuming app should run the\n * cron in a single worker.\n */\nexport function createFileSystemFreshnessStore(\n options: FileSystemFreshnessStoreOptions,\n): KnowledgeFreshnessStore {\n const path = join(options.root, '.agent-knowledge', 'freshness.json')\n let writeQueue: Promise<unknown> = Promise.resolve()\n\n const read = async (): Promise<Record<string, FreshnessRecord>> => {\n try {\n const text = await readFile(path, 'utf8')\n const parsed = JSON.parse(text) as { records?: Record<string, FreshnessRecord> }\n return parsed.records ?? {}\n } catch {\n return {}\n }\n }\n\n const write = async (records: Record<string, FreshnessRecord>): Promise<void> => {\n await mkdir(dirname(path), { recursive: true })\n await writeFile(path, `${JSON.stringify({ records }, null, 2)}\\n`, 'utf8')\n }\n\n return {\n async last(key) {\n const records = await read()\n const record = records[buildKey(key)]\n return record ? new Date(record.lastRefreshedAt) : null\n },\n async mark(input) {\n writeQueue = writeQueue.then(async () => {\n const records = await read()\n records[buildKey(input)] = {\n workspaceId: input.workspaceId,\n sourceId: input.sourceId,\n lastRefreshedAt: input.when.toISOString(),\n contentHash: input.contentHash,\n }\n await write(records)\n })\n await writeQueue\n },\n async stale(input) {\n const last = await this.last(input)\n if (!last) return true\n const now = input.now ?? new Date()\n return now.getTime() - last.getTime() > input.ttlMs\n },\n async list(workspaceId) {\n const records = await read()\n return Object.values(records).filter((r) => r.workspaceId === workspaceId)\n },\n }\n}\n\n/**\n * D1 / Postgres adapter scaffold. Production consumers implement\n * `D1Adapter` against their own driver (better-sqlite3, postgres,\n * Cloudflare D1 binding, ...). This factory wires the adapter to the\n * `KnowledgeFreshnessStore` interface.\n *\n * The expected schema:\n *\n * ```sql\n * CREATE TABLE knowledge_freshness (\n * workspace_id TEXT NOT NULL,\n * source_id TEXT NOT NULL,\n * last_refreshed_at TEXT NOT NULL,\n * content_hash TEXT,\n * PRIMARY KEY (workspace_id, source_id)\n * );\n * ```\n */\nexport interface D1Adapter {\n get(workspaceId: string, sourceId: string): Promise<FreshnessRecord | null>\n upsert(record: FreshnessRecord): Promise<void>\n listByWorkspace(workspaceId: string): Promise<FreshnessRecord[]>\n}\n\nexport function createD1FreshnessStoreStub(adapter: D1Adapter): KnowledgeFreshnessStore {\n return {\n async last(key) {\n const record = await adapter.get(key.workspaceId, key.sourceId)\n return record ? new Date(record.lastRefreshedAt) : null\n },\n async mark(input) {\n await adapter.upsert({\n workspaceId: input.workspaceId,\n sourceId: input.sourceId,\n lastRefreshedAt: input.when.toISOString(),\n contentHash: input.contentHash,\n })\n },\n async stale(input) {\n const last = await this.last(input)\n if (!last) return true\n const now = input.now ?? new Date()\n return now.getTime() - last.getTime() > input.ttlMs\n },\n async list(workspaceId) {\n return adapter.listByWorkspace(workspaceId)\n },\n }\n}\n\nfunction buildKey(key: FreshnessKey): string {\n return `${key.workspaceId}::${key.sourceId}`\n}\n","import { mkdir, readFile, writeFile } from 'node:fs/promises'\nimport { dirname, join } from 'node:path'\nimport type { KnowledgeEventQuery } from './events'\nimport { buildKnowledgeGraph } from './graph'\nimport type { KnowledgeEvent, KnowledgeIndex, KnowledgePage, SourceRecord } from './types'\n\nexport interface KbStore {\n putSource(source: SourceRecord): Promise<void>\n getSource(id: string): Promise<SourceRecord | null>\n listSources(): Promise<SourceRecord[]>\n putPage(page: KnowledgePage): Promise<void>\n getPage(idOrPath: string): Promise<KnowledgePage | null>\n listPages(): Promise<KnowledgePage[]>\n putIndex(index: KnowledgeIndex): Promise<void>\n getIndex(): Promise<KnowledgeIndex | null>\n putEvent(event: KnowledgeEvent): Promise<void>\n listEvents(query?: KnowledgeEventQuery): Promise<KnowledgeEvent[]>\n}\n\nexport class MemoryKbStore implements KbStore {\n private readonly sources = new Map<string, SourceRecord>()\n private readonly pages = new Map<string, KnowledgePage>()\n private readonly events: KnowledgeEvent[] = []\n private index: KnowledgeIndex | null = null\n\n async putSource(source: SourceRecord): Promise<void> {\n this.sources.set(source.id, clone(source))\n }\n\n async getSource(id: string): Promise<SourceRecord | null> {\n return clone(this.sources.get(id) ?? null)\n }\n\n async listSources(): Promise<SourceRecord[]> {\n return [...this.sources.values()].map(clone)\n }\n\n async putPage(page: KnowledgePage): Promise<void> {\n this.pages.set(page.id, clone(page))\n }\n\n async getPage(idOrPath: string): Promise<KnowledgePage | null> {\n return clone(\n this.pages.get(idOrPath) ??\n [...this.pages.values()].find((page) => page.path === idOrPath) ??\n null,\n )\n }\n\n async listPages(): Promise<KnowledgePage[]> {\n return [...this.pages.values()].map(clone)\n }\n\n async putIndex(index: KnowledgeIndex): Promise<void> {\n this.index = clone(index)\n }\n\n async getIndex(): Promise<KnowledgeIndex | null> {\n if (this.index) return clone(this.index)\n const pages = await this.listPages()\n const sources = await this.listSources()\n return {\n root: 'memory',\n generatedAt: new Date().toISOString(),\n sources,\n pages,\n graph: buildKnowledgeGraph(pages),\n }\n }\n\n async putEvent(event: KnowledgeEvent): Promise<void> {\n this.events.push(clone(event))\n }\n\n async listEvents(query: KnowledgeEventQuery = {}): Promise<KnowledgeEvent[]> {\n let out = this.events\n if (query.type) out = out.filter((event) => event.type === query.type)\n if (query.target) out = out.filter((event) => event.target === query.target)\n out = [...out].sort((a, b) => a.createdAt.localeCompare(b.createdAt))\n return out.slice(-(query.limit ?? out.length)).map(clone)\n }\n}\n\nexport class FileSystemKbStore extends MemoryKbStore {\n constructor(private readonly dir: string) {\n super()\n }\n\n async putIndex(index: KnowledgeIndex): Promise<void> {\n await super.putIndex(index)\n await writeJson(join(this.dir, 'index.json'), index)\n }\n\n async getIndex(): Promise<KnowledgeIndex | null> {\n try {\n return JSON.parse(await readFile(join(this.dir, 'index.json'), 'utf8')) as KnowledgeIndex\n } catch {\n return await super.getIndex()\n }\n }\n\n async putEvent(event: KnowledgeEvent): Promise<void> {\n await super.putEvent(event)\n const events = await this.listEvents()\n await writeJson(join(this.dir, 'events.json'), events)\n }\n}\n\nasync function writeJson(path: string, value: unknown): Promise<void> {\n await mkdir(dirname(path), { recursive: true })\n await writeFile(path, `${JSON.stringify(value, null, 2)}\\n`, 'utf8')\n}\n\nfunction clone<T>(value: T): T {\n return value == null ? value : (JSON.parse(JSON.stringify(value)) as T)\n}\n","/**\n * Bridge from `AnalystFinding` (agent-eval) to knowledge proposals.\n *\n * Closes the failure → wiki side of the recursive-self-improvement\n * loop: a knowledge-gap or knowledge-poisoning finding produced by an\n * analyst becomes a concrete proposal an operator (or auto-merge bot)\n * can review and apply. The bridge is intentionally lossless on the\n * fail-loud side — a finding the parser can't classify returns a\n * `KnowledgeProposalParseError` rather than a silent skip, so the\n * loop never accepts an underspecified edit.\n *\n * Subject grammar this bridge understands (analyst-side convention,\n * stamped in the kind prompts):\n *\n * agent-knowledge:wiki:<page-slug> create / update page\n * agent-knowledge:wiki:<page-slug>#<heading> insert section under page\n * agent-knowledge:claim:<topic> draft claim row\n * agent-knowledge:raw:<source-id> lift raw → curated\n * agent-knowledge:stale:<page-slug> mark page superseded\n *\n * Anything else (websearch:outdated:*, tool-doc:*, system-prompt:*,\n * memory:*) is NOT a knowledge-base concern and returns `null` so the\n * loop's improvement-applier handles it.\n */\n\nimport type { AnalystFinding, AnalystSeverity } from '@tangle-network/agent-eval'\nimport type { ClaimRef, KnowledgeClaim, KnowledgeWriteBlock } from './types'\n\nexport interface KnowledgeProposal {\n /**\n * Stable id derived from the finding so cross-run diffs share an\n * identity. Re-proposing the same finding produces the same id.\n */\n id: string\n /** The finding that generated this proposal — useful for audit + revert. */\n sourceFindingId: string\n /** What the proposal does. */\n kind:\n | 'create-page'\n | 'update-page'\n | 'append-section'\n | 'create-claim'\n | 'lift-raw'\n | 'mark-stale'\n /** Locus on disk (page slug or claim topic). */\n locus: string\n /**\n * Page write blocks the standard `applyKnowledgeWriteBlocks` consumer\n * accepts. Empty for proposals that don't change page text (e.g.\n * `create-claim` produces a `claim` field instead).\n */\n writeBlocks: KnowledgeWriteBlock[]\n /**\n * Granular claim draft for proposals whose unit-of-change is a claim\n * row rather than a whole page. `status: 'draft'` until reviewed.\n */\n claim?: KnowledgeClaim\n /** Per-proposal metadata: severity, confidence, source span. */\n metadata: {\n severity: AnalystSeverity\n confidence: number\n evidence_uri?: string\n analyst_id: string\n }\n}\n\nexport class KnowledgeProposalParseError extends Error {\n constructor(\n public readonly findingId: string,\n public readonly subject: string,\n message: string,\n ) {\n super(`proposeFromFinding(${findingId}, subject=${subject}): ${message}`)\n this.name = 'KnowledgeProposalParseError'\n }\n}\n\n/**\n * Convert one `AnalystFinding` into a knowledge proposal. Returns\n * `null` when the finding's locus isn't a knowledge-base concern\n * (`websearch:outdated:*`, `tool-doc:*`, `system-prompt:*`,\n * `memory:*`, missing subject). Throws when the locus IS a\n * knowledge-base concern but is malformed — that's a bug in the\n * analyst prompt and should fail loud.\n *\n * Caller convention: feed the function the analyst's full findings\n * list and filter out the `null`s; the orchestrator passes the\n * remaining proposals to the existing review / apply pipeline.\n */\nexport function proposeFromFinding(finding: AnalystFinding): KnowledgeProposal | null {\n if (!finding.subject) return null\n if (!finding.subject.startsWith('agent-knowledge:')) return null\n\n const rest = finding.subject.slice('agent-knowledge:'.length)\n const [kindPart, ...locusParts] = rest.split(':')\n const locus = locusParts.join(':')\n if (!kindPart || !locus) {\n throw new KnowledgeProposalParseError(\n finding.finding_id,\n finding.subject,\n 'expected `agent-knowledge:<kind>:<locus>` shape',\n )\n }\n\n const baseMeta: KnowledgeProposal['metadata'] = {\n severity: finding.severity,\n confidence: finding.confidence,\n evidence_uri: finding.evidence_refs[0]?.uri,\n analyst_id: finding.analyst_id,\n }\n\n switch (kindPart) {\n case 'wiki':\n return wikiProposal(finding, locus, baseMeta)\n case 'claim':\n return claimProposal(finding, locus, baseMeta)\n case 'raw':\n return liftRawProposal(finding, locus, baseMeta)\n case 'stale':\n return markStaleProposal(finding, locus, baseMeta)\n default:\n throw new KnowledgeProposalParseError(\n finding.finding_id,\n finding.subject,\n `unknown kind \"${kindPart}\" (expected one of: wiki | claim | raw | stale)`,\n )\n }\n}\n\nfunction wikiProposal(\n finding: AnalystFinding,\n locus: string,\n metadata: KnowledgeProposal['metadata'],\n): KnowledgeProposal {\n const hashIdx = locus.indexOf('#')\n const pageSlug = hashIdx >= 0 ? locus.slice(0, hashIdx) : locus\n const heading = hashIdx >= 0 ? locus.slice(hashIdx + 1) : null\n const path = `knowledge/${ensureSlug(pageSlug)}.md`\n\n const body = renderWikiBody(finding, pageSlug, heading)\n return {\n id: `prop-${finding.finding_id}`,\n sourceFindingId: finding.finding_id,\n kind: heading ? 'append-section' : 'create-page',\n locus: pageSlug,\n writeBlocks: [{ path, content: body }],\n metadata,\n }\n}\n\nfunction claimProposal(\n finding: AnalystFinding,\n locus: string,\n metadata: KnowledgeProposal['metadata'],\n): KnowledgeProposal {\n const refs: ClaimRef[] = finding.evidence_refs\n .filter((r) => r.uri)\n .map((r) => ({\n sourceId: `analyst-finding:${finding.finding_id}`,\n anchorId: r.uri,\n quote: r.excerpt,\n }))\n const claim: KnowledgeClaim = {\n id: `claim-${finding.finding_id}`,\n text: finding.recommended_action ?? finding.claim,\n refs,\n confidence: finding.confidence,\n status: 'draft',\n metadata: {\n analyst_id: finding.analyst_id,\n source_finding_id: finding.finding_id,\n topic: locus,\n },\n }\n return {\n id: `prop-${finding.finding_id}`,\n sourceFindingId: finding.finding_id,\n kind: 'create-claim',\n locus,\n writeBlocks: [],\n claim,\n metadata,\n }\n}\n\nfunction liftRawProposal(\n finding: AnalystFinding,\n sourceId: string,\n metadata: KnowledgeProposal['metadata'],\n): KnowledgeProposal {\n const path = `knowledge/${ensureSlug(sourceId)}.md`\n const body = [\n '---',\n `title: ${sourceId}`,\n `source: ${sourceId}`,\n `status: draft`,\n `lifted_from_finding: ${finding.finding_id}`,\n '---',\n '',\n '## Why this page exists',\n '',\n finding.claim,\n '',\n ...(finding.rationale ? ['## Rationale', '', finding.rationale, ''] : []),\n ...(finding.recommended_action\n ? ['## Recommended action', '', finding.recommended_action, '']\n : []),\n ].join('\\n')\n return {\n id: `prop-${finding.finding_id}`,\n sourceFindingId: finding.finding_id,\n kind: 'lift-raw',\n locus: sourceId,\n writeBlocks: [{ path, content: body }],\n metadata,\n }\n}\n\nfunction markStaleProposal(\n finding: AnalystFinding,\n pageSlug: string,\n metadata: KnowledgeProposal['metadata'],\n): KnowledgeProposal {\n const path = `knowledge/${ensureSlug(pageSlug)}.stale.md`\n const body = [\n '---',\n `title: ${pageSlug} (marked stale)`,\n `status: superseded`,\n `superseded_by_finding: ${finding.finding_id}`,\n `confidence: ${finding.confidence}`,\n '---',\n '',\n '## Why marked stale',\n '',\n finding.claim,\n '',\n ...(finding.rationale ? ['## Evidence', '', finding.rationale, ''] : []),\n ...(finding.recommended_action ? ['## Action', '', finding.recommended_action, ''] : []),\n ].join('\\n')\n return {\n id: `prop-${finding.finding_id}`,\n sourceFindingId: finding.finding_id,\n kind: 'mark-stale',\n locus: pageSlug,\n writeBlocks: [{ path, content: body }],\n metadata,\n }\n}\n\n/**\n * Plural convenience: filter + map across an entire findings batch\n * with one call. Parse errors collect into `errors[]`; the loop\n * decides per-error whether to abort or continue.\n */\nexport interface ProposeFromFindingsResult {\n proposals: KnowledgeProposal[]\n skipped: number\n errors: KnowledgeProposalParseError[]\n}\n\nexport function proposeFromFindings(\n findings: ReadonlyArray<AnalystFinding>,\n): ProposeFromFindingsResult {\n const proposals: KnowledgeProposal[] = []\n const errors: KnowledgeProposalParseError[] = []\n let skipped = 0\n for (const f of findings) {\n try {\n const p = proposeFromFinding(f)\n if (p) proposals.push(p)\n else skipped += 1\n } catch (err) {\n if (err instanceof KnowledgeProposalParseError) errors.push(err)\n else throw err\n }\n }\n return { proposals, skipped, errors }\n}\n\nfunction ensureSlug(s: string): string {\n return (\n s\n .toLowerCase()\n .replace(/[^a-z0-9-]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 200) || 'untitled'\n )\n}\n\nfunction renderWikiBody(finding: AnalystFinding, slug: string, heading: string | null): string {\n const title = humanize(slug)\n if (heading) {\n return [\n `## ${heading}`,\n '',\n finding.claim,\n '',\n ...(finding.rationale ? ['### Rationale', '', finding.rationale, ''] : []),\n ...(finding.recommended_action ? ['### Action', '', finding.recommended_action, ''] : []),\n `_Drafted from finding ${finding.finding_id} (confidence ${finding.confidence.toFixed(2)})._`,\n ].join('\\n')\n }\n return [\n '---',\n `title: ${title}`,\n `status: draft`,\n `drafted_from_finding: ${finding.finding_id}`,\n `confidence: ${finding.confidence}`,\n '---',\n '',\n `# ${title}`,\n '',\n finding.claim,\n '',\n ...(finding.rationale ? ['## Rationale', '', finding.rationale, ''] : []),\n ...(finding.recommended_action\n ? ['## Recommended action', '', finding.recommended_action, '']\n : []),\n ].join('\\n')\n}\n\nfunction humanize(slug: string): string {\n return (\n slug\n .split('-')\n .filter(Boolean)\n .map((w) => w[0]?.toUpperCase() + w.slice(1))\n .join(' ') || slug\n )\n}\n","import {\n evaluateReleaseConfidence,\n type GateDecision,\n type ReleaseConfidenceScorecard,\n type ReleaseTraceEvidence,\n type RunRecord,\n validateRunRecord,\n} from '@tangle-network/agent-eval'\nimport { stableId } from './ids'\nimport type { KnowledgeRelease } from './types'\n\nexport interface KnowledgeReleaseReport {\n release: KnowledgeRelease\n scorecard: ReleaseConfidenceScorecard\n candidateRuns: RunRecord[]\n baselineRuns: RunRecord[]\n}\n\n/**\n * Campaign-native release report. The caller (a consumer's KB self-improvement\n * loop) supplies the candidate/baseline `RunRecord[]` (e.g. via\n * `campaignToRunRecords`) + optional per-instance `ReleaseTraceEvidence` + the\n * gate decision; this folds them into a `ReleaseConfidenceScorecard` + a\n * `KnowledgeRelease`. Release confidence is computed from run records + traces,\n * independent of any optimizer result shape.\n */\nexport interface KnowledgeReleaseInput {\n candidateId: string\n baselineId?: string\n candidateRuns: RunRecord[]\n baselineRuns?: RunRecord[]\n traces?: ReleaseTraceEvidence[]\n gateDecision?: GateDecision | null\n /**\n * True when a held-out split was evaluated (drives the holdout threshold).\n *\n * Constraint: the substrate gate keys the holdout requirement off a scenario\n * corpus, which this run-only report does not carry. With `hasHoldout: true`\n * the gate fails closed (`missing_holdout_split`) even when holdout RunRecords\n * are supplied. Callers that gate on a real held-out split should drive\n * `evaluateReleaseConfidence` directly with a dataset/scenarios.\n */\n hasHoldout?: boolean\n /** Candidate is the search-best variant — a promotion precondition. Default true. */\n promotedIsBest?: boolean\n createdAt?: string\n minScore?: number\n}\n\nexport function knowledgeReleaseReport(input: KnowledgeReleaseInput): KnowledgeReleaseReport {\n const baselineRuns = input.baselineRuns ?? []\n const runRecords = [...input.candidateRuns, ...baselineRuns].map(validateRunRecord)\n const scorecard = evaluateReleaseConfidence({\n target: 'agent-knowledge-base',\n candidateId: input.candidateId,\n baselineId: input.baselineId ?? 'baseline',\n traces: input.traces ?? [],\n runs: runRecords,\n gateDecision: input.gateDecision ?? null,\n thresholds: {\n requireCorpus: false,\n // The report gates on RunRecord-level outcomes, not on a separate scenario\n // corpus (KnowledgeReleaseInput carries no dataset/scenarios). The scenario\n // floor must therefore be 0 — otherwise the corpus axis fails closed on a\n // dimension the report has no way to satisfy, masking the run-based gate.\n minScenarioCount: 0,\n requireHoldout: input.hasHoldout ?? false,\n minHoldoutRuns: input.hasHoldout ? 1 : 0,\n minSearchRuns: 1,\n minMeanScore: input.minScore ?? 0.7,\n },\n })\n const release: KnowledgeRelease = {\n id: stableId('krel', `${input.candidateId}:${input.createdAt ?? new Date().toISOString()}`),\n candidateId: input.candidateId,\n createdAt: input.createdAt ?? new Date().toISOString(),\n promoted: scorecard.status !== 'fail' && (input.promotedIsBest ?? true),\n scorecard,\n runRecordIds: runRecords.map((record) => record.runId),\n }\n return { release, scorecard, candidateRuns: input.candidateRuns, baselineRuns }\n}\n","import {\n blockingKnowledgeEval,\n type ControlEvalResult,\n type ControlRuntimeConfig,\n objectiveEval,\n} from '@tangle-network/agent-eval'\nimport type {\n BuildEvalKnowledgeBundleOptions,\n EvalKnowledgeBundleBuildResult,\n KnowledgeReadinessSpec,\n} from './eval-readiness'\nimport { createKnowledgeEvent } from './events'\nimport { buildKnowledgeIndex } from './indexer'\nimport { lintKnowledgeIndex } from './lint'\nimport { type ApplyWriteBlocksResult, applyKnowledgeWriteBlocks } from './proposals'\nimport { readinessFor } from './readiness-helpers'\nimport {\n type AddSourceOptions,\n type AddSourceTextInput,\n addSourcePath,\n addSourceText,\n} from './sources'\nimport { initKnowledgeBase } from './store'\nimport type { KnowledgeEvent, KnowledgeIndex, KnowledgeLintFinding, SourceRecord } from './types'\nimport {\n type ValidateKnowledgeOptions,\n type ValidateKnowledgeResult,\n validateKnowledgeIndex,\n} from './validate'\n\nexport interface KnowledgeResearchLoopContext {\n root: string\n goal: string\n iteration: number\n index: KnowledgeIndex\n lintFindings: KnowledgeLintFinding[]\n validation: ValidateKnowledgeResult\n readiness?: EvalKnowledgeBundleBuildResult\n previousSteps: KnowledgeResearchLoopStep[]\n signal?: AbortSignal\n}\n\nexport interface KnowledgeResearchLoopDecision {\n /**\n * Free-form notes from the researcher. Keep this human-readable; products can\n * store it as the research transcript.\n */\n notes?: string\n /**\n * Local files to register as immutable sources before applying proposals.\n */\n sourcePaths?: string[]\n /**\n * Textual source artifacts discovered by an agent, browser worker, connector,\n * or deep-research process.\n */\n sourceTexts?: AddSourceTextInput[]\n /**\n * Safe write protocol text. The loop parses and applies only accepted\n * `---FILE: knowledge/...---` blocks.\n */\n proposalText?: string\n /**\n * The researcher decides when the wiki is good enough. The loop deliberately\n * does not encode a domain-specific definition of \"done\".\n */\n done?: boolean\n metadata?: Record<string, unknown>\n}\n\nexport interface KnowledgeResearchLoopStep {\n iteration: number\n notes?: string\n addedSources: SourceRecord[]\n applied?: ApplyWriteBlocksResult\n lintFindings: KnowledgeLintFinding[]\n validation: ValidateKnowledgeResult\n readiness?: EvalKnowledgeBundleBuildResult\n event: KnowledgeEvent\n done: boolean\n metadata?: Record<string, unknown>\n}\n\nexport interface RunKnowledgeResearchLoopOptions {\n root: string\n goal: string\n maxIterations?: number\n actor?: string\n strict?: ValidateKnowledgeOptions['strict']\n readinessSpecs?: KnowledgeReadinessSpec[]\n readinessTaskId?: string\n readiness?: Omit<BuildEvalKnowledgeBundleOptions, 'taskId' | 'index' | 'specs'>\n sourceOptions?: Pick<AddSourceOptions, 'adapters' | 'now'>\n signal?: AbortSignal\n step(\n context: KnowledgeResearchLoopContext,\n ): Promise<KnowledgeResearchLoopDecision> | KnowledgeResearchLoopDecision\n onStep?: (step: KnowledgeResearchLoopStep) => Promise<void> | void\n}\n\nexport interface KnowledgeResearchLoopResult {\n root: string\n goal: string\n iterations: number\n done: boolean\n index: KnowledgeIndex\n lintFindings: KnowledgeLintFinding[]\n validation: ValidateKnowledgeResult\n readiness?: EvalKnowledgeBundleBuildResult\n steps: KnowledgeResearchLoopStep[]\n}\n\nexport type KnowledgeControlLoopState = KnowledgeResearchLoopContext\nexport type KnowledgeControlLoopAction = KnowledgeResearchLoopDecision\nexport type KnowledgeControlLoopActionResult = KnowledgeResearchLoopStep\n\nexport interface KnowledgeControlLoopAdapterOptions {\n root: string\n goal: string\n actor?: string\n strict?: ValidateKnowledgeOptions['strict']\n readinessSpecs?: KnowledgeReadinessSpec[]\n readinessTaskId?: string\n readiness?: Omit<BuildEvalKnowledgeBundleOptions, 'taskId' | 'index' | 'specs'>\n sourceOptions?: Pick<AddSourceOptions, 'adapters' | 'now'>\n}\n\nexport type KnowledgeControlLoopAdapter = Pick<\n ControlRuntimeConfig<\n KnowledgeControlLoopState,\n KnowledgeControlLoopAction,\n KnowledgeControlLoopActionResult,\n ControlEvalResult\n >,\n 'intent' | 'observe' | 'validate' | 'act' | 'shouldStop'\n>\n\n/**\n * Adapter for running knowledge growth through `agent-eval`'s generic control\n * runtime. The caller still owns `decide`: that can be a proposer agent,\n * reviewer agent, deterministic policy, or a composition of all three.\n */\nexport function createKnowledgeControlLoopAdapter(\n options: KnowledgeControlLoopAdapterOptions,\n): KnowledgeControlLoopAdapter {\n let initialized = false\n const appliedSteps: KnowledgeResearchLoopStep[] = []\n\n return {\n intent: options.goal,\n async observe({ history, abortSignal }) {\n if (abortSignal.aborted) throw new Error('Knowledge control loop aborted')\n if (!initialized) {\n await initKnowledgeBase(options.root)\n initialized = true\n }\n const index = await buildKnowledgeIndex(options.root)\n const validation = validateKnowledgeIndex(index, { strict: options.strict })\n const lintFindings = lintKnowledgeIndex(index)\n const readiness = readinessFor(options, index)\n return {\n root: options.root,\n goal: options.goal,\n iteration: history.length + 1,\n index,\n lintFindings,\n validation,\n readiness,\n previousSteps: [...appliedSteps],\n signal: abortSignal,\n }\n },\n validate({ state }) {\n const errorFindings = state.validation.findings.filter(\n (finding) => finding.severity === 'error',\n )\n const evals: ControlEvalResult[] = [\n objectiveEval({\n id: 'knowledge-valid',\n passed: state.validation.ok,\n severity: 'critical',\n detail: state.validation.ok\n ? 'Knowledge index is valid.'\n : 'Knowledge index has validation errors.',\n metadata: { findings: state.validation.findings },\n }),\n objectiveEval({\n id: 'knowledge-lint-errors',\n passed: errorFindings.length === 0,\n severity: 'error',\n detail:\n errorFindings.length === 0\n ? 'No lint errors.'\n : `${errorFindings.length} lint error(s).`,\n metadata: { findings: errorFindings },\n }),\n ]\n if (state.readiness) evals.push(blockingKnowledgeEval(state.readiness.report))\n return evals\n },\n shouldStop() {\n return { stop: false, pass: false, reason: 'knowledge driver owns stop decisions' }\n },\n async act(action, ctx) {\n const step = await applyKnowledgeResearchDecision(options, action, ctx.state.iteration)\n appliedSteps.push(step)\n return step\n },\n }\n}\n\nexport async function runKnowledgeResearchLoop(\n options: RunKnowledgeResearchLoopOptions,\n): Promise<KnowledgeResearchLoopResult> {\n const maxIterations = Math.max(1, options.maxIterations ?? 3)\n await initKnowledgeBase(options.root)\n const steps: KnowledgeResearchLoopStep[] = []\n let index = await buildKnowledgeIndex(options.root)\n let validation = validateKnowledgeIndex(index, { strict: options.strict })\n let lintFindings = lintKnowledgeIndex(index)\n let readiness = readinessFor(options, index)\n let done = false\n\n for (let iteration = 1; iteration <= maxIterations; iteration++) {\n if (options.signal?.aborted) throw new Error('Knowledge research loop aborted')\n const decision = await options.step({\n root: options.root,\n goal: options.goal,\n iteration,\n index,\n lintFindings,\n validation,\n readiness,\n previousSteps: steps,\n signal: options.signal,\n })\n\n done = Boolean(decision.done)\n const step = await applyKnowledgeResearchDecision(options, decision, iteration)\n index = await buildKnowledgeIndex(options.root)\n validation = step.validation\n lintFindings = step.lintFindings\n readiness = step.readiness\n steps.push(step)\n await options.onStep?.(step)\n\n if (done) break\n if (!step.applied && step.addedSources.length === 0) break\n }\n\n return {\n root: options.root,\n goal: options.goal,\n iterations: steps.length,\n done,\n index,\n lintFindings,\n validation,\n readiness,\n steps,\n }\n}\n\nasync function applyKnowledgeResearchDecision(\n options: KnowledgeControlLoopAdapterOptions,\n decision: KnowledgeResearchLoopDecision,\n iteration = 1,\n): Promise<KnowledgeResearchLoopStep> {\n const addedSources: SourceRecord[] = []\n for (const sourcePath of decision.sourcePaths ?? []) {\n addedSources.push(...(await addSourcePath(options.root, sourcePath, options.sourceOptions)))\n }\n for (const sourceText of decision.sourceTexts ?? []) {\n addedSources.push(await addSourceText(options.root, sourceText, options.sourceOptions))\n }\n\n const applied = decision.proposalText\n ? await applyKnowledgeWriteBlocks(options.root, decision.proposalText)\n : undefined\n\n const index = await buildKnowledgeIndex(options.root)\n const validation = validateKnowledgeIndex(index, { strict: options.strict })\n const lintFindings = lintKnowledgeIndex(index)\n const readiness = readinessFor(options, index)\n const done = Boolean(decision.done)\n const event = createKnowledgeEvent({\n type: 'research.iteration',\n actor: options.actor,\n target: options.root,\n metadata: {\n goal: options.goal,\n iteration,\n done,\n addedSourceCount: addedSources.length,\n written: applied?.written,\n warningCount: applied?.warnings.length ?? 0,\n errorCount: validation.findings.filter((finding) => finding.severity === 'error').length,\n },\n })\n\n return {\n iteration,\n notes: decision.notes,\n addedSources,\n applied,\n lintFindings,\n validation,\n readiness,\n event,\n done,\n metadata: decision.metadata,\n }\n}\n","import {\n type BuildEvalKnowledgeBundleOptions,\n buildEvalKnowledgeBundle,\n type EvalKnowledgeBundleBuildResult,\n type KnowledgeReadinessSpec,\n} from './eval-readiness'\nimport type { KnowledgeIndex } from './types'\n\n/**\n * The subset of a research-loop options object the readiness gate needs:\n * the goal (default task id), the readiness specs, an optional task-id\n * override, and the rest of the bundle options minus the fields this helper\n * supplies (`taskId`/`index`/`specs`).\n *\n * Both the single-agent (`research-loop`) and two-agent (`two-agent-research-loop`)\n * options shapes satisfy this structurally, so the gate is single-sourced.\n */\nexport interface ReadinessGateOptions {\n goal: string\n readinessSpecs?: KnowledgeReadinessSpec[]\n readinessTaskId?: string\n readiness?: Omit<BuildEvalKnowledgeBundleOptions, 'taskId' | 'index' | 'specs'>\n}\n\n/**\n * Build the readiness bundle for a loop run over the given index, or\n * `undefined` when no readiness specs are configured (no gate to report).\n */\nexport function readinessFor(\n options: ReadinessGateOptions,\n index: KnowledgeIndex,\n): EvalKnowledgeBundleBuildResult | undefined {\n if (!options.readinessSpecs?.length) return undefined\n return buildEvalKnowledgeBundle({\n ...(options.readiness ?? {}),\n taskId: options.readinessTaskId ?? options.goal,\n index,\n specs: options.readinessSpecs,\n })\n}\n","import {\n type Budget,\n type DeliverableSpec,\n type ExecutorConfig,\n type SupervisedResult,\n type SuperviseOptions,\n type SupervisorProfile,\n supervise,\n} from '@tangle-network/agent-runtime/loops'\nimport type { BuildEvalKnowledgeBundleOptions, KnowledgeReadinessSpec } from './eval-readiness'\nimport { buildKnowledgeIndex } from './indexer'\nimport { researcherProfile } from './profiles/researcher'\nimport { readinessFor } from './readiness-helpers'\nimport { initKnowledgeBase } from './store'\n\n/**\n * Default standing instructions for a research supervisor. It does NOT solve the\n * research itself — it DECOMPOSES the goal into sub-topics and spawns one\n * researcher per sub-topic over the live `Scope`, widening/narrowing until the\n * readiness gate (the deliverable check) reports the knowledge base is ready.\n */\nexport const RESEARCH_SUPERVISOR_SYSTEM_PROMPT = [\n 'You are a research SUPERVISOR. You do not answer the research question yourself —',\n 'you create and manage a team of researcher workers that grow ONE shared',\n 'knowledge base until it is ready.',\n '',\n 'Each round:',\n ' 1. Read the goal and the gaps the readiness gate still reports.',\n ' 2. DECOMPOSE the open work into independent sub-topics. Spawn ONE researcher',\n ' per sub-topic (spawn_agent) — split by sub-topic, never duplicate work.',\n ' Spawn more researchers for a broad goal, fewer for a narrow one.',\n ' 3. Wait for the researchers to settle (await_event). Steer or re-spawn for',\n ' the sub-topics that came back thin.',\n ' 4. STOP as soon as the deliverable check reports the knowledge base is ready.',\n '',\n 'Conserve your budget: every spawn spends from one shared pool. Prefer a small',\n 'number of well-scoped researchers over a large fanout that re-covers the same',\n 'ground.',\n].join('\\n')\n\nexport interface ResearchSupervisorOptions {\n /** Where the knowledge base lives. The deliverable check reads readiness here. */\n root: string\n /** The research goal handed to the supervisor as its task. */\n goal: string\n /**\n * Readiness specs define DONE: the supervisor's deliverable check passes once\n * `scoreKnowledgeReadiness` reports no blocking gaps over the live KB. Required\n * — without a gate the supervisor would never know when to stop.\n */\n readinessSpecs: KnowledgeReadinessSpec[]\n readinessTaskId?: string\n readiness?: Omit<BuildEvalKnowledgeBundleOptions, 'taskId' | 'index' | 'specs'>\n /** The conserved compute pool for the whole supervised run. */\n budget: Budget\n /**\n * WHERE researcher workers run — the caller's real backend seam (sandbox with\n * a `sandboxClient`, or router/cli/bridge). The supervisor spawns researchers\n * on this backend; assembling the seam (creds, sandbox SDK) is the caller's,\n * so there is no fabricated default.\n */\n backend: ExecutorConfig\n /** Harness for the researcher worker profile (default `researcherProfile`'s). */\n harness?: string\n /** The supervisor brain's router model. */\n supervisorModel?: string\n /** Custom supervisor instructions. Defaults to {@link RESEARCH_SUPERVISOR_SYSTEM_PROMPT}. */\n supervisorSystemPrompt?: string\n /** Extra `supervise()` knobs (perWorker, maxLiveWorkers, router, allowedModels, …). */\n superviseOptions?: Partial<Omit<SuperviseOptions, 'budget' | 'backend' | 'deliverable'>>\n}\n\n/**\n * Build the deliverable (completion oracle) for a research supervisor: \"settled\n * ⟺ the knowledge base is ready\". It re-reads the KB from disk and runs the\n * readiness gate, so the supervisor stops on the REAL grounded state — not on a\n * worker's self-report.\n */\nexport function knowledgeReadinessDeliverable(\n options: Pick<\n ResearchSupervisorOptions,\n 'root' | 'goal' | 'readinessSpecs' | 'readinessTaskId' | 'readiness'\n >,\n): DeliverableSpec<unknown> {\n return {\n describe: `knowledge base at ${options.root} is ready for: ${options.goal}`,\n async check() {\n const index = await buildKnowledgeIndex(options.root)\n const bundle = readinessFor(options, index)\n // `readinessSpecs` is required on the supervisor, so the bundle is always\n // built; an empty/undefined gate means nothing blocks readiness.\n return (bundle?.report.blockingMissingRequirements.length ?? 0) === 0\n },\n }\n}\n\n/**\n * A research supervisor: a SUPERVISOR brain creates the topology dynamically —\n * how many researchers, split by sub-topic — over the `Scope`, with the\n * knowledge-readiness gate as its stop condition.\n *\n * This is a thin wrapper over `supervise(profile, goal, opts)`. It composes the\n * existing pieces and builds nothing new: the researcher `AgentProfile` (from\n * `researcherProfile`) is the worker shape the supervisor spawns, and\n * `knowledgeReadinessDeliverable` (over `scoreKnowledgeReadiness`) is the\n * completion oracle that decides when the KB is ready.\n *\n * Needs creds (a real supervisor router brain + a worker backend), so it is the\n * LIVE path. The offline two-agent loop is `runTwoAgentResearchLoop`.\n */\nexport async function runResearchSupervisor(\n options: ResearchSupervisorOptions,\n): Promise<SupervisedResult<unknown>> {\n await initKnowledgeBase(options.root)\n\n // The researcher profile is the worker CONTRACT the supervisor spawns against.\n // Its system prompt is appended so the supervisor authors researcher workers\n // that emit source-grounded, propose-don't-apply contributions.\n const { profile: workerProfile } = researcherProfile({ harness: options.harness })\n const baseInstructions = options.supervisorSystemPrompt ?? RESEARCH_SUPERVISOR_SYSTEM_PROMPT\n const workerContract = workerProfile.prompt?.systemPrompt\n const systemPrompt = workerContract\n ? `${baseInstructions}\\n\\nEach researcher worker you spawn follows this contract:\\n${workerContract}`\n : baseInstructions\n\n const profile: SupervisorProfile = {\n name: 'research-supervisor',\n model: options.supervisorModel,\n systemPrompt,\n }\n\n return supervise(profile, options.goal, {\n ...options.superviseOptions,\n budget: options.budget,\n backend: options.backend,\n deliverable: knowledgeReadinessDeliverable(options),\n })\n}\n","import type { KnowledgeReadinessReport } from '@tangle-network/agent-eval'\nimport {\n type BuildEvalKnowledgeBundleOptions,\n buildEvalKnowledgeBundle,\n type EvalKnowledgeBundleBuildResult,\n type KnowledgeReadinessSpec,\n} from './eval-readiness'\nimport { createKnowledgeEvent } from './events'\nimport { buildKnowledgeIndex } from './indexer'\nimport { applyKnowledgeWriteBlocks } from './proposals'\nimport { readinessFor } from './readiness-helpers'\nimport { searchKnowledge } from './search'\nimport { type AddSourceOptions, type AddSourceTextInput, addSourceText } from './sources'\nimport { initKnowledgeBase } from './store'\nimport type { KnowledgeEvent, KnowledgeIndex, KnowledgeSearchResult, SourceRecord } from './types'\n\n/**\n * A knowledge gap the loop surfaces from `scoreKnowledgeReadiness`. The worker\n * targets these; the driver folds the unfilled remainder into the worker's next\n * prompt and runs its own gap-fill pass over them.\n */\nexport interface KnowledgeGap {\n /** Readiness-spec id this gap belongs to. */\n id: string\n /** Human-readable description of what's missing. */\n description: string\n /** The search query the readiness check ran for this requirement. */\n query: string\n /** True when the gap blocks readiness (vs. a soft, non-blocking gap). */\n blocking: boolean\n}\n\n/** A new source the worker (or driver) discovered and wants to add to the KB. */\nexport type ResearchSourceProposal = AddSourceTextInput\n\n/**\n * What a research agent contributes in one round. Both the worker and (when\n * `driverResearches` is on) the driver produce this shape — the worker ADDS\n * primary findings, the driver gap-FILLS the ones the worker missed.\n *\n * `proposalText` is the safe write-protocol text (`---FILE: knowledge/...---`\n * blocks). The loop only applies it AFTER the driver has verified the round's\n * sources, so a rejected source never reaches the curated pages.\n */\nexport interface ResearchContribution {\n /** Immutable sources to register (the raw evidence). */\n sources?: ResearchSourceProposal[]\n /** Safe write-protocol text producing curated `knowledge/*.md` pages. */\n proposalText?: string\n /**\n * Build the page write-protocol text FROM the sources the driver accepted —\n * the curated, citing pages the readiness gate searches. Receives the\n * registered `SourceRecord`s (with their assigned ids, so a page's frontmatter\n * `sources:` can cite them). Returns `---FILE: knowledge/...---` block text or\n * `undefined`. Runs after verification, so a page never cites a rejected\n * source. Concatenated after any static `proposalText`.\n */\n buildPages?: (acceptedSources: SourceRecord[]) => string | undefined\n /** Free-form research transcript — products can persist this. */\n notes?: string\n metadata?: Record<string, unknown>\n}\n\n/** Context handed to the worker each round. */\nexport interface WorkerResearchContext {\n root: string\n goal: string\n round: number\n index: KnowledgeIndex\n /** Gaps the readiness gate currently reports — what the worker should close. */\n gaps: KnowledgeGap[]\n /** Steer text the driver folded in from the previous round's remaining gaps. */\n steer?: string\n readiness: EvalKnowledgeBundleBuildResult\n signal?: AbortSignal\n}\n\n/** Context handed to the driver's verifier for one candidate source. */\nexport interface SourceVerificationContext {\n root: string\n goal: string\n round: number\n index: KnowledgeIndex\n gaps: KnowledgeGap[]\n /** Sources already accepted earlier THIS round (in-round dedup). */\n acceptedThisRound: ResearchSourceProposal[]\n signal?: AbortSignal\n}\n\n/** A single rejected source plus the reason the driver gave. */\nexport interface RejectedSource {\n source: ResearchSourceProposal\n reason: string\n}\n\n/** Context handed to the driver's gap-fill pass (only when `driverResearches`). */\nexport interface DriverResearchContext {\n root: string\n goal: string\n round: number\n index: KnowledgeIndex\n /** Gaps STILL open after the worker's accepted contribution applied. */\n remainingGaps: KnowledgeGap[]\n readiness: EvalKnowledgeBundleBuildResult\n signal?: AbortSignal\n}\n\n/**\n * The differentiated driver role.\n *\n * - `verifySource` — the gate the worker's additions pass before they commit.\n * Return `{ accept: true }` to keep a source or `{ accept: false, reason }`\n * to reject it (not real / not relevant / duplicate). The loop dedups exact\n * duplicates (same `uri` already in the KB or accepted this round) BEFORE\n * calling this, so the verifier only sees genuinely-new candidates.\n * - `research` — the driver's OWN gap-fill pass over the gaps the worker left\n * open. Only invoked when `driverResearches` is true.\n * - `foldGaps` — turn the remaining gaps into a steer string for the worker's\n * next prompt. Defaults to a compact bulleted list when omitted.\n */\nexport interface ResearchDriver {\n verifySource(\n source: ResearchSourceProposal,\n ctx: SourceVerificationContext,\n ): Promise<SourceVerdict> | SourceVerdict\n research?(ctx: DriverResearchContext): Promise<ResearchContribution> | ResearchContribution\n foldGaps?(gaps: KnowledgeGap[]): string\n}\n\nexport type SourceVerdict = { accept: true } | { accept: false; reason: string }\n\n/** The worker: primary research targeting the round's gaps. */\nexport type ResearchWorker = (\n ctx: WorkerResearchContext,\n) => Promise<ResearchContribution> | ResearchContribution\n\nexport interface TwoAgentResearchLoopOptions {\n root: string\n goal: string\n worker: ResearchWorker\n driver: ResearchDriver\n /**\n * When false (default), the driver ONLY verifies + gates — a pure coordinator\n * that contributes no research of its own (the \"doesn't participate in the\n * work\" mode). When true, the driver also runs its `research` gap-fill pass\n * each round over the gaps the worker left open.\n */\n driverResearches?: boolean\n maxRounds?: number\n actor?: string\n /** Readiness specs define the gate; an empty list means the loop never gates. */\n readinessSpecs?: KnowledgeReadinessSpec[]\n readinessTaskId?: string\n readiness?: Omit<BuildEvalKnowledgeBundleOptions, 'taskId' | 'index' | 'specs'>\n sourceOptions?: Pick<AddSourceOptions, 'adapters' | 'now'>\n signal?: AbortSignal\n onRound?: (round: TwoAgentResearchRound) => Promise<void> | void\n}\n\nexport interface TwoAgentResearchRound {\n round: number\n /** Gaps reported at the START of the round (what the worker targeted). */\n gaps: KnowledgeGap[]\n /** Worker sources accepted by the driver and written to the KB. */\n acceptedWorkerSources: SourceRecord[]\n /** Worker sources the driver rejected (with reasons) — never written. */\n rejectedWorkerSources: RejectedSource[]\n /** Sources the driver itself added in its gap-fill pass. */\n driverSources: SourceRecord[]\n /** Curated pages written this round (worker proposal + driver proposal). */\n writtenPages: string[]\n readiness?: EvalKnowledgeBundleBuildResult\n /** True once the readiness gate reports no blocking gaps. */\n ready: boolean\n event: KnowledgeEvent\n notes: { worker?: string; driver?: string }\n}\n\nexport interface TwoAgentResearchLoopResult {\n root: string\n goal: string\n rounds: number\n ready: boolean\n index: KnowledgeIndex\n readiness?: EvalKnowledgeBundleBuildResult\n steps: TwoAgentResearchRound[]\n}\n\n/**\n * Two-agent (driver + worker) sibling of `runKnowledgeResearchLoop`.\n *\n * Both agents research to grow ONE knowledge base. The roles are differentiated:\n *\n * - **WORKER** = primary research. Each round it reads the open gaps, discovers\n * new sources, and proposes additions (`sources` + `proposalText`). It ADDS.\n * - **DRIVER** = the verifier / gap-filler / gate. It (1) VERIFIES the worker's\n * sources before they commit — dedup against the KB, then `verifySource`\n * rejects ones that aren't real/relevant; (2) GAP-FILLS the gaps the worker\n * missed with its own research pass (when `driverResearches`); (3) folds the\n * remaining gaps into the worker's next prompt; and (4) GATES on\n * `scoreKnowledgeReadiness` — the loop stops as soon as there are no blocking\n * gaps.\n *\n * Set `driverResearches: false` (default) for the pure-coordinator mode: the\n * driver only verifies + gates and contributes no research itself.\n *\n * Composes existing atoms — `initKnowledgeBase`, `addSourceText`,\n * `applyKnowledgeWriteBlocks`, `buildEvalKnowledgeBundle` (the readiness gate),\n * and `searchKnowledge` — and reinvents none of them.\n */\nexport async function runTwoAgentResearchLoop(\n options: TwoAgentResearchLoopOptions,\n): Promise<TwoAgentResearchLoopResult> {\n const maxRounds = Math.max(1, options.maxRounds ?? 3)\n await initKnowledgeBase(options.root)\n const steps: TwoAgentResearchRound[] = []\n let index = await buildKnowledgeIndex(options.root)\n let readiness = readinessFor(options, index)\n let ready = isReady(readiness?.report)\n let steer: string | undefined\n\n for (let round = 1; round <= maxRounds && !ready; round++) {\n if (options.signal?.aborted) throw new Error('Two-agent research loop aborted')\n\n const gaps = gapsFromReadiness(readiness)\n\n // 1. WORKER: primary research over the open gaps.\n const workerContribution = await options.worker({\n root: options.root,\n goal: options.goal,\n round,\n index,\n gaps,\n steer,\n readiness: requireReadiness(readiness, options),\n signal: options.signal,\n })\n\n // 2. DRIVER VERIFIES the worker's sources before they commit.\n const accepted: ResearchSourceProposal[] = []\n const rejectedWorkerSources: RejectedSource[] = []\n // Dedup against the ORIGINAL input uri. `addSourceText` rewrites `record.uri`\n // to a slugified raw path and stashes the caller's uri under\n // `metadata.originalUri`, so that — not the stored uri — is the round-to-round\n // identity a verifier dedups against.\n const existingUris = new Set(\n index.sources.flatMap((source) =>\n typeof source.metadata?.originalUri === 'string' ? [source.metadata.originalUri] : [],\n ),\n )\n for (const source of workerContribution.sources ?? []) {\n if (isDuplicate(source, existingUris, accepted)) {\n rejectedWorkerSources.push({ source, reason: 'duplicate: already in the knowledge base' })\n continue\n }\n const verdict = await options.driver.verifySource(source, {\n root: options.root,\n goal: options.goal,\n round,\n index,\n gaps,\n acceptedThisRound: accepted,\n signal: options.signal,\n })\n if (verdict.accept) accepted.push(source)\n else rejectedWorkerSources.push({ source, reason: verdict.reason })\n }\n\n // Register the accepted worker sources, then apply the worker's curated\n // pages — but only when at least one source survived verification, so a\n // page never cites a rejected source.\n const acceptedWorkerSources = await registerSources(options, accepted)\n const writtenPages: string[] = []\n writtenPages.push(\n ...(await applyPages(options.root, workerContribution, acceptedWorkerSources)),\n )\n\n // Re-index so the driver's gap-fill pass sees the worker's contribution.\n index = await buildKnowledgeIndex(options.root)\n readiness = readinessFor(options, index)\n\n // 3. DRIVER GAP-FILLS the gaps the worker left open (opt-in).\n let driverSources: SourceRecord[] = []\n let driverNotes: string | undefined\n if (options.driverResearches && options.driver.research) {\n const remainingGaps = gapsFromReadiness(readiness)\n const driverContribution = await options.driver.research({\n root: options.root,\n goal: options.goal,\n round,\n index,\n remainingGaps,\n readiness: requireReadiness(readiness, options),\n signal: options.signal,\n })\n driverNotes = driverContribution.notes\n driverSources = await registerSources(options, driverContribution.sources ?? [])\n writtenPages.push(...(await applyPages(options.root, driverContribution, driverSources)))\n index = await buildKnowledgeIndex(options.root)\n readiness = readinessFor(options, index)\n }\n\n // 4. DRIVER GATES on readiness and folds the remainder into the next prompt.\n ready = isReady(readiness?.report)\n const remainingGaps = gapsFromReadiness(readiness)\n steer = ready ? undefined : foldGaps(options.driver, remainingGaps)\n\n const step: TwoAgentResearchRound = {\n round,\n gaps,\n acceptedWorkerSources,\n rejectedWorkerSources,\n driverSources,\n writtenPages,\n readiness,\n ready,\n event: createKnowledgeEvent({\n type: 'research.iteration',\n actor: options.actor,\n target: options.root,\n metadata: {\n goal: options.goal,\n round,\n ready,\n acceptedWorkerSourceCount: acceptedWorkerSources.length,\n rejectedWorkerSourceCount: rejectedWorkerSources.length,\n driverSourceCount: driverSources.length,\n writtenPageCount: writtenPages.length,\n remainingGapCount: remainingGaps.length,\n },\n }),\n notes: { worker: workerContribution.notes, driver: driverNotes },\n }\n steps.push(step)\n await options.onRound?.(step)\n }\n\n return {\n root: options.root,\n goal: options.goal,\n rounds: steps.length,\n ready,\n index,\n readiness,\n steps,\n }\n}\n\nfunction isReady(report: KnowledgeReadinessReport | undefined): boolean {\n // No specs ⇒ no gate ⇒ the loop runs to `maxRounds`. With specs, the gate is\n // \"no blocking gaps remain\".\n if (!report) return false\n return report.blockingMissingRequirements.length === 0\n}\n\nfunction gapsFromReadiness(readiness: EvalKnowledgeBundleBuildResult | undefined): KnowledgeGap[] {\n if (!readiness) return []\n const blocking = readiness.report.blockingMissingRequirements.map((requirement) =>\n gapFor(requirement, readiness, true),\n )\n const nonBlocking = readiness.report.nonBlockingGaps.map((requirement) =>\n gapFor(requirement, readiness, false),\n )\n return [...blocking, ...nonBlocking]\n}\n\nfunction gapFor(\n requirement: { id: string; description: string; metadata?: Record<string, unknown> },\n readiness: EvalKnowledgeBundleBuildResult,\n blocking: boolean,\n): KnowledgeGap {\n const spec = readiness.requirements.find((entry) => entry.id === requirement.id)\n const query =\n typeof spec?.metadata?.query === 'string' ? spec.metadata.query : requirement.description\n return { id: requirement.id, description: requirement.description, query, blocking }\n}\n\nfunction foldGaps(driver: ResearchDriver, gaps: KnowledgeGap[]): string | undefined {\n if (gaps.length === 0) return undefined\n if (driver.foldGaps) return driver.foldGaps(gaps)\n return [\n 'The knowledge base is still missing the following. Prioritise these next round:',\n ...gaps.map(\n (gap) => `- (${gap.blocking ? 'blocking' : 'soft'}) ${gap.description} [${gap.id}]`,\n ),\n ].join('\\n')\n}\n\nfunction isDuplicate(\n source: ResearchSourceProposal,\n existingUris: Set<string>,\n accepted: ResearchSourceProposal[],\n): boolean {\n return existingUris.has(source.uri) || accepted.some((candidate) => candidate.uri === source.uri)\n}\n\nasync function registerSources(\n options: TwoAgentResearchLoopOptions,\n sources: ResearchSourceProposal[],\n): Promise<SourceRecord[]> {\n const records: SourceRecord[] = []\n for (const source of sources) {\n records.push(await addSourceText(options.root, source, options.sourceOptions))\n }\n return records\n}\n\n/**\n * Apply a contribution's curated pages. Static `proposalText` plus a\n * `buildPages(acceptedSources)` result are concatenated and run through the safe\n * write protocol — but ONLY when at least one source survived verification, so a\n * page never cites a rejected (or absent) source.\n */\nasync function applyPages(\n root: string,\n contribution: ResearchContribution,\n acceptedSources: SourceRecord[],\n): Promise<string[]> {\n if (acceptedSources.length === 0) return []\n const parts: string[] = []\n if (contribution.proposalText) parts.push(contribution.proposalText)\n const built = contribution.buildPages?.(acceptedSources)\n if (built) parts.push(built)\n if (parts.length === 0) return []\n const applied = await applyKnowledgeWriteBlocks(root, parts.join('\\n'))\n return applied.written\n}\n\nfunction requireReadiness(\n readiness: EvalKnowledgeBundleBuildResult | undefined,\n options: TwoAgentResearchLoopOptions,\n): EvalKnowledgeBundleBuildResult {\n if (readiness) return readiness\n // The worker/driver contexts type `readiness` as required for ergonomics; when\n // no specs are configured there is no gate to report, so synthesise an empty\n // bundle rather than forcing every caller to handle `undefined`.\n return buildEvalKnowledgeBundle({\n ...(options.readiness ?? {}),\n taskId: options.readinessTaskId ?? options.goal,\n index: emptyIndex(options.root),\n specs: [],\n })\n}\n\nfunction emptyIndex(root: string): KnowledgeIndex {\n return {\n root,\n generatedAt: new Date(0).toISOString(),\n sources: [],\n pages: [],\n graph: { nodes: [], edges: [] },\n }\n}\n\n/**\n * Helper for verifiers: does the candidate source's text/title overlap any page\n * the readiness search returns for a gap query? A cheap relevance heuristic the\n * driver can compose into `verifySource` (real verifiers can do more).\n */\nexport function sourceMatchesGaps(\n source: ResearchSourceProposal,\n index: KnowledgeIndex,\n gaps: KnowledgeGap[],\n): KnowledgeSearchResult[] {\n const haystack = `${source.title ?? ''}\\n${source.text}`.toLowerCase()\n const hits: KnowledgeSearchResult[] = []\n for (const gap of gaps) {\n for (const token of gap.query.toLowerCase().split(/\\s+/).filter(Boolean)) {\n if (haystack.includes(token)) {\n hits.push(...searchKnowledge(index, gap.query, 1))\n break\n }\n }\n }\n return hits\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkFO,SAAS,cACd,MACA,MACA,UAAgC,CAAC,GACZ;AACrB,QAAM,mBAAmB,QAAQ,oBAAoB;AACrD,QAAM,WAAqB,CAAC;AAE5B,QAAM,EAAE,KAAK,SAAS,UAAU,SAAS,IAAI,eAAe,MAAM,kBAAkB,MAAM;AAC1F,QAAM,EAAE,KAAK,SAAS,UAAU,SAAS,IAAI,eAAe,MAAM,kBAAkB,MAAM;AAC1F,WAAS,KAAK,GAAG,UAAU,GAAG,QAAQ;AAEtC,QAAM,UAA6B,CAAC;AACpC,QAAM,OAAO,oBAAI,IAAY;AAE7B,aAAW,CAAC,IAAI,YAAY,KAAK,SAAS;AACxC,SAAK,IAAI,EAAE;AACX,UAAM,eAAe,QAAQ,IAAI,EAAE;AACnC,QAAI,CAAC,cAAc;AACjB,cAAQ,KAAK;AAAA,QACX,YAAY;AAAA,QACZ,MAAM;AAAA,QACN,MAAM,EAAE,OAAO,aAAa,KAAK;AAAA,QACjC,oBAAoB,MAAM,aAAa,cAAc;AAAA,QACrD,KAAK,aAAa,WAAW;AAAA,QAC7B,YAAY,aAAa,WAAW;AAAA,MACtC,CAAC;AACD;AAAA,IACF;AACA,QAAI,aAAa,aAAa,aAAa,UAAU;AACnD,cAAQ,KAAK;AAAA,QACX,YAAY;AAAA,QACZ,MAAM;AAAA,QACN,MAAM,EAAE,QAAQ,aAAa,MAAM,OAAO,aAAa,KAAK;AAAA,QAC5D,oBAAoB,MAAM,CAAC,GAAG,aAAa,gBAAgB,GAAG,aAAa,cAAc,CAAC;AAAA,QAC1F,KAAK,aAAa,WAAW;AAAA,QAC7B,YAAY,aAAa,WAAW;AAAA,MACtC,CAAC;AAAA,IACH;AAAA,EACF;AAEA,aAAW,CAAC,IAAI,YAAY,KAAK,SAAS;AACxC,QAAI,KAAK,IAAI,EAAE,EAAG;AAClB,YAAQ,KAAK;AAAA,MACX,YAAY;AAAA,MACZ,MAAM;AAAA,MACN,MAAM,EAAE,QAAQ,aAAa,KAAK;AAAA,MAClC,oBAAoB,MAAM,aAAa,cAAc;AAAA,MACrD,KAAK,aAAa,WAAW;AAAA,MAC7B,YAAY,aAAa,WAAW;AAAA,IACtC,CAAC;AAAA,EACH;AAEA,QAAM,WAAW,QAAQ,mBACrB,QAAQ,OAAO,CAAC,MAAM,EAAE,mBAAmB,KAAK,CAAC,MAAM,QAAQ,kBAAkB,SAAS,CAAC,CAAC,CAAC,IAC7F;AAEJ,SAAO;AAAA,IACL,SAAS;AAAA,IACT,SAAS;AAAA,MACP,OAAO,SAAS,OAAO,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE;AAAA,MAClD,SAAS,SAAS,OAAO,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE;AAAA,MACtD,UAAU,SAAS,OAAO,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE;AAAA,IAC1D;AAAA,IACA;AAAA,EACF;AACF;AAEA,SAAS,eACP,WACA,kBACA,MAC6D;AAC7D,QAAM,MAAM,oBAAI,IAA+B;AAC/C,QAAM,WAAqB,CAAC;AAC5B,MAAI,UAAU;AACd,aAAW,YAAY,WAAW;AAChC,QAAI,oBAAoB,CAAC,SAAS,WAAW,YAAY;AACvD,iBAAW;AACX;AAAA,IACF;AACA,QAAI,IAAI,IAAI,SAAS,EAAE,GAAG;AACxB,eAAS,KAAK,GAAG,IAAI,2BAA2B,SAAS,EAAE,sBAAiB;AAAA,IAC9E;AACA,QAAI,IAAI,SAAS,IAAI,QAAQ;AAAA,EAC/B;AACA,MAAI,UAAU,GAAG;AACf,aAAS,KAAK,GAAG,IAAI,aAAa,OAAO,uCAAuC;AAAA,EAClF;AACA,SAAO,EAAE,KAAK,SAAS;AACzB;AAEA,SAAS,MAAS,OAAiB;AACjC,SAAO,CAAC,GAAG,IAAI,IAAI,KAAK,CAAC;AAC3B;;;AChKA,IAAM,kBAAmC;AAAA,EACvC,aAAa;AAAA,EACb,UAAU;AAAA,EACV,UAAU;AAAA,EACV,cAAc;AAChB;AAEO,SAAS,cACd,SACA,SACkB;AAClB,QAAM,OAAO,iBAAiB,EAAE,GAAG,iBAAiB,GAAI,WAAW,CAAC,EAAG,CAAC;AACxE,QAAM,EAAE,MAAM,WAAW,IAAI,iBAAiB,OAAO;AACrD,MAAI,KAAK,KAAK,MAAM,GAAI,QAAO,CAAC;AAEhC,QAAM,WAAW,cAAc,MAAM,UAAU;AAC/C,QAAM,SAA2B,CAAC;AAClC,aAAW,WAAW,UAAU;AAC9B,eAAW,QAAQ,UAAU,QAAQ,MAAM,IAAI,GAAG;AAChD,YAAM,QAAQ,QAAQ,QAAQ,KAAK;AACnC,aAAO,KAAK;AAAA,QACV,OAAO,OAAO;AAAA,QACd,MAAM,KAAK;AAAA,QACX,aAAa,QAAQ;AAAA,QACrB,WAAW;AAAA,QACX,SAAS,QAAQ,KAAK,KAAK;AAAA,QAC3B,WAAW,KAAK,KAAK,SAAS,KAAK;AAAA,MACrC,CAAC;AAAA,IACH;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,iBAAiB,SAAuD;AACtF,QAAM,aAAa,QAAQ,QAAQ,SAAS,IAAI;AAChD,MAAI,CAAC,WAAW,WAAW,OAAO,EAAG,QAAO,EAAE,MAAM,YAAY,YAAY,EAAE;AAC9E,QAAM,QAAQ,4BAA4B,KAAK,UAAU;AACzD,MAAI,CAAC,MAAO,QAAO,EAAE,MAAM,YAAY,YAAY,EAAE;AACrD,SAAO,EAAE,MAAM,WAAW,MAAM,MAAM,CAAC,EAAE,MAAM,GAAG,YAAY,MAAM,CAAC,EAAE,OAAO;AAChF;AAEA,SAAS,iBAAiB,MAAwC;AAChE,MAAI,KAAK,WAAW,KAAK,YAAa,MAAK,WAAW,KAAK;AAC3D,MAAI,KAAK,gBAAgB,KAAK,YAAa,MAAK,eAAe,KAAK,MAAM,KAAK,cAAc,CAAC;AAC9F,SAAO;AACT;AAQA,SAAS,cAAc,MAAc,YAA+B;AAClE,QAAM,QAAQ,KAAK,MAAM,IAAI;AAC7B,QAAM,WAAsB,CAAC;AAC7B,QAAM,WAAmC,CAAC;AAC1C,MAAI,UAAmE;AAAA,IACrE,OAAO,CAAC;AAAA,IACR,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AACA,MAAI,SAAS;AACb,MAAI,QAAuB;AAE3B,QAAM,QAAQ,MAAM;AAClB,UAAM,OAAO,QAAQ,MAAM,KAAK,IAAI;AACpC,QAAI,KAAK,KAAK,MAAM;AAClB,eAAS,KAAK,EAAE,MAAM,OAAO,QAAQ,OAAO,aAAa,QAAQ,YAAY,CAAC;AAAA,EAClF;AAEA,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,UAAM,OAAO,MAAM,CAAC;AACpB,UAAM,UAAU,KAAK,UAAU,IAAI,MAAM,SAAS,IAAI,IAAI;AAC1D,UAAM,aAAa,iBAAiB,KAAK,IAAI;AAC7C,QAAI,YAAY;AACd,YAAM,SAAS,WAAW,CAAC;AAC3B,cAAQ,UAAU,OAAO,SAAS,KAAK,WAAW,KAAK,IAAI,OAAO;AAClE,cAAQ,MAAM,KAAK,IAAI;AACvB,gBAAU;AACV;AAAA,IACF;AACA,UAAM,UAAU,UAAU,OAAO,wBAAwB,KAAK,IAAI,IAAI;AACtE,QAAI,SAAS;AACX,YAAM;AACN,YAAM,QAAQ,QAAQ,CAAC,EAAG;AAC1B,eAAS,KAAK,IAAI,QAAQ,CAAC,EAAG,KAAK;AACnC,eAAS,OAAO,QAAQ,GAAG,QAAQ,GAAG,OAAQ,QAAO,SAAS,IAAI;AAClE,gBAAU;AAAA,QACR,OAAO,CAAC,IAAI;AAAA,QACZ,OAAO;AAAA,QACP,aAAa,OAAO,QAAQ,QAAQ,EACjC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,OAAO,CAAC,IAAI,OAAO,CAAC,CAAC,EACxC,IAAI,CAAC,CAAC,WAAW,KAAK,MAAM,GAAG,IAAI,OAAO,OAAO,SAAS,CAAC,CAAC,IAAI,KAAK,EAAE,EACvE,KAAK,KAAK;AAAA,MACf;AACA,gBAAU;AACV;AAAA,IACF;AACA,YAAQ,MAAM,KAAK,IAAI;AACvB,cAAU;AAAA,EACZ;AACA,QAAM;AACN,SAAO;AACT;AAEA,SAAS,UAAU,MAAc,MAA+D;AAC9F,MAAI,KAAK,UAAU,KAAK,YAAa,QAAO,CAAC,EAAE,MAAM,OAAO,EAAE,CAAC;AAC/D,QAAM,QAAQ,WAAW,IAAI;AAC7B,QAAM,SAAiD,CAAC;AACxD,MAAI,SAAS;AACb,MAAI,cAAc;AAClB,aAAW,QAAQ,OAAO;AACxB,QAAI,WAAW,GAAI,eAAc,KAAK;AACtC,QAAI,OAAO,SAAS,KAAK,OAAO,SAAS,KAAK,KAAK,SAAS,KAAK,aAAa;AAC5E,aAAO,KAAK,EAAE,MAAM,OAAO,QAAQ,GAAG,OAAO,YAAY,CAAC;AAC1D,YAAM,UAAU,OAAO,MAAM,KAAK,IAAI,GAAG,OAAO,SAAS,KAAK,YAAY,CAAC;AAC3E,eAAS,UAAU,KAAK;AACxB,oBAAc,KAAK,IAAI,aAAa,KAAK,QAAQ,QAAQ,MAAM;AAAA,IACjE,OAAO;AACL,gBAAU,KAAK;AAAA,IACjB;AAAA,EACF;AACA,MAAI,OAAO,KAAK,MAAM,GAAI,QAAO,KAAK,EAAE,MAAM,OAAO,QAAQ,GAAG,OAAO,YAAY,CAAC;AACpF,SAAO,gBAAgB,QAAQ,IAAI;AACrC;AAEA,SAAS,WAAW,MAAsD;AACxE,QAAM,QAAgD,CAAC;AACvD,QAAM,aAAa,KAAK,MAAM,WAAW;AACzC,MAAI,SAAS;AACb,WAAS,IAAI,GAAG,IAAI,WAAW,QAAQ,KAAK,GAAG;AAC7C,UAAM,aAAa,WAAW,CAAC,KAAK,OAAO,WAAW,IAAI,CAAC,KAAK;AAChE,QAAI,cAAc,GAAI;AACtB,UAAM,KAAK,EAAE,MAAM,WAAW,OAAO,OAAO,CAAC;AAC7C,cAAU,UAAU;AAAA,EACtB;AACA,SAAO;AACT;AAEA,SAAS,gBACP,QACA,MACwC;AACxC,QAAM,MAA8C,CAAC;AACrD,aAAW,SAAS,QAAQ;AAC1B,UAAM,OAAO,IAAI,IAAI,SAAS,CAAC;AAC/B,QACE,QACA,MAAM,KAAK,SAAS,KAAK,YACzB,KAAK,KAAK,SAAS,MAAM,KAAK,UAAU,KAAK,UAC7C;AACA,WAAK,OAAO,GAAG,KAAK,IAAI;AAAA;AAAA,EAAO,MAAM,IAAI;AAAA,IAC3C,OAAO;AACL,UAAI,KAAK,EAAE,GAAG,MAAM,CAAC;AAAA,IACvB;AAAA,EACF;AACA,SAAO;AACT;;;AC/IO,SAAS,+BACd,QAC8B;AAC9B,SAAO;AAAA,IACL,MAAM,SAAS,OAAO,UAAU,CAAC,GAAG;AAClC,YAAM,cAAc,KAAK,IAAI,GAAG,QAAQ,eAAe,CAAC;AACxD,YAAM,UAA6B,CAAC;AACpC,UAAI,SAAS;AACb,qBAAe,UAAyB;AACtC,eAAO,SAAS,MAAM,QAAQ;AAC5B,cAAI,QAAQ,QAAQ,QAAS,OAAM,IAAI,MAAM,4BAA4B;AACzE,gBAAM,OAAO,MAAM,QAAQ;AAC3B,kBAAQ,KAAK,MAAM,OAAO,IAAI,MAAM,QAAQ,MAAM,CAAC;AAAA,QACrD;AAAA,MACF;AACA,YAAM,QAAQ,IAAI,MAAM,KAAK,EAAE,QAAQ,KAAK,IAAI,aAAa,MAAM,MAAM,EAAE,GAAG,OAAO,CAAC;AACtF,aAAO,QAAQ;AAAA,QACb,CAAC,GAAG,MACF,MAAM,UAAU,CAAC,SAAS,KAAK,OAAO,EAAE,MAAM,IAC9C,MAAM,UAAU,CAAC,SAAS,KAAK,OAAO,EAAE,MAAM;AAAA,MAClD;AAAA,IACF;AAAA,EACF;AACF;;;ACtDA;AAAA,EACE;AAAA,EAUA;AAAA,EAEA;AAAA,OACK;AA8BA,IAAM,0BAA0B;AAAA,EACrC,UAAU;AAAA,EACV,iBAAiB;AAAA,EACjB,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,aAAa;AAAA,EACb,kBAAkB;AAAA,EAClB,YAAY;AAAA,EACZ,SAAS;AACX;AAqDO,SAAS,oBAAoB,OAAyD;AAC3F,SAAO;AAAA,IACL,GAAG;AAAA,IACH,GAAG;AAAA,EACL;AACF;AAqBO,SAAS,yBACd,SACgC;AAChC,QAAM,cAAc,QAAQ,eAAe;AAC3C,QAAM,MAAM,QAAQ,OAAO,oBAAI,KAAK;AACpC,QAAM,6BAAsE,CAAC;AAC7E,QAAM,eAAe,QAAQ,MAAM,IAAI,CAAC,SAAS;AAC/C,UAAM,UAAU,gBAAgB,QAAQ,OAAO,KAAK,OAAO,WAAW;AACtE,+BAA2B,KAAK,EAAE,IAAI;AACtC,WAAO,sBAAsB,QAAQ,OAAO,MAAM,SAAS,GAAG;AAAA,EAChE,CAAC;AACD,QAAM,SAAS,wBAAwB;AAAA,IACrC,QAAQ,QAAQ;AAAA,IAChB;AAAA,IACA,aAAa,QAAQ;AAAA,IACrB,aAAa,aAAa,QAAQ,CAAC,gBAAgB,YAAY,WAAW;AAAA,IAC1E,UAAU,CAAC;AAAA,IACX,aAAa;AAAA,MACX,aAAa;AAAA,QAAQ,CAAC,gBACpB,mBAAmB,2BAA2B,YAAY,EAAE,KAAK,CAAC,CAAC;AAAA,MACrE;AAAA,IACF;AAAA,IACA,UAAU,QAAQ;AAAA,EACpB,CAAC;AACD,QAAM,YAAY,8BAA8B,OAAO,2BAA2B;AAClF,QAAM,mBAAmB,iCAAiC;AAAA,IACxD,GAAG,OAAO;AAAA,IACV,GAAG,OAAO;AAAA,EACZ,CAAC;AAED,SAAO;AAAA,IACL,QAAQ,OAAO;AAAA,IACf;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEA,SAAS,sBACP,OACA,MACA,SACA,KACsB;AACtB,QAAM,WAAW,QAAQ;AACzB,QAAM,YAAY,OAAO,QAAQ,QAAQ,CAAC,WAAW,OAAO,KAAK,SAAS,CAAC;AAC3E,QAAM,UAAU,MAAM,QAAQ,OAAO,CAAC,WAAW,UAAU,SAAS,OAAO,EAAE,CAAC;AAC9E,QAAM,YAAY,QAAQ,CAAC,GAAG,mBAAmB;AACjD,QAAM,iBAAiB,KAAK,aACxB,KAAK,IAAI,GAAG,UAAU,SAAS,KAAK,UAAU,IAC9C,UAAU,SAAS,IACjB,IACA;AACN,QAAM,cAAc,KAAK,UAAU,KAAK,IAAI,GAAG,WAAW,KAAK,OAAO,IAAI,WAAW,IAAI,IAAI;AAC7F,QAAM,YAAY,gBAAgB,SAAS,GAAG;AAC9C,QAAM,oBAAoB,MAAM,KAAK,IAAI,WAAW,gBAAgB,aAAa,UAAU,KAAK,CAAC;AAEjG,SAAO;AAAA,IACL,IAAI,KAAK;AAAA,IACT,aAAa,KAAK;AAAA,IAClB,aAAa,KAAK;AAAA,IAClB,UAAU,KAAK;AAAA,IACf,iBAAiB,KAAK;AAAA,IACtB,YAAY,KAAK;AAAA,IACjB,WAAW,KAAK;AAAA,IAChB,aAAa,KAAK;AAAA,IAClB,kBAAkB,KAAK;AAAA,IACvB;AAAA,IACA,aAAa,OAAO;AAAA,MAClB,GAAG,UAAU,IAAI,CAAC,aAAa,UAAU,QAAQ,EAAE;AAAA,MACnD,GAAG,QAAQ,IAAI,CAAC,WAAW,QAAQ,OAAO,KAAK,EAAE,EAAE;AAAA,IACrD,CAAC;AAAA,IACD,gBACE,KAAK,mBAAmB,KAAK,eAAe,aAAa,UAAU;AAAA,IACrE,UAAU;AAAA,MACR,GAAG,KAAK;AAAA,MACR,OAAO,KAAK;AAAA,MACZ;AAAA,MACA,aAAa,UAAU;AAAA,MACvB,qBAAqB;AAAA,MACrB,kBAAkB,UAAU;AAAA,MAC5B,gBAAgB,UAAU;AAAA,MAC1B,YAAY,UAAU;AAAA,MACtB,gBAAgB,UAAU;AAAA,IAC5B;AAAA,EACF;AACF;AAEA,SAAS,gBACP,SACA,KAC6F;AAC7F,MAAI,QAAQ,WAAW,EAAG,QAAO,EAAE,OAAO,GAAG,kBAAkB,CAAC,EAAE;AAClE,QAAM,mBAAmB,QACtB;AAAA,IACC,CAAC,WACC,OAAO,cACP,eAAe,OAAO,UAAU,YAAY,KAC5C,eAAe,OAAO,UAAU,WAAW;AAAA,EAC/C,EACC,OAAO,SAAS;AACnB,QAAM,qBAAqB,QACxB,IAAI,CAAC,WAAW,OAAO,kBAAkB,eAAe,OAAO,UAAU,gBAAgB,CAAC,EAC1F,OAAO,SAAS;AACnB,QAAM,mBAAmB,QACtB,OAAO,CAAC,WAAW;AAClB,UAAM,aACJ,OAAO,cACP,eAAe,OAAO,UAAU,YAAY,KAC5C,eAAe,OAAO,UAAU,WAAW;AAC7C,WAAO,aAAa,KAAK,MAAM,UAAU,KAAK,IAAI,QAAQ,IAAI;AAAA,EAChE,CAAC,EACA,IAAI,CAAC,WAAW,OAAO,EAAE;AAC5B,SAAO;AAAA,IACL,OAAO,iBAAiB,SAAS,IAAI,IAAI;AAAA,IACzC,YAAY,YAAY,gBAAgB;AAAA,IACxC,gBAAgB,UAAU,kBAAkB;AAAA,IAC5C;AAAA,EACF;AACF;AAEA,SAAS,UAAU,OAA4C;AAC7D,SAAO,QAAQ,SAAS,OAAO,SAAS,KAAK,MAAM,KAAK,CAAC,CAAC;AAC5D;AAEA,SAAS,YAAY,QAAsC;AACzD,SAAO,OAAO,KAAK,CAAC,GAAG,MAAM,KAAK,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC;AAC/D;AAEA,SAAS,UAAU,QAAsC;AACvD,SAAO,OAAO,KAAK,CAAC,GAAG,MAAM,KAAK,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC;AAC/D;AAEA,SAAS,mBAAmB,SAA4C;AACtE,SAAO,QAAQ,IAAI,CAAC,WAAW,OAAO,KAAK,EAAE;AAC/C;AAEA,SAAS,OAAU,OAAiB;AAClC,SAAO,CAAC,GAAG,IAAI,IAAI,KAAK,CAAC;AAC3B;AAEA,SAAS,MAAM,OAAuB;AACpC,SAAO,KAAK,MAAM,QAAQ,GAAI,IAAI;AACpC;;;AC5QO,SAAS,qBAAqB,OAMlB;AACjB,QAAM,aAAa,MAAM,QAAQ,MAAM,oBAAI,KAAK,IAAI,EAAE,YAAY;AAClE,SAAO;AAAA,IACL,IAAI;AAAA,MACF;AAAA,MACA,GAAG,MAAM,IAAI,IAAI,MAAM,UAAU,EAAE,IAAI,SAAS,IAAI,KAAK,UAAU,MAAM,YAAY,CAAC,CAAC,CAAC;AAAA,IAC1F;AAAA,IACA,MAAM,MAAM;AAAA,IACZ;AAAA,IACA,OAAO,MAAM;AAAA,IACb,QAAQ,MAAM;AAAA,IACd,UAAU,MAAM;AAAA,EAClB;AACF;;;AC5BA,SAAS,OAAO,UAAU,iBAAiB;AAC3C,SAAS,SAAS,YAAY;AA2FvB,SAAS,+BACd,SACyB;AACzB,QAAM,OAAO,KAAK,QAAQ,MAAM,oBAAoB,gBAAgB;AACpE,MAAI,aAA+B,QAAQ,QAAQ;AAEnD,QAAM,OAAO,YAAsD;AACjE,QAAI;AACF,YAAM,OAAO,MAAM,SAAS,MAAM,MAAM;AACxC,YAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,aAAO,OAAO,WAAW,CAAC;AAAA,IAC5B,QAAQ;AACN,aAAO,CAAC;AAAA,IACV;AAAA,EACF;AAEA,QAAM,QAAQ,OAAO,YAA4D;AAC/E,UAAM,MAAM,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC9C,UAAM,UAAU,MAAM,GAAG,KAAK,UAAU,EAAE,QAAQ,GAAG,MAAM,CAAC,CAAC;AAAA,GAAM,MAAM;AAAA,EAC3E;AAEA,SAAO;AAAA,IACL,MAAM,KAAK,KAAK;AACd,YAAM,UAAU,MAAM,KAAK;AAC3B,YAAM,SAAS,QAAQ,SAAS,GAAG,CAAC;AACpC,aAAO,SAAS,IAAI,KAAK,OAAO,eAAe,IAAI;AAAA,IACrD;AAAA,IACA,MAAM,KAAK,OAAO;AAChB,mBAAa,WAAW,KAAK,YAAY;AACvC,cAAM,UAAU,MAAM,KAAK;AAC3B,gBAAQ,SAAS,KAAK,CAAC,IAAI;AAAA,UACzB,aAAa,MAAM;AAAA,UACnB,UAAU,MAAM;AAAA,UAChB,iBAAiB,MAAM,KAAK,YAAY;AAAA,UACxC,aAAa,MAAM;AAAA,QACrB;AACA,cAAM,MAAM,OAAO;AAAA,MACrB,CAAC;AACD,YAAM;AAAA,IACR;AAAA,IACA,MAAM,MAAM,OAAO;AACjB,YAAM,OAAO,MAAM,KAAK,KAAK,KAAK;AAClC,UAAI,CAAC,KAAM,QAAO;AAClB,YAAM,MAAM,MAAM,OAAO,oBAAI,KAAK;AAClC,aAAO,IAAI,QAAQ,IAAI,KAAK,QAAQ,IAAI,MAAM;AAAA,IAChD;AAAA,IACA,MAAM,KAAK,aAAa;AACtB,YAAM,UAAU,MAAM,KAAK;AAC3B,aAAO,OAAO,OAAO,OAAO,EAAE,OAAO,CAAC,MAAM,EAAE,gBAAgB,WAAW;AAAA,IAC3E;AAAA,EACF;AACF;AA0BO,SAAS,2BAA2B,SAA6C;AACtF,SAAO;AAAA,IACL,MAAM,KAAK,KAAK;AACd,YAAM,SAAS,MAAM,QAAQ,IAAI,IAAI,aAAa,IAAI,QAAQ;AAC9D,aAAO,SAAS,IAAI,KAAK,OAAO,eAAe,IAAI;AAAA,IACrD;AAAA,IACA,MAAM,KAAK,OAAO;AAChB,YAAM,QAAQ,OAAO;AAAA,QACnB,aAAa,MAAM;AAAA,QACnB,UAAU,MAAM;AAAA,QAChB,iBAAiB,MAAM,KAAK,YAAY;AAAA,QACxC,aAAa,MAAM;AAAA,MACrB,CAAC;AAAA,IACH;AAAA,IACA,MAAM,MAAM,OAAO;AACjB,YAAM,OAAO,MAAM,KAAK,KAAK,KAAK;AAClC,UAAI,CAAC,KAAM,QAAO;AAClB,YAAM,MAAM,MAAM,OAAO,oBAAI,KAAK;AAClC,aAAO,IAAI,QAAQ,IAAI,KAAK,QAAQ,IAAI,MAAM;AAAA,IAChD;AAAA,IACA,MAAM,KAAK,aAAa;AACtB,aAAO,QAAQ,gBAAgB,WAAW;AAAA,IAC5C;AAAA,EACF;AACF;AAEA,SAAS,SAAS,KAA2B;AAC3C,SAAO,GAAG,IAAI,WAAW,KAAK,IAAI,QAAQ;AAC5C;;;ACrMA,SAAS,SAAAA,QAAO,YAAAC,WAAU,aAAAC,kBAAiB;AAC3C,SAAS,WAAAC,UAAS,QAAAC,aAAY;AAkBvB,IAAM,gBAAN,MAAuC;AAAA,EAC3B,UAAU,oBAAI,IAA0B;AAAA,EACxC,QAAQ,oBAAI,IAA2B;AAAA,EACvC,SAA2B,CAAC;AAAA,EACrC,QAA+B;AAAA,EAEvC,MAAM,UAAU,QAAqC;AACnD,SAAK,QAAQ,IAAI,OAAO,IAAI,MAAM,MAAM,CAAC;AAAA,EAC3C;AAAA,EAEA,MAAM,UAAU,IAA0C;AACxD,WAAO,MAAM,KAAK,QAAQ,IAAI,EAAE,KAAK,IAAI;AAAA,EAC3C;AAAA,EAEA,MAAM,cAAuC;AAC3C,WAAO,CAAC,GAAG,KAAK,QAAQ,OAAO,CAAC,EAAE,IAAI,KAAK;AAAA,EAC7C;AAAA,EAEA,MAAM,QAAQ,MAAoC;AAChD,SAAK,MAAM,IAAI,KAAK,IAAI,MAAM,IAAI,CAAC;AAAA,EACrC;AAAA,EAEA,MAAM,QAAQ,UAAiD;AAC7D,WAAO;AAAA,MACL,KAAK,MAAM,IAAI,QAAQ,KACrB,CAAC,GAAG,KAAK,MAAM,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,KAAK,SAAS,QAAQ,KAC9D;AAAA,IACJ;AAAA,EACF;AAAA,EAEA,MAAM,YAAsC;AAC1C,WAAO,CAAC,GAAG,KAAK,MAAM,OAAO,CAAC,EAAE,IAAI,KAAK;AAAA,EAC3C;AAAA,EAEA,MAAM,SAAS,OAAsC;AACnD,SAAK,QAAQ,MAAM,KAAK;AAAA,EAC1B;AAAA,EAEA,MAAM,WAA2C;AAC/C,QAAI,KAAK,MAAO,QAAO,MAAM,KAAK,KAAK;AACvC,UAAM,QAAQ,MAAM,KAAK,UAAU;AACnC,UAAM,UAAU,MAAM,KAAK,YAAY;AACvC,WAAO;AAAA,MACL,MAAM;AAAA,MACN,cAAa,oBAAI,KAAK,GAAE,YAAY;AAAA,MACpC;AAAA,MACA;AAAA,MACA,OAAO,oBAAoB,KAAK;AAAA,IAClC;AAAA,EACF;AAAA,EAEA,MAAM,SAAS,OAAsC;AACnD,SAAK,OAAO,KAAK,MAAM,KAAK,CAAC;AAAA,EAC/B;AAAA,EAEA,MAAM,WAAW,QAA6B,CAAC,GAA8B;AAC3E,QAAI,MAAM,KAAK;AACf,QAAI,MAAM,KAAM,OAAM,IAAI,OAAO,CAAC,UAAU,MAAM,SAAS,MAAM,IAAI;AACrE,QAAI,MAAM,OAAQ,OAAM,IAAI,OAAO,CAAC,UAAU,MAAM,WAAW,MAAM,MAAM;AAC3E,UAAM,CAAC,GAAG,GAAG,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,UAAU,cAAc,EAAE,SAAS,CAAC;AACpE,WAAO,IAAI,MAAM,EAAE,MAAM,SAAS,IAAI,OAAO,EAAE,IAAI,KAAK;AAAA,EAC1D;AACF;AAEO,IAAM,oBAAN,cAAgC,cAAc;AAAA,EACnD,YAA6B,KAAa;AACxC,UAAM;AADqB;AAAA,EAE7B;AAAA,EAF6B;AAAA,EAI7B,MAAM,SAAS,OAAsC;AACnD,UAAM,MAAM,SAAS,KAAK;AAC1B,UAAMC,WAAUC,MAAK,KAAK,KAAK,YAAY,GAAG,KAAK;AAAA,EACrD;AAAA,EAEA,MAAM,WAA2C;AAC/C,QAAI;AACF,aAAO,KAAK,MAAM,MAAMC,UAASD,MAAK,KAAK,KAAK,YAAY,GAAG,MAAM,CAAC;AAAA,IACxE,QAAQ;AACN,aAAO,MAAM,MAAM,SAAS;AAAA,IAC9B;AAAA,EACF;AAAA,EAEA,MAAM,SAAS,OAAsC;AACnD,UAAM,MAAM,SAAS,KAAK;AAC1B,UAAM,SAAS,MAAM,KAAK,WAAW;AACrC,UAAMD,WAAUC,MAAK,KAAK,KAAK,aAAa,GAAG,MAAM;AAAA,EACvD;AACF;AAEA,eAAeD,WAAU,MAAc,OAA+B;AACpE,QAAMG,OAAMC,SAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC9C,QAAMC,WAAU,MAAM,GAAG,KAAK,UAAU,OAAO,MAAM,CAAC,CAAC;AAAA,GAAM,MAAM;AACrE;AAEA,SAAS,MAAS,OAAa;AAC7B,SAAO,SAAS,OAAO,QAAS,KAAK,MAAM,KAAK,UAAU,KAAK,CAAC;AAClE;;;ACjDO,IAAM,8BAAN,cAA0C,MAAM;AAAA,EACrD,YACkB,WACA,SAChB,SACA;AACA,UAAM,sBAAsB,SAAS,aAAa,OAAO,MAAM,OAAO,EAAE;AAJxD;AACA;AAIhB,SAAK,OAAO;AAAA,EACd;AAAA,EANkB;AAAA,EACA;AAMpB;AAcO,SAAS,mBAAmB,SAAmD;AACpF,MAAI,CAAC,QAAQ,QAAS,QAAO;AAC7B,MAAI,CAAC,QAAQ,QAAQ,WAAW,kBAAkB,EAAG,QAAO;AAE5D,QAAM,OAAO,QAAQ,QAAQ,MAAM,mBAAmB,MAAM;AAC5D,QAAM,CAAC,UAAU,GAAG,UAAU,IAAI,KAAK,MAAM,GAAG;AAChD,QAAM,QAAQ,WAAW,KAAK,GAAG;AACjC,MAAI,CAAC,YAAY,CAAC,OAAO;AACvB,UAAM,IAAI;AAAA,MACR,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,QAAM,WAA0C;AAAA,IAC9C,UAAU,QAAQ;AAAA,IAClB,YAAY,QAAQ;AAAA,IACpB,cAAc,QAAQ,cAAc,CAAC,GAAG;AAAA,IACxC,YAAY,QAAQ;AAAA,EACtB;AAEA,UAAQ,UAAU;AAAA,IAChB,KAAK;AACH,aAAO,aAAa,SAAS,OAAO,QAAQ;AAAA,IAC9C,KAAK;AACH,aAAO,cAAc,SAAS,OAAO,QAAQ;AAAA,IAC/C,KAAK;AACH,aAAO,gBAAgB,SAAS,OAAO,QAAQ;AAAA,IACjD,KAAK;AACH,aAAO,kBAAkB,SAAS,OAAO,QAAQ;AAAA,IACnD;AACE,YAAM,IAAI;AAAA,QACR,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,iBAAiB,QAAQ;AAAA,MAC3B;AAAA,EACJ;AACF;AAEA,SAAS,aACP,SACA,OACA,UACmB;AACnB,QAAM,UAAU,MAAM,QAAQ,GAAG;AACjC,QAAM,WAAW,WAAW,IAAI,MAAM,MAAM,GAAG,OAAO,IAAI;AAC1D,QAAM,UAAU,WAAW,IAAI,MAAM,MAAM,UAAU,CAAC,IAAI;AAC1D,QAAM,OAAO,aAAa,WAAW,QAAQ,CAAC;AAE9C,QAAM,OAAO,eAAe,SAAS,UAAU,OAAO;AACtD,SAAO;AAAA,IACL,IAAI,QAAQ,QAAQ,UAAU;AAAA,IAC9B,iBAAiB,QAAQ;AAAA,IACzB,MAAM,UAAU,mBAAmB;AAAA,IACnC,OAAO;AAAA,IACP,aAAa,CAAC,EAAE,MAAM,SAAS,KAAK,CAAC;AAAA,IACrC;AAAA,EACF;AACF;AAEA,SAAS,cACP,SACA,OACA,UACmB;AACnB,QAAM,OAAmB,QAAQ,cAC9B,OAAO,CAAC,MAAM,EAAE,GAAG,EACnB,IAAI,CAAC,OAAO;AAAA,IACX,UAAU,mBAAmB,QAAQ,UAAU;AAAA,IAC/C,UAAU,EAAE;AAAA,IACZ,OAAO,EAAE;AAAA,EACX,EAAE;AACJ,QAAM,QAAwB;AAAA,IAC5B,IAAI,SAAS,QAAQ,UAAU;AAAA,IAC/B,MAAM,QAAQ,sBAAsB,QAAQ;AAAA,IAC5C;AAAA,IACA,YAAY,QAAQ;AAAA,IACpB,QAAQ;AAAA,IACR,UAAU;AAAA,MACR,YAAY,QAAQ;AAAA,MACpB,mBAAmB,QAAQ;AAAA,MAC3B,OAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AAAA,IACL,IAAI,QAAQ,QAAQ,UAAU;AAAA,IAC9B,iBAAiB,QAAQ;AAAA,IACzB,MAAM;AAAA,IACN;AAAA,IACA,aAAa,CAAC;AAAA,IACd;AAAA,IACA;AAAA,EACF;AACF;AAEA,SAAS,gBACP,SACA,UACA,UACmB;AACnB,QAAM,OAAO,aAAa,WAAW,QAAQ,CAAC;AAC9C,QAAM,OAAO;AAAA,IACX;AAAA,IACA,UAAU,QAAQ;AAAA,IAClB,WAAW,QAAQ;AAAA,IACnB;AAAA,IACA,wBAAwB,QAAQ,UAAU;AAAA,IAC1C;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ;AAAA,IACR;AAAA,IACA,GAAI,QAAQ,YAAY,CAAC,gBAAgB,IAAI,QAAQ,WAAW,EAAE,IAAI,CAAC;AAAA,IACvE,GAAI,QAAQ,qBACR,CAAC,yBAAyB,IAAI,QAAQ,oBAAoB,EAAE,IAC5D,CAAC;AAAA,EACP,EAAE,KAAK,IAAI;AACX,SAAO;AAAA,IACL,IAAI,QAAQ,QAAQ,UAAU;AAAA,IAC9B,iBAAiB,QAAQ;AAAA,IACzB,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aAAa,CAAC,EAAE,MAAM,SAAS,KAAK,CAAC;AAAA,IACrC;AAAA,EACF;AACF;AAEA,SAAS,kBACP,SACA,UACA,UACmB;AACnB,QAAM,OAAO,aAAa,WAAW,QAAQ,CAAC;AAC9C,QAAM,OAAO;AAAA,IACX;AAAA,IACA,UAAU,QAAQ;AAAA,IAClB;AAAA,IACA,0BAA0B,QAAQ,UAAU;AAAA,IAC5C,eAAe,QAAQ,UAAU;AAAA,IACjC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ;AAAA,IACR;AAAA,IACA,GAAI,QAAQ,YAAY,CAAC,eAAe,IAAI,QAAQ,WAAW,EAAE,IAAI,CAAC;AAAA,IACtE,GAAI,QAAQ,qBAAqB,CAAC,aAAa,IAAI,QAAQ,oBAAoB,EAAE,IAAI,CAAC;AAAA,EACxF,EAAE,KAAK,IAAI;AACX,SAAO;AAAA,IACL,IAAI,QAAQ,QAAQ,UAAU;AAAA,IAC9B,iBAAiB,QAAQ;AAAA,IACzB,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aAAa,CAAC,EAAE,MAAM,SAAS,KAAK,CAAC;AAAA,IACrC;AAAA,EACF;AACF;AAaO,SAAS,oBACd,UAC2B;AAC3B,QAAM,YAAiC,CAAC;AACxC,QAAM,SAAwC,CAAC;AAC/C,MAAI,UAAU;AACd,aAAW,KAAK,UAAU;AACxB,QAAI;AACF,YAAM,IAAI,mBAAmB,CAAC;AAC9B,UAAI,EAAG,WAAU,KAAK,CAAC;AAAA,UAClB,YAAW;AAAA,IAClB,SAAS,KAAK;AACZ,UAAI,eAAe,4BAA6B,QAAO,KAAK,GAAG;AAAA,UAC1D,OAAM;AAAA,IACb;AAAA,EACF;AACA,SAAO,EAAE,WAAW,SAAS,OAAO;AACtC;AAEA,SAAS,WAAW,GAAmB;AACrC,SACE,EACG,YAAY,EACZ,QAAQ,gBAAgB,GAAG,EAC3B,QAAQ,YAAY,EAAE,EACtB,MAAM,GAAG,GAAG,KAAK;AAExB;AAEA,SAAS,eAAe,SAAyB,MAAc,SAAgC;AAC7F,QAAM,QAAQ,SAAS,IAAI;AAC3B,MAAI,SAAS;AACX,WAAO;AAAA,MACL,MAAM,OAAO;AAAA,MACb;AAAA,MACA,QAAQ;AAAA,MACR;AAAA,MACA,GAAI,QAAQ,YAAY,CAAC,iBAAiB,IAAI,QAAQ,WAAW,EAAE,IAAI,CAAC;AAAA,MACxE,GAAI,QAAQ,qBAAqB,CAAC,cAAc,IAAI,QAAQ,oBAAoB,EAAE,IAAI,CAAC;AAAA,MACvF,yBAAyB,QAAQ,UAAU,gBAAgB,QAAQ,WAAW,QAAQ,CAAC,CAAC;AAAA,IAC1F,EAAE,KAAK,IAAI;AAAA,EACb;AACA,SAAO;AAAA,IACL;AAAA,IACA,UAAU,KAAK;AAAA,IACf;AAAA,IACA,yBAAyB,QAAQ,UAAU;AAAA,IAC3C,eAAe,QAAQ,UAAU;AAAA,IACjC;AAAA,IACA;AAAA,IACA,KAAK,KAAK;AAAA,IACV;AAAA,IACA,QAAQ;AAAA,IACR;AAAA,IACA,GAAI,QAAQ,YAAY,CAAC,gBAAgB,IAAI,QAAQ,WAAW,EAAE,IAAI,CAAC;AAAA,IACvE,GAAI,QAAQ,qBACR,CAAC,yBAAyB,IAAI,QAAQ,oBAAoB,EAAE,IAC5D,CAAC;AAAA,EACP,EAAE,KAAK,IAAI;AACb;AAEA,SAAS,SAAS,MAAsB;AACtC,SACE,KACG,MAAM,GAAG,EACT,OAAO,OAAO,EACd,IAAI,CAAC,MAAM,EAAE,CAAC,GAAG,YAAY,IAAI,EAAE,MAAM,CAAC,CAAC,EAC3C,KAAK,GAAG,KAAK;AAEpB;;;ACzUA;AAAA,EACE;AAAA,EAKA;AAAA,OACK;AA0CA,SAAS,uBAAuB,OAAsD;AAC3F,QAAM,eAAe,MAAM,gBAAgB,CAAC;AAC5C,QAAM,aAAa,CAAC,GAAG,MAAM,eAAe,GAAG,YAAY,EAAE,IAAI,iBAAiB;AAClF,QAAM,YAAY,0BAA0B;AAAA,IAC1C,QAAQ;AAAA,IACR,aAAa,MAAM;AAAA,IACnB,YAAY,MAAM,cAAc;AAAA,IAChC,QAAQ,MAAM,UAAU,CAAC;AAAA,IACzB,MAAM;AAAA,IACN,cAAc,MAAM,gBAAgB;AAAA,IACpC,YAAY;AAAA,MACV,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA,MAKf,kBAAkB;AAAA,MAClB,gBAAgB,MAAM,cAAc;AAAA,MACpC,gBAAgB,MAAM,aAAa,IAAI;AAAA,MACvC,eAAe;AAAA,MACf,cAAc,MAAM,YAAY;AAAA,IAClC;AAAA,EACF,CAAC;AACD,QAAM,UAA4B;AAAA,IAChC,IAAI,SAAS,QAAQ,GAAG,MAAM,WAAW,IAAI,MAAM,cAAa,oBAAI,KAAK,GAAE,YAAY,CAAC,EAAE;AAAA,IAC1F,aAAa,MAAM;AAAA,IACnB,WAAW,MAAM,cAAa,oBAAI,KAAK,GAAE,YAAY;AAAA,IACrD,UAAU,UAAU,WAAW,WAAW,MAAM,kBAAkB;AAAA,IAClE;AAAA,IACA,cAAc,WAAW,IAAI,CAAC,WAAW,OAAO,KAAK;AAAA,EACvD;AACA,SAAO,EAAE,SAAS,WAAW,eAAe,MAAM,eAAe,aAAa;AAChF;;;ACjFA;AAAA,EACE;AAAA,EAGA;AAAA,OACK;;;ACuBA,SAAS,aACd,SACA,OAC4C;AAC5C,MAAI,CAAC,QAAQ,gBAAgB,OAAQ,QAAO;AAC5C,SAAO,yBAAyB;AAAA,IAC9B,GAAI,QAAQ,aAAa,CAAC;AAAA,IAC1B,QAAQ,QAAQ,mBAAmB,QAAQ;AAAA,IAC3C;AAAA,IACA,OAAO,QAAQ;AAAA,EACjB,CAAC;AACH;;;ADuGO,SAAS,kCACd,SAC6B;AAC7B,MAAI,cAAc;AAClB,QAAM,eAA4C,CAAC;AAEnD,SAAO;AAAA,IACL,QAAQ,QAAQ;AAAA,IAChB,MAAM,QAAQ,EAAE,SAAS,YAAY,GAAG;AACtC,UAAI,YAAY,QAAS,OAAM,IAAI,MAAM,gCAAgC;AACzE,UAAI,CAAC,aAAa;AAChB,cAAM,kBAAkB,QAAQ,IAAI;AACpC,sBAAc;AAAA,MAChB;AACA,YAAM,QAAQ,MAAM,oBAAoB,QAAQ,IAAI;AACpD,YAAM,aAAa,uBAAuB,OAAO,EAAE,QAAQ,QAAQ,OAAO,CAAC;AAC3E,YAAM,eAAe,mBAAmB,KAAK;AAC7C,YAAM,YAAY,aAAa,SAAS,KAAK;AAC7C,aAAO;AAAA,QACL,MAAM,QAAQ;AAAA,QACd,MAAM,QAAQ;AAAA,QACd,WAAW,QAAQ,SAAS;AAAA,QAC5B;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,eAAe,CAAC,GAAG,YAAY;AAAA,QAC/B,QAAQ;AAAA,MACV;AAAA,IACF;AAAA,IACA,SAAS,EAAE,MAAM,GAAG;AAClB,YAAM,gBAAgB,MAAM,WAAW,SAAS;AAAA,QAC9C,CAAC,YAAY,QAAQ,aAAa;AAAA,MACpC;AACA,YAAM,QAA6B;AAAA,QACjC,cAAc;AAAA,UACZ,IAAI;AAAA,UACJ,QAAQ,MAAM,WAAW;AAAA,UACzB,UAAU;AAAA,UACV,QAAQ,MAAM,WAAW,KACrB,8BACA;AAAA,UACJ,UAAU,EAAE,UAAU,MAAM,WAAW,SAAS;AAAA,QAClD,CAAC;AAAA,QACD,cAAc;AAAA,UACZ,IAAI;AAAA,UACJ,QAAQ,cAAc,WAAW;AAAA,UACjC,UAAU;AAAA,UACV,QACE,cAAc,WAAW,IACrB,oBACA,GAAG,cAAc,MAAM;AAAA,UAC7B,UAAU,EAAE,UAAU,cAAc;AAAA,QACtC,CAAC;AAAA,MACH;AACA,UAAI,MAAM,UAAW,OAAM,KAAK,sBAAsB,MAAM,UAAU,MAAM,CAAC;AAC7E,aAAO;AAAA,IACT;AAAA,IACA,aAAa;AACX,aAAO,EAAE,MAAM,OAAO,MAAM,OAAO,QAAQ,uCAAuC;AAAA,IACpF;AAAA,IACA,MAAM,IAAI,QAAQ,KAAK;AACrB,YAAM,OAAO,MAAM,+BAA+B,SAAS,QAAQ,IAAI,MAAM,SAAS;AACtF,mBAAa,KAAK,IAAI;AACtB,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAEA,eAAsB,yBACpB,SACsC;AACtC,QAAM,gBAAgB,KAAK,IAAI,GAAG,QAAQ,iBAAiB,CAAC;AAC5D,QAAM,kBAAkB,QAAQ,IAAI;AACpC,QAAM,QAAqC,CAAC;AAC5C,MAAI,QAAQ,MAAM,oBAAoB,QAAQ,IAAI;AAClD,MAAI,aAAa,uBAAuB,OAAO,EAAE,QAAQ,QAAQ,OAAO,CAAC;AACzE,MAAI,eAAe,mBAAmB,KAAK;AAC3C,MAAI,YAAY,aAAa,SAAS,KAAK;AAC3C,MAAI,OAAO;AAEX,WAAS,YAAY,GAAG,aAAa,eAAe,aAAa;AAC/D,QAAI,QAAQ,QAAQ,QAAS,OAAM,IAAI,MAAM,iCAAiC;AAC9E,UAAM,WAAW,MAAM,QAAQ,KAAK;AAAA,MAClC,MAAM,QAAQ;AAAA,MACd,MAAM,QAAQ;AAAA,MACd;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,eAAe;AAAA,MACf,QAAQ,QAAQ;AAAA,IAClB,CAAC;AAED,WAAO,QAAQ,SAAS,IAAI;AAC5B,UAAM,OAAO,MAAM,+BAA+B,SAAS,UAAU,SAAS;AAC9E,YAAQ,MAAM,oBAAoB,QAAQ,IAAI;AAC9C,iBAAa,KAAK;AAClB,mBAAe,KAAK;AACpB,gBAAY,KAAK;AACjB,UAAM,KAAK,IAAI;AACf,UAAM,QAAQ,SAAS,IAAI;AAE3B,QAAI,KAAM;AACV,QAAI,CAAC,KAAK,WAAW,KAAK,aAAa,WAAW,EAAG;AAAA,EACvD;AAEA,SAAO;AAAA,IACL,MAAM,QAAQ;AAAA,IACd,MAAM,QAAQ;AAAA,IACd,YAAY,MAAM;AAAA,IAClB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEA,eAAe,+BACb,SACA,UACA,YAAY,GACwB;AACpC,QAAM,eAA+B,CAAC;AACtC,aAAW,cAAc,SAAS,eAAe,CAAC,GAAG;AACnD,iBAAa,KAAK,GAAI,MAAM,cAAc,QAAQ,MAAM,YAAY,QAAQ,aAAa,CAAE;AAAA,EAC7F;AACA,aAAW,cAAc,SAAS,eAAe,CAAC,GAAG;AACnD,iBAAa,KAAK,MAAM,cAAc,QAAQ,MAAM,YAAY,QAAQ,aAAa,CAAC;AAAA,EACxF;AAEA,QAAM,UAAU,SAAS,eACrB,MAAM,0BAA0B,QAAQ,MAAM,SAAS,YAAY,IACnE;AAEJ,QAAM,QAAQ,MAAM,oBAAoB,QAAQ,IAAI;AACpD,QAAM,aAAa,uBAAuB,OAAO,EAAE,QAAQ,QAAQ,OAAO,CAAC;AAC3E,QAAM,eAAe,mBAAmB,KAAK;AAC7C,QAAM,YAAY,aAAa,SAAS,KAAK;AAC7C,QAAM,OAAO,QAAQ,SAAS,IAAI;AAClC,QAAM,QAAQ,qBAAqB;AAAA,IACjC,MAAM;AAAA,IACN,OAAO,QAAQ;AAAA,IACf,QAAQ,QAAQ;AAAA,IAChB,UAAU;AAAA,MACR,MAAM,QAAQ;AAAA,MACd;AAAA,MACA;AAAA,MACA,kBAAkB,aAAa;AAAA,MAC/B,SAAS,SAAS;AAAA,MAClB,cAAc,SAAS,SAAS,UAAU;AAAA,MAC1C,YAAY,WAAW,SAAS,OAAO,CAAC,YAAY,QAAQ,aAAa,OAAO,EAAE;AAAA,IACpF;AAAA,EACF,CAAC;AAED,SAAO;AAAA,IACL;AAAA,IACA,OAAO,SAAS;AAAA,IAChB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,UAAU,SAAS;AAAA,EACrB;AACF;;;AExTA;AAAA,EAOE;AAAA,OACK;AAaA,IAAM,oCAAoC;AAAA,EAC/C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,EAAE,KAAK,IAAI;AAwCJ,SAAS,8BACd,SAI0B;AAC1B,SAAO;AAAA,IACL,UAAU,qBAAqB,QAAQ,IAAI,kBAAkB,QAAQ,IAAI;AAAA,IACzE,MAAM,QAAQ;AACZ,YAAM,QAAQ,MAAM,oBAAoB,QAAQ,IAAI;AACpD,YAAM,SAAS,aAAa,SAAS,KAAK;AAG1C,cAAQ,QAAQ,OAAO,4BAA4B,UAAU,OAAO;AAAA,IACtE;AAAA,EACF;AACF;AAgBA,eAAsB,sBACpB,SACoC;AACpC,QAAM,kBAAkB,QAAQ,IAAI;AAKpC,QAAM,EAAE,SAAS,cAAc,IAAI,kBAAkB,EAAE,SAAS,QAAQ,QAAQ,CAAC;AACjF,QAAM,mBAAmB,QAAQ,0BAA0B;AAC3D,QAAM,iBAAiB,cAAc,QAAQ;AAC7C,QAAM,eAAe,iBACjB,GAAG,gBAAgB;AAAA;AAAA;AAAA,EAAgE,cAAc,KACjG;AAEJ,QAAM,UAA6B;AAAA,IACjC,MAAM;AAAA,IACN,OAAO,QAAQ;AAAA,IACf;AAAA,EACF;AAEA,SAAO,UAAU,SAAS,QAAQ,MAAM;AAAA,IACtC,GAAG,QAAQ;AAAA,IACX,QAAQ,QAAQ;AAAA,IAChB,SAAS,QAAQ;AAAA,IACjB,aAAa,8BAA8B,OAAO;AAAA,EACpD,CAAC;AACH;;;ACyEA,eAAsB,wBACpB,SACqC;AACrC,QAAM,YAAY,KAAK,IAAI,GAAG,QAAQ,aAAa,CAAC;AACpD,QAAM,kBAAkB,QAAQ,IAAI;AACpC,QAAM,QAAiC,CAAC;AACxC,MAAI,QAAQ,MAAM,oBAAoB,QAAQ,IAAI;AAClD,MAAI,YAAY,aAAa,SAAS,KAAK;AAC3C,MAAI,QAAQ,QAAQ,WAAW,MAAM;AACrC,MAAI;AAEJ,WAASC,SAAQ,GAAGA,UAAS,aAAa,CAAC,OAAOA,UAAS;AACzD,QAAI,QAAQ,QAAQ,QAAS,OAAM,IAAI,MAAM,iCAAiC;AAE9E,UAAM,OAAO,kBAAkB,SAAS;AAGxC,UAAM,qBAAqB,MAAM,QAAQ,OAAO;AAAA,MAC9C,MAAM,QAAQ;AAAA,MACd,MAAM,QAAQ;AAAA,MACd,OAAAA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW,iBAAiB,WAAW,OAAO;AAAA,MAC9C,QAAQ,QAAQ;AAAA,IAClB,CAAC;AAGD,UAAM,WAAqC,CAAC;AAC5C,UAAM,wBAA0C,CAAC;AAKjD,UAAM,eAAe,IAAI;AAAA,MACvB,MAAM,QAAQ;AAAA,QAAQ,CAAC,WACrB,OAAO,OAAO,UAAU,gBAAgB,WAAW,CAAC,OAAO,SAAS,WAAW,IAAI,CAAC;AAAA,MACtF;AAAA,IACF;AACA,eAAW,UAAU,mBAAmB,WAAW,CAAC,GAAG;AACrD,UAAI,YAAY,QAAQ,cAAc,QAAQ,GAAG;AAC/C,8BAAsB,KAAK,EAAE,QAAQ,QAAQ,2CAA2C,CAAC;AACzF;AAAA,MACF;AACA,YAAM,UAAU,MAAM,QAAQ,OAAO,aAAa,QAAQ;AAAA,QACxD,MAAM,QAAQ;AAAA,QACd,MAAM,QAAQ;AAAA,QACd,OAAAA;AAAA,QACA;AAAA,QACA;AAAA,QACA,mBAAmB;AAAA,QACnB,QAAQ,QAAQ;AAAA,MAClB,CAAC;AACD,UAAI,QAAQ,OAAQ,UAAS,KAAK,MAAM;AAAA,UACnC,uBAAsB,KAAK,EAAE,QAAQ,QAAQ,QAAQ,OAAO,CAAC;AAAA,IACpE;AAKA,UAAM,wBAAwB,MAAM,gBAAgB,SAAS,QAAQ;AACrE,UAAM,eAAyB,CAAC;AAChC,iBAAa;AAAA,MACX,GAAI,MAAM,WAAW,QAAQ,MAAM,oBAAoB,qBAAqB;AAAA,IAC9E;AAGA,YAAQ,MAAM,oBAAoB,QAAQ,IAAI;AAC9C,gBAAY,aAAa,SAAS,KAAK;AAGvC,QAAI,gBAAgC,CAAC;AACrC,QAAI;AACJ,QAAI,QAAQ,oBAAoB,QAAQ,OAAO,UAAU;AACvD,YAAMC,iBAAgB,kBAAkB,SAAS;AACjD,YAAM,qBAAqB,MAAM,QAAQ,OAAO,SAAS;AAAA,QACvD,MAAM,QAAQ;AAAA,QACd,MAAM,QAAQ;AAAA,QACd,OAAAD;AAAA,QACA;AAAA,QACA,eAAAC;AAAA,QACA,WAAW,iBAAiB,WAAW,OAAO;AAAA,QAC9C,QAAQ,QAAQ;AAAA,MAClB,CAAC;AACD,oBAAc,mBAAmB;AACjC,sBAAgB,MAAM,gBAAgB,SAAS,mBAAmB,WAAW,CAAC,CAAC;AAC/E,mBAAa,KAAK,GAAI,MAAM,WAAW,QAAQ,MAAM,oBAAoB,aAAa,CAAE;AACxF,cAAQ,MAAM,oBAAoB,QAAQ,IAAI;AAC9C,kBAAY,aAAa,SAAS,KAAK;AAAA,IACzC;AAGA,YAAQ,QAAQ,WAAW,MAAM;AACjC,UAAM,gBAAgB,kBAAkB,SAAS;AACjD,YAAQ,QAAQ,SAAY,SAAS,QAAQ,QAAQ,aAAa;AAElE,UAAM,OAA8B;AAAA,MAClC,OAAAD;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,OAAO,qBAAqB;AAAA,QAC1B,MAAM;AAAA,QACN,OAAO,QAAQ;AAAA,QACf,QAAQ,QAAQ;AAAA,QAChB,UAAU;AAAA,UACR,MAAM,QAAQ;AAAA,UACd,OAAAA;AAAA,UACA;AAAA,UACA,2BAA2B,sBAAsB;AAAA,UACjD,2BAA2B,sBAAsB;AAAA,UACjD,mBAAmB,cAAc;AAAA,UACjC,kBAAkB,aAAa;AAAA,UAC/B,mBAAmB,cAAc;AAAA,QACnC;AAAA,MACF,CAAC;AAAA,MACD,OAAO,EAAE,QAAQ,mBAAmB,OAAO,QAAQ,YAAY;AAAA,IACjE;AACA,UAAM,KAAK,IAAI;AACf,UAAM,QAAQ,UAAU,IAAI;AAAA,EAC9B;AAEA,SAAO;AAAA,IACL,MAAM,QAAQ;AAAA,IACd,MAAM,QAAQ;AAAA,IACd,QAAQ,MAAM;AAAA,IACd;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEA,SAAS,QAAQ,QAAuD;AAGtE,MAAI,CAAC,OAAQ,QAAO;AACpB,SAAO,OAAO,4BAA4B,WAAW;AACvD;AAEA,SAAS,kBAAkB,WAAuE;AAChG,MAAI,CAAC,UAAW,QAAO,CAAC;AACxB,QAAM,WAAW,UAAU,OAAO,4BAA4B;AAAA,IAAI,CAAC,gBACjE,OAAO,aAAa,WAAW,IAAI;AAAA,EACrC;AACA,QAAM,cAAc,UAAU,OAAO,gBAAgB;AAAA,IAAI,CAAC,gBACxD,OAAO,aAAa,WAAW,KAAK;AAAA,EACtC;AACA,SAAO,CAAC,GAAG,UAAU,GAAG,WAAW;AACrC;AAEA,SAAS,OACP,aACA,WACA,UACc;AACd,QAAM,OAAO,UAAU,aAAa,KAAK,CAAC,UAAU,MAAM,OAAO,YAAY,EAAE;AAC/E,QAAM,QACJ,OAAO,MAAM,UAAU,UAAU,WAAW,KAAK,SAAS,QAAQ,YAAY;AAChF,SAAO,EAAE,IAAI,YAAY,IAAI,aAAa,YAAY,aAAa,OAAO,SAAS;AACrF;AAEA,SAAS,SAAS,QAAwB,MAA0C;AAClF,MAAI,KAAK,WAAW,EAAG,QAAO;AAC9B,MAAI,OAAO,SAAU,QAAO,OAAO,SAAS,IAAI;AAChD,SAAO;AAAA,IACL;AAAA,IACA,GAAG,KAAK;AAAA,MACN,CAAC,QAAQ,MAAM,IAAI,WAAW,aAAa,MAAM,KAAK,IAAI,WAAW,KAAK,IAAI,EAAE;AAAA,IAClF;AAAA,EACF,EAAE,KAAK,IAAI;AACb;AAEA,SAAS,YACP,QACA,cACA,UACS;AACT,SAAO,aAAa,IAAI,OAAO,GAAG,KAAK,SAAS,KAAK,CAAC,cAAc,UAAU,QAAQ,OAAO,GAAG;AAClG;AAEA,eAAe,gBACb,SACA,SACyB;AACzB,QAAM,UAA0B,CAAC;AACjC,aAAW,UAAU,SAAS;AAC5B,YAAQ,KAAK,MAAM,cAAc,QAAQ,MAAM,QAAQ,QAAQ,aAAa,CAAC;AAAA,EAC/E;AACA,SAAO;AACT;AAQA,eAAe,WACb,MACA,cACA,iBACmB;AACnB,MAAI,gBAAgB,WAAW,EAAG,QAAO,CAAC;AAC1C,QAAM,QAAkB,CAAC;AACzB,MAAI,aAAa,aAAc,OAAM,KAAK,aAAa,YAAY;AACnE,QAAM,QAAQ,aAAa,aAAa,eAAe;AACvD,MAAI,MAAO,OAAM,KAAK,KAAK;AAC3B,MAAI,MAAM,WAAW,EAAG,QAAO,CAAC;AAChC,QAAM,UAAU,MAAM,0BAA0B,MAAM,MAAM,KAAK,IAAI,CAAC;AACtE,SAAO,QAAQ;AACjB;AAEA,SAAS,iBACP,WACA,SACgC;AAChC,MAAI,UAAW,QAAO;AAItB,SAAO,yBAAyB;AAAA,IAC9B,GAAI,QAAQ,aAAa,CAAC;AAAA,IAC1B,QAAQ,QAAQ,mBAAmB,QAAQ;AAAA,IAC3C,OAAO,WAAW,QAAQ,IAAI;AAAA,IAC9B,OAAO,CAAC;AAAA,EACV,CAAC;AACH;AAEA,SAAS,WAAW,MAA8B;AAChD,SAAO;AAAA,IACL;AAAA,IACA,cAAa,oBAAI,KAAK,CAAC,GAAE,YAAY;AAAA,IACrC,SAAS,CAAC;AAAA,IACV,OAAO,CAAC;AAAA,IACR,OAAO,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,EAAE;AAAA,EAChC;AACF;AAOO,SAAS,kBACd,QACA,OACA,MACyB;AACzB,QAAM,WAAW,GAAG,OAAO,SAAS,EAAE;AAAA,EAAK,OAAO,IAAI,GAAG,YAAY;AACrE,QAAM,OAAgC,CAAC;AACvC,aAAW,OAAO,MAAM;AACtB,eAAW,SAAS,IAAI,MAAM,YAAY,EAAE,MAAM,KAAK,EAAE,OAAO,OAAO,GAAG;AACxE,UAAI,SAAS,SAAS,KAAK,GAAG;AAC5B,aAAK,KAAK,GAAG,gBAAgB,OAAO,IAAI,OAAO,CAAC,CAAC;AACjD;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;","names":["mkdir","readFile","writeFile","dirname","join","writeJson","join","readFile","mkdir","dirname","writeFile","round","remainingGaps"]}
|
package/dist/profiles/index.d.ts
CHANGED
|
@@ -165,7 +165,7 @@ declare function multiHarnessResearcherFanout(options?: MultiHarnessResearcherFa
|
|
|
165
165
|
agentRuns: AgentRunSpec<ResearchTask>[];
|
|
166
166
|
output: OutputAdapter<ResearchOutput>;
|
|
167
167
|
validator: Validator<ResearchOutput>;
|
|
168
|
-
driver: Driver<ResearchTask, ResearchOutput, '
|
|
168
|
+
driver: Driver<ResearchTask, ResearchOutput, 'done'>;
|
|
169
169
|
};
|
|
170
170
|
/**
|
|
171
171
|
* Build a validator that closes over a specific `ResearchTask`'s constraints.
|