@tangle-network/agent-eval 0.77.0 → 0.79.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/dist/adapters/http.d.ts +2 -2
- package/dist/adapters/langchain.d.ts +2 -2
- package/dist/adapters/otel.d.ts +4 -4
- package/dist/{agent-profile-DYRboYWu.d.ts → agent-profile-aSEaJ9Pl.d.ts} +1 -1
- package/dist/analyst/index.d.ts +42 -8
- package/dist/analyst/index.js +32 -2
- package/dist/analyst/index.js.map +1 -1
- package/dist/authenticity/index.d.ts +54 -1
- package/dist/authenticity/index.js +88 -1
- package/dist/authenticity/index.js.map +1 -1
- package/dist/benchmarks/index.d.ts +2 -2
- package/dist/campaign/index.d.ts +11 -11
- package/dist/campaign/index.js +4 -4
- package/dist/{chunk-7W4SM7FD.js → chunk-5LVWPNS5.js} +91 -91
- package/dist/chunk-5LVWPNS5.js.map +1 -0
- package/dist/{chunk-WYIHD6EB.js → chunk-CF67I6QY.js} +1 -1
- package/dist/chunk-CF67I6QY.js.map +1 -0
- package/dist/{chunk-XPILG2CA.js → chunk-GXHLRXDI.js} +2 -2
- package/dist/{chunk-F3SRAAZO.js → chunk-KWRRMR3J.js} +15 -1
- package/dist/chunk-KWRRMR3J.js.map +1 -0
- package/dist/{chunk-JYE3WOTE.js → chunk-RPLZ4OIB.js} +10 -1
- package/dist/chunk-RPLZ4OIB.js.map +1 -0
- package/dist/{chunk-6EKXFFGQ.js → chunk-RTWFUK6A.js} +2 -2
- package/dist/{chunk-XGNCBAVZ.js → chunk-XQL22JDG.js} +2 -2
- package/dist/{chunk-GJJNJVIR.js → chunk-XXNIODOM.js} +2 -2
- package/dist/contract/index.d.ts +12 -12
- package/dist/contract/index.js +2 -2
- package/dist/{control-BgA6BYTm.d.ts → control-CehLtoET.d.ts} +1 -1
- package/dist/control.d.ts +2 -2
- package/dist/control.js +2 -2
- package/dist/hosted/index.d.ts +4 -4
- package/dist/{index-DsnOpCO6.d.ts → index-B1RKber3.d.ts} +1 -1
- package/dist/index.d.ts +126 -25
- package/dist/index.js +32 -7
- package/dist/index.js.map +1 -1
- package/dist/{insight-report-Df3lxYXM.d.ts → insight-report-dlpEzQDi.d.ts} +1 -1
- package/dist/{kind-factory-DW9XWPvM.d.ts → kind-factory-DqV2t1Xk.d.ts} +1 -1
- package/dist/meta-eval/index.d.ts +2 -2
- package/dist/openapi.json +1 -1
- package/dist/{provenance-B-TFszPW.d.ts → provenance-CEAJI9rm.d.ts} +3 -3
- package/dist/{registry-DuVYiTvw.d.ts → registry-BmEuU94S.d.ts} +2 -2
- package/dist/{release-report-CN8hJlhk.d.ts → release-report-CXXZlR8g.d.ts} +2 -2
- package/dist/reporting.d.ts +4 -4
- package/dist/{researcher-C_KJyIGg.d.ts → researcher-rInLj9De.d.ts} +2 -2
- package/dist/rl.d.ts +6 -6
- package/dist/rl.js +2 -2
- package/dist/{rubric-predictive-validity-D_4BSXGV.d.ts → rubric-predictive-validity-CWyWWLBg.d.ts} +1 -1
- package/dist/{run-improvement-loop-BqYH2vCR.d.ts → run-improvement-loop-Bgu4C59E.d.ts} +2 -4
- package/dist/{run-record-BgTFzO2r.d.ts → run-record-sItO5ftF.d.ts} +11 -0
- package/dist/{semantic-concept-judge-CV9Wlx4t.d.ts → semantic-concept-judge-Du4ZVyef.d.ts} +3 -3
- package/dist/{summary-report-ByiOUrHj.d.ts → summary-report-BTaXq1TS.d.ts} +1 -1
- package/dist/traces.d.ts +1 -1
- package/dist/traces.js +2 -2
- package/dist/{types-CRD68aH7.d.ts → types-DRvV0zRo.d.ts} +10 -1
- package/dist/{types-Bba0vl1V.d.ts → types-QHG0KnkF.d.ts} +11 -3
- package/dist/workflow/index.d.ts +4 -4
- package/dist/workflow/index.js +1 -1
- package/docs/auto-research-loop-end-to-end.md +1 -1
- package/docs/feature-guide.md +4 -4
- package/docs/multi-shot-optimization.md +61 -115
- package/docs/product-eval-adoption.md +1 -1
- package/docs/three-package-architecture.md +1 -1
- package/docs/trace-analysis.md +19 -0
- package/package.json +1 -1
- package/dist/chunk-7W4SM7FD.js.map +0 -1
- package/dist/chunk-F3SRAAZO.js.map +0 -1
- package/dist/chunk-JYE3WOTE.js.map +0 -1
- package/dist/chunk-WYIHD6EB.js.map +0 -1
- /package/dist/{chunk-XPILG2CA.js.map → chunk-GXHLRXDI.js.map} +0 -0
- /package/dist/{chunk-6EKXFFGQ.js.map → chunk-RTWFUK6A.js.map} +0 -0
- /package/dist/{chunk-XGNCBAVZ.js.map → chunk-XQL22JDG.js.map} +0 -0
- /package/dist/{chunk-GJJNJVIR.js.map → chunk-XXNIODOM.js.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/analyst/types.ts","../src/analyst/finding-subject.ts","../src/analyst/parse-tolerant.ts","../src/analyst/finding-signature.ts","../src/analyst/structure-findings.ts","../src/analyst/kind-factory.ts","../src/analyst/tool-groups.ts","../src/analyst/kinds/failure-mode.ts","../src/analyst/kinds/improvement.ts","../src/analyst/kinds/knowledge-gap.ts","../src/analyst/kinds/knowledge-poisoning.ts","../src/analyst/kinds/index.ts","../src/analyst/registry.ts"],"sourcesContent":["/**\n * Analyst contract — the missing orchestration layer over agent-eval's\n * existing analyzers (analyzeTraces, MultiLayerVerifier, RunCritic,\n * SemanticConceptJudge, JudgeFn, ...).\n *\n * Each existing primitive returns its own output shape. The Analyst\n * contract is the single envelope every primitive lifts into, so a\n * registry can run N analysts against a run and a single renderer can\n * compose findings without knowing which analyzer produced them.\n *\n * The contract is intentionally domain-agnostic: nothing here knows\n * about code, voice, RAG, or any particular agent stack. Analysts\n * declare what INPUT KIND they need (a trace store, an artifact dir,\n * a RunRecord, a JudgeInput, or `custom`), and the registry routes\n * the matching input from `AnalystRunInputs`.\n */\n\nimport { createHash } from 'node:crypto'\nimport type { RunRecord } from '../run-record'\nimport type { TraceAnalysisStore } from '../trace-analyst/store'\nimport type { JudgeInput } from '../types'\nimport type { ChatClient } from './chat-client'\n\n/**\n * Unified envelope every analyst emits. Schema-versioned so renderers\n * and time-series diffs survive future field additions.\n */\nexport interface AnalystFinding {\n schema_version: '1.0.0'\n /**\n * Stable hash over identity-defining fields (analyst_id + canonical\n * claim + area + optional subject). Two findings from two runs that\n * \"are the same finding\" share this id — that's what `diffFindings`\n * uses to compute appeared/disappeared sets across runs.\n */\n finding_id: string\n analyst_id: string\n produced_at: string\n severity: AnalystSeverity\n /**\n * Coarse classification. Renderers group by this. Free-form so\n * domain-specific analysts can introduce categories without a\n * schema change ('agent-reasoning', 'verification', 'cost',\n * 'tool-use', 'safety', 'latency', 'data-quality', ...).\n */\n area: string\n claim: string\n rationale?: string\n evidence_refs: EvidenceRef[]\n recommended_action?: string\n validation_plan?: string\n /** 0..1 — the analyst's own confidence. Not calibrated across analysts. */\n confidence: number\n /**\n * Optional subject the finding is about — leaf id, agent id, request\n * id. Included in finding_id when present so per-subject findings\n * diff cleanly across runs.\n */\n subject?: string\n /** Analyst-private extras; renderers ignore unless they know the analyst. */\n metadata?: Record<string, unknown>\n}\n\nexport type AnalystSeverity = 'critical' | 'high' | 'medium' | 'low' | 'info'\n\nexport interface EvidenceRef {\n /**\n * Where the evidence lives. `span` and `event` refer to OTLP trace\n * elements; `artifact` to a file inside the run's artifact tree;\n * `finding` to another AnalystFinding (cross-analyst chaining);\n * `metric` to a named scalar reading the renderer knows how to read.\n */\n kind: 'span' | 'event' | 'artifact' | 'finding' | 'metric'\n uri: string\n excerpt?: string\n}\n\n// ── Analyst contract ─────────────────────────────────────────────────\n\n/**\n * The discriminator the registry uses to pass the right input.\n * `custom` is the escape hatch — analysts that need something else\n * (e.g. an embedding cache, a partner SDK handle) read it from\n * `AnalystRunInputs.custom[<analyst id>]`.\n */\nexport type AnalystInputKind =\n | 'trace-store'\n | 'artifact-dir'\n | 'run-record'\n | 'judge-input'\n | 'custom'\n\nexport interface AnalystCost {\n /** `deterministic` analysts MUST NOT call the LLM. */\n kind: 'deterministic' | 'llm'\n /** Optional declared upper bound; the registry can enforce a budget. */\n est_usd_per_run?: number\n /** Models the analyst expects to use (informational). */\n models?: string[]\n}\n\nexport interface AnalystRequirements {\n /** Min number of shots / samples the analyst needs to produce signal. */\n min_shots?: number\n /** Capabilities the runtime must supply (e.g. ['network', 'gpu']). */\n capabilities?: string[]\n}\n\n/**\n * What's passed to every analyst call. The registry resolves which\n * field the analyst's `inputKind` selects and asserts it's present.\n */\nexport interface AnalystRunInputs {\n traceStore?: TraceAnalysisStore\n artifactDir?: string\n runRecord?: RunRecord\n judgeInput?: JudgeInput\n /** Keyed by analyst id; populated by callers that registered custom analysts. */\n custom?: Record<string, unknown>\n}\n\nexport interface AnalystContext {\n runId: string\n /** Stable correlation id so logs from a single registry.run() share a tag. */\n correlationId: string\n /** Wall-clock deadline (epoch ms). Analysts SHOULD honor for graceful cancel. */\n deadlineMs?: number\n /** Per-analyst USD budget. Analysts MAY check before issuing LLM calls. */\n budgetUsd?: number\n /**\n * Shared chat client. Analysts that call an LLM go through this so\n * the operator picks transport (sandbox-sdk | router | cli-bridge |\n * direct-provider | mock) at the registry boundary without touching\n * analyst code.\n */\n chat?: ChatClient\n /**\n * Findings from a prior run the operator wants the analyst to see as\n * retrieval context. Kinds that take advantage of cross-run memory\n * (failure-mode \"I saw this cluster last run\", knowledge-gap \"the wiki\n * page I asked for is still missing\") render these into the actor's\n * working set. Filtering is the operator's job: pass the slice that\n * matches the analyst's id, or pass everything and let the kind\n * filter. Empty / absent means no cross-run context.\n */\n priorFindings?: ReadonlyArray<AnalystFinding>\n /** Free-form runtime tags (env, host, op). Findings can echo these into metadata. */\n tags?: Record<string, string>\n /** Logger callback — analysts SHOULD prefer this over console.* for testability. */\n log?: (msg: string, fields?: Record<string, unknown>) => void\n /** Optional abort signal. Analysts SHOULD pass it through to LLM calls. */\n signal?: AbortSignal\n}\n\n/**\n * The minimal contract. Concrete analysts can refine `TInput` so\n * implementations stay type-safe (e.g. a trace analyst's `TInput` is\n * `TraceAnalysisStore`); the registry passes the right field from\n * `AnalystRunInputs` based on `inputKind`.\n */\nexport interface Analyst<TInput = unknown> {\n /** Stable identifier — appears in finding_id, telemetry, and registry exclusion lists. */\n readonly id: string\n /** Human-readable. One sentence. */\n readonly description: string\n readonly inputKind: AnalystInputKind\n readonly cost: AnalystCost\n readonly requires?: AnalystRequirements\n /** Bump on breaking changes to claim wording or area so old finding_ids don't collide. */\n readonly version: string\n analyze(input: TInput, ctx: AnalystContext): Promise<AnalystFinding[]>\n}\n\n// ── finding_id stability ─────────────────────────────────────────────\n\n/**\n * Compute the stable finding_id from the identity-defining fields.\n * Default implementation hashes {analyst_id, area, subject, normalized claim}.\n * Analysts that emit findings whose claim text varies per run (timestamps,\n * counts) SHOULD either: (a) pass an explicit `id_basis` to fix the hash,\n * or (b) move the variable part into `rationale`/`metadata` and keep the\n * `claim` static.\n */\nexport function computeFindingId(input: {\n analyst_id: string\n area: string\n subject?: string\n claim: string\n /** Override the claim for hashing — use when the displayed claim has run-specific bits. */\n id_basis?: string\n}): string {\n const basis = JSON.stringify({\n a: input.analyst_id,\n r: input.area,\n s: input.subject ?? '',\n c: normalizeClaim(input.id_basis ?? input.claim),\n })\n return `f_${createHash('sha256').update(basis).digest('hex').slice(0, 20)}`\n}\n\nfunction normalizeClaim(c: string): string {\n // Lowercase, collapse whitespace, strip trailing punctuation. Goal:\n // \"Leaf X failed install\" and \"Leaf X failed install.\" hash the same.\n return c\n .toLowerCase()\n .replace(/\\s+/g, ' ')\n .replace(/[.!?;:,]+$/g, '')\n .trim()\n}\n\n/**\n * Convenience factory: produce a fully-formed AnalystFinding with the\n * id computed automatically. Analyst code stays terse.\n */\nexport function makeFinding(\n init: Omit<AnalystFinding, 'schema_version' | 'finding_id' | 'produced_at'> & {\n id_basis?: string\n produced_at?: string\n },\n): AnalystFinding {\n const { id_basis, produced_at, ...rest } = init\n return {\n schema_version: '1.0.0',\n finding_id: computeFindingId({\n analyst_id: rest.analyst_id,\n area: rest.area,\n subject: rest.subject,\n claim: rest.claim,\n id_basis,\n }),\n produced_at: produced_at ?? new Date().toISOString(),\n ...rest,\n }\n}\n\n// ── Registry result envelope ────────────────────────────────────────\n\nexport interface AnalystRunSummary {\n analyst_id: string\n status: 'ok' | 'skipped' | 'failed'\n /** Why skipped — missing input, budget exceeded, capability unmet. */\n reason?: string\n findings_count: number\n latency_ms: number\n cost_usd: number\n /** When `status='failed'`: the error class + message, never the full stack. */\n error?: { class: string; message: string }\n}\n\nexport interface AnalystRunResult {\n run_id: string\n correlation_id: string\n started_at: string\n ended_at: string\n findings: AnalystFinding[]\n per_analyst: AnalystRunSummary[]\n /** Total LLM cost in USD across all analysts in this registry.run(). */\n total_cost_usd: number\n}\n\n// ── Streaming event envelope ────────────────────────────────────────\n\n/**\n * Events emitted by `AnalystRegistry.runStream(...)` in real time as\n * the registry executes. UIs subscribe via `for await (const ev of\n * registry.runStream(...))`; `registry.run(...)` is a thin collector\n * over the same stream, so the two surfaces share their invariants.\n *\n * Per-finding events are intentionally omitted — analyzers are batch\n * operations (an Ax actor returns the full `findings:json[]` at the\n * end of the responder), so streaming inside one analyst would only\n * emit partial JSON consumers can't render. The kind-completion event\n * is the right granularity; subscribers wanting per-finding rendering\n * iterate `event.findings` themselves.\n */\nexport type AnalystRunEvent =\n | {\n type: 'run-started'\n run_id: string\n correlation_id: string\n started_at: string\n /** The ordered list of analyst ids the registry will run. */\n analyst_ids: ReadonlyArray<string>\n }\n | {\n type: 'analyst-skipped'\n summary: AnalystRunSummary\n }\n | {\n type: 'analyst-started'\n analyst_id: string\n started_at: string\n }\n | {\n type: 'analyst-completed'\n /** `summary.status` is `'ok'` for clean completion or `'failed'` for thrown analysts. */\n summary: AnalystRunSummary\n findings: ReadonlyArray<AnalystFinding>\n }\n | {\n type: 'run-completed'\n result: AnalystRunResult\n }\n","/**\n * Typed `FindingSubject` — the canonical grammar every analyst kind emits.\n *\n * Background: kind actor prompts have always documented a subject grammar\n * (e.g. `system-prompt:<section>`, `agent-knowledge:wiki:<slug>`) but the\n * LLM was unconstrained — it could emit `subject: \"fix the prompt\"`\n * (prose) and downstream adapters routed on `startsWith(...)` would\n * silently skip it. Every per-vertical `ImprovementAdapter` had a\n * routing table that mostly caught nothing.\n *\n * This module fixes that:\n * - `parseFindingSubject(raw)` — returns the typed `FindingSubject`\n * when `raw` matches the grammar, else `null`. Used at the\n * `RawAnalystFindingSchema` boundary so malformed subjects are\n * rejected loudly instead of silently lifted into the registry.\n * - `FindingSubjectKind` — the union of valid locus categories. Each\n * variant carries the typed components downstream adapters resolve\n * against the agent's surface manifest (no string parsing in the\n * adapter).\n * - `FINDING_SUBJECT_GRAMMAR_PROMPT` — single source of truth for the\n * grammar string embedded in kind actor prompts. Drift between\n * prompt and parser is impossible if every kind imports this.\n *\n * The grammar is intentionally NARROW — only loci the substrate's\n * default `ImprovementAdapter` / `KnowledgeAdapter` can act on. A\n * finding with a subject outside this set fails the parser; the kind\n * author either extends the grammar here (and adds adapter routing)\n * or rephrases the prompt to map onto an existing variant.\n *\n * `failure-mode` is the one exception — its subjects are free-form\n * cluster labels, not loci. The schema preserves them as\n * `{ kind: 'cluster', label }` and the adapters skip them (cluster\n * findings are evidence, not actionable mutations).\n */\n\nimport { z } from 'zod'\n\n// ── canonical grammar ─────────────────────────────────────────────────\n\n/**\n * Discriminated union of every locus the substrate can route findings to.\n *\n * Adapters narrow on `kind` and use the typed components (no string\n * parsing). Adding a variant here REQUIRES updating the parser, the\n * grammar prompt, and at least one adapter — by design.\n */\nexport type FindingSubject =\n // ── agent-knowledge:* — routed to the KnowledgeAdapter ──\n | { kind: 'knowledge.wiki'; slug: string; heading?: string }\n | { kind: 'knowledge.claim'; topic: string }\n | { kind: 'knowledge.raw'; sourceId: string }\n | { kind: 'knowledge.stale'; slug: string }\n // ── system-prompt / tool / new-tool / rag / memory / scaffolding / output-schema ──\n // routed to the ImprovementAdapter\n | { kind: 'system-prompt'; section: string }\n | { kind: 'tool-doc'; tool: string; aspect?: string }\n | { kind: 'new-tool'; name: string }\n | { kind: 'rag'; corpus: string; docId: string }\n | { kind: 'memory'; key: string }\n | { kind: 'scaffolding'; concern: string }\n | { kind: 'output-schema'; field: string }\n // ── websearch / prior-run-summary — routed to the KnowledgeAdapter as stale signals\n | { kind: 'websearch.outdated'; topic: string }\n | { kind: 'prior-run-summary'; topic: string }\n // ── failure-mode cluster label — preserved verbatim, not routed\n | { kind: 'cluster'; label: string }\n\nexport type FindingSubjectKind = FindingSubject['kind']\n\nexport const FINDING_SUBJECT_KINDS: ReadonlyArray<FindingSubjectKind> = [\n 'knowledge.wiki',\n 'knowledge.claim',\n 'knowledge.raw',\n 'knowledge.stale',\n 'system-prompt',\n 'tool-doc',\n 'new-tool',\n 'rag',\n 'memory',\n 'scaffolding',\n 'output-schema',\n 'websearch.outdated',\n 'prior-run-summary',\n 'cluster',\n]\n\n// ── parser ────────────────────────────────────────────────────────────\n\n/**\n * Parse a raw subject string emitted by an analyst kind's actor.\n *\n * Returns the typed `FindingSubject` when `raw` matches the grammar,\n * else `null`. Callers use the `null` return as a signal to either\n * (a) reject the finding at parse time (kinds that emit typed loci —\n * knowledge-gap, improvement, knowledge-poisoning) or (b) lift it as\n * a cluster label (failure-mode).\n *\n * Slugs are constrained to `[a-z0-9-]+` (lowercase kebab) to keep file\n * paths sane downstream. Topics / keys / sections allow any non-empty\n * string (free-form for the LLM's voice) but get trimmed.\n *\n * Empty / whitespace-only inputs return `null`. `undefined` returns\n * `null`. Both are surfaced by the caller as a rejected subject.\n */\nexport function parseFindingSubject(raw: string | null | undefined): FindingSubject | null {\n if (raw === null || raw === undefined) return null\n const trimmed = raw.trim()\n if (trimmed.length === 0) return null\n\n // agent-knowledge:wiki:<slug>[#<heading>]\n const wiki = trimmed.match(\n /^agent-knowledge:wiki:([a-z0-9][a-z0-9-]*)(?:#([a-z0-9][a-z0-9-]*))?$/,\n )\n if (wiki)\n return { kind: 'knowledge.wiki', slug: wiki[1]!, ...(wiki[2] ? { heading: wiki[2] } : {}) }\n\n // agent-knowledge:claim:<topic>\n const claim = trimmed.match(/^agent-knowledge:claim:(.+)$/)\n if (claim && claim[1]!.trim().length > 0)\n return { kind: 'knowledge.claim', topic: claim[1]!.trim() }\n\n // agent-knowledge:raw:<source-id>\n const raw_ = trimmed.match(/^agent-knowledge:raw:(.+)$/)\n if (raw_ && raw_[1]!.trim().length > 0)\n return { kind: 'knowledge.raw', sourceId: raw_[1]!.trim() }\n\n // agent-knowledge:stale:<slug>\n const stale = trimmed.match(/^agent-knowledge:stale:([a-z0-9][a-z0-9-]*)$/)\n if (stale) return { kind: 'knowledge.stale', slug: stale[1]! }\n\n // system-prompt:<section>\n const sp = trimmed.match(/^system-prompt:(.+)$/)\n if (sp && sp[1]!.trim().length > 0) return { kind: 'system-prompt', section: sp[1]!.trim() }\n\n // tool-doc:<tool>[:<aspect>]\n const tdAspect = trimmed.match(/^tool-doc:([a-z0-9][a-z0-9_-]*):(.+)$/)\n if (tdAspect && tdAspect[2]!.trim().length > 0) {\n return { kind: 'tool-doc', tool: tdAspect[1]!, aspect: tdAspect[2]!.trim() }\n }\n const td = trimmed.match(/^tool-doc:([a-z0-9][a-z0-9_-]*)$/)\n if (td) return { kind: 'tool-doc', tool: td[1]! }\n\n // new-tool:<name>\n const nt = trimmed.match(/^new-tool:([a-z0-9][a-z0-9_-]*)$/)\n if (nt) return { kind: 'new-tool', name: nt[1]! }\n\n // rag:<corpus>:<doc-id>\n const rag = trimmed.match(/^rag:([a-z0-9][a-z0-9_-]*):(.+)$/)\n if (rag && rag[2]!.trim().length > 0) {\n return { kind: 'rag', corpus: rag[1]!, docId: rag[2]!.trim() }\n }\n\n // memory:<key>\n const mem = trimmed.match(/^memory:(.+)$/)\n if (mem && mem[1]!.trim().length > 0) return { kind: 'memory', key: mem[1]!.trim() }\n\n // scaffolding:<concern>\n const sc = trimmed.match(/^scaffolding:(.+)$/)\n if (sc && sc[1]!.trim().length > 0) return { kind: 'scaffolding', concern: sc[1]!.trim() }\n\n // output-schema:<field>\n const os = trimmed.match(/^output-schema:(.+)$/)\n if (os && os[1]!.trim().length > 0) return { kind: 'output-schema', field: os[1]!.trim() }\n\n // websearch:outdated:<topic>\n const ws = trimmed.match(/^websearch:outdated:(.+)$/)\n if (ws && ws[1]!.trim().length > 0) return { kind: 'websearch.outdated', topic: ws[1]!.trim() }\n\n // prior-run-summary:<topic>\n const prs = trimmed.match(/^prior-run-summary:(.+)$/)\n if (prs && prs[1]!.trim().length > 0) return { kind: 'prior-run-summary', topic: prs[1]!.trim() }\n\n // cluster (no prefix — a free-form evidence label, never a routed locus, so\n // it admits dotted/underscored identifiers like `appworld.task.530b157_1`.\n // ':' stays excluded so it cannot collide with the prefixed grammars above.)\n if (/^[a-z0-9][a-z0-9._-]*$/.test(trimmed) && trimmed.length <= 80) {\n return { kind: 'cluster', label: trimmed }\n }\n\n return null\n}\n\n/**\n * Render the parsed subject back to its canonical string form. Inverse\n * of `parseFindingSubject`; useful when the substrate constructs new\n * findings programmatically (e.g. for tests, replays, or\n * `id_basis` carry-forward).\n */\nexport function renderFindingSubject(s: FindingSubject): string {\n switch (s.kind) {\n case 'knowledge.wiki':\n return s.heading\n ? `agent-knowledge:wiki:${s.slug}#${s.heading}`\n : `agent-knowledge:wiki:${s.slug}`\n case 'knowledge.claim':\n return `agent-knowledge:claim:${s.topic}`\n case 'knowledge.raw':\n return `agent-knowledge:raw:${s.sourceId}`\n case 'knowledge.stale':\n return `agent-knowledge:stale:${s.slug}`\n case 'system-prompt':\n return `system-prompt:${s.section}`\n case 'tool-doc':\n return s.aspect ? `tool-doc:${s.tool}:${s.aspect}` : `tool-doc:${s.tool}`\n case 'new-tool':\n return `new-tool:${s.name}`\n case 'rag':\n return `rag:${s.corpus}:${s.docId}`\n case 'memory':\n return `memory:${s.key}`\n case 'scaffolding':\n return `scaffolding:${s.concern}`\n case 'output-schema':\n return `output-schema:${s.field}`\n case 'websearch.outdated':\n return `websearch:outdated:${s.topic}`\n case 'prior-run-summary':\n return `prior-run-summary:${s.topic}`\n case 'cluster':\n return s.label\n }\n}\n\n// ── grammar prompt — single source of truth for actor instructions ──\n\n/**\n * The grammar text embedded into kind actor prompts. Kinds opt into\n * the subset of variants they emit (e.g. `improvement` excludes the\n * cluster variant; `failure-mode` includes ONLY the cluster variant).\n *\n * Drift between prompt and parser is impossible: every kind imports\n * this constant + the matching `expects` set, and the unit tests below\n * lock the table to the parser.\n */\nexport const FINDING_SUBJECT_GRAMMAR_PROMPT = [\n 'Subjects MUST match this grammar — anything else is rejected at parse time and your work is wasted:',\n '',\n ' Knowledge loci (write to the agent-knowledge base):',\n ' agent-knowledge:wiki:<slug>[#<heading>] create / update a wiki page',\n ' agent-knowledge:claim:<topic> draft a claim / relation triple',\n ' agent-knowledge:raw:<source-id> lift a raw source into a curated page',\n ' agent-knowledge:stale:<slug> mark a page superseded',\n '',\n ' Runtime mutable surfaces (write to prompts / tools / scaffolding):',\n ' system-prompt:<section> add / replace a system-prompt section',\n ' tool-doc:<tool>[:<aspect>] rewrite a tool description',\n ' new-tool:<name> propose a new tool surface',\n ' rag:<corpus>:<doc-id> ingest / correct a RAG document',\n ' memory:<key> invalidate / set a memory entry',\n ' scaffolding:<concern> change a precondition / retry / verifier',\n ' output-schema:<field> constrain the agent output shape',\n '',\n ' Stale signals (knowledge-poisoning only):',\n ' websearch:outdated:<topic> stale web result',\n ' prior-run-summary:<topic> stale prior-run summary',\n '',\n ' Cluster label (failure-mode only):',\n ' <kebab-case-label> short cluster id, e.g. \"tool-call-loop\"',\n '',\n 'Slugs / tool ids: [a-z0-9-]+ (lowercase kebab). Topics / keys / sections: free-form, trimmed.',\n].join('\\n')\n\n// ── kind expects sets ─────────────────────────────────────────────────\n\n/**\n * The variants each kind is allowed to emit. Used at the kind factory\n * boundary so a knowledge-gap finding can't sneak in a `system-prompt:*`\n * subject (the improvement-analyst's job) and vice versa.\n *\n * `failure-mode` is restricted to `cluster` — the only kind that emits\n * a non-locus subject.\n */\nexport const KIND_EXPECTED_SUBJECTS: Record<string, ReadonlyArray<FindingSubjectKind>> = {\n 'failure-mode': ['cluster'],\n 'knowledge-gap': [\n 'knowledge.wiki',\n 'knowledge.claim',\n 'knowledge.raw',\n 'knowledge.stale',\n 'tool-doc',\n 'system-prompt',\n 'memory',\n 'websearch.outdated',\n 'prior-run-summary',\n ],\n 'knowledge-poisoning': [\n 'knowledge.wiki',\n 'knowledge.claim',\n 'knowledge.raw',\n 'tool-doc',\n 'system-prompt',\n 'memory',\n 'websearch.outdated',\n 'prior-run-summary',\n ],\n improvement: [\n 'system-prompt',\n 'tool-doc',\n 'new-tool',\n 'rag',\n 'memory',\n 'scaffolding',\n 'output-schema',\n 'knowledge.wiki',\n 'knowledge.claim',\n ],\n}\n\n// ── Zod schema for boundary validation ───────────────────────────────\n\n/**\n * Zod schema that validates a raw subject string and returns the parsed\n * `FindingSubject`. Embedded in `RawAnalystFindingSchema` via\n * `transform`, so `subject` arrives at the kind factory either as a\n * typed locus or as a parse error attached to a single Zod issue.\n *\n * Optionality is preserved: subjects ARE optional on the wire (some\n * findings are descriptive, not actionable). When present, they MUST\n * parse — emitting a malformed subject is a contract violation, not a\n * soft signal.\n */\nexport const FindingSubjectStringSchema = z\n .string()\n .refine((s) => parseFindingSubject(s) !== null, {\n message: 'subject does not match the finding-subject grammar',\n })\n","/**\n * Forgiving pre-parse for analyst findings. Weak models routinely emit\n * schema-correct content in an unusable wrapper — fenced ```json blocks, a\n * single object where an array is expected, trailing commas. Measured: GPT-4o\n * drops to 0% usable output purely from markdown-fence wrapping\n * (arXiv:2605.02363). A five-line de-fence recovers most of it. This module is\n * the de-fence/coerce step that runs BEFORE Zod, so a recoverable finding is\n * repaired, not dropped.\n *\n * Pure + deterministic. No model, no network.\n */\n\n/** Strip a ```lang ... ``` (or bare ``` ... ```) code fence, if the string is one. */\nexport function stripCodeFences(text: string): string {\n const t = text.trim()\n const fence = /^```[a-zA-Z0-9]*\\s*\\n?([\\s\\S]*?)\\n?```$/\n const m = t.match(fence)\n return m ? m[1]!.trim() : t\n}\n\n/** Remove trailing commas before } or ] — the most common near-JSON defect. */\nfunction dropTrailingCommas(s: string): string {\n return s.replace(/,(\\s*[}\\]])/g, '$1')\n}\n\n/**\n * Best-effort parse of a string into JSON. De-fences, drops trailing commas,\n * then `JSON.parse`. Returns `undefined` (never throws) when unrecoverable.\n */\nexport function coerceJson(text: string): unknown {\n const candidate = dropTrailingCommas(stripCodeFences(text))\n try {\n return JSON.parse(candidate)\n } catch {\n return undefined\n }\n}\n\n/**\n * Coerce arbitrary actor/structurer output into an array of candidate finding\n * rows: a JSON string → parse; a single object → 1-element array; an array →\n * as-is; anything else → []. Callers still run each row through Zod\n * (`parseRawFinding`) — this only fixes the SHAPE, never invents fields.\n */\nexport function coerceToFindingRows(raw: unknown): unknown[] {\n let value = raw\n if (typeof value === 'string') {\n const parsed = coerceJson(value)\n if (parsed === undefined) return []\n value = parsed\n }\n if (Array.isArray(value)) return value\n if (value && typeof value === 'object') {\n // Some models wrap the array as { findings: [...] } — unwrap that one case.\n const inner = (value as Record<string, unknown>).findings\n if (Array.isArray(inner)) return inner\n return [value]\n }\n return []\n}\n","/**\n * Typed Ax output for analyst findings.\n *\n * Replaces the legacy `findings:string[]` pattern (where every bullet\n * became a flat-severity `AnalystFinding`) with a structured object\n * array. Ax binds the field as `findings:json[]` so the provider emits\n * native structured output; at the kind-factory boundary we Zod-validate\n * each emitted finding so malformed rows fail loud instead of being\n * silently lifted with default severity.\n *\n * Why not `f.object().array()` directly in the signature? The Ax\n * signature string `question:string -> findings:json[]` already lets\n * the provider emit JSON arrays. A Zod boundary is required either\n * way (the provider can return any JSON), and Zod gives us a single\n * validation surface independent of which Ax version is installed.\n */\n\nimport { z } from 'zod'\nimport { parseFindingSubject } from './finding-subject'\nimport { coerceJson } from './parse-tolerant'\n\nexport const ANALYST_SEVERITIES = ['critical', 'high', 'medium', 'low', 'info'] as const\n\nexport const RawAnalystFindingSchema = z\n .object({\n severity: z.enum(ANALYST_SEVERITIES),\n claim: z.string().min(1).max(2000),\n /**\n * Subject locus the finding is about. Validated at parse time\n * against the documented grammar (`finding-subject.ts`). Findings\n * with a malformed subject are rejected — they would have been\n * silently skipped by every downstream adapter, so failing loud at\n * parse time turns a hidden no-op into a kind-prompt audit signal.\n *\n * Optional because purely descriptive findings (no actionable\n * locus) are legitimate; they just don't route through the\n * KnowledgeAdapter / ImprovementAdapter.\n */\n subject: z\n .string()\n .max(400)\n .refine((s) => parseFindingSubject(s) !== null, {\n message: 'subject does not match the finding-subject grammar',\n })\n .optional(),\n evidence_uri: z.string().min(1).max(2000),\n evidence_excerpt: z.string().max(2000).optional(),\n confidence: z.number().min(0).max(1),\n rationale: z.string().max(4000).optional(),\n recommended_action: z.string().max(2000).optional(),\n })\n .strict()\n\nexport type RawAnalystFinding = z.infer<typeof RawAnalystFindingSchema>\n\n/**\n * Description embedded into the actor prompt so the LLM knows what\n * shape to emit. Kept here so kinds share one source of truth rather\n * than restating the schema in every prompt.\n */\nexport const RAW_FINDING_SCHEMA_PROMPT = `Each finding MUST be a JSON object with these fields:\n - severity: one of \"critical\" | \"high\" | \"medium\" | \"low\" | \"info\"\n - claim: one-sentence statement (max 2000 chars)\n - subject?: the routing locus this finding is about. It MUST be one of the exact subject forms listed in this kind's instructions above (e.g. \\`system-prompt:<section>\\`, \\`agent-knowledge:wiki:<slug>\\`, \\`tool-doc:<tool>\\`). A free phrase, a bare noun, or any form not in that list is REJECTED at parse time and the finding is discarded — omit subject entirely rather than guess a form.\n - evidence_uri: REQUIRED, never blank. Exactly one of \"span://<trace_id>/<span_id>\" (trace evidence), \"artifact://<relative-path>\" (files), \"metric://<name>\" (named scalars) — ALWAYS cite a real id surfaced by the tools. If you have no citable id, do not emit the finding.\n - evidence_excerpt?: short quote (<=2000 chars) from the cited span/artifact\n - confidence: number 0..1 — 0.9+ when backed by exact quotes, 0.6-0.8 for inferred patterns, <0.5 for speculative\n - rationale?: one or two sentences explaining the reasoning\n - recommended_action?: concrete change phrased as an imperative (\"Add ...\", \"Replace ...\", \"Stop ...\") — omit when the finding is purely descriptive\n\nEmit an empty array when the question has no findings to report. Do not fabricate evidence.`\n\n/**\n * Validate one row emitted by the LLM. Returns the typed finding on\n * success; returns `null` and logs the reason on failure so the kind\n * factory can skip-and-count rather than abort the whole analyst run.\n */\nexport function parseRawFinding(\n row: unknown,\n log?: (msg: string, fields?: Record<string, unknown>) => void,\n): RawAnalystFinding | null {\n const result = RawAnalystFindingSchema.safeParse(row)\n if (result.success) return result.data\n // A schema-correct finding in an unusable wrapper (a JSON string, a fenced\n // block) should be repaired, not dropped. Coerce the shape and retry ONCE.\n if (typeof row === 'string') {\n const coerced = coerceJson(row)\n if (coerced !== undefined) {\n const retry = RawAnalystFindingSchema.safeParse(coerced)\n if (retry.success) return retry.data\n }\n }\n log?.('finding rejected: schema failure', {\n issues: result.error.issues.map((i) => ({\n path: i.path.join('.'),\n code: i.code,\n message: i.message,\n })),\n })\n return null\n}\n","/**\n * `structureFindings` — the deferred structuring pass (DSPy TwoStepAdapter /\n * HALO `synthesize_traces` analog). The agentic actor reasons FREE-FORM and\n * emits a prose `report` (which any model does reliably); this separate, cheap\n * call's ONLY job is to turn that report into `AnalystFinding[]`. Decoupling\n * reasoning from structuring is what makes the SEMANTIC findings model-agnostic\n * — the reasoning model never has to satisfy a strict typed-array contract\n * while it diagnoses.\n *\n * Forgiving: the response runs through `coerceToFindingRows` (de-fence, lift\n * single→array) before Zod, and on a zero-finding extraction from a substantive\n * report it reasks ONCE with the schema restated. Returns a typed outcome so a\n * legitimate \"nothing to report\" is distinguishable from a failed extraction\n * (no silent empty).\n */\n\nimport { callLlm, type LlmClientOptions } from '../llm-client'\nimport { parseRawFinding, type RawAnalystFinding } from './finding-signature'\nimport { coerceToFindingRows } from './parse-tolerant'\nimport { type AnalystFinding, makeFinding } from './types'\n\nexport interface StructureFindingsOptions {\n /** The actor's free-form diagnosis prose. */\n report: string\n analystId: string\n /** Coarse classification stamped on every extracted finding. */\n area: string\n model: string\n baseUrl: string\n apiKey?: string\n /** Max reask attempts after a zero/invalid extraction. Default 1. */\n maxReasks?: number\n /** Test seam: inject a fetch (no network in unit tests). */\n fetchImpl?: LlmClientOptions['fetch']\n}\n\nexport interface StructureFindingsResult {\n findings: AnalystFinding[]\n outcome: 'ok' | 'extraction_failed'\n}\n\nconst SYSTEM = [\n 'You convert a free-form trace-analysis report into a STRICT JSON array of findings.',\n 'Output ONLY the JSON array — no prose, no code fences.',\n 'Each element: {\"severity\":\"critical|high|medium|low|info\",\"claim\":string,\"evidence_uri\":string,',\n '\"subject\"?:string,\"rationale\"?:string,\"recommended_action\"?:string,\"confidence\":number(0..1)}.',\n 'evidence_uri cites the trace element the report referenced (e.g. \"span://<trace>/<span>\") or \"report://summary\".',\n 'If the report asserts NO problems, output exactly [].',\n].join(' ')\n\nfunction buildRows(raw: unknown, analystId: string, area: string): AnalystFinding[] {\n const rows = coerceToFindingRows(raw)\n const out: AnalystFinding[] = []\n for (const row of rows) {\n // Recovery findings are extracted from PROSE — the report itself is the\n // evidence. A weak model often returns a sound claim + severity but omits\n // `evidence_uri`; default it to the report rather than dropping the row\n // (the strict evidence_uri requirement is a recovery yield-killer).\n const normalized =\n row &&\n typeof row === 'object' &&\n !Array.isArray(row) &&\n !(row as Record<string, unknown>).evidence_uri\n ? { ...(row as Record<string, unknown>), evidence_uri: 'report://summary' }\n : row\n const parsed: RawAnalystFinding | null = parseRawFinding(normalized)\n if (!parsed) continue\n out.push(\n makeFinding({\n analyst_id: analystId,\n area,\n subject: parsed.subject,\n claim: parsed.claim,\n rationale: parsed.rationale,\n severity: parsed.severity,\n confidence: parsed.confidence,\n evidence_refs: [\n {\n kind: parsed.evidence_uri.startsWith('span://') ? 'span' : 'artifact',\n uri: parsed.evidence_uri,\n excerpt: parsed.evidence_excerpt,\n },\n ],\n recommended_action: parsed.recommended_action,\n }),\n )\n }\n return out\n}\n\nexport async function structureFindings(\n opts: StructureFindingsOptions,\n): Promise<StructureFindingsResult> {\n const maxReasks = opts.maxReasks ?? 1\n const llm = { baseUrl: opts.baseUrl, apiKey: opts.apiKey, fetch: opts.fetchImpl }\n let user = `TRACE-ANALYSIS REPORT:\\n${opts.report}\\n\\nReturn the findings JSON array.`\n\n for (let attempt = 0; attempt <= maxReasks; attempt++) {\n const res = await callLlm(\n {\n model: opts.model,\n messages: [\n { role: 'system', content: SYSTEM },\n { role: 'user', content: user },\n ],\n },\n llm,\n )\n const text = res.content.trim()\n const findings = buildRows(text, opts.analystId, opts.area)\n if (findings.length > 0) return { findings, outcome: 'ok' }\n // A report that asserts nothing is a legitimate empty — only reask when the\n // report is substantive (the extraction, not the diagnosis, likely failed).\n if (opts.report.trim().length < 200) return { findings: [], outcome: 'ok' }\n user = `${user}\\n\\nThat produced no valid findings. The report DOES describe issues — re-extract them as the strict JSON array described in the system prompt. Output ONLY the array.`\n }\n return { findings: [], outcome: 'extraction_failed' }\n}\n","/**\n * Analyst-kind factory — the typed, focused replacement for the\n * legacy `createTraceAnalystAdapter`.\n *\n * A \"kind\" is a specialized analyst whose actor prompt, tool subset,\n * and Ax recursion config target one failure-mode lens (failure-mode\n * classification, knowledge gap discovery, knowledge poisoning, recursive\n * self-improvement, ...). Kinds emit findings in the typed `RawAnalystFinding`\n * shape via a JSON-array Ax output; the factory validates each row with\n * Zod and lifts it into `AnalystFinding[]` with no shape guessing.\n *\n * Composition rules:\n * - Each kind owns its actor description. No generic \"answer this\n * question\" prompt — the prompt names the failure lens.\n * - Each kind picks a narrow tool subset from `ANALYST_TOOL_GROUPS`.\n * A kind that never needs full-trace dumps can drop `viewTrace` /\n * `viewSpans` and stay cheap.\n * - Each kind declares its recursion + parallelism budget. Discovery-\n * heavy kinds (failure-mode) get higher `maxDepth`; lens kinds\n * (poisoning) usually stay at 0 since they have a tighter brief.\n *\n * Optimizer hook: kinds may declare `goldens` — labeled examples used\n * by `AxMiPRO` / `AxBootstrapFewShot` / `AxGEPA` to fit the actor\n * description programmatically. Stored on the kind, not the registry,\n * because the right metric is kind-specific.\n */\n\nimport type { AxAIService, AxFunction } from '@ax-llm/ax'\nimport { AxJSRuntime, agent } from '@ax-llm/ax'\nimport type { TraceAnalysisStore } from '../trace-analyst/store'\nimport { TraceFileMissingError } from '../trace-analyst/store-otlp'\nimport {\n parseRawFinding,\n RAW_FINDING_SCHEMA_PROMPT,\n type RawAnalystFinding,\n} from './finding-signature'\nimport { KIND_EXPECTED_SUBJECTS, parseFindingSubject } from './finding-subject'\nimport { structureFindings } from './structure-findings'\nimport type { Analyst, AnalystContext, AnalystCost, AnalystFinding } from './types'\nimport { makeFinding } from './types'\n\n/**\n * Per-kind specification. The factory turns this into a regular\n * `Analyst<TraceAnalysisStore>` ready for `AnalystRegistry.register()`.\n */\nexport interface TraceAnalystKindSpec {\n /** Stable id. Appears in finding_id, telemetry, and registry exclusions. */\n id: string\n /** One-sentence description shown in `registry.list()`. */\n description: string\n /** Coarse classification stamped on every emitted finding (`failure-mode`, `knowledge-gap`, ...). */\n area: string\n /** Bump on any breaking change to the actor prompt or output schema. */\n version: string\n /** Actor system prompt. Must instruct the LLM to emit `findings` per the schema. */\n actorDescription: string\n /** Responder system prompt; falls back to a minimal \"format the findings\" instruction. */\n responderDescription?: string\n /** Tool functions the actor may call. Pick narrow subsets via `ANALYST_TOOL_GROUPS`. */\n buildTools: (store: TraceAnalysisStore) => AxFunction[]\n /** Recursion budget. `maxDepth: 0` disables subagents. */\n recursion?: { maxDepth: number; maxParallelSubagents?: number }\n /** Actor turn cap. Default 12. */\n maxTurns?: number\n /** Runtime char cap. Default 6000. */\n maxRuntimeChars?: number\n /** Cost classification surfaced in `registry.list()` and budget enforcement. */\n cost: AnalystCost\n /** Per-finding-row hook — kinds may reject / rewrite before lifting. */\n postProcess?: (row: RawAnalystFinding, ctx: AnalystContext) => RawAnalystFinding | null\n /** Optional optimizer hook — populated when a kind wants to fit its prompt against labeled examples. */\n goldens?: TraceAnalystGolden[]\n}\n\n/**\n * One labeled example consumed by Ax optimizers (MIPRO / GEPA / Bootstrap).\n * Each input is the same `{question}` an analyst would receive; `expected`\n * is the ground-truth finding set a fitted prompt should produce on this\n * input. Metric: kind-specific (default: F1 on `finding_id` overlap).\n */\nexport interface TraceAnalystGolden {\n question: string\n expected: ReadonlyArray<Omit<RawAnalystFinding, 'confidence'>>\n}\n\nexport interface CreateTraceAnalystKindOpts {\n /** AxAIService bound at registration time. */\n ai: AxAIService\n /** Optional model override; falls back to the AI service's default. */\n model?: string\n /** Override the spec's `version` (e.g. when an optimizer has fitted a new prompt). */\n versionSuffix?: string\n /**\n * Optional two-phase recovery: when the agentic harvest is empty but the\n * actor produced a substantive free-form `report`, extract findings from that\n * prose via a tolerant chat-completions pass (`structureFindings`) — no\n * strict-emission contract, so it works on weak models. Omit to leave the\n * actor's harvest as-is (the report is still surfaced fail-loud either way).\n */\n recovery?: { baseUrl: string; apiKey?: string; model?: string; fetchImpl?: typeof fetch }\n}\n\n/**\n * Build an `Analyst<TraceAnalysisStore>` from a kind spec.\n *\n * Lifts the Ax pipeline once at registration time so the registry\n * gets a stateless analyst. The Ax agent is freshly constructed per\n * `analyze()` call (the agent carries chat-log + usage state we don't\n * want shared across analyst runs).\n */\nexport function createTraceAnalystKind(\n spec: TraceAnalystKindSpec,\n opts: CreateTraceAnalystKindOpts,\n): Analyst<TraceAnalysisStore> {\n const version = opts.versionSuffix ? `${spec.version}+${opts.versionSuffix}` : spec.version\n return {\n id: spec.id,\n description: spec.description,\n inputKind: 'trace-store',\n cost: spec.cost,\n version,\n async analyze(store, ctx) {\n const tools = spec.buildTools(store)\n const maxDepth = spec.recursion?.maxDepth ?? 0\n const maxParallel = spec.recursion?.maxParallelSubagents ?? 2\n const priorContext = renderPriorFindings(ctx.priorFindings)\n\n const actorDescription =\n spec.actorDescription.trim() +\n priorContext +\n '\\n\\n' +\n RAW_FINDING_SCHEMA_PROMPT +\n '\\n\\nFirst write `report`: a concise free-form prose diagnosis of what ' +\n 'the traces show — what succeeded, what was suboptimal or failed — with ' +\n 'concrete trace ids and numbers. THEN return the structured `findings` ' +\n 'array (it MAY be empty when there is nothing to report). Use `final(...)` ' +\n 'with the `{ report, findings }` payload when you are done.'\n\n const ax = agent<{ question: string }, { report: string; findings: unknown[] }>(\n 'question:string -> report:string, findings:json[]',\n {\n agentIdentity: {\n name: spec.id,\n description: spec.description,\n },\n contextFields: ['question'],\n runtime: new AxJSRuntime({\n permissions: [],\n blockDynamicImport: true,\n allowedModules: [],\n freezeIntrinsics: true,\n blockShadowRealm: true,\n preventGlobalThisExtensions: false,\n }),\n mode: maxDepth > 0 ? 'advanced' : 'simple',\n recursionOptions: maxDepth > 0 ? { maxDepth } : undefined,\n maxTurns: spec.maxTurns ?? 12,\n maxRuntimeChars: spec.maxRuntimeChars ?? 6000,\n maxBatchedLlmQueryConcurrency: maxParallel,\n promptLevel: 'detailed',\n // Trace analysis depends on exact prior tool results and runtime variables.\n contextPolicy: { preset: 'full', budget: 'balanced' },\n functions: { local: tools },\n actorOptions: {\n description: actorDescription,\n ...(opts.model ? { model: opts.model } : {}),\n showThoughts: false,\n thinkingTokenBudget: 'none',\n },\n responderOptions: {\n description:\n spec.responderDescription ??\n \"Pass through the actor's `report` prose verbatim, and format the `findings` array exactly as the actor produced it. Do not add, drop, or summarize entries.\",\n ...(opts.model ? { model: opts.model } : {}),\n showThoughts: false,\n },\n bubbleErrors: [TraceFileMissingError],\n },\n )\n\n ctx.log?.(`analyst.kind ${spec.id} forward`, {\n max_depth: maxDepth,\n tool_count: tools.length,\n tags: ctx.tags,\n })\n\n const result = await ax.forward(opts.ai, { question: deriveQuestion(ctx, spec) })\n\n const expectedSubjects = KIND_EXPECTED_SUBJECTS[spec.id]\n const out: AnalystFinding[] = []\n const rawRows = Array.isArray(result.findings) ? result.findings : []\n let rejectedWrongKind = 0\n for (const row of rawRows) {\n const parsed = parseRawFinding(row, ctx.log)\n if (!parsed) continue\n // Subject-grammar check: if the kind has a declared expects-set\n // (every shipped kind does), the finding's subject MUST parse to\n // one of the declared variants. A wrong-kind subject is a\n // contract violation — the actor's prompt drifted from the\n // grammar — and we count it for prompt-audit visibility.\n if (expectedSubjects && parsed.subject !== undefined) {\n const parsedSubject = parseFindingSubject(parsed.subject)\n if (parsedSubject === null) {\n ctx.log?.('finding rejected: subject failed to parse', {\n kind: spec.id,\n subject: parsed.subject,\n })\n rejectedWrongKind += 1\n continue\n }\n if (!expectedSubjects.includes(parsedSubject.kind)) {\n ctx.log?.('finding rejected: subject variant not allowed for this kind', {\n kind: spec.id,\n subject_kind: parsedSubject.kind,\n subject: parsed.subject,\n allowed: expectedSubjects,\n })\n rejectedWrongKind += 1\n continue\n }\n }\n const postProcessed = spec.postProcess?.(parsed, ctx) ?? parsed\n if (!postProcessed) continue\n out.push(toAnalystFinding(spec, postProcessed))\n }\n\n ctx.log?.(`analyst.kind ${spec.id} done`, {\n emitted: rawRows.length,\n accepted: out.length,\n rejected_wrong_subject: rejectedWrongKind,\n })\n\n // Two-phase recovery / fail-loud. The actor reasons free-form (the\n // `report`); a weak model often produces a sound diagnosis but fails the\n // strict findings emission (or the rows get rejected). If the harvest is\n // empty but the report is substantive, recover findings from the prose\n // via the tolerant structuring pass (opt-in), and — either way — surface\n // the report as a visible info finding so an empty harvest is never a\n // silent zero. A genuinely empty diagnosis (short/no report) stays empty.\n const report = typeof result.report === 'string' ? result.report : ''\n if (out.length === 0 && report.trim().length >= 200) {\n if (opts.recovery) {\n const recovered = await structureFindings({\n report,\n analystId: spec.id,\n area: spec.area,\n model: opts.recovery.model ?? opts.model ?? '',\n baseUrl: opts.recovery.baseUrl,\n apiKey: opts.recovery.apiKey,\n fetchImpl: opts.recovery.fetchImpl,\n })\n out.push(...recovered.findings)\n ctx.log?.(`analyst.kind ${spec.id} recovery`, {\n outcome: recovered.outcome,\n recovered: recovered.findings.length,\n })\n }\n if (out.length === 0) {\n out.push(\n makeFinding({\n analyst_id: spec.id,\n area: spec.area,\n claim: 'Analyst produced a diagnosis but no structured findings — see report.',\n rationale: report.slice(0, 1500),\n severity: 'info',\n confidence: 0.3,\n evidence_refs: [\n { kind: 'artifact', uri: 'report://summary', excerpt: report.slice(0, 2000) },\n ],\n metadata: { outcome: 'extraction_failed' },\n }),\n )\n }\n }\n return out\n },\n }\n}\n\nfunction deriveQuestion(ctx: AnalystContext, spec: TraceAnalystKindSpec): string {\n // The actor's user message must orient it at the task, not echo the kind id.\n // A bare id like \"failure-mode\" gives the actor nothing to act on, so it\n // spends turns inspecting the input instead of reading traces. Operators can\n // still steer with `tags.focus = \"leaf-X\"`, appended to the task directive.\n const focus = ctx.tags?.focus?.trim()\n const task = `Analyze this trace dataset with the available tools and report ${spec.area} findings. ${spec.description}`\n return focus ? `${task} Focus: ${focus}.` : task\n}\n\nfunction toAnalystFinding(spec: TraceAnalystKindSpec, raw: RawAnalystFinding): AnalystFinding {\n return makeFinding({\n analyst_id: spec.id,\n area: spec.area,\n subject: raw.subject,\n claim: raw.claim,\n rationale: raw.rationale,\n severity: raw.severity,\n confidence: raw.confidence,\n evidence_refs: [\n {\n kind: evidenceKindFromUri(raw.evidence_uri),\n uri: raw.evidence_uri,\n excerpt: raw.evidence_excerpt,\n },\n ],\n recommended_action: raw.recommended_action,\n metadata: { kind_version: spec.version },\n })\n}\n\nfunction evidenceKindFromUri(uri: string): 'span' | 'artifact' | 'metric' | 'event' | 'finding' {\n if (uri.startsWith('span://')) return 'span'\n if (uri.startsWith('artifact://')) return 'artifact'\n if (uri.startsWith('metric://')) return 'metric'\n if (uri.startsWith('event://')) return 'event'\n if (uri.startsWith('finding://')) return 'finding'\n return 'artifact'\n}\n\n/**\n * Render a compact prior-findings block the actor reads alongside its\n * brief. Each row is one line so the actor can scan dozens cheaply.\n * The kind's prompt instructs the actor to (a) check whether a new\n * cluster matches a prior `finding_id` (carry the id forward via\n * `id_basis` to keep diffs stable) and (b) raise severity / confidence\n * when a prior finding has reappeared without remediation.\n *\n * Returns the empty string when there are no prior findings — most\n * runs are \"first-of-its-kind\" and the prompt stays unchanged.\n *\n * Exported for tests + for consumers that build their own actor\n * prompts (e.g. specialized analysts living outside the default kinds).\n */\nexport function renderPriorFindings(prior: AnalystContext['priorFindings']): string {\n if (!prior || prior.length === 0) return ''\n const MAX_ROWS = 40 // keep the block under ~2KB; older history is summarized externally\n const rows = prior.slice(0, MAX_ROWS).map((f) => {\n const subject = f.subject ? ` [${f.subject}]` : ''\n return ` - id=${f.finding_id} ${f.severity}${subject} ${truncateForContext(f.claim, 160)}`\n })\n const overflow =\n prior.length > MAX_ROWS\n ? `\\n ... +${prior.length - MAX_ROWS} more prior findings (older history truncated)`\n : ''\n return [\n '',\n '',\n 'PRIOR FINDINGS (from a previous run on related data):',\n 'When the work you do now matches a row below, REUSE the `finding_id` (pass it as `id_basis`) so the cross-run diff stays stable.',\n 'A finding that reappears with no remediation evidence SHOULD raise its `confidence` and may justify a higher `severity`.',\n ...rows,\n overflow,\n ]\n .filter(Boolean)\n .join('\\n')\n}\n\nfunction truncateForContext(s: string, max: number): string {\n if (s.length <= max) return s\n return `${s.slice(0, max - 1).trimEnd()}…`\n}\n","/**\n * Pre-curated tool subsets for analyst kinds.\n *\n * The full trace-analyst tool set is seven functions. Most kinds only\n * need three or four. Picking from named groups instead of importing\n * the whole bundle keeps every kind's actor-context budget tight and\n * makes \"what can this analyst see?\" obvious at registration time.\n *\n * Each function in the group keeps its full `name`/`description` from\n * `buildTraceAnalystTools` — we filter, we don't re-implement.\n */\n\nimport type { AxFunction } from '@ax-llm/ax'\nimport type { TraceAnalysisStore } from '../trace-analyst/store'\nimport { buildTraceAnalystTools } from '../trace-analyst/tools'\n\n/** Named tool sets. Kinds pass `tools: TRACE_TOOL_GROUPS.failureForensics` etc. */\nexport type TraceToolGroupName =\n /** All seven tools. Use for open-ended discovery kinds. */\n | 'all'\n /** Overview + paginated query + count. No deep reads. Cheap. */\n | 'discovery'\n /** Discovery + viewTrace + viewSpans. Deep-read but no regex search. */\n | 'discoveryAndRead'\n /** Discovery + search tools. For pattern-matching across many traces. */\n | 'discoveryAndSearch'\n /** Discovery + viewSpans + searchSpan. Targeted-span work after another kind narrows down. */\n | 'targeted'\n\nconst TOOL_NAMES_BY_GROUP: Record<TraceToolGroupName, ReadonlySet<string>> = {\n all: new Set(),\n discovery: new Set(['getDatasetOverview', 'queryTraces', 'countTraces']),\n discoveryAndRead: new Set([\n 'getDatasetOverview',\n 'queryTraces',\n 'countTraces',\n 'viewTrace',\n 'viewSpans',\n ]),\n discoveryAndSearch: new Set([\n 'getDatasetOverview',\n 'queryTraces',\n 'countTraces',\n 'searchTrace',\n 'searchSpan',\n ]),\n targeted: new Set(['getDatasetOverview', 'queryTraces', 'viewSpans', 'searchSpan']),\n}\n\n/**\n * Build the tool set for a named group bound to a specific trace store.\n *\n * `all` returns every tool. Other groups filter `buildTraceAnalystTools`\n * by name to the documented subset. An unrecognised group name throws —\n * silently returning all tools would defeat the cost-control point.\n */\nexport function buildTraceToolsForGroup(\n group: TraceToolGroupName,\n store: TraceAnalysisStore,\n): AxFunction[] {\n const all = buildTraceAnalystTools({ store })\n if (group === 'all') return all\n const allow = TOOL_NAMES_BY_GROUP[group]\n if (!allow) throw new Error(`unknown trace tool group: ${group}`)\n return all.filter((tool) => allow.has((tool as { name: string }).name))\n}\n","/**\n * Failure-mode analyst — classifies what went wrong and why.\n *\n * Brief: read the trace dataset, identify the top failure modes across\n * runs, classify each with severity + evidence, and surface them as\n * findings. The actor's job is *taxonomy + evidence*, not fix-design —\n * that's the improvement-analyst's job.\n *\n * Recursion is deep (`maxDepth: 3`) because real failure-mode\n * discovery is genuinely tree-shaped: the actor splits the dataset\n * into candidate clusters, each cluster spawns a focused investigator\n * that drills into representative traces, and a deeply-recursed\n * investigator may itself split a confounded mode into two sub-modes.\n * Each level fans out 4-way, so the analyst can investigate up to\n * ~16 leaf clusters before hitting the depth ceiling.\n */\n\nimport type { TraceAnalystKindSpec } from '../kind-factory'\nimport { buildTraceToolsForGroup } from '../tool-groups'\n\nconst ACTOR_PROMPT = `You are a failure-mode classifier for an OTLP trace dataset. Your job is to identify the **distinct ways agents failed** in this dataset, not to grade individual runs.\n\nDISCOVERY → CLUSTER → CITE protocol:\n\n1. Call \\`traces.getDatasetOverview({})\\` first. Use \\`has_errors\\`, \\`models\\`, \\`agent_names\\`, \\`tools\\`, and \\`sample_trace_ids\\` to size the failure surface.\n2. Use \\`traces.queryTraces({ filters: { has_errors: true }, limit })\\` to pull error-bearing traces. Combine with \\`traces.countTraces\\` to see what fraction of the dataset failed.\n3. For each candidate failure cluster, use \\`traces.searchTrace\\` with regex like \\`STATUS_CODE_ERROR\\`, \\`MaxTurnsExceeded\\`, \\`assertion\\`, \\`unauthorized\\`, \\`timeout\\`, \\`429\\`, \\`5\\\\d\\\\d\\`, the agent's specific error strings, or the names of its tools. Pull one or two representative traces per cluster, **not all** of them.\n4. **Cluster, do not enumerate.** Two failures with the same root cause should be ONE finding citing both traces, not two findings. The point of this analyst is to compress N runs into K modes.\n5. For each cluster you can defend with evidence, emit ONE finding with:\n - \\`area\\` = \"failure-mode\"\n - \\`subject\\` = a short label for the cluster (\"tool-call-loop\", \"auth-revoked-mid-run\", \"agent-asked-clarification-too-late\", ...)\n - \\`claim\\` = one sentence stating the mode\n - \\`severity\\` = \"critical\" when it blocks the run, \"high\" when the run finished degraded, \"medium\" when it slowed convergence\n - \\`evidence_uri\\` = \\`span://<trace_id>/<span_id>\\` of the most representative span\n - \\`evidence_excerpt\\` = the exact quote (e.g. error message, stuck tool call payload, contradictory turn output)\n - \\`confidence\\` = 0.85+ when multiple traces show the same shape; 0.6-0.8 for a single-trace inference; <0.5 for speculative.\n - \\`recommended_action\\` = imperative-phrased fix idea (kept short — the improvement-analyst will expand on these)\n\nIf the dataset has no failures, return an empty findings array — do NOT pad with low-confidence speculation.\n\n**Delegate aggressively.** The recursion budget is there to be used:\n- After your first \\`getDatasetOverview\\` + \\`queryTraces\\` calls, you should have 3-6 candidate failure clusters in mind. Spawn one \\`llmQuery\\` per cluster in a single batch — they investigate in parallel.\n- A sub-investigator that finds its cluster is actually two distinct modes should split again at its own level. Recursion is meant to discover sub-modes, not to do trivial drilling that the parent could do in-line.\n- Pass narrow context to each subagent: { question: 'investigate the auth-revoked-mid-run cluster', context: { trace_ids: ['abc', 'def'], suspected_root_cause: 'token refresh skipped on idle sessions' } }. Subagents need enough context to skip re-discovery but not the whole conversation.\n- Each subagent returns its findings as JSON; the parent merges them. Do NOT have subagents call \\`final()\\` — they return their findings list to you, and you call \\`final()\\` once at the top.\n\nOBSERVABILITY rules:\n- Each non-final turn must emit at least one \\`console.log\\` for evidence.\n- Reuse runtime variables across turns; don't recompute.\n- Call \\`final({ findings: [...] })\\` exactly once, after you've gathered evidence for every cluster you intend to report.`\n\nexport const FAILURE_MODE_KIND_SPEC: TraceAnalystKindSpec = {\n id: 'failure-mode',\n description:\n 'Clusters trace-dataset failures into distinct failure modes with cited evidence and a short recommended action.',\n area: 'failure-mode',\n version: '1.0.0',\n actorDescription: ACTOR_PROMPT,\n buildTools: (store) => buildTraceToolsForGroup('all', store),\n recursion: { maxDepth: 3, maxParallelSubagents: 4 },\n maxTurns: 24,\n cost: { kind: 'llm' },\n}\n","/**\n * Improvement analyst — actionable, recursive self-improvement findings.\n *\n * Brief: read findings from upstream analysts (failure-mode,\n * knowledge-gap, knowledge-poisoning) AND the trace dataset itself,\n * then propose **concrete edits** to the agent's runtime: prompt\n * additions, RAG documents to ingest, tool descriptions to rewrite,\n * scaffolding changes to make, memory entries to invalidate. Each\n * finding is one proposed edit with the locus, the diff, and the\n * expected effect.\n *\n * This is the recursive-self-improvement loop's last mile: the prior\n * kinds describe *what's wrong*; this kind describes *what to change*.\n *\n * Recursion is deep (`maxDepth: 3`) because real improvement proposals\n * are competitive: for each failure-mode there are usually 2-3 viable\n * fix directions (tighten prompt vs add tool vs adjust scaffolding),\n * and the actor should explore each with a focused subagent before\n * picking the highest-leverage one to recommend.\n */\n\nimport type { TraceAnalystKindSpec } from '../kind-factory'\nimport { buildTraceToolsForGroup } from '../tool-groups'\n\nconst ACTOR_PROMPT = `You are a recursive-self-improvement analyst. Your job is to propose **concrete, locus-named edits** the agent's runtime should adopt to fix the failure modes, knowledge gaps, and poisonings present in this dataset.\n\nUpstream analysts have already classified the problems. Your job is to convert each problem into a *change to make* and grade its expected leverage. Each finding is one proposed edit.\n\nDISCOVERY → CANDIDATE-FIXES → COMPETE → CITE protocol:\n\n1. \\`traces.getDatasetOverview({})\\` first. Note the agents, tools, and any system-prompt fingerprints (look for the prompt text echoed in early spans).\n2. For each high-severity failure pattern, generate 2-3 candidate fixes. Real candidate axes:\n - **System-prompt edit** — add an instruction, remove a misleading one, restructure precedence\n - **Tool description edit** — rewrite a tool's description so the agent picks it correctly / passes valid args\n - **New tool** — add a tool the agent kept emulating in code\n - **RAG ingestion** — add a document or correct a stale one\n - **Memory invalidation** — clear cached prior-run decisions that no longer apply\n - **Scaffolding** — add a precondition check, a retry policy, a turn budget, a verification step\n - **Output schema** — narrow the agent's output to forbid the failure shape\n3. **Compete candidate fixes via subagents.** For each failure cluster, spawn one \\`llmQuery\\` per candidate-fix axis you want to evaluate. Each subagent's job: simulate the fix on the cited traces and report (i) likely effect, (ii) side effects, (iii) implementation cost as small/medium/large. Pass the cluster's failing trace_ids and the candidate axis as context.\n4. After subagents return, **pick the winning candidate per cluster** based on (effect / cost) and emit ONE finding. Discard the losing candidates — the output is the recommendation, not the candidate set.\n5. **Cross-reference upstream findings.** If a finding cites a prior failure-mode or knowledge-gap finding, use \\`evidence_uri = \"finding://<prior-finding-id>\"\\` (the registry supports this kind). This builds the dependency graph that lets the dashboard show \"fix #X resolves failure modes A, B, C.\"\n\nFor each winning recommendation, emit ONE finding with:\n- \\`area\\` = \"improvement\"\n- \\`subject\\` = the locus to edit: \\`system-prompt:<section>\\`, \\`tool-doc:<tool-name>\\`, \\`new-tool:<proposed-name>\\`, \\`rag:<corpus>:<doc-id>\\`, \\`memory:<key>\\`, \\`scaffolding:<concern>\\`, \\`output-schema:<field>\\`\n- \\`claim\\` = one sentence stating the edit (\"Add a precondition check to refuse tool X calls without arg Y\")\n- \\`severity\\` = leverage rating: \"critical\" when fix resolves a critical failure mode; \"high\" when it resolves a high; \"medium\" when it's a quality-of-life win; \"info\" when it's a cleanup with no behavioral effect\n- \\`evidence_uri\\` = the failure-mode finding id this fix targets (\\`finding://<id>\\`) when it exists; else the most representative span\n- \\`evidence_excerpt\\` = a fragment showing the problem the fix targets\n- \\`confidence\\` = 0.85+ when the fix is mechanical and the failure mode is well-evidenced; 0.6-0.8 when the fix requires judgment; <0.5 for speculative\n- \\`rationale\\` = why this candidate beat its alternatives (2 sentences max)\n- \\`recommended_action\\` = the **literal edit**, phrased as a diff or a quoted replacement: \"Replace section X with: '...'\" or \"Add tool with description: '...'\" or \"Set retry policy to max_attempts=3 with exponential backoff\"\n\nIf no upstream failure findings exist in this run, derive your own from the trace dataset using the failure-mode protocol inline (\\`searchTrace\\` for STATUS_CODE_ERROR / MaxTurnsExceeded / etc.). But prefer to consume upstream findings when present — the kinds are designed to chain.\n\nDo NOT propose a fix you cannot defend with evidence. \"Tighten the prompt\" is not a finding; \"Add 'When the user asks for X, always Y' to the system prompt section \"request-classification\"\" is.\n\nOBSERVABILITY rules:\n- Each non-final turn must emit at least one \\`console.log\\` for evidence.\n- Call \\`final({ findings: [...] })\\` exactly once at the top level.`\n\nexport const IMPROVEMENT_KIND_SPEC: TraceAnalystKindSpec = {\n id: 'improvement',\n description:\n 'Converts upstream failure / gap / poisoning findings into concrete locus-named edits (prompt, tool-doc, RAG, scaffolding) with leverage grades.',\n area: 'improvement',\n version: '1.0.0',\n actorDescription: ACTOR_PROMPT,\n buildTools: (store) => buildTraceToolsForGroup('all', store),\n recursion: { maxDepth: 3, maxParallelSubagents: 4 },\n maxTurns: 30,\n maxRuntimeChars: 12000,\n cost: { kind: 'llm' },\n}\n","/**\n * Knowledge-gap analyst — what did the agent NOT know that it needed?\n *\n * Brief: find moments in the trace where the agent had to guess, ask\n * the user to fill in context, recover from a wrong assumption, or\n * loop on a retrieval. Each finding names a *missing or outdated piece\n * of knowledge* the agent's curated knowledge base should have held —\n * or a downstream lookup (web, docs, tool description) that surfaced\n * stale or outdated information.\n *\n * The primary expected store is `@tangle-network/agent-knowledge`: a\n * Karpathy-style wiki the agent maintains with raw ↔ curated pages,\n * source anchors, and claim/relation triples. A gap is anything the\n * agent had to discover at run-time that should already have lived\n * there. Secondary loci: web-search results that returned outdated\n * pages, tool descriptions that omitted critical behavior, system-\n * prompt sections that didn't cover the case.\n *\n * Distinct from failure-mode: failure-mode classifies *how* it broke;\n * knowledge-gap names the *information* whose absence (or staleness)\n * caused the break. One failure-mode often maps to several gaps.\n *\n * Recursion (`maxDepth: 2`) is enough to fan out one subagent per\n * candidate gap-source layer; each subagent runs a focused detection.\n */\n\nimport type { TraceAnalystKindSpec } from '../kind-factory'\nimport { buildTraceToolsForGroup } from '../tool-groups'\n\nconst ACTOR_PROMPT = `You are a knowledge-gap analyst for an OTLP trace dataset. Your job is to identify the **specific pieces of information the agent lacked, or that were stale**, that caused poor decisions.\n\nThe agent under analysis maintains a curated knowledge base via \\`@tangle-network/agent-knowledge\\` — a wiki of \\`KnowledgePage\\`s with raw source anchors, claims, and relations. The primary expected store of agent-knowable facts IS that wiki. A \"knowledge gap\" is anything the agent had to discover or guess at run-time that the wiki should have held — or an outdated/contradictory fact the agent picked up from a non-wiki source.\n\nDISCOVERY → ATTRIBUTE-TO-LAYER → CITE protocol:\n\n1. \\`traces.getDatasetOverview({})\\` first. Note which agents, tools, and models appear.\n2. Pull traces where the agent shows gap signals. The strongest signals are:\n - Self-correction turns (\"I assumed X but…\", \"let me re-check\", \"actually,\")\n - Clarifying-question turns where the agent asked the user something the runtime should have surfaced\n - Repeated retrieval / lookup calls for the same artifact with slightly varied queries\n - Tool errors that name a missing argument or unknown resource\n - Web-search calls returning pages dated before a known cutoff for content that changes (versioned APIs, schemas, policies)\n - Agent quoting a tool's docs / system prompt incorrectly because the actual text was insufficient\n - Fabricated identifiers that don't appear in dataset \\`sample_trace_ids\\`\n Use \\`traces.searchTrace\\` with patterns like \\`I (don.?t|do not) know\\`, \\`assumed\\`, \\`unclear\\`, \\`could you (clarify|tell me|provide)\\`, \\`not found\\`, \\`undefined\\`, \\`unknown\\`, \\`null\\`, dates older than the analysis window, or the agent's specific clarification phrases.\n3. For each gap, identify the **layer of the runtime that should have prevented it**. The locus is the value of \\`subject\\` on the finding. Use one of:\n - \\`agent-knowledge:wiki:<page-slug>\\` — the wiki page that should exist but doesn't, or exists but lacks the claim\n - \\`agent-knowledge:wiki:<page-slug>#<heading>\\` — wiki page exists but a specific section is missing\n - \\`agent-knowledge:claim:<topic>\\` — a specific claim/relation triple that should be in the wiki\n - \\`agent-knowledge:raw:<source-id>\\` — raw source captured but never lifted into a curated page\n - \\`agent-knowledge:stale:<page-slug>\\` — wiki page exists but contradicts ground-truth evidence in this trace (the wiki itself drifted)\n - \\`websearch:outdated:<topic>\\` — agent relied on a web result that was stale; wiki should have superseded it\n - \\`tool-doc:<tool-name>:<aspect>\\` — tool description missed a behavior aspect (return shape, failure modes, side effects)\n - \\`system-prompt:<section>\\` — system prompt should have stated the rule directly\n - \\`memory:<key>\\` — prior-run memory should have surfaced an earlier decision\n4. For each gap you can defend with evidence, emit ONE finding with:\n - \\`area\\` = \"knowledge-gap\"\n - \\`subject\\` = the locus string from the list above\n - \\`claim\\` = a sentence naming the missing or stale knowledge (\"wiki has no page on invoice line-item shape, agent had to re-derive it from raw spans\")\n - \\`severity\\` = \"high\" when the gap caused a failure or a clarifying question; \"medium\" when it caused unnecessary turns; \"low\" when it caused minor inefficiency\n - \\`evidence_uri\\` = \\`span://<trace_id>/<span_id>\\` of the moment the gap surfaced (the question, the self-correction, the retrieval miss, the stale web result)\n - \\`evidence_excerpt\\` = exact quote where the agent showed the gap\n - \\`confidence\\` = 0.85+ when the agent itself articulated the gap; 0.6-0.8 when inferred from behavior\n - \\`recommended_action\\` = phrased as a wiki edit when the locus is \\`agent-knowledge:*\\` (\"Create wiki page \\`invoice-line-items\\` with claims: ...\"), or as a prompt/tool-doc edit otherwise\n\n**Delegate per layer.** After your first scan, you should have candidates spread across \\`agent-knowledge:*\\`, \\`websearch:outdated\\`, \\`tool-doc:*\\`, \\`system-prompt:*\\`, and \\`memory:*\\`. Spawn one \\`llmQuery\\` per layer in parallel — each subagent runs a focused detection (e.g. the \\`agent-knowledge\\` subagent looks for both missing-pages AND stale-pages; the \\`websearch\\` subagent looks specifically for date staleness signals; the \\`tool-doc\\` subagent looks for tool-call argument errors a fuller description would have prevented). Subagents return findings; you merge and emit one \\`final({ findings })\\` at the top.\n\nDo NOT report a gap that the agent later recovered from cleanly within the same turn — that's resilience, not a gap. Cite the *non-recovery* version when both exist.\n\nOBSERVABILITY rules:\n- Each non-final turn must emit at least one \\`console.log\\` for evidence.\n- Call \\`final({ findings: [...] })\\` exactly once at the top level.`\n\nexport const KNOWLEDGE_GAP_KIND_SPEC: TraceAnalystKindSpec = {\n id: 'knowledge-gap',\n description:\n 'Identifies missing or stale pieces of knowledge — primarily against the agent-knowledge wiki — and attributes each to the runtime layer (wiki page, claim, raw source, websearch, tool-doc, system-prompt, memory) that should have held it.',\n area: 'knowledge-gap',\n version: '1.0.0',\n actorDescription: ACTOR_PROMPT,\n buildTools: (store) => buildTraceToolsForGroup('discoveryAndSearch', store),\n recursion: { maxDepth: 2, maxParallelSubagents: 4 },\n maxTurns: 18,\n cost: { kind: 'llm' },\n}\n","/**\n * Knowledge-poisoning analyst — what FALSE information misled the agent?\n *\n * Brief: find moments where the agent acted on information that was\n * *wrong* — stale memory, RAG documents that contradicted ground truth,\n * tool descriptions that lied about return shapes, system-prompt\n * instructions that no longer matched reality, prior-run summaries that\n * cached a wrong decision.\n *\n * Distinct from knowledge-gap: a gap is \"the agent didn't know X\"; a\n * poisoning is \"the agent confidently used X, but X was wrong.\" Gaps\n * surface as questions / self-correction; poisonings surface as\n * confident-but-wrong actions that downstream evidence contradicts.\n *\n * Recursion is moderate (`maxDepth: 2`) because each candidate\n * poisoning typically needs two sub-investigations: one to confirm\n * the agent acted on the false belief, one to confirm the belief\n * itself is actually false in ground truth.\n */\n\nimport type { TraceAnalystKindSpec } from '../kind-factory'\nimport { buildTraceToolsForGroup } from '../tool-groups'\n\nconst ACTOR_PROMPT = `You are a knowledge-poisoning analyst for an OTLP trace dataset. Your job is to identify cases where the agent **confidently used wrong information** — not where it lacked information (that's the knowledge-gap analyst).\n\nDISCOVERY → DUAL-VERIFY → CITE protocol:\n\n1. \\`traces.getDatasetOverview({})\\` first. Identify the agents, models, and tools.\n2. Pull traces where the agent's confident action was later contradicted. Strongest signals:\n - Agent stated a fact in one span; a later span surfaced contradictory evidence; the agent then proceeded anyway or fabricated reconciliation.\n - Tool call with stale arguments (an id that no longer exists, an API shape that changed).\n - Agent cited an \\`agent-knowledge\\` wiki page or claim whose content contradicts the trace's own evidence — the wiki itself drifted.\n - Web-search result the agent cited that returned an outdated page; agent treated it as canonical.\n - System-prompt instruction the agent followed that ground-truth evidence in the trace contradicts (e.g. prompt says \"use endpoint A\"; tool reply says \"endpoint A deprecated, use B\").\n - Repeated wrong-shape parsing despite the tool's actual output proving the shape.\n3. Use \\`traces.searchTrace\\` with regex on phrases like \\`actually\\`, \\`turns out\\`, \\`previously assumed\\`, \\`old version\\`, \\`deprecated\\`, \\`updated to\\`, \\`now uses\\`, or specific entity names you suspect have changed.\n4. For each candidate poisoning, **DUAL-VERIFY**:\n - Confirm the agent actually acted on the false belief (cite the span where it did)\n - Confirm the belief is actually false in this trace's own evidence (cite the span that contradicts it)\n Only emit a finding when both halves are nailed down. If you can only nail one, drop it — single-evidence poisoning findings are too speculative to be useful.\n\n**Delegate the dual-verify.** Use the recursion budget so each candidate poisoning gets one subagent investigating \"did the agent act?\" and one investigating \"is the belief false?\". After your first scan, fire off N parallel \\`llmQuery\\` pairs (one cluster per pair). Subagents return their findings; you accept only the ones where BOTH halves of the pair were confirmed.\n\nFor each confirmed poisoning, emit ONE finding with:\n- \\`area\\` = \"knowledge-poisoning\"\n- \\`subject\\` = the source of the false belief, one of: \\`agent-knowledge:wiki:<page-slug>\\` (wiki page contradicts current ground truth), \\`agent-knowledge:claim:<topic>\\` (a specific claim/relation went stale), \\`agent-knowledge:raw:<source-id>\\` (the raw source is outdated and the wiki inherited the drift), \\`websearch:outdated:<url-or-topic>\\`, \\`tool-doc:<tool>\\`, \\`system-prompt:<section>\\`, \\`memory:<key>\\`, \\`prior-run-summary:<topic>\\`\n- \\`claim\\` = one sentence: \"agent believed X (from source S); evidence in trace shows X is false\"\n- \\`severity\\` = \"critical\" when poisoning caused a wrong user-visible action; \"high\" when caught internally but wasted significant work; \"medium\" for inefficiency only\n- \\`evidence_uri\\` = \\`span://<trace_id>/<span_id>\\` of the action span (the moment the agent acted on the false belief)\n- \\`evidence_excerpt\\` = exact quote of the confident-but-wrong claim or action\n- \\`confidence\\` = 0.85+ when both halves are exact-quote backed; 0.6-0.8 when one half is inferred\n- \\`recommended_action\\` = where the source should be updated and how (\"Update wiki page \\`X\\` claim \\`Y\\` to '...'\", \"Invalidate raw source \\`Z\\` and re-curate\", \"Replace system-prompt section X with 'tool foo now returns Y'\")\n\nDo NOT report a finding if the agent caught and corrected the false belief in the same turn — that's the system working. Reserve poisoning for cases where the false belief shaped downstream action.\n\nOBSERVABILITY rules:\n- Each non-final turn must emit at least one \\`console.log\\` for evidence.\n- Call \\`final({ findings: [...] })\\` exactly once at the top level.`\n\nexport const KNOWLEDGE_POISONING_KIND_SPEC: TraceAnalystKindSpec = {\n id: 'knowledge-poisoning',\n description:\n 'Identifies confident-but-wrong actions caused by stale memory, contradicting RAG, deprecated tool docs, or outdated system-prompt instructions.',\n area: 'knowledge-poisoning',\n version: '1.0.0',\n actorDescription: ACTOR_PROMPT,\n buildTools: (store) => buildTraceToolsForGroup('all', store),\n recursion: { maxDepth: 2, maxParallelSubagents: 4 },\n maxTurns: 20,\n cost: { kind: 'llm' },\n}\n","/**\n * Default analyst kinds focused on agent failure + recursive\n * self-improvement.\n *\n * The four kinds chain: failure-mode classifies; knowledge-gap and\n * knowledge-poisoning explain *why* in two orthogonal ways; improvement\n * proposes concrete edits. Register all four against the same trace\n * store and the registry runs them in dependency order if the operator\n * pipes findings between them.\n */\n\nexport { FAILURE_MODE_KIND_SPEC } from './failure-mode'\nexport { IMPROVEMENT_KIND_SPEC } from './improvement'\nexport { KNOWLEDGE_GAP_KIND_SPEC } from './knowledge-gap'\nexport { KNOWLEDGE_POISONING_KIND_SPEC } from './knowledge-poisoning'\n\nimport type { TraceAnalystKindSpec } from '../kind-factory'\nimport { FAILURE_MODE_KIND_SPEC } from './failure-mode'\nimport { IMPROVEMENT_KIND_SPEC } from './improvement'\nimport { KNOWLEDGE_GAP_KIND_SPEC } from './knowledge-gap'\nimport { KNOWLEDGE_POISONING_KIND_SPEC } from './knowledge-poisoning'\n\n/**\n * The default kind suite. Order is the run order operators should\n * use: failure-mode first (no upstream deps), gap + poisoning next\n * (both depend on failures), improvement last (chains all three).\n */\nexport const DEFAULT_TRACE_ANALYST_KINDS: readonly TraceAnalystKindSpec[] = [\n FAILURE_MODE_KIND_SPEC,\n KNOWLEDGE_GAP_KIND_SPEC,\n KNOWLEDGE_POISONING_KIND_SPEC,\n IMPROVEMENT_KIND_SPEC,\n] as const\n","/**\n * AnalystRegistry — orchestrate N analysts against one run.\n *\n * Owns three responsibilities and only three:\n * 1. Registration — ids must be unique; bad registrations fail loudly\n * at register-time, not run-time.\n * 2. Routing — each analyst declares its `inputKind`; the registry\n * picks the matching field from AnalystRunInputs and skips the\n * analyst with a logged reason if it's missing.\n * 3. Isolation — one analyst's exception MUST NOT stop other analysts.\n * Failed analysts produce zero findings + a 'failed' summary row.\n *\n * Cross-cutting concerns (telemetry, error → finding conversion, cost\n * ingestion, storage rotation) live in `AnalystHooks`. Budget shaping\n * (equal split vs weighted vs custom) lives in `BudgetPolicy`. Both\n * have sensible defaults; consumers override only what they need.\n */\n\nimport { randomUUID } from 'node:crypto'\nimport type { ChatClient } from './chat-client'\nimport type {\n Analyst,\n AnalystContext,\n AnalystFinding,\n AnalystRunEvent,\n AnalystRunInputs,\n AnalystRunResult,\n AnalystRunSummary,\n} from './types'\n\n// ── Hook + policy surfaces ─────────────────────────────────────────\n\nexport interface AnalystHooks {\n /** Before analyze() — last chance to mutate ctx (e.g. inject tags, override budget). */\n onBeforeAnalyze?(args: {\n analyst: Analyst\n ctx: AnalystContext\n runId: string\n }): void | Promise<void>\n /** After every analyst (ok | failed | skipped). Use for telemetry, ingestion, rotation. */\n onAfterAnalyze?(args: {\n analyst: Analyst\n summary: AnalystRunSummary\n findings: AnalystFinding[]\n runId: string\n }): void | Promise<void>\n /**\n * On analyst exception. Hook MAY return findings to convert the\n * error into structured findings; the summary still reports 'failed'.\n * Return void to keep the default empty-findings behavior.\n */\n onError?(args: {\n analyst: Analyst\n error: Error\n runId: string\n }): AnalystFinding[] | undefined | Promise<AnalystFinding[] | undefined>\n /** Once after registry.run() completes. Use for final aggregation, persistence. */\n onComplete?(args: { result: AnalystRunResult }): void | Promise<void>\n}\n\nexport interface BudgetPolicy {\n /** Overall USD cap across the registry.run(). */\n totalUsd?: number\n /** Per-analyst weight for the default allocator. Missing ids get weight 1. */\n weights?: Record<string, number>\n /**\n * Custom allocator — receives the analyst, remaining/total budget, and\n * the count of analysts that will run. Returns the per-analyst budget\n * (or undefined to leave it uncapped). Overrides weights when set.\n */\n allocate?: (args: {\n analyst: Analyst\n totalUsd: number | undefined\n remainingUsd: number | undefined\n runningCount: number\n }) => number | undefined\n}\n\nexport interface AnalystRegistryOptions {\n /** Shared chat client passed to every LLM analyst via AnalystContext. */\n chat?: ChatClient\n /** Logger callback. Defaults to a no-op. */\n log?: (msg: string, fields?: Record<string, unknown>) => void\n /** Hooks invoked around analyze() — observability + customization seam. */\n hooks?: AnalystHooks\n /** Default budget when run() doesn't override. */\n defaultBudget?: BudgetPolicy\n}\n\nexport interface RegistryRunOpts {\n /** Restrict to a subset of registered analysts by id. */\n only?: string[]\n /** Skip these analysts even if registered. Useful for cheap iteration. */\n skip?: string[]\n /** Budget policy — totalUsd + optional weights/allocator. Falls back to options.defaultBudget. */\n budget?: BudgetPolicy\n /** Wall-clock cap. Analysts SHOULD honor `ctx.deadlineMs`. */\n timeoutMs?: number\n /** Abort signal — forwarded into every analyst's context. */\n signal?: AbortSignal\n /** Tags echoed into AnalystContext.tags — useful for tracking environment/version in findings. */\n tags?: Record<string, string>\n /**\n * Prior-run findings made available as retrieval context to every\n * analyst via `ctx.priorFindings`. The registry forwards the slice\n * whose `analyst_id` matches each registered analyst so a kind sees\n * only its own history. Pass `{ '*': findings }` to broadcast to\n * every analyst (useful for cross-kind chaining where the improvement\n * analyst consumes upstream failure findings).\n */\n priorFindings?: ReadonlyArray<AnalystFinding> | Record<string, ReadonlyArray<AnalystFinding>>\n}\n\nexport class AnalystRegistry {\n private readonly analysts = new Map<string, Analyst>()\n private readonly options: AnalystRegistryOptions\n\n constructor(options: AnalystRegistryOptions = {}) {\n this.options = options\n }\n\n register(analyst: Analyst): void {\n if (!analyst.id) throw new Error('AnalystRegistry.register: analyst.id is required')\n if (this.analysts.has(analyst.id)) {\n throw new Error(`AnalystRegistry.register: duplicate analyst id \"${analyst.id}\"`)\n }\n if (!analyst.version) {\n throw new Error(`AnalystRegistry.register: analyst \"${analyst.id}\" must declare a version`)\n }\n this.analysts.set(analyst.id, analyst)\n }\n\n list(): ReadonlyArray<{\n id: string\n description: string\n version: string\n cost: Analyst['cost']\n }> {\n return Array.from(this.analysts.values()).map((a) => ({\n id: a.id,\n description: a.description,\n version: a.version,\n cost: a.cost,\n }))\n }\n\n async run(\n runId: string,\n inputs: AnalystRunInputs,\n runOpts: RegistryRunOpts = {},\n ): Promise<AnalystRunResult> {\n // Thin collector over `runStream`. Both surfaces share the same\n // loop body so they cannot drift on isolation / hook order / cost.\n for await (const ev of this.runStream(runId, inputs, runOpts)) {\n if (ev.type === 'run-completed') return ev.result\n }\n throw new Error('AnalystRegistry.run: stream completed without run-completed event')\n }\n\n /**\n * Streaming counterpart to `run()`. Emits `AnalystRunEvent` values\n * in real time — `run-started`, then per-analyst `skipped` /\n * `started` / `completed`, then a terminal `run-completed` whose\n * payload is the full `AnalystRunResult`. UIs use this to render\n * progress; persistence consumers use `run()` and read the result.\n *\n * Hooks (`onBeforeAnalyze` / `onAfterAnalyze` / `onError` /\n * `onComplete`) fire as before — streaming is additive, not a hook\n * replacement.\n */\n async *runStream(\n runId: string,\n inputs: AnalystRunInputs,\n runOpts: RegistryRunOpts = {},\n ): AsyncGenerator<AnalystRunEvent, void, void> {\n const correlationId = `ar_${randomUUID().slice(0, 12)}`\n const log = this.options.log ?? (() => {})\n const hooks = this.options.hooks ?? {}\n const startedAt = new Date().toISOString()\n const started = Date.now()\n const deadlineMs = runOpts.timeoutMs ? started + runOpts.timeoutMs : undefined\n\n const selected = this.selectAnalysts(runOpts)\n const budget = runOpts.budget ?? this.options.defaultBudget\n\n yield {\n type: 'run-started',\n run_id: runId,\n correlation_id: correlationId,\n started_at: startedAt,\n analyst_ids: selected.map((a) => a.id),\n }\n\n const summaries: AnalystRunSummary[] = []\n const allFindings: AnalystFinding[] = []\n let totalCost = 0\n let remainingUsd = budget?.totalUsd\n\n for (const analyst of selected) {\n const t0 = Date.now()\n const input = this.routeInput(analyst, inputs)\n if (input.kind === 'missing') {\n const summary: AnalystRunSummary = {\n analyst_id: analyst.id,\n status: 'skipped',\n reason: `missing input of kind '${analyst.inputKind}'`,\n findings_count: 0,\n latency_ms: 0,\n cost_usd: 0,\n }\n summaries.push(summary)\n log(`[analyst] skip ${analyst.id} — missing input`, { runId, kind: analyst.inputKind })\n await hooks.onAfterAnalyze?.({ analyst, summary, findings: [], runId })\n yield { type: 'analyst-skipped', summary }\n continue\n }\n\n const perBudget = allocateBudget(budget, {\n analyst,\n remainingUsd,\n runningCount: selected.length,\n })\n\n const ctx: AnalystContext = {\n runId,\n correlationId,\n deadlineMs,\n budgetUsd: perBudget,\n chat: this.options.chat,\n tags: runOpts.tags,\n log: (msg, fields) => log(`[${analyst.id}] ${msg}`, { runId, correlationId, ...fields }),\n signal: runOpts.signal,\n priorFindings: selectPriorFindings(runOpts.priorFindings, analyst.id),\n }\n\n await hooks.onBeforeAnalyze?.({ analyst, ctx, runId })\n yield {\n type: 'analyst-started',\n analyst_id: analyst.id,\n started_at: new Date(t0).toISOString(),\n }\n\n try {\n const findings = await (analyst as Analyst<unknown>).analyze(input.value, ctx)\n const latency = Date.now() - t0\n const cost = sumFindingCost(findings)\n totalCost += cost\n if (typeof remainingUsd === 'number') remainingUsd = Math.max(0, remainingUsd - cost)\n allFindings.push(...findings)\n const summary: AnalystRunSummary = {\n analyst_id: analyst.id,\n status: 'ok',\n findings_count: findings.length,\n latency_ms: latency,\n cost_usd: cost,\n }\n summaries.push(summary)\n log(`[analyst] ok ${analyst.id}`, {\n runId,\n findings: findings.length,\n latency_ms: latency,\n cost_usd: cost,\n })\n await hooks.onAfterAnalyze?.({ analyst, summary, findings, runId })\n yield { type: 'analyst-completed', summary, findings }\n } catch (err) {\n const latency = Date.now() - t0\n const e = err instanceof Error ? err : new Error(String(err))\n // Hook gets first chance to convert the error into findings.\n const hookFindings = (await hooks.onError?.({ analyst, error: e, runId })) ?? []\n if (hookFindings.length) allFindings.push(...hookFindings)\n const summary: AnalystRunSummary = {\n analyst_id: analyst.id,\n status: 'failed',\n findings_count: hookFindings.length,\n latency_ms: latency,\n cost_usd: 0,\n error: { class: e.constructor.name, message: e.message },\n }\n summaries.push(summary)\n log(`[analyst] FAIL ${analyst.id}`, {\n runId,\n error_class: e.constructor.name,\n error: e.message,\n })\n await hooks.onAfterAnalyze?.({ analyst, summary, findings: hookFindings, runId })\n yield { type: 'analyst-completed', summary, findings: hookFindings }\n // Continue — isolation invariant.\n }\n }\n\n const result: AnalystRunResult = {\n run_id: runId,\n correlation_id: correlationId,\n started_at: startedAt,\n ended_at: new Date().toISOString(),\n findings: allFindings,\n per_analyst: summaries,\n total_cost_usd: totalCost,\n }\n await hooks.onComplete?.({ result })\n yield { type: 'run-completed', result }\n }\n\n private selectAnalysts(opts: RegistryRunOpts): Analyst[] {\n let candidates = Array.from(this.analysts.values())\n if (opts.only?.length) {\n const only = new Set(opts.only)\n candidates = candidates.filter((a) => only.has(a.id))\n }\n if (opts.skip?.length) {\n const skip = new Set(opts.skip)\n candidates = candidates.filter((a) => !skip.has(a.id))\n }\n return candidates\n }\n\n private routeInput(\n analyst: Analyst,\n inputs: AnalystRunInputs,\n ): { kind: 'present'; value: unknown } | { kind: 'missing' } {\n switch (analyst.inputKind) {\n case 'trace-store':\n return inputs.traceStore\n ? { kind: 'present', value: inputs.traceStore }\n : { kind: 'missing' }\n case 'artifact-dir':\n return inputs.artifactDir\n ? { kind: 'present', value: inputs.artifactDir }\n : { kind: 'missing' }\n case 'run-record':\n return inputs.runRecord ? { kind: 'present', value: inputs.runRecord } : { kind: 'missing' }\n case 'judge-input':\n return inputs.judgeInput\n ? { kind: 'present', value: inputs.judgeInput }\n : { kind: 'missing' }\n case 'custom': {\n const v = inputs.custom?.[analyst.id]\n return v !== undefined ? { kind: 'present', value: v } : { kind: 'missing' }\n }\n }\n }\n}\n\n/**\n * Default budget allocator: prefer the custom `allocate` callback if\n * provided; else weighted split when weights are set; else equal split\n * across `runningCount`. Returns undefined when no totalUsd is known.\n */\nfunction allocateBudget(\n policy: BudgetPolicy | undefined,\n args: { analyst: Analyst; remainingUsd: number | undefined; runningCount: number },\n): number | undefined {\n if (!policy) return undefined\n if (policy.allocate) {\n return policy.allocate({\n analyst: args.analyst,\n totalUsd: policy.totalUsd,\n remainingUsd: args.remainingUsd,\n runningCount: args.runningCount,\n })\n }\n if (policy.totalUsd == null) return undefined\n if (policy.weights) {\n // Weighted split: caller-supplied weights, default 1 for missing ids.\n // We can only normalize against the analysts in this run, but the\n // registry doesn't know all ids at allocator-time without passing\n // them. We approximate by treating `runningCount` as the count of\n // weight=1 analysts when the weight map omits ids. The exact split\n // is left to consumers that need precision via `allocate`.\n const w = policy.weights[args.analyst.id] ?? 1\n const totalWeight = Math.max(1, args.runningCount) // see note above\n return (policy.totalUsd * w) / totalWeight\n }\n return policy.totalUsd / Math.max(1, args.runningCount)\n}\n\n/**\n * Findings may carry their cost in `metadata.cost_usd` when the analyst\n * tracks it (the LLM-driven adapters do this — they sum chat-client\n * responses). Deterministic findings have no cost field.\n */\nfunction sumFindingCost(findings: AnalystFinding[]): number {\n let sum = 0\n for (const f of findings) {\n const c = f.metadata?.cost_usd\n if (typeof c === 'number' && Number.isFinite(c)) sum += c\n }\n return sum\n}\n\n/**\n * Resolve the `priorFindings` slice an analyst sees.\n *\n * - Array form → the analyst sees only findings whose `analyst_id`\n * matches its own id, so a kind never reads\n * another kind's history by accident.\n * - Record form → the analyst gets the entry keyed by its id, with\n * the `'*'` wildcard appended (in that order). Use\n * the wildcard for cross-kind chaining, e.g. when\n * `improvement` should see all upstream failure /\n * gap / poisoning findings.\n */\nfunction selectPriorFindings(\n source: RegistryRunOpts['priorFindings'],\n analystId: string,\n): ReadonlyArray<AnalystFinding> | undefined {\n if (!source) return undefined\n if (Array.isArray(source)) {\n const own = source.filter((f) => f.analyst_id === analystId)\n return own.length > 0 ? own : undefined\n }\n const record = source as Record<string, ReadonlyArray<AnalystFinding>>\n const own = record[analystId] ?? []\n const wildcard = record['*'] ?? []\n const merged = [...own, ...wildcard]\n return merged.length > 0 ? merged : undefined\n}\n"],"mappings":";;;;;;;;;AAiBA,SAAS,kBAAkB;AAsKpB,SAAS,iBAAiB,OAOtB;AACT,QAAM,QAAQ,KAAK,UAAU;AAAA,IAC3B,GAAG,MAAM;AAAA,IACT,GAAG,MAAM;AAAA,IACT,GAAG,MAAM,WAAW;AAAA,IACpB,GAAG,eAAe,MAAM,YAAY,MAAM,KAAK;AAAA,EACjD,CAAC;AACD,SAAO,KAAK,WAAW,QAAQ,EAAE,OAAO,KAAK,EAAE,OAAO,KAAK,EAAE,MAAM,GAAG,EAAE,CAAC;AAC3E;AAEA,SAAS,eAAe,GAAmB;AAGzC,SAAO,EACJ,YAAY,EACZ,QAAQ,QAAQ,GAAG,EACnB,QAAQ,eAAe,EAAE,EACzB,KAAK;AACV;AAMO,SAAS,YACd,MAIgB;AAChB,QAAM,EAAE,UAAU,aAAa,GAAG,KAAK,IAAI;AAC3C,SAAO;AAAA,IACL,gBAAgB;AAAA,IAChB,YAAY,iBAAiB;AAAA,MAC3B,YAAY,KAAK;AAAA,MACjB,MAAM,KAAK;AAAA,MACX,SAAS,KAAK;AAAA,MACd,OAAO,KAAK;AAAA,MACZ;AAAA,IACF,CAAC;AAAA,IACD,aAAa,gBAAe,oBAAI,KAAK,GAAE,YAAY;AAAA,IACnD,GAAG;AAAA,EACL;AACF;;;ACtMA,SAAS,SAAS;AAkCX,IAAM,wBAA2D;AAAA,EACtE;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;AAoBO,SAAS,oBAAoB,KAAuD;AACzF,MAAI,QAAQ,QAAQ,QAAQ,OAAW,QAAO;AAC9C,QAAM,UAAU,IAAI,KAAK;AACzB,MAAI,QAAQ,WAAW,EAAG,QAAO;AAGjC,QAAM,OAAO,QAAQ;AAAA,IACnB;AAAA,EACF;AACA,MAAI;AACF,WAAO,EAAE,MAAM,kBAAkB,MAAM,KAAK,CAAC,GAAI,GAAI,KAAK,CAAC,IAAI,EAAE,SAAS,KAAK,CAAC,EAAE,IAAI,CAAC,EAAG;AAG5F,QAAM,QAAQ,QAAQ,MAAM,8BAA8B;AAC1D,MAAI,SAAS,MAAM,CAAC,EAAG,KAAK,EAAE,SAAS;AACrC,WAAO,EAAE,MAAM,mBAAmB,OAAO,MAAM,CAAC,EAAG,KAAK,EAAE;AAG5D,QAAM,OAAO,QAAQ,MAAM,4BAA4B;AACvD,MAAI,QAAQ,KAAK,CAAC,EAAG,KAAK,EAAE,SAAS;AACnC,WAAO,EAAE,MAAM,iBAAiB,UAAU,KAAK,CAAC,EAAG,KAAK,EAAE;AAG5D,QAAM,QAAQ,QAAQ,MAAM,8CAA8C;AAC1E,MAAI,MAAO,QAAO,EAAE,MAAM,mBAAmB,MAAM,MAAM,CAAC,EAAG;AAG7D,QAAM,KAAK,QAAQ,MAAM,sBAAsB;AAC/C,MAAI,MAAM,GAAG,CAAC,EAAG,KAAK,EAAE,SAAS,EAAG,QAAO,EAAE,MAAM,iBAAiB,SAAS,GAAG,CAAC,EAAG,KAAK,EAAE;AAG3F,QAAM,WAAW,QAAQ,MAAM,uCAAuC;AACtE,MAAI,YAAY,SAAS,CAAC,EAAG,KAAK,EAAE,SAAS,GAAG;AAC9C,WAAO,EAAE,MAAM,YAAY,MAAM,SAAS,CAAC,GAAI,QAAQ,SAAS,CAAC,EAAG,KAAK,EAAE;AAAA,EAC7E;AACA,QAAM,KAAK,QAAQ,MAAM,kCAAkC;AAC3D,MAAI,GAAI,QAAO,EAAE,MAAM,YAAY,MAAM,GAAG,CAAC,EAAG;AAGhD,QAAM,KAAK,QAAQ,MAAM,kCAAkC;AAC3D,MAAI,GAAI,QAAO,EAAE,MAAM,YAAY,MAAM,GAAG,CAAC,EAAG;AAGhD,QAAM,MAAM,QAAQ,MAAM,kCAAkC;AAC5D,MAAI,OAAO,IAAI,CAAC,EAAG,KAAK,EAAE,SAAS,GAAG;AACpC,WAAO,EAAE,MAAM,OAAO,QAAQ,IAAI,CAAC,GAAI,OAAO,IAAI,CAAC,EAAG,KAAK,EAAE;AAAA,EAC/D;AAGA,QAAM,MAAM,QAAQ,MAAM,eAAe;AACzC,MAAI,OAAO,IAAI,CAAC,EAAG,KAAK,EAAE,SAAS,EAAG,QAAO,EAAE,MAAM,UAAU,KAAK,IAAI,CAAC,EAAG,KAAK,EAAE;AAGnF,QAAM,KAAK,QAAQ,MAAM,oBAAoB;AAC7C,MAAI,MAAM,GAAG,CAAC,EAAG,KAAK,EAAE,SAAS,EAAG,QAAO,EAAE,MAAM,eAAe,SAAS,GAAG,CAAC,EAAG,KAAK,EAAE;AAGzF,QAAM,KAAK,QAAQ,MAAM,sBAAsB;AAC/C,MAAI,MAAM,GAAG,CAAC,EAAG,KAAK,EAAE,SAAS,EAAG,QAAO,EAAE,MAAM,iBAAiB,OAAO,GAAG,CAAC,EAAG,KAAK,EAAE;AAGzF,QAAM,KAAK,QAAQ,MAAM,2BAA2B;AACpD,MAAI,MAAM,GAAG,CAAC,EAAG,KAAK,EAAE,SAAS,EAAG,QAAO,EAAE,MAAM,sBAAsB,OAAO,GAAG,CAAC,EAAG,KAAK,EAAE;AAG9F,QAAM,MAAM,QAAQ,MAAM,0BAA0B;AACpD,MAAI,OAAO,IAAI,CAAC,EAAG,KAAK,EAAE,SAAS,EAAG,QAAO,EAAE,MAAM,qBAAqB,OAAO,IAAI,CAAC,EAAG,KAAK,EAAE;AAKhG,MAAI,yBAAyB,KAAK,OAAO,KAAK,QAAQ,UAAU,IAAI;AAClE,WAAO,EAAE,MAAM,WAAW,OAAO,QAAQ;AAAA,EAC3C;AAEA,SAAO;AACT;AAQO,SAAS,qBAAqB,GAA2B;AAC9D,UAAQ,EAAE,MAAM;AAAA,IACd,KAAK;AACH,aAAO,EAAE,UACL,wBAAwB,EAAE,IAAI,IAAI,EAAE,OAAO,KAC3C,wBAAwB,EAAE,IAAI;AAAA,IACpC,KAAK;AACH,aAAO,yBAAyB,EAAE,KAAK;AAAA,IACzC,KAAK;AACH,aAAO,uBAAuB,EAAE,QAAQ;AAAA,IAC1C,KAAK;AACH,aAAO,yBAAyB,EAAE,IAAI;AAAA,IACxC,KAAK;AACH,aAAO,iBAAiB,EAAE,OAAO;AAAA,IACnC,KAAK;AACH,aAAO,EAAE,SAAS,YAAY,EAAE,IAAI,IAAI,EAAE,MAAM,KAAK,YAAY,EAAE,IAAI;AAAA,IACzE,KAAK;AACH,aAAO,YAAY,EAAE,IAAI;AAAA,IAC3B,KAAK;AACH,aAAO,OAAO,EAAE,MAAM,IAAI,EAAE,KAAK;AAAA,IACnC,KAAK;AACH,aAAO,UAAU,EAAE,GAAG;AAAA,IACxB,KAAK;AACH,aAAO,eAAe,EAAE,OAAO;AAAA,IACjC,KAAK;AACH,aAAO,iBAAiB,EAAE,KAAK;AAAA,IACjC,KAAK;AACH,aAAO,sBAAsB,EAAE,KAAK;AAAA,IACtC,KAAK;AACH,aAAO,qBAAqB,EAAE,KAAK;AAAA,IACrC,KAAK;AACH,aAAO,EAAE;AAAA,EACb;AACF;AAaO,IAAM,iCAAiC;AAAA,EAC5C;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;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,EAAE,KAAK,IAAI;AAYJ,IAAM,yBAA4E;AAAA,EACvF,gBAAgB,CAAC,SAAS;AAAA,EAC1B,iBAAiB;AAAA,IACf;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,uBAAuB;AAAA,IACrB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,aAAa;AAAA,IACX;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAeO,IAAM,6BAA6B,EACvC,OAAO,EACP,OAAO,CAAC,MAAM,oBAAoB,CAAC,MAAM,MAAM;AAAA,EAC9C,SAAS;AACX,CAAC;;;ACxTI,SAAS,gBAAgB,MAAsB;AACpD,QAAM,IAAI,KAAK,KAAK;AACpB,QAAM,QAAQ;AACd,QAAM,IAAI,EAAE,MAAM,KAAK;AACvB,SAAO,IAAI,EAAE,CAAC,EAAG,KAAK,IAAI;AAC5B;AAGA,SAAS,mBAAmB,GAAmB;AAC7C,SAAO,EAAE,QAAQ,gBAAgB,IAAI;AACvC;AAMO,SAAS,WAAW,MAAuB;AAChD,QAAM,YAAY,mBAAmB,gBAAgB,IAAI,CAAC;AAC1D,MAAI;AACF,WAAO,KAAK,MAAM,SAAS;AAAA,EAC7B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAQO,SAAS,oBAAoB,KAAyB;AAC3D,MAAI,QAAQ;AACZ,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAM,SAAS,WAAW,KAAK;AAC/B,QAAI,WAAW,OAAW,QAAO,CAAC;AAClC,YAAQ;AAAA,EACV;AACA,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO;AACjC,MAAI,SAAS,OAAO,UAAU,UAAU;AAEtC,UAAM,QAAS,MAAkC;AACjD,QAAI,MAAM,QAAQ,KAAK,EAAG,QAAO;AACjC,WAAO,CAAC,KAAK;AAAA,EACf;AACA,SAAO,CAAC;AACV;;;AC1CA,SAAS,KAAAA,UAAS;AAIX,IAAM,qBAAqB,CAAC,YAAY,QAAQ,UAAU,OAAO,MAAM;AAEvE,IAAM,0BAA0BC,GACpC,OAAO;AAAA,EACN,UAAUA,GAAE,KAAK,kBAAkB;AAAA,EACnC,OAAOA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYjC,SAASA,GACN,OAAO,EACP,IAAI,GAAG,EACP,OAAO,CAAC,MAAM,oBAAoB,CAAC,MAAM,MAAM;AAAA,IAC9C,SAAS;AAAA,EACX,CAAC,EACA,SAAS;AAAA,EACZ,cAAcA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAI;AAAA,EACxC,kBAAkBA,GAAE,OAAO,EAAE,IAAI,GAAI,EAAE,SAAS;AAAA,EAChD,YAAYA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC;AAAA,EACnC,WAAWA,GAAE,OAAO,EAAE,IAAI,GAAI,EAAE,SAAS;AAAA,EACzC,oBAAoBA,GAAE,OAAO,EAAE,IAAI,GAAI,EAAE,SAAS;AACpD,CAAC,EACA,OAAO;AASH,IAAM,4BAA4B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiBlC,SAAS,gBACd,KACA,KAC0B;AAC1B,QAAM,SAAS,wBAAwB,UAAU,GAAG;AACpD,MAAI,OAAO,QAAS,QAAO,OAAO;AAGlC,MAAI,OAAO,QAAQ,UAAU;AAC3B,UAAM,UAAU,WAAW,GAAG;AAC9B,QAAI,YAAY,QAAW;AACzB,YAAM,QAAQ,wBAAwB,UAAU,OAAO;AACvD,UAAI,MAAM,QAAS,QAAO,MAAM;AAAA,IAClC;AAAA,EACF;AACA,QAAM,oCAAoC;AAAA,IACxC,QAAQ,OAAO,MAAM,OAAO,IAAI,CAAC,OAAO;AAAA,MACtC,MAAM,EAAE,KAAK,KAAK,GAAG;AAAA,MACrB,MAAM,EAAE;AAAA,MACR,SAAS,EAAE;AAAA,IACb,EAAE;AAAA,EACJ,CAAC;AACD,SAAO;AACT;;;AC3DA,IAAM,SAAS;AAAA,EACb;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,EAAE,KAAK,GAAG;AAEV,SAAS,UAAU,KAAc,WAAmB,MAAgC;AAClF,QAAM,OAAO,oBAAoB,GAAG;AACpC,QAAM,MAAwB,CAAC;AAC/B,aAAW,OAAO,MAAM;AAKtB,UAAM,aACJ,OACA,OAAO,QAAQ,YACf,CAAC,MAAM,QAAQ,GAAG,KAClB,CAAE,IAAgC,eAC9B,EAAE,GAAI,KAAiC,cAAc,mBAAmB,IACxE;AACN,UAAM,SAAmC,gBAAgB,UAAU;AACnE,QAAI,CAAC,OAAQ;AACb,QAAI;AAAA,MACF,YAAY;AAAA,QACV,YAAY;AAAA,QACZ;AAAA,QACA,SAAS,OAAO;AAAA,QAChB,OAAO,OAAO;AAAA,QACd,WAAW,OAAO;AAAA,QAClB,UAAU,OAAO;AAAA,QACjB,YAAY,OAAO;AAAA,QACnB,eAAe;AAAA,UACb;AAAA,YACE,MAAM,OAAO,aAAa,WAAW,SAAS,IAAI,SAAS;AAAA,YAC3D,KAAK,OAAO;AAAA,YACZ,SAAS,OAAO;AAAA,UAClB;AAAA,QACF;AAAA,QACA,oBAAoB,OAAO;AAAA,MAC7B,CAAC;AAAA,IACH;AAAA,EACF;AACA,SAAO;AACT;AAEA,eAAsB,kBACpB,MACkC;AAClC,QAAM,YAAY,KAAK,aAAa;AACpC,QAAM,MAAM,EAAE,SAAS,KAAK,SAAS,QAAQ,KAAK,QAAQ,OAAO,KAAK,UAAU;AAChF,MAAI,OAAO;AAAA,EAA2B,KAAK,MAAM;AAAA;AAAA;AAEjD,WAAS,UAAU,GAAG,WAAW,WAAW,WAAW;AACrD,UAAM,MAAM,MAAM;AAAA,MAChB;AAAA,QACE,OAAO,KAAK;AAAA,QACZ,UAAU;AAAA,UACR,EAAE,MAAM,UAAU,SAAS,OAAO;AAAA,UAClC,EAAE,MAAM,QAAQ,SAAS,KAAK;AAAA,QAChC;AAAA,MACF;AAAA,MACA;AAAA,IACF;AACA,UAAM,OAAO,IAAI,QAAQ,KAAK;AAC9B,UAAM,WAAW,UAAU,MAAM,KAAK,WAAW,KAAK,IAAI;AAC1D,QAAI,SAAS,SAAS,EAAG,QAAO,EAAE,UAAU,SAAS,KAAK;AAG1D,QAAI,KAAK,OAAO,KAAK,EAAE,SAAS,IAAK,QAAO,EAAE,UAAU,CAAC,GAAG,SAAS,KAAK;AAC1E,WAAO,GAAG,IAAI;AAAA;AAAA;AAAA,EAChB;AACA,SAAO,EAAE,UAAU,CAAC,GAAG,SAAS,oBAAoB;AACtD;;;ACzFA,SAAS,aAAa,aAAa;AAkF5B,SAAS,uBACd,MACA,MAC6B;AAC7B,QAAM,UAAU,KAAK,gBAAgB,GAAG,KAAK,OAAO,IAAI,KAAK,aAAa,KAAK,KAAK;AACpF,SAAO;AAAA,IACL,IAAI,KAAK;AAAA,IACT,aAAa,KAAK;AAAA,IAClB,WAAW;AAAA,IACX,MAAM,KAAK;AAAA,IACX;AAAA,IACA,MAAM,QAAQ,OAAO,KAAK;AACxB,YAAM,QAAQ,KAAK,WAAW,KAAK;AACnC,YAAM,WAAW,KAAK,WAAW,YAAY;AAC7C,YAAM,cAAc,KAAK,WAAW,wBAAwB;AAC5D,YAAM,eAAe,oBAAoB,IAAI,aAAa;AAE1D,YAAM,mBACJ,KAAK,iBAAiB,KAAK,IAC3B,eACA,SACA,4BACA;AAMF,YAAM,KAAK;AAAA,QACT;AAAA,QACA;AAAA,UACE,eAAe;AAAA,YACb,MAAM,KAAK;AAAA,YACX,aAAa,KAAK;AAAA,UACpB;AAAA,UACA,eAAe,CAAC,UAAU;AAAA,UAC1B,SAAS,IAAI,YAAY;AAAA,YACvB,aAAa,CAAC;AAAA,YACd,oBAAoB;AAAA,YACpB,gBAAgB,CAAC;AAAA,YACjB,kBAAkB;AAAA,YAClB,kBAAkB;AAAA,YAClB,6BAA6B;AAAA,UAC/B,CAAC;AAAA,UACD,MAAM,WAAW,IAAI,aAAa;AAAA,UAClC,kBAAkB,WAAW,IAAI,EAAE,SAAS,IAAI;AAAA,UAChD,UAAU,KAAK,YAAY;AAAA,UAC3B,iBAAiB,KAAK,mBAAmB;AAAA,UACzC,+BAA+B;AAAA,UAC/B,aAAa;AAAA;AAAA,UAEb,eAAe,EAAE,QAAQ,QAAQ,QAAQ,WAAW;AAAA,UACpD,WAAW,EAAE,OAAO,MAAM;AAAA,UAC1B,cAAc;AAAA,YACZ,aAAa;AAAA,YACb,GAAI,KAAK,QAAQ,EAAE,OAAO,KAAK,MAAM,IAAI,CAAC;AAAA,YAC1C,cAAc;AAAA,YACd,qBAAqB;AAAA,UACvB;AAAA,UACA,kBAAkB;AAAA,YAChB,aACE,KAAK,wBACL;AAAA,YACF,GAAI,KAAK,QAAQ,EAAE,OAAO,KAAK,MAAM,IAAI,CAAC;AAAA,YAC1C,cAAc;AAAA,UAChB;AAAA,UACA,cAAc,CAAC,qBAAqB;AAAA,QACtC;AAAA,MACF;AAEA,UAAI,MAAM,gBAAgB,KAAK,EAAE,YAAY;AAAA,QAC3C,WAAW;AAAA,QACX,YAAY,MAAM;AAAA,QAClB,MAAM,IAAI;AAAA,MACZ,CAAC;AAED,YAAM,SAAS,MAAM,GAAG,QAAQ,KAAK,IAAI,EAAE,UAAU,eAAe,KAAK,IAAI,EAAE,CAAC;AAEhF,YAAM,mBAAmB,uBAAuB,KAAK,EAAE;AACvD,YAAM,MAAwB,CAAC;AAC/B,YAAM,UAAU,MAAM,QAAQ,OAAO,QAAQ,IAAI,OAAO,WAAW,CAAC;AACpE,UAAI,oBAAoB;AACxB,iBAAW,OAAO,SAAS;AACzB,cAAM,SAAS,gBAAgB,KAAK,IAAI,GAAG;AAC3C,YAAI,CAAC,OAAQ;AAMb,YAAI,oBAAoB,OAAO,YAAY,QAAW;AACpD,gBAAM,gBAAgB,oBAAoB,OAAO,OAAO;AACxD,cAAI,kBAAkB,MAAM;AAC1B,gBAAI,MAAM,6CAA6C;AAAA,cACrD,MAAM,KAAK;AAAA,cACX,SAAS,OAAO;AAAA,YAClB,CAAC;AACD,iCAAqB;AACrB;AAAA,UACF;AACA,cAAI,CAAC,iBAAiB,SAAS,cAAc,IAAI,GAAG;AAClD,gBAAI,MAAM,+DAA+D;AAAA,cACvE,MAAM,KAAK;AAAA,cACX,cAAc,cAAc;AAAA,cAC5B,SAAS,OAAO;AAAA,cAChB,SAAS;AAAA,YACX,CAAC;AACD,iCAAqB;AACrB;AAAA,UACF;AAAA,QACF;AACA,cAAM,gBAAgB,KAAK,cAAc,QAAQ,GAAG,KAAK;AACzD,YAAI,CAAC,cAAe;AACpB,YAAI,KAAK,iBAAiB,MAAM,aAAa,CAAC;AAAA,MAChD;AAEA,UAAI,MAAM,gBAAgB,KAAK,EAAE,SAAS;AAAA,QACxC,SAAS,QAAQ;AAAA,QACjB,UAAU,IAAI;AAAA,QACd,wBAAwB;AAAA,MAC1B,CAAC;AASD,YAAM,SAAS,OAAO,OAAO,WAAW,WAAW,OAAO,SAAS;AACnE,UAAI,IAAI,WAAW,KAAK,OAAO,KAAK,EAAE,UAAU,KAAK;AACnD,YAAI,KAAK,UAAU;AACjB,gBAAM,YAAY,MAAM,kBAAkB;AAAA,YACxC;AAAA,YACA,WAAW,KAAK;AAAA,YAChB,MAAM,KAAK;AAAA,YACX,OAAO,KAAK,SAAS,SAAS,KAAK,SAAS;AAAA,YAC5C,SAAS,KAAK,SAAS;AAAA,YACvB,QAAQ,KAAK,SAAS;AAAA,YACtB,WAAW,KAAK,SAAS;AAAA,UAC3B,CAAC;AACD,cAAI,KAAK,GAAG,UAAU,QAAQ;AAC9B,cAAI,MAAM,gBAAgB,KAAK,EAAE,aAAa;AAAA,YAC5C,SAAS,UAAU;AAAA,YACnB,WAAW,UAAU,SAAS;AAAA,UAChC,CAAC;AAAA,QACH;AACA,YAAI,IAAI,WAAW,GAAG;AACpB,cAAI;AAAA,YACF,YAAY;AAAA,cACV,YAAY,KAAK;AAAA,cACjB,MAAM,KAAK;AAAA,cACX,OAAO;AAAA,cACP,WAAW,OAAO,MAAM,GAAG,IAAI;AAAA,cAC/B,UAAU;AAAA,cACV,YAAY;AAAA,cACZ,eAAe;AAAA,gBACb,EAAE,MAAM,YAAY,KAAK,oBAAoB,SAAS,OAAO,MAAM,GAAG,GAAI,EAAE;AAAA,cAC9E;AAAA,cACA,UAAU,EAAE,SAAS,oBAAoB;AAAA,YAC3C,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAEA,SAAS,eAAe,KAAqB,MAAoC;AAK/E,QAAM,QAAQ,IAAI,MAAM,OAAO,KAAK;AACpC,QAAM,OAAO,kEAAkE,KAAK,IAAI,cAAc,KAAK,WAAW;AACtH,SAAO,QAAQ,GAAG,IAAI,WAAW,KAAK,MAAM;AAC9C;AAEA,SAAS,iBAAiB,MAA4B,KAAwC;AAC5F,SAAO,YAAY;AAAA,IACjB,YAAY,KAAK;AAAA,IACjB,MAAM,KAAK;AAAA,IACX,SAAS,IAAI;AAAA,IACb,OAAO,IAAI;AAAA,IACX,WAAW,IAAI;AAAA,IACf,UAAU,IAAI;AAAA,IACd,YAAY,IAAI;AAAA,IAChB,eAAe;AAAA,MACb;AAAA,QACE,MAAM,oBAAoB,IAAI,YAAY;AAAA,QAC1C,KAAK,IAAI;AAAA,QACT,SAAS,IAAI;AAAA,MACf;AAAA,IACF;AAAA,IACA,oBAAoB,IAAI;AAAA,IACxB,UAAU,EAAE,cAAc,KAAK,QAAQ;AAAA,EACzC,CAAC;AACH;AAEA,SAAS,oBAAoB,KAAmE;AAC9F,MAAI,IAAI,WAAW,SAAS,EAAG,QAAO;AACtC,MAAI,IAAI,WAAW,aAAa,EAAG,QAAO;AAC1C,MAAI,IAAI,WAAW,WAAW,EAAG,QAAO;AACxC,MAAI,IAAI,WAAW,UAAU,EAAG,QAAO;AACvC,MAAI,IAAI,WAAW,YAAY,EAAG,QAAO;AACzC,SAAO;AACT;AAgBO,SAAS,oBAAoB,OAAgD;AAClF,MAAI,CAAC,SAAS,MAAM,WAAW,EAAG,QAAO;AACzC,QAAM,WAAW;AACjB,QAAM,OAAO,MAAM,MAAM,GAAG,QAAQ,EAAE,IAAI,CAAC,MAAM;AAC/C,UAAM,UAAU,EAAE,UAAU,KAAK,EAAE,OAAO,MAAM;AAChD,WAAO,UAAU,EAAE,UAAU,IAAI,EAAE,QAAQ,GAAG,OAAO,IAAI,mBAAmB,EAAE,OAAO,GAAG,CAAC;AAAA,EAC3F,CAAC;AACD,QAAM,WACJ,MAAM,SAAS,WACX;AAAA,SAAY,MAAM,SAAS,QAAQ,mDACnC;AACN,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG;AAAA,IACH;AAAA,EACF,EACG,OAAO,OAAO,EACd,KAAK,IAAI;AACd;AAEA,SAAS,mBAAmB,GAAW,KAAqB;AAC1D,MAAI,EAAE,UAAU,IAAK,QAAO;AAC5B,SAAO,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,EAAE,QAAQ,CAAC;AACzC;;;AC3UA,IAAM,sBAAuE;AAAA,EAC3E,KAAK,oBAAI,IAAI;AAAA,EACb,WAAW,oBAAI,IAAI,CAAC,sBAAsB,eAAe,aAAa,CAAC;AAAA,EACvE,kBAAkB,oBAAI,IAAI;AAAA,IACxB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAAA,EACD,oBAAoB,oBAAI,IAAI;AAAA,IAC1B;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAAA,EACD,UAAU,oBAAI,IAAI,CAAC,sBAAsB,eAAe,aAAa,YAAY,CAAC;AACpF;AASO,SAAS,wBACd,OACA,OACc;AACd,QAAM,MAAM,uBAAuB,EAAE,MAAM,CAAC;AAC5C,MAAI,UAAU,MAAO,QAAO;AAC5B,QAAM,QAAQ,oBAAoB,KAAK;AACvC,MAAI,CAAC,MAAO,OAAM,IAAI,MAAM,6BAA6B,KAAK,EAAE;AAChE,SAAO,IAAI,OAAO,CAAC,SAAS,MAAM,IAAK,KAA0B,IAAI,CAAC;AACxE;;;AC7CA,IAAM,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA+Bd,IAAM,yBAA+C;AAAA,EAC1D,IAAI;AAAA,EACJ,aACE;AAAA,EACF,MAAM;AAAA,EACN,SAAS;AAAA,EACT,kBAAkB;AAAA,EAClB,YAAY,CAAC,UAAU,wBAAwB,OAAO,KAAK;AAAA,EAC3D,WAAW,EAAE,UAAU,GAAG,sBAAsB,EAAE;AAAA,EAClD,UAAU;AAAA,EACV,MAAM,EAAE,MAAM,MAAM;AACtB;;;ACtCA,IAAMC,gBAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAsCd,IAAM,wBAA8C;AAAA,EACzD,IAAI;AAAA,EACJ,aACE;AAAA,EACF,MAAM;AAAA,EACN,SAAS;AAAA,EACT,kBAAkBA;AAAA,EAClB,YAAY,CAAC,UAAU,wBAAwB,OAAO,KAAK;AAAA,EAC3D,WAAW,EAAE,UAAU,GAAG,sBAAsB,EAAE;AAAA,EAClD,UAAU;AAAA,EACV,iBAAiB;AAAA,EACjB,MAAM,EAAE,MAAM,MAAM;AACtB;;;AC7CA,IAAMC,gBAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4Cd,IAAM,0BAAgD;AAAA,EAC3D,IAAI;AAAA,EACJ,aACE;AAAA,EACF,MAAM;AAAA,EACN,SAAS;AAAA,EACT,kBAAkBA;AAAA,EAClB,YAAY,CAAC,UAAU,wBAAwB,sBAAsB,KAAK;AAAA,EAC1E,WAAW,EAAE,UAAU,GAAG,sBAAsB,EAAE;AAAA,EAClD,UAAU;AAAA,EACV,MAAM,EAAE,MAAM,MAAM;AACtB;;;AC7DA,IAAMC,gBAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAoCd,IAAM,gCAAsD;AAAA,EACjE,IAAI;AAAA,EACJ,aACE;AAAA,EACF,MAAM;AAAA,EACN,SAAS;AAAA,EACT,kBAAkBA;AAAA,EAClB,YAAY,CAAC,UAAU,wBAAwB,OAAO,KAAK;AAAA,EAC3D,WAAW,EAAE,UAAU,GAAG,sBAAsB,EAAE;AAAA,EAClD,UAAU;AAAA,EACV,MAAM,EAAE,MAAM,MAAM;AACtB;;;AC3CO,IAAM,8BAA+D;AAAA,EAC1E;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;;;ACdA,SAAS,kBAAkB;AA+FpB,IAAM,kBAAN,MAAsB;AAAA,EACV,WAAW,oBAAI,IAAqB;AAAA,EACpC;AAAA,EAEjB,YAAY,UAAkC,CAAC,GAAG;AAChD,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,SAAS,SAAwB;AAC/B,QAAI,CAAC,QAAQ,GAAI,OAAM,IAAI,MAAM,kDAAkD;AACnF,QAAI,KAAK,SAAS,IAAI,QAAQ,EAAE,GAAG;AACjC,YAAM,IAAI,MAAM,mDAAmD,QAAQ,EAAE,GAAG;AAAA,IAClF;AACA,QAAI,CAAC,QAAQ,SAAS;AACpB,YAAM,IAAI,MAAM,sCAAsC,QAAQ,EAAE,0BAA0B;AAAA,IAC5F;AACA,SAAK,SAAS,IAAI,QAAQ,IAAI,OAAO;AAAA,EACvC;AAAA,EAEA,OAKG;AACD,WAAO,MAAM,KAAK,KAAK,SAAS,OAAO,CAAC,EAAE,IAAI,CAAC,OAAO;AAAA,MACpD,IAAI,EAAE;AAAA,MACN,aAAa,EAAE;AAAA,MACf,SAAS,EAAE;AAAA,MACX,MAAM,EAAE;AAAA,IACV,EAAE;AAAA,EACJ;AAAA,EAEA,MAAM,IACJ,OACA,QACA,UAA2B,CAAC,GACD;AAG3B,qBAAiB,MAAM,KAAK,UAAU,OAAO,QAAQ,OAAO,GAAG;AAC7D,UAAI,GAAG,SAAS,gBAAiB,QAAO,GAAG;AAAA,IAC7C;AACA,UAAM,IAAI,MAAM,mEAAmE;AAAA,EACrF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,OAAO,UACL,OACA,QACA,UAA2B,CAAC,GACiB;AAC7C,UAAM,gBAAgB,MAAM,WAAW,EAAE,MAAM,GAAG,EAAE,CAAC;AACrD,UAAM,MAAM,KAAK,QAAQ,QAAQ,MAAM;AAAA,IAAC;AACxC,UAAM,QAAQ,KAAK,QAAQ,SAAS,CAAC;AACrC,UAAM,aAAY,oBAAI,KAAK,GAAE,YAAY;AACzC,UAAM,UAAU,KAAK,IAAI;AACzB,UAAM,aAAa,QAAQ,YAAY,UAAU,QAAQ,YAAY;AAErE,UAAM,WAAW,KAAK,eAAe,OAAO;AAC5C,UAAM,SAAS,QAAQ,UAAU,KAAK,QAAQ;AAE9C,UAAM;AAAA,MACJ,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,gBAAgB;AAAA,MAChB,YAAY;AAAA,MACZ,aAAa,SAAS,IAAI,CAAC,MAAM,EAAE,EAAE;AAAA,IACvC;AAEA,UAAM,YAAiC,CAAC;AACxC,UAAM,cAAgC,CAAC;AACvC,QAAI,YAAY;AAChB,QAAI,eAAe,QAAQ;AAE3B,eAAW,WAAW,UAAU;AAC9B,YAAM,KAAK,KAAK,IAAI;AACpB,YAAM,QAAQ,KAAK,WAAW,SAAS,MAAM;AAC7C,UAAI,MAAM,SAAS,WAAW;AAC5B,cAAM,UAA6B;AAAA,UACjC,YAAY,QAAQ;AAAA,UACpB,QAAQ;AAAA,UACR,QAAQ,0BAA0B,QAAQ,SAAS;AAAA,UACnD,gBAAgB;AAAA,UAChB,YAAY;AAAA,UACZ,UAAU;AAAA,QACZ;AACA,kBAAU,KAAK,OAAO;AACtB,YAAI,kBAAkB,QAAQ,EAAE,yBAAoB,EAAE,OAAO,MAAM,QAAQ,UAAU,CAAC;AACtF,cAAM,MAAM,iBAAiB,EAAE,SAAS,SAAS,UAAU,CAAC,GAAG,MAAM,CAAC;AACtE,cAAM,EAAE,MAAM,mBAAmB,QAAQ;AACzC;AAAA,MACF;AAEA,YAAM,YAAY,eAAe,QAAQ;AAAA,QACvC;AAAA,QACA;AAAA,QACA,cAAc,SAAS;AAAA,MACzB,CAAC;AAED,YAAM,MAAsB;AAAA,QAC1B;AAAA,QACA;AAAA,QACA;AAAA,QACA,WAAW;AAAA,QACX,MAAM,KAAK,QAAQ;AAAA,QACnB,MAAM,QAAQ;AAAA,QACd,KAAK,CAAC,KAAK,WAAW,IAAI,IAAI,QAAQ,EAAE,KAAK,GAAG,IAAI,EAAE,OAAO,eAAe,GAAG,OAAO,CAAC;AAAA,QACvF,QAAQ,QAAQ;AAAA,QAChB,eAAe,oBAAoB,QAAQ,eAAe,QAAQ,EAAE;AAAA,MACtE;AAEA,YAAM,MAAM,kBAAkB,EAAE,SAAS,KAAK,MAAM,CAAC;AACrD,YAAM;AAAA,QACJ,MAAM;AAAA,QACN,YAAY,QAAQ;AAAA,QACpB,YAAY,IAAI,KAAK,EAAE,EAAE,YAAY;AAAA,MACvC;AAEA,UAAI;AACF,cAAM,WAAW,MAAO,QAA6B,QAAQ,MAAM,OAAO,GAAG;AAC7E,cAAM,UAAU,KAAK,IAAI,IAAI;AAC7B,cAAM,OAAO,eAAe,QAAQ;AACpC,qBAAa;AACb,YAAI,OAAO,iBAAiB,SAAU,gBAAe,KAAK,IAAI,GAAG,eAAe,IAAI;AACpF,oBAAY,KAAK,GAAG,QAAQ;AAC5B,cAAM,UAA6B;AAAA,UACjC,YAAY,QAAQ;AAAA,UACpB,QAAQ;AAAA,UACR,gBAAgB,SAAS;AAAA,UACzB,YAAY;AAAA,UACZ,UAAU;AAAA,QACZ;AACA,kBAAU,KAAK,OAAO;AACtB,YAAI,gBAAgB,QAAQ,EAAE,IAAI;AAAA,UAChC;AAAA,UACA,UAAU,SAAS;AAAA,UACnB,YAAY;AAAA,UACZ,UAAU;AAAA,QACZ,CAAC;AACD,cAAM,MAAM,iBAAiB,EAAE,SAAS,SAAS,UAAU,MAAM,CAAC;AAClE,cAAM,EAAE,MAAM,qBAAqB,SAAS,SAAS;AAAA,MACvD,SAAS,KAAK;AACZ,cAAM,UAAU,KAAK,IAAI,IAAI;AAC7B,cAAM,IAAI,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AAE5D,cAAM,eAAgB,MAAM,MAAM,UAAU,EAAE,SAAS,OAAO,GAAG,MAAM,CAAC,KAAM,CAAC;AAC/E,YAAI,aAAa,OAAQ,aAAY,KAAK,GAAG,YAAY;AACzD,cAAM,UAA6B;AAAA,UACjC,YAAY,QAAQ;AAAA,UACpB,QAAQ;AAAA,UACR,gBAAgB,aAAa;AAAA,UAC7B,YAAY;AAAA,UACZ,UAAU;AAAA,UACV,OAAO,EAAE,OAAO,EAAE,YAAY,MAAM,SAAS,EAAE,QAAQ;AAAA,QACzD;AACA,kBAAU,KAAK,OAAO;AACtB,YAAI,kBAAkB,QAAQ,EAAE,IAAI;AAAA,UAClC;AAAA,UACA,aAAa,EAAE,YAAY;AAAA,UAC3B,OAAO,EAAE;AAAA,QACX,CAAC;AACD,cAAM,MAAM,iBAAiB,EAAE,SAAS,SAAS,UAAU,cAAc,MAAM,CAAC;AAChF,cAAM,EAAE,MAAM,qBAAqB,SAAS,UAAU,aAAa;AAAA,MAErE;AAAA,IACF;AAEA,UAAM,SAA2B;AAAA,MAC/B,QAAQ;AAAA,MACR,gBAAgB;AAAA,MAChB,YAAY;AAAA,MACZ,WAAU,oBAAI,KAAK,GAAE,YAAY;AAAA,MACjC,UAAU;AAAA,MACV,aAAa;AAAA,MACb,gBAAgB;AAAA,IAClB;AACA,UAAM,MAAM,aAAa,EAAE,OAAO,CAAC;AACnC,UAAM,EAAE,MAAM,iBAAiB,OAAO;AAAA,EACxC;AAAA,EAEQ,eAAe,MAAkC;AACvD,QAAI,aAAa,MAAM,KAAK,KAAK,SAAS,OAAO,CAAC;AAClD,QAAI,KAAK,MAAM,QAAQ;AACrB,YAAM,OAAO,IAAI,IAAI,KAAK,IAAI;AAC9B,mBAAa,WAAW,OAAO,CAAC,MAAM,KAAK,IAAI,EAAE,EAAE,CAAC;AAAA,IACtD;AACA,QAAI,KAAK,MAAM,QAAQ;AACrB,YAAM,OAAO,IAAI,IAAI,KAAK,IAAI;AAC9B,mBAAa,WAAW,OAAO,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC;AAAA,IACvD;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,WACN,SACA,QAC2D;AAC3D,YAAQ,QAAQ,WAAW;AAAA,MACzB,KAAK;AACH,eAAO,OAAO,aACV,EAAE,MAAM,WAAW,OAAO,OAAO,WAAW,IAC5C,EAAE,MAAM,UAAU;AAAA,MACxB,KAAK;AACH,eAAO,OAAO,cACV,EAAE,MAAM,WAAW,OAAO,OAAO,YAAY,IAC7C,EAAE,MAAM,UAAU;AAAA,MACxB,KAAK;AACH,eAAO,OAAO,YAAY,EAAE,MAAM,WAAW,OAAO,OAAO,UAAU,IAAI,EAAE,MAAM,UAAU;AAAA,MAC7F,KAAK;AACH,eAAO,OAAO,aACV,EAAE,MAAM,WAAW,OAAO,OAAO,WAAW,IAC5C,EAAE,MAAM,UAAU;AAAA,MACxB,KAAK,UAAU;AACb,cAAM,IAAI,OAAO,SAAS,QAAQ,EAAE;AACpC,eAAO,MAAM,SAAY,EAAE,MAAM,WAAW,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU;AAAA,MAC7E;AAAA,IACF;AAAA,EACF;AACF;AAOA,SAAS,eACP,QACA,MACoB;AACpB,MAAI,CAAC,OAAQ,QAAO;AACpB,MAAI,OAAO,UAAU;AACnB,WAAO,OAAO,SAAS;AAAA,MACrB,SAAS,KAAK;AAAA,MACd,UAAU,OAAO;AAAA,MACjB,cAAc,KAAK;AAAA,MACnB,cAAc,KAAK;AAAA,IACrB,CAAC;AAAA,EACH;AACA,MAAI,OAAO,YAAY,KAAM,QAAO;AACpC,MAAI,OAAO,SAAS;AAOlB,UAAM,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,KAAK;AAC7C,UAAM,cAAc,KAAK,IAAI,GAAG,KAAK,YAAY;AACjD,WAAQ,OAAO,WAAW,IAAK;AAAA,EACjC;AACA,SAAO,OAAO,WAAW,KAAK,IAAI,GAAG,KAAK,YAAY;AACxD;AAOA,SAAS,eAAe,UAAoC;AAC1D,MAAI,MAAM;AACV,aAAW,KAAK,UAAU;AACxB,UAAM,IAAI,EAAE,UAAU;AACtB,QAAI,OAAO,MAAM,YAAY,OAAO,SAAS,CAAC,EAAG,QAAO;AAAA,EAC1D;AACA,SAAO;AACT;AAcA,SAAS,oBACP,QACA,WAC2C;AAC3C,MAAI,CAAC,OAAQ,QAAO;AACpB,MAAI,MAAM,QAAQ,MAAM,GAAG;AACzB,UAAMC,OAAM,OAAO,OAAO,CAAC,MAAM,EAAE,eAAe,SAAS;AAC3D,WAAOA,KAAI,SAAS,IAAIA,OAAM;AAAA,EAChC;AACA,QAAM,SAAS;AACf,QAAM,MAAM,OAAO,SAAS,KAAK,CAAC;AAClC,QAAM,WAAW,OAAO,GAAG,KAAK,CAAC;AACjC,QAAM,SAAS,CAAC,GAAG,KAAK,GAAG,QAAQ;AACnC,SAAO,OAAO,SAAS,IAAI,SAAS;AACtC;","names":["z","z","ACTOR_PROMPT","ACTOR_PROMPT","ACTOR_PROMPT","own"]}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|