@tangle-network/agent-eval 0.23.1 → 0.25.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/CHANGELOG.md +145 -0
- package/README.md +212 -79
- package/dist/baseline-4R5deP0N.d.ts +108 -0
- package/dist/benchmarks/index.d.ts +3 -2
- package/dist/benchmarks/index.js +1 -1
- package/dist/builder-eval/index.d.ts +249 -0
- package/dist/builder-eval/index.js +391 -0
- package/dist/builder-eval/index.js.map +1 -0
- package/dist/{chunk-IOXMGMHQ.js → chunk-2A5XJB43.js} +142 -318
- package/dist/chunk-2A5XJB43.js.map +1 -0
- package/dist/chunk-47X6LRCE.js +76 -0
- package/dist/chunk-47X6LRCE.js.map +1 -0
- package/dist/{chunk-6M774GY6.js → chunk-4F5DQN55.js} +1 -1
- package/dist/chunk-4F5DQN55.js.map +1 -0
- package/dist/{chunk-KAO3Q65R.js → chunk-4S4BM3QQ.js} +15 -13
- package/dist/chunk-4S4BM3QQ.js.map +1 -0
- package/dist/chunk-5BKGXME7.js +65 -0
- package/dist/chunk-5BKGXME7.js.map +1 -0
- package/dist/{chunk-6KQG5HAH.js → chunk-5LBB5B3Z.js} +376 -72
- package/dist/chunk-5LBB5B3Z.js.map +1 -0
- package/dist/{chunk-42I2QC2L.js → chunk-6QDKWHLS.js} +18 -14
- package/dist/chunk-6QDKWHLS.js.map +1 -0
- package/dist/{chunk-VQQSPGSM.js → chunk-EDUKQ5AM.js} +247 -189
- package/dist/chunk-EDUKQ5AM.js.map +1 -0
- package/dist/chunk-I4MBDTY5.js +272 -0
- package/dist/chunk-I4MBDTY5.js.map +1 -0
- package/dist/chunk-JLZQWFV3.js +618 -0
- package/dist/chunk-JLZQWFV3.js.map +1 -0
- package/dist/chunk-K2TPS5LB.js +569 -0
- package/dist/chunk-K2TPS5LB.js.map +1 -0
- package/dist/chunk-KKHDIONI.js +414 -0
- package/dist/chunk-KKHDIONI.js.map +1 -0
- package/dist/chunk-KMPRBJK4.js +74 -0
- package/dist/chunk-KMPRBJK4.js.map +1 -0
- package/dist/{chunk-QUKKGHTZ.js → chunk-KTGTIOFD.js} +6 -3
- package/dist/chunk-KTGTIOFD.js.map +1 -0
- package/dist/chunk-LSH4MMOZ.js +838 -0
- package/dist/chunk-LSH4MMOZ.js.map +1 -0
- package/dist/chunk-NG236HPC.js +57 -0
- package/dist/chunk-NG236HPC.js.map +1 -0
- package/dist/{chunk-QBW3YBTR.js → chunk-NLMNWKVM.js} +14 -6
- package/dist/chunk-NLMNWKVM.js.map +1 -0
- package/dist/chunk-NU65VQ7M.js +99 -0
- package/dist/chunk-NU65VQ7M.js.map +1 -0
- package/dist/chunk-OWLAAMME.js +250 -0
- package/dist/chunk-OWLAAMME.js.map +1 -0
- package/dist/{chunk-SQQLHODJ.js → chunk-PC4UYEBM.js} +7 -4
- package/dist/chunk-PC4UYEBM.js.map +1 -0
- package/dist/{chunk-7EAUOUQS.js → chunk-RAF443UI.js} +213 -115
- package/dist/chunk-RAF443UI.js.map +1 -0
- package/dist/chunk-RZTMDUO7.js +49 -0
- package/dist/chunk-RZTMDUO7.js.map +1 -0
- package/dist/{chunk-EXGR4XEM.js → chunk-SESZDQPX.js} +23 -19
- package/dist/chunk-SESZDQPX.js.map +1 -0
- package/dist/{chunk-5IIQKMD5.js → chunk-TVVP3ZZQ.js} +14 -4
- package/dist/chunk-TVVP3ZZQ.js.map +1 -0
- package/dist/chunk-WWYCWKUM.js +196 -0
- package/dist/chunk-WWYCWKUM.js.map +1 -0
- package/dist/{chunk-AXHNWLIX.js → chunk-YRZ4M5GS.js} +2 -90
- package/dist/chunk-YRZ4M5GS.js.map +1 -0
- package/dist/chunk-ZN274SWR.js +613 -0
- package/dist/chunk-ZN274SWR.js.map +1 -0
- package/dist/cli.js +10 -6
- package/dist/cli.js.map +1 -1
- package/dist/{control-DvkH87qJ.d.ts → control-CBShYYA6.d.ts} +32 -33
- package/dist/control-runtime-BuJHoLg0.d.ts +180 -0
- package/dist/control.d.ts +8 -6
- package/dist/control.js +10 -7
- package/dist/{dataset-B9qvlm_o.d.ts → dataset-CiK_3LDr.d.ts} +5 -2
- package/dist/{emitter-B2XqDKFU.d.ts → emitter-DP_cSSiw.d.ts} +1 -1
- package/dist/errors-BZ9sTdz7.d.ts +70 -0
- package/dist/failure-cluster-C2EGSDiT.d.ts +76 -0
- package/dist/feedback-trajectory-DfFdrraJ.d.ts +169 -0
- package/dist/governance/index.d.ts +5 -0
- package/dist/governance/index.js +18 -0
- package/dist/governance/index.js.map +1 -0
- package/dist/{index-DDTlbHEK.d.ts → index--fVrWDiR.d.ts} +1 -1
- package/dist/index-Oj9fAPPN.d.ts +270 -0
- package/dist/index.d.ts +2018 -3003
- package/dist/index.js +7443 -9102
- package/dist/index.js.map +1 -1
- package/dist/{integrity-Cr5YodSY.d.ts → integrity-DK2EBVZC.d.ts} +4 -3
- package/dist/knowledge/index.d.ts +102 -0
- package/dist/knowledge/index.js +18 -0
- package/dist/knowledge/index.js.map +1 -0
- package/dist/meta-eval/index.d.ts +99 -0
- package/dist/meta-eval/index.js +324 -0
- package/dist/meta-eval/index.js.map +1 -0
- package/dist/multi-layer-verifier-LkP3LVKj.d.ts +141 -0
- package/dist/openapi.json +491 -1
- package/dist/optimization.d.ts +11 -8
- package/dist/optimization.js +11 -9
- package/dist/outcome-store-D6KWmYvj.d.ts +63 -0
- package/dist/pipelines/index.d.ts +172 -0
- package/dist/pipelines/index.js +345 -0
- package/dist/pipelines/index.js.map +1 -0
- package/dist/prm/index.d.ts +99 -0
- package/dist/prm/index.js +222 -0
- package/dist/prm/index.js.map +1 -0
- package/dist/query-DODUYdPg.d.ts +30 -0
- package/dist/release-report-BNgMdqPF.d.ts +292 -0
- package/dist/replay-BL96gCEP.d.ts +226 -0
- package/dist/reporting.d.ts +10 -295
- package/dist/reporting.js +10 -6
- package/dist/{eval-campaign-Ds5QljIh.d.ts → researcher-BPT8x_NT.d.ts} +148 -146
- package/dist/rl.d.ts +1762 -8
- package/dist/rl.js +2035 -58
- package/dist/rl.js.map +1 -1
- package/dist/rubric-D5tjHNJQ.d.ts +72 -0
- package/dist/rubric-predictive-validity-C0uDYwG6.d.ts +105 -0
- package/dist/{run-record-DNiOMBrZ.d.ts → run-record-CqzahIbx.d.ts} +4 -1
- package/dist/sequential-Dgz1n51-.d.ts +139 -0
- package/dist/{store-u47QaJ9G.d.ts → store-Db2Bv8Cf.d.ts} +1 -1
- package/dist/{summary-report-Ce1r4EYo.d.ts → summary-report-C7VPYEj2.d.ts} +3 -76
- package/dist/telemetry/file.js +4 -1
- package/dist/telemetry/file.js.map +1 -1
- package/dist/telemetry/index.js +57 -57
- package/dist/telemetry/index.js.map +1 -1
- package/dist/test-graded-scenario-B2kWEdh9.d.ts +146 -0
- package/dist/traces.d.ts +142 -387
- package/dist/traces.js +1302 -40
- package/dist/traces.js.map +1 -1
- package/dist/trajectory-CnoBo-JY.d.ts +32 -0
- package/dist/wire/index.d.ts +369 -25
- package/dist/wire/index.js +22 -3
- package/package.json +44 -18
- package/dist/chunk-42I2QC2L.js.map +0 -1
- package/dist/chunk-5IIQKMD5.js.map +0 -1
- package/dist/chunk-6KQG5HAH.js.map +0 -1
- package/dist/chunk-6M774GY6.js.map +0 -1
- package/dist/chunk-7EAUOUQS.js.map +0 -1
- package/dist/chunk-AXHNWLIX.js.map +0 -1
- package/dist/chunk-EXGR4XEM.js.map +0 -1
- package/dist/chunk-IOXMGMHQ.js.map +0 -1
- package/dist/chunk-KAO3Q65R.js.map +0 -1
- package/dist/chunk-LZKIOBG2.js +0 -2026
- package/dist/chunk-LZKIOBG2.js.map +0 -1
- package/dist/chunk-QBW3YBTR.js.map +0 -1
- package/dist/chunk-QUKKGHTZ.js.map +0 -1
- package/dist/chunk-SQQLHODJ.js.map +0 -1
- package/dist/chunk-V5QSWN7L.js +0 -1310
- package/dist/chunk-V5QSWN7L.js.map +0 -1
- package/dist/chunk-VQQSPGSM.js.map +0 -1
- package/dist/chunk-XPHOZPOM.js +0 -1947
- package/dist/chunk-XPHOZPOM.js.map +0 -1
- package/dist/feedback-trajectory-c43WGtTX.d.ts +0 -346
- package/dist/index-ekBXweiQ.d.ts +0 -1894
- package/dist/sequential-DgU2mFsE.d.ts +0 -304
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/trace/store.ts","../src/trace/otel.ts","../src/trace/redact.ts","../src/replay.ts"],"sourcesContent":["/**\n * TraceStore — persistence + query over the TraceSchema v1 corpus.\n *\n * Two implementations ship in the core:\n * - InMemoryTraceStore: dev + tests, fully in-process\n * - FileSystemTraceStore: NDJSON append-only files per entity, suitable\n * for long-running CI jobs; rolled over at 32MB\n *\n * Downstream adapters (DuckDB, Langfuse, R2 parquet) implement this same\n * interface — the rest of the framework is storage-agnostic.\n */\n\nimport type {\n Artifact,\n BudgetLedgerEntry,\n EventKind,\n Run,\n RunStatus,\n Span,\n SpanKind,\n TraceEvent,\n} from './schema'\n\nexport interface RunFilter {\n scenarioId?: string\n variantId?: string\n status?: RunStatus\n since?: number\n until?: number\n tag?: { key: string; value: string }\n parentRunId?: string\n projectId?: string\n chatId?: string\n layer?: import('./schema').RunLayer\n}\n\nexport interface SpanFilter {\n runId?: string\n parentSpanId?: string\n kind?: SpanKind\n name?: string\n toolName?: string\n judgeId?: string\n since?: number\n until?: number\n}\n\nexport interface EventFilter {\n runId?: string\n spanId?: string\n kind?: EventKind\n since?: number\n until?: number\n}\n\nexport interface TraceStore {\n appendRun(run: Run): Promise<void>\n updateRun(runId: string, patch: Partial<Run>): Promise<void>\n appendSpan(span: Span): Promise<void>\n updateSpan(spanId: string, patch: Partial<Span>): Promise<void>\n appendEvent(event: TraceEvent): Promise<void>\n appendArtifact(artifact: Artifact): Promise<void>\n appendBudgetEntry(entry: BudgetLedgerEntry): Promise<void>\n\n getRun(runId: string): Promise<Run | undefined>\n listRuns(filter?: RunFilter): Promise<Run[]>\n spans(filter?: SpanFilter): Promise<Span[]>\n events(filter?: EventFilter): Promise<TraceEvent[]>\n budget(runId: string): Promise<BudgetLedgerEntry[]>\n artifacts(runId: string): Promise<Artifact[]>\n}\n\n// ── In-memory ────────────────────────────────────────────────────────\n\nexport class InMemoryTraceStore implements TraceStore {\n private runs = new Map<string, Run>()\n private allSpans: Span[] = []\n private allEvents: TraceEvent[] = []\n private allArtifacts: Artifact[] = []\n private allBudget: BudgetLedgerEntry[] = []\n\n async appendRun(run: Run): Promise<void> {\n if (this.runs.has(run.runId)) throw new Error(`run ${run.runId} already exists`)\n this.runs.set(run.runId, { ...run })\n }\n\n async updateRun(runId: string, patch: Partial<Run>): Promise<void> {\n const existing = this.runs.get(runId)\n if (!existing) throw new Error(`run ${runId} not found`)\n this.runs.set(runId, { ...existing, ...patch })\n }\n\n async appendSpan(span: Span): Promise<void> {\n this.allSpans.push({ ...span })\n }\n\n async updateSpan(spanId: string, patch: Partial<Span>): Promise<void> {\n const idx = this.allSpans.findIndex((s) => s.spanId === spanId)\n if (idx < 0) throw new Error(`span ${spanId} not found`)\n this.allSpans[idx] = { ...this.allSpans[idx], ...patch } as Span\n }\n\n async appendEvent(event: TraceEvent): Promise<void> {\n this.allEvents.push({ ...event })\n }\n\n async appendArtifact(artifact: Artifact): Promise<void> {\n this.allArtifacts.push({ ...artifact })\n }\n\n async appendBudgetEntry(entry: BudgetLedgerEntry): Promise<void> {\n this.allBudget.push({ ...entry })\n }\n\n async getRun(runId: string): Promise<Run | undefined> {\n const r = this.runs.get(runId)\n return r ? { ...r } : undefined\n }\n\n async listRuns(filter: RunFilter = {}): Promise<Run[]> {\n return [...this.runs.values()].filter((r) => matchesRun(r, filter))\n }\n\n async spans(filter: SpanFilter = {}): Promise<Span[]> {\n return this.allSpans.filter((s) => matchesSpan(s, filter)).map((s) => ({ ...s }))\n }\n\n async events(filter: EventFilter = {}): Promise<TraceEvent[]> {\n return this.allEvents.filter((e) => matchesEvent(e, filter)).map((e) => ({ ...e }))\n }\n\n async budget(runId: string): Promise<BudgetLedgerEntry[]> {\n return this.allBudget.filter((b) => b.runId === runId).map((b) => ({ ...b }))\n }\n\n async artifacts(runId: string): Promise<Artifact[]> {\n return this.allArtifacts.filter((a) => a.runId === runId).map((a) => ({ ...a }))\n }\n}\n\nfunction matchesRun(r: Run, f: RunFilter): boolean {\n if (f.scenarioId && r.scenarioId !== f.scenarioId) return false\n if (f.variantId && r.variantId !== f.variantId) return false\n if (f.status && r.status !== f.status) return false\n if (f.since !== undefined && r.startedAt < f.since) return false\n if (f.until !== undefined && r.startedAt > f.until) return false\n if (f.tag && r.tags?.[f.tag.key] !== f.tag.value) return false\n if (f.parentRunId && r.parentRunId !== f.parentRunId) return false\n if (f.projectId && r.projectId !== f.projectId) return false\n if (f.chatId && r.chatId !== f.chatId) return false\n if (f.layer && r.layer !== f.layer) return false\n return true\n}\n\nfunction matchesSpan(s: Span, f: SpanFilter): boolean {\n if (f.runId && s.runId !== f.runId) return false\n if (f.parentSpanId && s.parentSpanId !== f.parentSpanId) return false\n if (f.kind && s.kind !== f.kind) return false\n if (f.name && s.name !== f.name) return false\n if (f.toolName && (s.kind !== 'tool' || s.toolName !== f.toolName)) return false\n if (f.judgeId && (s.kind !== 'judge' || s.judgeId !== f.judgeId)) return false\n if (f.since !== undefined && s.startedAt < f.since) return false\n if (f.until !== undefined && s.startedAt > f.until) return false\n return true\n}\n\nfunction matchesEvent(e: TraceEvent, f: EventFilter): boolean {\n if (f.runId && e.runId !== f.runId) return false\n if (f.spanId && e.spanId !== f.spanId) return false\n if (f.kind && e.kind !== f.kind) return false\n if (f.since !== undefined && e.timestamp < f.since) return false\n if (f.until !== undefined && e.timestamp > f.until) return false\n return true\n}\n\n// ── Filesystem (NDJSON append-only, one file per entity) ─────────────\n\nexport interface FileSystemTraceStoreOptions {\n dir: string\n /** Roll over NDJSON files when they exceed this size in bytes. Default 32 MB. */\n maxBytes?: number\n}\n\nexport class FileSystemTraceStore implements TraceStore {\n private dir: string\n private maxBytes: number\n /** Lazy in-memory index for queries — populated on first read. */\n private index?: InMemoryTraceStore\n private loaded = false\n\n constructor(options: FileSystemTraceStoreOptions) {\n this.dir = options.dir\n this.maxBytes = options.maxBytes ?? 32 * 1024 * 1024\n }\n\n private async ensureDir(): Promise<void> {\n const fs = await import('node:fs/promises')\n await fs.mkdir(this.dir, { recursive: true })\n }\n\n private async append(name: string, record: unknown): Promise<void> {\n await this.ensureDir()\n const fs = await import('node:fs/promises')\n const path = await import('node:path')\n const active = path.join(this.dir, `${name}.ndjson`)\n try {\n const stat = await fs.stat(active)\n if (stat.size >= this.maxBytes) {\n const rolled = path.join(this.dir, `${name}.${Date.now()}.ndjson`)\n await fs.rename(active, rolled)\n }\n } catch {\n /* file doesn't exist yet */\n }\n await fs.appendFile(active, `${JSON.stringify(record)}\\n`, 'utf8')\n // Mirror genuinely-new rows into the lazy index. Update rows (marked\n // with `_update: true` by updateRun/updateSpan) are applied by those\n // methods directly via the index's update* APIs — re-inserting them\n // here would trigger a duplicate-id error after the first read has\n // populated the index. Regression: 0.23.0 endRun on the second\n // variant against a shared store threw \"run X already exists\".\n if (this.index && !(record as { _update?: boolean })?._update) {\n void this.insertInto(name, record)\n }\n }\n\n private async insertInto(name: string, record: unknown): Promise<void> {\n if (!this.index) return\n switch (name) {\n case 'runs':\n await this.index.appendRun(record as Run)\n break\n case 'spans':\n await this.index.appendSpan(record as Span)\n break\n case 'events':\n await this.index.appendEvent(record as TraceEvent)\n break\n case 'artifacts':\n await this.index.appendArtifact(record as Artifact)\n break\n case 'budget':\n await this.index.appendBudgetEntry(record as BudgetLedgerEntry)\n break\n }\n }\n\n private async load(): Promise<InMemoryTraceStore> {\n if (this.loaded && this.index) return this.index\n const fs = await import('node:fs/promises')\n const path = await import('node:path')\n const store = new InMemoryTraceStore()\n try {\n const entries = await fs.readdir(this.dir)\n for (const file of entries) {\n if (!file.endsWith('.ndjson')) continue\n const full = path.join(this.dir, file)\n const content = await fs.readFile(full, 'utf8')\n const base = file.split('.')[0]\n for (const line of content.split('\\n')) {\n if (!line.trim()) continue\n const record = JSON.parse(line)\n if (base === 'runs') {\n // Allow re-loading without duplicate error\n try {\n await store.appendRun(record)\n } catch {\n await store.updateRun(record.runId, record)\n }\n } else if (base === 'spans') {\n // `updateSpan` appends an `_update: true` patch row instead of\n // rewriting the original span. On reload we must collapse those\n // patches into the prior span — otherwise a fresh\n // FileSystemTraceStore reading the same dir reports duplicate\n // spans (one full, one fragment with no runId/kind/name), which\n // breaks any downstream consumer that re-opens the store\n // cross-process (e.g. the canonical eval's OTLP converter).\n if (record?._update) {\n try {\n await store.updateSpan(record.spanId, record)\n } catch {\n // Patch row arrived before the original — should not happen\n // with locked append order, but fall through to append so we\n // don't lose data.\n await store.appendSpan(record)\n }\n } else {\n await store.appendSpan(record)\n }\n } else if (base === 'events') {\n await store.appendEvent(record)\n } else if (base === 'artifacts') {\n await store.appendArtifact(record)\n } else if (base === 'budget') {\n await store.appendBudgetEntry(record)\n }\n }\n }\n } catch {\n /* empty dir, first run */\n }\n this.index = store\n this.loaded = true\n return store\n }\n\n async appendRun(run: Run): Promise<void> {\n await this.append('runs', run)\n }\n async updateRun(runId: string, patch: Partial<Run>): Promise<void> {\n // NDJSON is append-only; record updates as new rows with the same runId —\n // readers collapse by last-write-wins on load.\n await this.append('runs', { runId, ...patch, _update: true })\n if (this.index) await this.index.updateRun(runId, patch)\n }\n async appendSpan(span: Span): Promise<void> {\n await this.append('spans', span)\n }\n async updateSpan(spanId: string, patch: Partial<Span>): Promise<void> {\n await this.append('spans', { spanId, ...patch, _update: true })\n if (this.index) await this.index.updateSpan(spanId, patch)\n }\n async appendEvent(event: TraceEvent): Promise<void> {\n await this.append('events', event)\n }\n async appendArtifact(artifact: Artifact): Promise<void> {\n await this.append('artifacts', artifact)\n }\n async appendBudgetEntry(entry: BudgetLedgerEntry): Promise<void> {\n await this.append('budget', entry)\n }\n\n async getRun(runId: string): Promise<Run | undefined> {\n return (await this.load()).getRun(runId)\n }\n async listRuns(filter?: RunFilter): Promise<Run[]> {\n return (await this.load()).listRuns(filter)\n }\n async spans(filter?: SpanFilter): Promise<Span[]> {\n return (await this.load()).spans(filter)\n }\n async events(filter?: EventFilter): Promise<TraceEvent[]> {\n return (await this.load()).events(filter)\n }\n async budget(runId: string): Promise<BudgetLedgerEntry[]> {\n return (await this.load()).budget(runId)\n }\n async artifacts(runId: string): Promise<Artifact[]> {\n return (await this.load()).artifacts(runId)\n }\n}\n","/**\n * OpenTelemetry JSON export — maps TraceSchema v1 to OTLP/JSON so\n * traces render natively in Jaeger / Honeycomb / Langfuse / Grafana.\n *\n * Wire format only. We do NOT depend on the @opentelemetry SDK — that\n * would drag in polyfills incompatible with Workers/Edge. Consumers\n * push the JSON to their collector of choice via HTTP.\n *\n * Reference: OTLP 1.3.2 (ResourceSpans / ScopeSpans / Span).\n */\n\nimport type { Run, Span, TraceEvent } from './schema'\nimport type { TraceStore } from './store'\n\nexport const OTEL_AGENT_EVAL_SCOPE = { name: '@tangle-network/agent-eval', version: '0.3.0' }\n\nexport interface OtlpSpan {\n traceId: string\n spanId: string\n parentSpanId?: string\n name: string\n kind: number\n startTimeUnixNano: string\n endTimeUnixNano: string\n attributes: Array<{\n key: string\n value: { stringValue?: string; intValue?: string; doubleValue?: number; boolValue?: boolean }\n }>\n events?: Array<{ timeUnixNano: string; name: string; attributes?: OtlpSpan['attributes'] }>\n status?: { code: number; message?: string }\n}\n\nexport interface OtlpResourceSpans {\n resource: { attributes: OtlpSpan['attributes'] }\n scopeSpans: Array<{ scope: typeof OTEL_AGENT_EVAL_SCOPE; spans: OtlpSpan[] }>\n}\n\nexport interface OtlpExport {\n resourceSpans: OtlpResourceSpans[]\n}\n\n/** Export a single run's spans + events in OTLP/JSON. */\nexport async function exportRunAsOtlp(\n store: TraceStore,\n runId: string,\n resourceAttrs: Record<string, string | number | boolean> = {},\n): Promise<OtlpExport> {\n const run = await store.getRun(runId)\n if (!run) throw new Error(`run ${runId} not found`)\n const spans = await store.spans({ runId })\n const events = await store.events({ runId })\n const eventsBySpan = new Map<string, TraceEvent[]>()\n for (const e of events) {\n if (!e.spanId) continue\n const arr = eventsBySpan.get(e.spanId) ?? []\n arr.push(e)\n eventsBySpan.set(e.spanId, arr)\n }\n const traceId = runToTraceId(run)\n const otlpSpans: OtlpSpan[] = spans.map((s) =>\n spanToOtlp(s, traceId, eventsBySpan.get(s.spanId) ?? []),\n )\n return {\n resourceSpans: [\n {\n resource: {\n attributes: toAttributes({\n 'service.name': 'agent-eval',\n 'run.id': run.runId,\n 'run.scenario_id': run.scenarioId,\n 'run.variant_id': run.variantId ?? '',\n 'run.dataset_version': run.datasetVersion ?? '',\n 'run.code_sha': run.codeSha ?? '',\n 'run.model_fingerprint': run.modelFingerprint ?? '',\n ...resourceAttrs,\n }),\n },\n scopeSpans: [{ scope: OTEL_AGENT_EVAL_SCOPE, spans: otlpSpans }],\n },\n ],\n }\n}\n\nfunction spanToOtlp(span: Span, traceId: string, events: TraceEvent[]): OtlpSpan {\n const endedAt = span.endedAt ?? span.startedAt\n return {\n traceId,\n spanId: padSpanId(span.spanId),\n parentSpanId: span.parentSpanId ? padSpanId(span.parentSpanId) : undefined,\n name: span.name,\n kind: 1, // SPAN_KIND_INTERNAL\n startTimeUnixNano: msToNs(span.startedAt),\n endTimeUnixNano: msToNs(endedAt),\n attributes: toAttributes(flattenSpanAttributes(span)),\n events: events.map((e) => ({\n timeUnixNano: msToNs(e.timestamp),\n name: e.kind,\n attributes: toAttributes(flattenPayload(e.payload)),\n })),\n status: span.status === 'error' ? { code: 2, message: span.error } : { code: 1 },\n }\n}\n\nfunction flattenSpanAttributes(span: Span): Record<string, string | number | boolean> {\n const base: Record<string, string | number | boolean> = {\n 'span.kind': span.kind,\n }\n if (span.kind === 'llm') {\n base['llm.model'] = span.model\n if (span.inputTokens !== undefined) base['llm.input_tokens'] = span.inputTokens\n if (span.outputTokens !== undefined) base['llm.output_tokens'] = span.outputTokens\n if (span.costUsd !== undefined) base['llm.cost_usd'] = span.costUsd\n if (span.finishReason) base['llm.finish_reason'] = span.finishReason\n } else if (span.kind === 'tool') {\n base['tool.name'] = span.toolName\n if (span.latencyMs !== undefined) base['tool.latency_ms'] = span.latencyMs\n } else if (span.kind === 'retrieval') {\n base['retrieval.query'] = span.query\n base['retrieval.hits'] = span.hits.length\n } else if (span.kind === 'judge') {\n base['judge.id'] = span.judgeId\n base['judge.dimension'] = span.dimension\n base['judge.score'] = span.score\n base['judge.target_span_id'] = span.targetSpanId\n } else if (span.kind === 'sandbox') {\n if (span.image) base['sandbox.image'] = span.image\n if (span.exitCode !== undefined) base['sandbox.exit_code'] = span.exitCode\n if (span.testsPassed !== undefined) base['sandbox.tests_passed'] = span.testsPassed\n if (span.testsTotal !== undefined) base['sandbox.tests_total'] = span.testsTotal\n }\n if (span.attributes) {\n for (const [k, v] of Object.entries(span.attributes)) {\n if (typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean') base[k] = v\n }\n }\n return base\n}\n\nfunction flattenPayload(\n payload: Record<string, unknown>,\n): Record<string, string | number | boolean> {\n const out: Record<string, string | number | boolean> = {}\n for (const [k, v] of Object.entries(payload)) {\n if (typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean') out[k] = v\n else out[k] = JSON.stringify(v)\n }\n return out\n}\n\nfunction toAttributes(record: Record<string, string | number | boolean>): OtlpSpan['attributes'] {\n return Object.entries(record).map(([key, value]) => ({\n key,\n value:\n typeof value === 'number'\n ? Number.isInteger(value)\n ? { intValue: value.toString() }\n : { doubleValue: value }\n : typeof value === 'boolean'\n ? { boolValue: value }\n : { stringValue: value },\n }))\n}\n\nfunction msToNs(ms: number): string {\n return (BigInt(Math.floor(ms)) * 1_000_000n).toString()\n}\n\nfunction padSpanId(id: string): string {\n // OTLP wants 16-hex spanIds. UUIDs are 32-hex; strip dashes and take first 16.\n const cleaned = id.replace(/-/g, '')\n return cleaned.slice(0, 16).padEnd(16, '0')\n}\n\nfunction runToTraceId(run: Run): string {\n // OTLP wants 32-hex traceIds. Use runId directly when it's 32-hex already,\n // else SHA-ish truncate.\n const cleaned = run.runId.replace(/-/g, '')\n return cleaned.slice(0, 32).padEnd(32, '0')\n}\n","/**\n * Redaction — remove PII / secrets from trace payloads before persist.\n *\n * Pre-persistence rules mean raw traces in storage are already scrubbed.\n * Unredacted variants (for debugging / post-mortems) live in a separate\n * storage layer with stricter access controls; this module only covers\n * the default scrub-then-persist path.\n *\n * Rules compose: pass an array of `RedactionRule`, each is applied in\n * order. Strings that match get replaced with a tagged sentinel so the\n * eval framework can count how many redactions happened per run\n * (surfaced via `redaction_applied` events).\n */\n\nexport interface RedactionRule {\n id: string\n pattern: RegExp\n /** Replacement — e.g. '[PII:email]'. Defaults to `[redacted:{id}]`. */\n replacement?: string\n}\n\nexport interface RedactionReport {\n redactionCount: number\n byRule: Record<string, number>\n}\n\n/** OWASP / common-sense defaults — extend per-domain. */\nexport const DEFAULT_REDACTION_RULES: RedactionRule[] = [\n { id: 'email', pattern: /\\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}\\b/gi },\n { id: 'ssn', pattern: /\\b\\d{3}-\\d{2}-\\d{4}\\b/g },\n { id: 'credit-card', pattern: /\\b(?:\\d[ -]*?){13,16}\\b/g },\n { id: 'phone-us', pattern: /\\b(?:\\+?1[-.\\s]?)?\\(?\\d{3}\\)?[-.\\s]?\\d{3}[-.\\s]?\\d{4}\\b/g },\n { id: 'ipv4', pattern: /\\b(?:\\d{1,3}\\.){3}\\d{1,3}\\b/g },\n { id: 'aws-access-key', pattern: /\\bAKIA[0-9A-Z]{16}\\b/g },\n { id: 'bearer', pattern: /\\bBearer\\s+[A-Za-z0-9._~+/=-]{10,}/gi },\n { id: 'sk-key', pattern: /\\bsk-[A-Za-z0-9_-]{10,}\\b/g },\n {\n id: 'private-key-block',\n pattern: /-----BEGIN (?:RSA |EC |OPENSSH |DSA )?PRIVATE KEY-----[\\s\\S]*?-----END[^-]*-----/g,\n },\n]\n\nexport const REDACTION_VERSION = '1.0.0'\n\n/**\n * Redact a single string. Returns the new string and a per-rule count of\n * how many substitutions fired.\n */\nexport function redactString(\n input: string,\n rules: RedactionRule[] = DEFAULT_REDACTION_RULES,\n): { output: string; report: RedactionReport } {\n const byRule: Record<string, number> = {}\n let redactionCount = 0\n let output = input\n for (const rule of rules) {\n let hits = 0\n output = output.replace(rule.pattern, () => {\n hits++\n return rule.replacement ?? `[redacted:${rule.id}]`\n })\n if (hits > 0) {\n byRule[rule.id] = hits\n redactionCount += hits\n }\n }\n return { output, report: { redactionCount, byRule } }\n}\n\n/**\n * Walk a JSON-ish value applying `redactString` to every string leaf.\n * Arrays and plain objects are recursed; other types pass through\n * untouched. Circular references throw — traces should be tree-shaped.\n */\nexport function redactValue(\n value: unknown,\n rules: RedactionRule[] = DEFAULT_REDACTION_RULES,\n report: RedactionReport = { redactionCount: 0, byRule: {} },\n): { value: unknown; report: RedactionReport } {\n if (typeof value === 'string') {\n const { output, report: r } = redactString(value, rules)\n report.redactionCount += r.redactionCount\n for (const [k, v] of Object.entries(r.byRule)) {\n report.byRule[k] = (report.byRule[k] ?? 0) + v\n }\n return { value: output, report }\n }\n if (Array.isArray(value)) {\n return {\n value: value.map((v) => redactValue(v, rules, report).value),\n report,\n }\n }\n if (value !== null && typeof value === 'object') {\n const next: Record<string, unknown> = {}\n for (const [k, v] of Object.entries(value)) {\n next[k] = redactValue(v, rules, report).value\n }\n return { value: next, report }\n }\n return { value, report }\n}\n","/**\n * Replay-from-raw-events — turn every captured campaign run into a\n * re-runnable artifact.\n *\n * The premise: 0.21 made `RawProviderSink` capture every provider HTTP\n * envelope. 0.22's `runEvalCampaign` makes capture the default. Together\n * they mean every past run is a complete fingerprint of what happened on\n * the wire — and that fingerprint is enough to replay the run without\n * burning new LLM cost.\n *\n * Three use cases this primitive enables:\n *\n * 1. **Post-hoc judging** — apply a new judge / rubric / scoring callback\n * to last week's runs without re-calling any LLM. The cost of trying\n * a new rubric drops from \"another full sweep\" to a CPU-bound replay.\n * 2. **Determinism audits** — replay the same campaign and verify the\n * raw responses match byte-for-byte. Any drift is a non-determinism\n * bug (in the harness, the prompt builder, the sandbox, …).\n * 3. **Free judge calibration** — run two judges on identical responses\n * and measure inter-judge agreement without doubling LLM spend.\n *\n * The interface is deliberately fetch-shaped. Inject `createReplayFetch`\n * into `LlmClientOptions.fetch` and every `callLlm` transparently reads\n * from the cache instead of calling the network. No new code path through\n * the LLM client is needed; the cache hit is invisible to the runner.\n */\n\nimport { ReplayError } from './errors'\nimport { canonicalize, hashJson } from './pre-registration'\nimport type { RawProviderEvent, RawProviderSink } from './trace/raw-provider-sink'\n\nexport class ReplayCacheMissError extends ReplayError {\n constructor(\n public readonly url: string,\n public readonly requestKey: string,\n message?: string,\n ) {\n super(message ?? `replay cache miss for ${url} (key=${requestKey})`)\n }\n}\n\nexport interface ReplayCacheEntry {\n request: RawProviderEvent\n response: RawProviderEvent\n}\n\nexport interface ReplayCacheStats {\n total: number\n byProvider: Record<string, number>\n byModel: Record<string, number>\n /** Spans for which we have a request but no response (run aborted mid-call). */\n orphanRequests: number\n}\n\n/**\n * In-memory deterministic cache of (request → response) keyed on a stable\n * hash of the request body. Built from a `RawProviderSink` containing\n * paired `request` and `response` events from a previous run.\n *\n * The cache is the source of truth for replay; `createReplayFetch` is a\n * thin wrapper that reads from it.\n */\nexport class ReplayCache {\n private byKey = new Map<string, ReplayCacheEntry>()\n private orphans = 0\n private byProvider: Record<string, number> = {}\n private byModel: Record<string, number> = {}\n\n /**\n * Build a cache from a sink's events. The sink must implement `list()`.\n * Filter by `runId` / `spanId` to scope to a specific replay.\n */\n static async fromSink(\n sink: RawProviderSink,\n filter: { runId?: string; spanId?: string } = {},\n ): Promise<ReplayCache> {\n if (!sink.list) {\n throw new ReplayError('ReplayCache.fromSink: sink must implement list() to be replayable.')\n }\n const events = await sink.list(filter)\n return ReplayCache.fromEvents(events)\n }\n\n /** Build a cache from an in-memory event list. */\n static async fromEvents(events: RawProviderEvent[]): Promise<ReplayCache> {\n const cache = new ReplayCache()\n // Group by (runId, spanId, attemptIndex) so request/response/error pairs are matched.\n // A cell can have many spans; each retry attempt within a span is a separate group.\n type GroupKey = string\n const groups = new Map<GroupKey, { req?: RawProviderEvent; res?: RawProviderEvent }>()\n for (const e of events) {\n const k = `${e.runId ?? ''}::${e.spanId ?? ''}::${e.attemptIndex}`\n const g = groups.get(k) ?? {}\n if (e.direction === 'request') g.req = e\n else g.res = e\n groups.set(k, g)\n }\n for (const g of groups.values()) {\n if (!g.req) continue\n if (!g.res) {\n cache.orphans += 1\n continue\n }\n const key = await requestKey(g.req)\n cache.byKey.set(key, { request: g.req, response: g.res })\n cache.byProvider[g.req.provider] = (cache.byProvider[g.req.provider] ?? 0) + 1\n cache.byModel[g.req.model] = (cache.byModel[g.req.model] ?? 0) + 1\n }\n return cache\n }\n\n /** Number of cacheable (request, response) pairs in the cache. */\n size(): number {\n return this.byKey.size\n }\n\n stats(): ReplayCacheStats {\n return {\n total: this.byKey.size,\n byProvider: { ...this.byProvider },\n byModel: { ...this.byModel },\n orphanRequests: this.orphans,\n }\n }\n\n /** Iterate every cached `(request, response)` pair in insertion order. */\n *entries(): IterableIterator<ReplayCacheEntry> {\n for (const entry of this.byKey.values()) yield entry\n }\n\n /**\n * Look up a cached response by hashing the (model, messages, temperature,\n * maxTokens, response_format) shape. Returns `undefined` on miss; the\n * caller decides whether to throw, fall back to the network, or skip.\n */\n async lookup(requestBody: unknown): Promise<ReplayCacheEntry | undefined> {\n const key = await keyFromBody(requestBody)\n return this.byKey.get(key)\n }\n}\n\nexport interface ReplayFetchOptions {\n /**\n * Behaviour on cache miss. Default `'throw'`. `'fallback'` calls the\n * `fallbackFetch` (typically `globalThis.fetch`) so a partial replay can\n * still complete; `'fail-closed'` returns a synthetic 599 response so the\n * call site sees a non-retriable failure.\n */\n onMiss?: 'throw' | 'fallback' | 'fail-closed'\n fallbackFetch?: typeof fetch\n /** Optional callback fired once per replayed call (for telemetry / counters). */\n onHit?: (info: { url: string; provider: string; model: string }) => void\n /** Optional callback fired on cache miss before the `onMiss` policy applies. */\n onMissNotify?: (info: { url: string; requestBody: unknown }) => void\n}\n\n/**\n * Build a `fetch`-shaped function that serves cached responses out of a\n * `ReplayCache` for any URL ending in `/chat/completions`. Pass through\n * `LlmClientOptions.fetch` and `callLlm` becomes free.\n *\n * Non-`/chat/completions` URLs are passed straight to the fallback fetch\n * (default: `globalThis.fetch`). This matters because non-LLM HTTP work\n * (judge HTTP servers, sandbox callbacks) sometimes flows through the same\n * `fetch` and shouldn't be intercepted.\n */\nexport function createReplayFetch(cache: ReplayCache, opts: ReplayFetchOptions = {}): typeof fetch {\n const onMiss = opts.onMiss ?? 'throw'\n const fallback = opts.fallbackFetch ?? globalThis.fetch?.bind(globalThis)\n\n return (async (input: RequestInfo | URL, init?: RequestInit) => {\n const url =\n typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url\n if (!/\\/chat\\/completions(?:[?#].*)?$/.test(url)) {\n if (!fallback)\n throw new ReplayError(\n `replay fetch: non-completions URL ${url} but no fallbackFetch configured`,\n )\n return fallback(input as RequestInfo, init)\n }\n let bodyParsed: unknown\n if (init?.body && typeof init.body === 'string') {\n try {\n bodyParsed = JSON.parse(init.body)\n } catch {\n /* raw body, not JSON */\n }\n }\n const hit = bodyParsed === undefined ? undefined : await cache.lookup(bodyParsed)\n if (hit) {\n opts.onHit?.({ url, provider: hit.request.provider, model: hit.request.model })\n const status = hit.response.statusCode ?? 200\n const headers = new Headers(\n Object.entries(hit.response.responseHeaders ?? { 'Content-Type': 'application/json' }),\n )\n const bodyText =\n typeof hit.response.responseBody === 'string'\n ? hit.response.responseBody\n : JSON.stringify(hit.response.responseBody ?? {})\n return new Response(bodyText, { status, headers })\n }\n opts.onMissNotify?.({ url, requestBody: bodyParsed })\n if (onMiss === 'throw') {\n const key = bodyParsed === undefined ? '<unparseable>' : await keyFromBody(bodyParsed)\n throw new ReplayCacheMissError(url, key)\n }\n if (onMiss === 'fail-closed') {\n return new Response(JSON.stringify({ error: 'replay_cache_miss' }), { status: 599 })\n }\n if (!fallback)\n throw new ReplayError('replay fetch: onMiss=fallback but no fallbackFetch configured')\n return fallback(input as RequestInfo, init)\n }) as typeof fetch\n}\n\n/**\n * Convenience iterator over `(request, response)` pairs in a sink — for\n * post-hoc scoring that doesn't need a `fetch` shim. The judge or scorer\n * runs purely in-process over cached LLM outputs.\n */\nexport async function* iterateRawCalls(\n sink: RawProviderSink,\n filter: { runId?: string; spanId?: string } = {},\n): AsyncGenerator<ReplayCacheEntry> {\n if (!sink.list) {\n throw new ReplayError('iterateRawCalls: sink must implement list().')\n }\n const events = await sink.list(filter)\n const cache = await ReplayCache.fromEvents(events)\n for (const entry of cache.entries()) yield entry\n}\n\n// ── Hashing ──────────────────────────────────────────────────────────────\n\n/**\n * Canonical request key.\n *\n * `model + messages + temperature + max_tokens|max_completion_tokens +\n * response_format` are the dimensions that affect the response shape.\n * Other fields (timestamp headers, provider-specific metadata) are\n * intentionally excluded so a request hashes the same across re-runs.\n */\nasync function requestKey(event: RawProviderEvent): Promise<string> {\n return keyFromBody(event.requestBody)\n}\n\nasync function keyFromBody(body: unknown): Promise<string> {\n if (body == null || typeof body !== 'object') return hashJson({ raw: String(body) })\n const b = body as Record<string, unknown>\n const reduced = canonicalize({\n model: b.model ?? null,\n messages: b.messages ?? null,\n temperature: b.temperature ?? null,\n max_tokens: b.max_tokens ?? null,\n max_completion_tokens: b.max_completion_tokens ?? null,\n response_format: b.response_format ?? null,\n })\n return hashJson(reduced)\n}\n"],"mappings":";;;;;;;;;AA0EO,IAAM,qBAAN,MAA+C;AAAA,EAC5C,OAAO,oBAAI,IAAiB;AAAA,EAC5B,WAAmB,CAAC;AAAA,EACpB,YAA0B,CAAC;AAAA,EAC3B,eAA2B,CAAC;AAAA,EAC5B,YAAiC,CAAC;AAAA,EAE1C,MAAM,UAAU,KAAyB;AACvC,QAAI,KAAK,KAAK,IAAI,IAAI,KAAK,EAAG,OAAM,IAAI,MAAM,OAAO,IAAI,KAAK,iBAAiB;AAC/E,SAAK,KAAK,IAAI,IAAI,OAAO,EAAE,GAAG,IAAI,CAAC;AAAA,EACrC;AAAA,EAEA,MAAM,UAAU,OAAe,OAAoC;AACjE,UAAM,WAAW,KAAK,KAAK,IAAI,KAAK;AACpC,QAAI,CAAC,SAAU,OAAM,IAAI,MAAM,OAAO,KAAK,YAAY;AACvD,SAAK,KAAK,IAAI,OAAO,EAAE,GAAG,UAAU,GAAG,MAAM,CAAC;AAAA,EAChD;AAAA,EAEA,MAAM,WAAW,MAA2B;AAC1C,SAAK,SAAS,KAAK,EAAE,GAAG,KAAK,CAAC;AAAA,EAChC;AAAA,EAEA,MAAM,WAAW,QAAgB,OAAqC;AACpE,UAAM,MAAM,KAAK,SAAS,UAAU,CAAC,MAAM,EAAE,WAAW,MAAM;AAC9D,QAAI,MAAM,EAAG,OAAM,IAAI,MAAM,QAAQ,MAAM,YAAY;AACvD,SAAK,SAAS,GAAG,IAAI,EAAE,GAAG,KAAK,SAAS,GAAG,GAAG,GAAG,MAAM;AAAA,EACzD;AAAA,EAEA,MAAM,YAAY,OAAkC;AAClD,SAAK,UAAU,KAAK,EAAE,GAAG,MAAM,CAAC;AAAA,EAClC;AAAA,EAEA,MAAM,eAAe,UAAmC;AACtD,SAAK,aAAa,KAAK,EAAE,GAAG,SAAS,CAAC;AAAA,EACxC;AAAA,EAEA,MAAM,kBAAkB,OAAyC;AAC/D,SAAK,UAAU,KAAK,EAAE,GAAG,MAAM,CAAC;AAAA,EAClC;AAAA,EAEA,MAAM,OAAO,OAAyC;AACpD,UAAM,IAAI,KAAK,KAAK,IAAI,KAAK;AAC7B,WAAO,IAAI,EAAE,GAAG,EAAE,IAAI;AAAA,EACxB;AAAA,EAEA,MAAM,SAAS,SAAoB,CAAC,GAAmB;AACrD,WAAO,CAAC,GAAG,KAAK,KAAK,OAAO,CAAC,EAAE,OAAO,CAAC,MAAM,WAAW,GAAG,MAAM,CAAC;AAAA,EACpE;AAAA,EAEA,MAAM,MAAM,SAAqB,CAAC,GAAoB;AACpD,WAAO,KAAK,SAAS,OAAO,CAAC,MAAM,YAAY,GAAG,MAAM,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,EAAE;AAAA,EAClF;AAAA,EAEA,MAAM,OAAO,SAAsB,CAAC,GAA0B;AAC5D,WAAO,KAAK,UAAU,OAAO,CAAC,MAAM,aAAa,GAAG,MAAM,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,EAAE;AAAA,EACpF;AAAA,EAEA,MAAM,OAAO,OAA6C;AACxD,WAAO,KAAK,UAAU,OAAO,CAAC,MAAM,EAAE,UAAU,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,EAAE;AAAA,EAC9E;AAAA,EAEA,MAAM,UAAU,OAAoC;AAClD,WAAO,KAAK,aAAa,OAAO,CAAC,MAAM,EAAE,UAAU,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,EAAE;AAAA,EACjF;AACF;AAEA,SAAS,WAAW,GAAQ,GAAuB;AACjD,MAAI,EAAE,cAAc,EAAE,eAAe,EAAE,WAAY,QAAO;AAC1D,MAAI,EAAE,aAAa,EAAE,cAAc,EAAE,UAAW,QAAO;AACvD,MAAI,EAAE,UAAU,EAAE,WAAW,EAAE,OAAQ,QAAO;AAC9C,MAAI,EAAE,UAAU,UAAa,EAAE,YAAY,EAAE,MAAO,QAAO;AAC3D,MAAI,EAAE,UAAU,UAAa,EAAE,YAAY,EAAE,MAAO,QAAO;AAC3D,MAAI,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,GAAG,MAAM,EAAE,IAAI,MAAO,QAAO;AACzD,MAAI,EAAE,eAAe,EAAE,gBAAgB,EAAE,YAAa,QAAO;AAC7D,MAAI,EAAE,aAAa,EAAE,cAAc,EAAE,UAAW,QAAO;AACvD,MAAI,EAAE,UAAU,EAAE,WAAW,EAAE,OAAQ,QAAO;AAC9C,MAAI,EAAE,SAAS,EAAE,UAAU,EAAE,MAAO,QAAO;AAC3C,SAAO;AACT;AAEA,SAAS,YAAY,GAAS,GAAwB;AACpD,MAAI,EAAE,SAAS,EAAE,UAAU,EAAE,MAAO,QAAO;AAC3C,MAAI,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,aAAc,QAAO;AAChE,MAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAM,QAAO;AACxC,MAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAM,QAAO;AACxC,MAAI,EAAE,aAAa,EAAE,SAAS,UAAU,EAAE,aAAa,EAAE,UAAW,QAAO;AAC3E,MAAI,EAAE,YAAY,EAAE,SAAS,WAAW,EAAE,YAAY,EAAE,SAAU,QAAO;AACzE,MAAI,EAAE,UAAU,UAAa,EAAE,YAAY,EAAE,MAAO,QAAO;AAC3D,MAAI,EAAE,UAAU,UAAa,EAAE,YAAY,EAAE,MAAO,QAAO;AAC3D,SAAO;AACT;AAEA,SAAS,aAAa,GAAe,GAAyB;AAC5D,MAAI,EAAE,SAAS,EAAE,UAAU,EAAE,MAAO,QAAO;AAC3C,MAAI,EAAE,UAAU,EAAE,WAAW,EAAE,OAAQ,QAAO;AAC9C,MAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAM,QAAO;AACxC,MAAI,EAAE,UAAU,UAAa,EAAE,YAAY,EAAE,MAAO,QAAO;AAC3D,MAAI,EAAE,UAAU,UAAa,EAAE,YAAY,EAAE,MAAO,QAAO;AAC3D,SAAO;AACT;AAUO,IAAM,uBAAN,MAAiD;AAAA,EAC9C;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA,SAAS;AAAA,EAEjB,YAAY,SAAsC;AAChD,SAAK,MAAM,QAAQ;AACnB,SAAK,WAAW,QAAQ,YAAY,KAAK,OAAO;AAAA,EAClD;AAAA,EAEA,MAAc,YAA2B;AACvC,UAAM,KAAK,MAAM,OAAO,aAAkB;AAC1C,UAAM,GAAG,MAAM,KAAK,KAAK,EAAE,WAAW,KAAK,CAAC;AAAA,EAC9C;AAAA,EAEA,MAAc,OAAO,MAAc,QAAgC;AACjE,UAAM,KAAK,UAAU;AACrB,UAAM,KAAK,MAAM,OAAO,aAAkB;AAC1C,UAAM,OAAO,MAAM,OAAO,MAAW;AACrC,UAAM,SAAS,KAAK,KAAK,KAAK,KAAK,GAAG,IAAI,SAAS;AACnD,QAAI;AACF,YAAM,OAAO,MAAM,GAAG,KAAK,MAAM;AACjC,UAAI,KAAK,QAAQ,KAAK,UAAU;AAC9B,cAAM,SAAS,KAAK,KAAK,KAAK,KAAK,GAAG,IAAI,IAAI,KAAK,IAAI,CAAC,SAAS;AACjE,cAAM,GAAG,OAAO,QAAQ,MAAM;AAAA,MAChC;AAAA,IACF,QAAQ;AAAA,IAER;AACA,UAAM,GAAG,WAAW,QAAQ,GAAG,KAAK,UAAU,MAAM,CAAC;AAAA,GAAM,MAAM;AAOjE,QAAI,KAAK,SAAS,CAAE,QAAkC,SAAS;AAC7D,WAAK,KAAK,WAAW,MAAM,MAAM;AAAA,IACnC;AAAA,EACF;AAAA,EAEA,MAAc,WAAW,MAAc,QAAgC;AACrE,QAAI,CAAC,KAAK,MAAO;AACjB,YAAQ,MAAM;AAAA,MACZ,KAAK;AACH,cAAM,KAAK,MAAM,UAAU,MAAa;AACxC;AAAA,MACF,KAAK;AACH,cAAM,KAAK,MAAM,WAAW,MAAc;AAC1C;AAAA,MACF,KAAK;AACH,cAAM,KAAK,MAAM,YAAY,MAAoB;AACjD;AAAA,MACF,KAAK;AACH,cAAM,KAAK,MAAM,eAAe,MAAkB;AAClD;AAAA,MACF,KAAK;AACH,cAAM,KAAK,MAAM,kBAAkB,MAA2B;AAC9D;AAAA,IACJ;AAAA,EACF;AAAA,EAEA,MAAc,OAAoC;AAChD,QAAI,KAAK,UAAU,KAAK,MAAO,QAAO,KAAK;AAC3C,UAAM,KAAK,MAAM,OAAO,aAAkB;AAC1C,UAAM,OAAO,MAAM,OAAO,MAAW;AACrC,UAAM,QAAQ,IAAI,mBAAmB;AACrC,QAAI;AACF,YAAM,UAAU,MAAM,GAAG,QAAQ,KAAK,GAAG;AACzC,iBAAW,QAAQ,SAAS;AAC1B,YAAI,CAAC,KAAK,SAAS,SAAS,EAAG;AAC/B,cAAM,OAAO,KAAK,KAAK,KAAK,KAAK,IAAI;AACrC,cAAM,UAAU,MAAM,GAAG,SAAS,MAAM,MAAM;AAC9C,cAAM,OAAO,KAAK,MAAM,GAAG,EAAE,CAAC;AAC9B,mBAAW,QAAQ,QAAQ,MAAM,IAAI,GAAG;AACtC,cAAI,CAAC,KAAK,KAAK,EAAG;AAClB,gBAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,cAAI,SAAS,QAAQ;AAEnB,gBAAI;AACF,oBAAM,MAAM,UAAU,MAAM;AAAA,YAC9B,QAAQ;AACN,oBAAM,MAAM,UAAU,OAAO,OAAO,MAAM;AAAA,YAC5C;AAAA,UACF,WAAW,SAAS,SAAS;AAQ3B,gBAAI,QAAQ,SAAS;AACnB,kBAAI;AACF,sBAAM,MAAM,WAAW,OAAO,QAAQ,MAAM;AAAA,cAC9C,QAAQ;AAIN,sBAAM,MAAM,WAAW,MAAM;AAAA,cAC/B;AAAA,YACF,OAAO;AACL,oBAAM,MAAM,WAAW,MAAM;AAAA,YAC/B;AAAA,UACF,WAAW,SAAS,UAAU;AAC5B,kBAAM,MAAM,YAAY,MAAM;AAAA,UAChC,WAAW,SAAS,aAAa;AAC/B,kBAAM,MAAM,eAAe,MAAM;AAAA,UACnC,WAAW,SAAS,UAAU;AAC5B,kBAAM,MAAM,kBAAkB,MAAM;AAAA,UACtC;AAAA,QACF;AAAA,MACF;AAAA,IACF,QAAQ;AAAA,IAER;AACA,SAAK,QAAQ;AACb,SAAK,SAAS;AACd,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,UAAU,KAAyB;AACvC,UAAM,KAAK,OAAO,QAAQ,GAAG;AAAA,EAC/B;AAAA,EACA,MAAM,UAAU,OAAe,OAAoC;AAGjE,UAAM,KAAK,OAAO,QAAQ,EAAE,OAAO,GAAG,OAAO,SAAS,KAAK,CAAC;AAC5D,QAAI,KAAK,MAAO,OAAM,KAAK,MAAM,UAAU,OAAO,KAAK;AAAA,EACzD;AAAA,EACA,MAAM,WAAW,MAA2B;AAC1C,UAAM,KAAK,OAAO,SAAS,IAAI;AAAA,EACjC;AAAA,EACA,MAAM,WAAW,QAAgB,OAAqC;AACpE,UAAM,KAAK,OAAO,SAAS,EAAE,QAAQ,GAAG,OAAO,SAAS,KAAK,CAAC;AAC9D,QAAI,KAAK,MAAO,OAAM,KAAK,MAAM,WAAW,QAAQ,KAAK;AAAA,EAC3D;AAAA,EACA,MAAM,YAAY,OAAkC;AAClD,UAAM,KAAK,OAAO,UAAU,KAAK;AAAA,EACnC;AAAA,EACA,MAAM,eAAe,UAAmC;AACtD,UAAM,KAAK,OAAO,aAAa,QAAQ;AAAA,EACzC;AAAA,EACA,MAAM,kBAAkB,OAAyC;AAC/D,UAAM,KAAK,OAAO,UAAU,KAAK;AAAA,EACnC;AAAA,EAEA,MAAM,OAAO,OAAyC;AACpD,YAAQ,MAAM,KAAK,KAAK,GAAG,OAAO,KAAK;AAAA,EACzC;AAAA,EACA,MAAM,SAAS,QAAoC;AACjD,YAAQ,MAAM,KAAK,KAAK,GAAG,SAAS,MAAM;AAAA,EAC5C;AAAA,EACA,MAAM,MAAM,QAAsC;AAChD,YAAQ,MAAM,KAAK,KAAK,GAAG,MAAM,MAAM;AAAA,EACzC;AAAA,EACA,MAAM,OAAO,QAA6C;AACxD,YAAQ,MAAM,KAAK,KAAK,GAAG,OAAO,MAAM;AAAA,EAC1C;AAAA,EACA,MAAM,OAAO,OAA6C;AACxD,YAAQ,MAAM,KAAK,KAAK,GAAG,OAAO,KAAK;AAAA,EACzC;AAAA,EACA,MAAM,UAAU,OAAoC;AAClD,YAAQ,MAAM,KAAK,KAAK,GAAG,UAAU,KAAK;AAAA,EAC5C;AACF;;;AChVO,IAAM,wBAAwB,EAAE,MAAM,8BAA8B,SAAS,QAAQ;AA4B5F,eAAsB,gBACpB,OACA,OACA,gBAA2D,CAAC,GACvC;AACrB,QAAM,MAAM,MAAM,MAAM,OAAO,KAAK;AACpC,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,OAAO,KAAK,YAAY;AAClD,QAAM,QAAQ,MAAM,MAAM,MAAM,EAAE,MAAM,CAAC;AACzC,QAAM,SAAS,MAAM,MAAM,OAAO,EAAE,MAAM,CAAC;AAC3C,QAAM,eAAe,oBAAI,IAA0B;AACnD,aAAW,KAAK,QAAQ;AACtB,QAAI,CAAC,EAAE,OAAQ;AACf,UAAM,MAAM,aAAa,IAAI,EAAE,MAAM,KAAK,CAAC;AAC3C,QAAI,KAAK,CAAC;AACV,iBAAa,IAAI,EAAE,QAAQ,GAAG;AAAA,EAChC;AACA,QAAM,UAAU,aAAa,GAAG;AAChC,QAAM,YAAwB,MAAM;AAAA,IAAI,CAAC,MACvC,WAAW,GAAG,SAAS,aAAa,IAAI,EAAE,MAAM,KAAK,CAAC,CAAC;AAAA,EACzD;AACA,SAAO;AAAA,IACL,eAAe;AAAA,MACb;AAAA,QACE,UAAU;AAAA,UACR,YAAY,aAAa;AAAA,YACvB,gBAAgB;AAAA,YAChB,UAAU,IAAI;AAAA,YACd,mBAAmB,IAAI;AAAA,YACvB,kBAAkB,IAAI,aAAa;AAAA,YACnC,uBAAuB,IAAI,kBAAkB;AAAA,YAC7C,gBAAgB,IAAI,WAAW;AAAA,YAC/B,yBAAyB,IAAI,oBAAoB;AAAA,YACjD,GAAG;AAAA,UACL,CAAC;AAAA,QACH;AAAA,QACA,YAAY,CAAC,EAAE,OAAO,uBAAuB,OAAO,UAAU,CAAC;AAAA,MACjE;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,WAAW,MAAY,SAAiB,QAAgC;AAC/E,QAAM,UAAU,KAAK,WAAW,KAAK;AACrC,SAAO;AAAA,IACL;AAAA,IACA,QAAQ,UAAU,KAAK,MAAM;AAAA,IAC7B,cAAc,KAAK,eAAe,UAAU,KAAK,YAAY,IAAI;AAAA,IACjE,MAAM,KAAK;AAAA,IACX,MAAM;AAAA;AAAA,IACN,mBAAmB,OAAO,KAAK,SAAS;AAAA,IACxC,iBAAiB,OAAO,OAAO;AAAA,IAC/B,YAAY,aAAa,sBAAsB,IAAI,CAAC;AAAA,IACpD,QAAQ,OAAO,IAAI,CAAC,OAAO;AAAA,MACzB,cAAc,OAAO,EAAE,SAAS;AAAA,MAChC,MAAM,EAAE;AAAA,MACR,YAAY,aAAa,eAAe,EAAE,OAAO,CAAC;AAAA,IACpD,EAAE;AAAA,IACF,QAAQ,KAAK,WAAW,UAAU,EAAE,MAAM,GAAG,SAAS,KAAK,MAAM,IAAI,EAAE,MAAM,EAAE;AAAA,EACjF;AACF;AAEA,SAAS,sBAAsB,MAAuD;AACpF,QAAM,OAAkD;AAAA,IACtD,aAAa,KAAK;AAAA,EACpB;AACA,MAAI,KAAK,SAAS,OAAO;AACvB,SAAK,WAAW,IAAI,KAAK;AACzB,QAAI,KAAK,gBAAgB,OAAW,MAAK,kBAAkB,IAAI,KAAK;AACpE,QAAI,KAAK,iBAAiB,OAAW,MAAK,mBAAmB,IAAI,KAAK;AACtE,QAAI,KAAK,YAAY,OAAW,MAAK,cAAc,IAAI,KAAK;AAC5D,QAAI,KAAK,aAAc,MAAK,mBAAmB,IAAI,KAAK;AAAA,EAC1D,WAAW,KAAK,SAAS,QAAQ;AAC/B,SAAK,WAAW,IAAI,KAAK;AACzB,QAAI,KAAK,cAAc,OAAW,MAAK,iBAAiB,IAAI,KAAK;AAAA,EACnE,WAAW,KAAK,SAAS,aAAa;AACpC,SAAK,iBAAiB,IAAI,KAAK;AAC/B,SAAK,gBAAgB,IAAI,KAAK,KAAK;AAAA,EACrC,WAAW,KAAK,SAAS,SAAS;AAChC,SAAK,UAAU,IAAI,KAAK;AACxB,SAAK,iBAAiB,IAAI,KAAK;AAC/B,SAAK,aAAa,IAAI,KAAK;AAC3B,SAAK,sBAAsB,IAAI,KAAK;AAAA,EACtC,WAAW,KAAK,SAAS,WAAW;AAClC,QAAI,KAAK,MAAO,MAAK,eAAe,IAAI,KAAK;AAC7C,QAAI,KAAK,aAAa,OAAW,MAAK,mBAAmB,IAAI,KAAK;AAClE,QAAI,KAAK,gBAAgB,OAAW,MAAK,sBAAsB,IAAI,KAAK;AACxE,QAAI,KAAK,eAAe,OAAW,MAAK,qBAAqB,IAAI,KAAK;AAAA,EACxE;AACA,MAAI,KAAK,YAAY;AACnB,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,KAAK,UAAU,GAAG;AACpD,UAAI,OAAO,MAAM,YAAY,OAAO,MAAM,YAAY,OAAO,MAAM,UAAW,MAAK,CAAC,IAAI;AAAA,IAC1F;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,eACP,SAC2C;AAC3C,QAAM,MAAiD,CAAC;AACxD,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,OAAO,GAAG;AAC5C,QAAI,OAAO,MAAM,YAAY,OAAO,MAAM,YAAY,OAAO,MAAM,UAAW,KAAI,CAAC,IAAI;AAAA,QAClF,KAAI,CAAC,IAAI,KAAK,UAAU,CAAC;AAAA,EAChC;AACA,SAAO;AACT;AAEA,SAAS,aAAa,QAA2E;AAC/F,SAAO,OAAO,QAAQ,MAAM,EAAE,IAAI,CAAC,CAAC,KAAK,KAAK,OAAO;AAAA,IACnD;AAAA,IACA,OACE,OAAO,UAAU,WACb,OAAO,UAAU,KAAK,IACpB,EAAE,UAAU,MAAM,SAAS,EAAE,IAC7B,EAAE,aAAa,MAAM,IACvB,OAAO,UAAU,YACf,EAAE,WAAW,MAAM,IACnB,EAAE,aAAa,MAAM;AAAA,EAC/B,EAAE;AACJ;AAEA,SAAS,OAAO,IAAoB;AAClC,UAAQ,OAAO,KAAK,MAAM,EAAE,CAAC,IAAI,UAAY,SAAS;AACxD;AAEA,SAAS,UAAU,IAAoB;AAErC,QAAM,UAAU,GAAG,QAAQ,MAAM,EAAE;AACnC,SAAO,QAAQ,MAAM,GAAG,EAAE,EAAE,OAAO,IAAI,GAAG;AAC5C;AAEA,SAAS,aAAa,KAAkB;AAGtC,QAAM,UAAU,IAAI,MAAM,QAAQ,MAAM,EAAE;AAC1C,SAAO,QAAQ,MAAM,GAAG,EAAE,EAAE,OAAO,IAAI,GAAG;AAC5C;;;ACvJO,IAAM,0BAA2C;AAAA,EACtD,EAAE,IAAI,SAAS,SAAS,8CAA8C;AAAA,EACtE,EAAE,IAAI,OAAO,SAAS,yBAAyB;AAAA,EAC/C,EAAE,IAAI,eAAe,SAAS,2BAA2B;AAAA,EACzD,EAAE,IAAI,YAAY,SAAS,2DAA2D;AAAA,EACtF,EAAE,IAAI,QAAQ,SAAS,+BAA+B;AAAA,EACtD,EAAE,IAAI,kBAAkB,SAAS,wBAAwB;AAAA,EACzD,EAAE,IAAI,UAAU,SAAS,uCAAuC;AAAA,EAChE,EAAE,IAAI,UAAU,SAAS,6BAA6B;AAAA,EACtD;AAAA,IACE,IAAI;AAAA,IACJ,SAAS;AAAA,EACX;AACF;AAEO,IAAM,oBAAoB;AAM1B,SAAS,aACd,OACA,QAAyB,yBACoB;AAC7C,QAAM,SAAiC,CAAC;AACxC,MAAI,iBAAiB;AACrB,MAAI,SAAS;AACb,aAAW,QAAQ,OAAO;AACxB,QAAI,OAAO;AACX,aAAS,OAAO,QAAQ,KAAK,SAAS,MAAM;AAC1C;AACA,aAAO,KAAK,eAAe,aAAa,KAAK,EAAE;AAAA,IACjD,CAAC;AACD,QAAI,OAAO,GAAG;AACZ,aAAO,KAAK,EAAE,IAAI;AAClB,wBAAkB;AAAA,IACpB;AAAA,EACF;AACA,SAAO,EAAE,QAAQ,QAAQ,EAAE,gBAAgB,OAAO,EAAE;AACtD;AAOO,SAAS,YACd,OACA,QAAyB,yBACzB,SAA0B,EAAE,gBAAgB,GAAG,QAAQ,CAAC,EAAE,GACb;AAC7C,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAM,EAAE,QAAQ,QAAQ,EAAE,IAAI,aAAa,OAAO,KAAK;AACvD,WAAO,kBAAkB,EAAE;AAC3B,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,EAAE,MAAM,GAAG;AAC7C,aAAO,OAAO,CAAC,KAAK,OAAO,OAAO,CAAC,KAAK,KAAK;AAAA,IAC/C;AACA,WAAO,EAAE,OAAO,QAAQ,OAAO;AAAA,EACjC;AACA,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,WAAO;AAAA,MACL,OAAO,MAAM,IAAI,CAAC,MAAM,YAAY,GAAG,OAAO,MAAM,EAAE,KAAK;AAAA,MAC3D;AAAA,IACF;AAAA,EACF;AACA,MAAI,UAAU,QAAQ,OAAO,UAAU,UAAU;AAC/C,UAAM,OAAgC,CAAC;AACvC,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,KAAK,GAAG;AAC1C,WAAK,CAAC,IAAI,YAAY,GAAG,OAAO,MAAM,EAAE;AAAA,IAC1C;AACA,WAAO,EAAE,OAAO,MAAM,OAAO;AAAA,EAC/B;AACA,SAAO,EAAE,OAAO,OAAO;AACzB;;;ACtEO,IAAM,uBAAN,cAAmC,YAAY;AAAA,EACpD,YACkB,KACAA,aAChB,SACA;AACA,UAAM,WAAW,yBAAyB,GAAG,SAASA,WAAU,GAAG;AAJnD;AACA,sBAAAA;AAAA,EAIlB;AAAA,EALkB;AAAA,EACA;AAKpB;AAuBO,IAAM,cAAN,MAAM,aAAY;AAAA,EACf,QAAQ,oBAAI,IAA8B;AAAA,EAC1C,UAAU;AAAA,EACV,aAAqC,CAAC;AAAA,EACtC,UAAkC,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,EAM3C,aAAa,SACX,MACA,SAA8C,CAAC,GACzB;AACtB,QAAI,CAAC,KAAK,MAAM;AACd,YAAM,IAAI,YAAY,oEAAoE;AAAA,IAC5F;AACA,UAAM,SAAS,MAAM,KAAK,KAAK,MAAM;AACrC,WAAO,aAAY,WAAW,MAAM;AAAA,EACtC;AAAA;AAAA,EAGA,aAAa,WAAW,QAAkD;AACxE,UAAM,QAAQ,IAAI,aAAY;AAI9B,UAAM,SAAS,oBAAI,IAAkE;AACrF,eAAW,KAAK,QAAQ;AACtB,YAAM,IAAI,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,YAAY;AAChE,YAAM,IAAI,OAAO,IAAI,CAAC,KAAK,CAAC;AAC5B,UAAI,EAAE,cAAc,UAAW,GAAE,MAAM;AAAA,UAClC,GAAE,MAAM;AACb,aAAO,IAAI,GAAG,CAAC;AAAA,IACjB;AACA,eAAW,KAAK,OAAO,OAAO,GAAG;AAC/B,UAAI,CAAC,EAAE,IAAK;AACZ,UAAI,CAAC,EAAE,KAAK;AACV,cAAM,WAAW;AACjB;AAAA,MACF;AACA,YAAM,MAAM,MAAM,WAAW,EAAE,GAAG;AAClC,YAAM,MAAM,IAAI,KAAK,EAAE,SAAS,EAAE,KAAK,UAAU,EAAE,IAAI,CAAC;AACxD,YAAM,WAAW,EAAE,IAAI,QAAQ,KAAK,MAAM,WAAW,EAAE,IAAI,QAAQ,KAAK,KAAK;AAC7E,YAAM,QAAQ,EAAE,IAAI,KAAK,KAAK,MAAM,QAAQ,EAAE,IAAI,KAAK,KAAK,KAAK;AAAA,IACnE;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,OAAe;AACb,WAAO,KAAK,MAAM;AAAA,EACpB;AAAA,EAEA,QAA0B;AACxB,WAAO;AAAA,MACL,OAAO,KAAK,MAAM;AAAA,MAClB,YAAY,EAAE,GAAG,KAAK,WAAW;AAAA,MACjC,SAAS,EAAE,GAAG,KAAK,QAAQ;AAAA,MAC3B,gBAAgB,KAAK;AAAA,IACvB;AAAA,EACF;AAAA;AAAA,EAGA,CAAC,UAA8C;AAC7C,eAAW,SAAS,KAAK,MAAM,OAAO,EAAG,OAAM;AAAA,EACjD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,OAAO,aAA6D;AACxE,UAAM,MAAM,MAAM,YAAY,WAAW;AACzC,WAAO,KAAK,MAAM,IAAI,GAAG;AAAA,EAC3B;AACF;AA2BO,SAAS,kBAAkB,OAAoB,OAA2B,CAAC,GAAiB;AACjG,QAAM,SAAS,KAAK,UAAU;AAC9B,QAAM,WAAW,KAAK,iBAAiB,WAAW,OAAO,KAAK,UAAU;AAExE,UAAQ,OAAO,OAA0B,SAAuB;AAC9D,UAAM,MACJ,OAAO,UAAU,WAAW,QAAQ,iBAAiB,MAAM,MAAM,SAAS,IAAI,MAAM;AACtF,QAAI,CAAC,kCAAkC,KAAK,GAAG,GAAG;AAChD,UAAI,CAAC;AACH,cAAM,IAAI;AAAA,UACR,qCAAqC,GAAG;AAAA,QAC1C;AACF,aAAO,SAAS,OAAsB,IAAI;AAAA,IAC5C;AACA,QAAI;AACJ,QAAI,MAAM,QAAQ,OAAO,KAAK,SAAS,UAAU;AAC/C,UAAI;AACF,qBAAa,KAAK,MAAM,KAAK,IAAI;AAAA,MACnC,QAAQ;AAAA,MAER;AAAA,IACF;AACA,UAAM,MAAM,eAAe,SAAY,SAAY,MAAM,MAAM,OAAO,UAAU;AAChF,QAAI,KAAK;AACP,WAAK,QAAQ,EAAE,KAAK,UAAU,IAAI,QAAQ,UAAU,OAAO,IAAI,QAAQ,MAAM,CAAC;AAC9E,YAAM,SAAS,IAAI,SAAS,cAAc;AAC1C,YAAM,UAAU,IAAI;AAAA,QAClB,OAAO,QAAQ,IAAI,SAAS,mBAAmB,EAAE,gBAAgB,mBAAmB,CAAC;AAAA,MACvF;AACA,YAAM,WACJ,OAAO,IAAI,SAAS,iBAAiB,WACjC,IAAI,SAAS,eACb,KAAK,UAAU,IAAI,SAAS,gBAAgB,CAAC,CAAC;AACpD,aAAO,IAAI,SAAS,UAAU,EAAE,QAAQ,QAAQ,CAAC;AAAA,IACnD;AACA,SAAK,eAAe,EAAE,KAAK,aAAa,WAAW,CAAC;AACpD,QAAI,WAAW,SAAS;AACtB,YAAM,MAAM,eAAe,SAAY,kBAAkB,MAAM,YAAY,UAAU;AACrF,YAAM,IAAI,qBAAqB,KAAK,GAAG;AAAA,IACzC;AACA,QAAI,WAAW,eAAe;AAC5B,aAAO,IAAI,SAAS,KAAK,UAAU,EAAE,OAAO,oBAAoB,CAAC,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IACrF;AACA,QAAI,CAAC;AACH,YAAM,IAAI,YAAY,+DAA+D;AACvF,WAAO,SAAS,OAAsB,IAAI;AAAA,EAC5C;AACF;AAOA,gBAAuB,gBACrB,MACA,SAA8C,CAAC,GACb;AAClC,MAAI,CAAC,KAAK,MAAM;AACd,UAAM,IAAI,YAAY,8CAA8C;AAAA,EACtE;AACA,QAAM,SAAS,MAAM,KAAK,KAAK,MAAM;AACrC,QAAM,QAAQ,MAAM,YAAY,WAAW,MAAM;AACjD,aAAW,SAAS,MAAM,QAAQ,EAAG,OAAM;AAC7C;AAYA,eAAe,WAAW,OAA0C;AAClE,SAAO,YAAY,MAAM,WAAW;AACtC;AAEA,eAAe,YAAY,MAAgC;AACzD,MAAI,QAAQ,QAAQ,OAAO,SAAS,SAAU,QAAO,SAAS,EAAE,KAAK,OAAO,IAAI,EAAE,CAAC;AACnF,QAAM,IAAI;AACV,QAAM,UAAU,aAAa;AAAA,IAC3B,OAAO,EAAE,SAAS;AAAA,IAClB,UAAU,EAAE,YAAY;AAAA,IACxB,aAAa,EAAE,eAAe;AAAA,IAC9B,YAAY,EAAE,cAAc;AAAA,IAC5B,uBAAuB,EAAE,yBAAyB;AAAA,IAClD,iBAAiB,EAAE,mBAAmB;AAAA,EACxC,CAAC;AACD,SAAO,SAAS,OAAO;AACzB;","names":["requestKey"]}
|
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
// src/governance/types.ts
|
|
2
|
+
function renderMarkdown(report) {
|
|
3
|
+
const sevEmoji = {
|
|
4
|
+
info: "\u2139\uFE0E",
|
|
5
|
+
low: "\xB7",
|
|
6
|
+
medium: "!",
|
|
7
|
+
high: "!!",
|
|
8
|
+
critical: "\u203C"
|
|
9
|
+
};
|
|
10
|
+
const lines = [];
|
|
11
|
+
lines.push(`# ${report.framework} report \u2014 ${report.context.systemName}`);
|
|
12
|
+
lines.push("");
|
|
13
|
+
lines.push(`- Organization: **${report.context.organization}**`);
|
|
14
|
+
lines.push(`- Period: ${report.context.periodStart} \u2192 ${report.context.periodEnd}`);
|
|
15
|
+
lines.push(
|
|
16
|
+
`- Owner: ${report.context.owner.role} ${report.context.owner.name} <${report.context.owner.email}>`
|
|
17
|
+
);
|
|
18
|
+
lines.push(`- Generated: ${report.generatedAt}`);
|
|
19
|
+
lines.push("");
|
|
20
|
+
lines.push(`## Summary \u2014 ${report.summary.overall}`);
|
|
21
|
+
lines.push("");
|
|
22
|
+
lines.push(`${report.summary.findings} finding(s).`);
|
|
23
|
+
for (const [sev, n] of Object.entries(report.summary.byeverity)) {
|
|
24
|
+
if (n > 0) lines.push(`- ${sevEmoji[sev]} ${sev}: ${n}`);
|
|
25
|
+
}
|
|
26
|
+
lines.push("");
|
|
27
|
+
lines.push("## Findings");
|
|
28
|
+
lines.push("");
|
|
29
|
+
for (const f of report.findings) {
|
|
30
|
+
lines.push(`### ${sevEmoji[f.severity]} ${f.id} \u2014 ${f.control}`);
|
|
31
|
+
lines.push("");
|
|
32
|
+
lines.push(f.summary);
|
|
33
|
+
if (f.evidence) {
|
|
34
|
+
lines.push("");
|
|
35
|
+
lines.push(`**Evidence:** ${f.evidence}`);
|
|
36
|
+
}
|
|
37
|
+
if (f.remediation) {
|
|
38
|
+
lines.push("");
|
|
39
|
+
lines.push(`**Remediation:** ${f.remediation}`);
|
|
40
|
+
}
|
|
41
|
+
lines.push("");
|
|
42
|
+
}
|
|
43
|
+
return lines.join("\n");
|
|
44
|
+
}
|
|
45
|
+
function summarize(findings) {
|
|
46
|
+
const byeverity = {
|
|
47
|
+
info: 0,
|
|
48
|
+
low: 0,
|
|
49
|
+
medium: 0,
|
|
50
|
+
high: 0,
|
|
51
|
+
critical: 0
|
|
52
|
+
};
|
|
53
|
+
for (const f of findings) byeverity[f.severity]++;
|
|
54
|
+
const overall = byeverity.critical + byeverity.high > 0 ? "non-compliant" : byeverity.medium + byeverity.low > 0 ? "compliant-with-findings" : "compliant";
|
|
55
|
+
return { findings: findings.length, byeverity, overall };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// src/governance/eu-ai-act.ts
|
|
59
|
+
function classifyEuAiRisk(signals) {
|
|
60
|
+
if (signals.biometricPublic || signals.socialScoring || signals.subliminal) return "unacceptable";
|
|
61
|
+
if (signals.annexIII) return "high";
|
|
62
|
+
if (signals.chatbot || signals.generatesSyntheticMedia) return "limited";
|
|
63
|
+
return "minimal";
|
|
64
|
+
}
|
|
65
|
+
async function euAiActReport(ctx, signals) {
|
|
66
|
+
const riskClass = classifyEuAiRisk(signals);
|
|
67
|
+
const findings = [];
|
|
68
|
+
if (riskClass === "unacceptable") {
|
|
69
|
+
findings.push({
|
|
70
|
+
id: "EU-ART-5",
|
|
71
|
+
severity: "critical",
|
|
72
|
+
control: "EU-AI-ACT:Article-5",
|
|
73
|
+
summary: "Use case matches a prohibited practice under Article 5.",
|
|
74
|
+
remediation: "Discontinue or substantially redesign the use case."
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
if (riskClass === "high") {
|
|
78
|
+
if (!ctx.redTeam) {
|
|
79
|
+
findings.push({
|
|
80
|
+
id: "EU-ART-9",
|
|
81
|
+
severity: "high",
|
|
82
|
+
control: "EU-AI-ACT:Article-9",
|
|
83
|
+
summary: "High-risk system lacks documented adversarial-testing evidence (Art. 9 risk mgmt).",
|
|
84
|
+
remediation: "Run redTeamDataset() + attach the report."
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
if (ctx.datasets.length === 0) {
|
|
88
|
+
findings.push({
|
|
89
|
+
id: "EU-ART-10",
|
|
90
|
+
severity: "high",
|
|
91
|
+
control: "EU-AI-ACT:Article-10",
|
|
92
|
+
summary: "No training/eval datasets recorded with provenance (Art. 10)."
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
const runs = await ctx.traceStore.listRuns({
|
|
96
|
+
since: Date.parse(ctx.periodStart),
|
|
97
|
+
until: Date.parse(ctx.periodEnd)
|
|
98
|
+
});
|
|
99
|
+
if (runs.length === 0) {
|
|
100
|
+
findings.push({
|
|
101
|
+
id: "EU-ART-11",
|
|
102
|
+
severity: "high",
|
|
103
|
+
control: "EU-AI-ACT:Article-11",
|
|
104
|
+
summary: "No eval runs recorded (Art. 11 technical documentation)."
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
if (!signals.chatbot && !signals.generatesSyntheticMedia) {
|
|
108
|
+
} else {
|
|
109
|
+
findings.push({
|
|
110
|
+
id: "EU-ART-13",
|
|
111
|
+
severity: "info",
|
|
112
|
+
control: "EU-AI-ACT:Article-13",
|
|
113
|
+
summary: "Chatbot/synthetic-media transparency obligations apply; verify user-facing disclosures."
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
if (!ctx.owner?.email) {
|
|
117
|
+
findings.push({
|
|
118
|
+
id: "EU-ART-14",
|
|
119
|
+
severity: "high",
|
|
120
|
+
control: "EU-AI-ACT:Article-14",
|
|
121
|
+
summary: "No designated human overseer (Art. 14).",
|
|
122
|
+
remediation: "Populate GovernanceContext.owner with the responsible individual."
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
if (!ctx.outcomeStore) {
|
|
126
|
+
findings.push({
|
|
127
|
+
id: "EU-ART-15",
|
|
128
|
+
severity: "medium",
|
|
129
|
+
control: "EU-AI-ACT:Article-15",
|
|
130
|
+
summary: "No post-deployment outcome measurement; accuracy + robustness are un-attested.",
|
|
131
|
+
remediation: "Attach an OutcomeStore + run correlationStudy() over the reporting period."
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (riskClass === "limited") {
|
|
136
|
+
findings.push({
|
|
137
|
+
id: "EU-ART-52",
|
|
138
|
+
severity: "info",
|
|
139
|
+
control: "EU-AI-ACT:Article-52",
|
|
140
|
+
summary: "Transparency obligations apply: disclose AI nature + synthetic content labeling.",
|
|
141
|
+
remediation: "Ensure user-facing surfaces label AI-generated content."
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
const payload = {
|
|
145
|
+
riskClass,
|
|
146
|
+
signals,
|
|
147
|
+
articlesReviewed: riskClass === "high" ? ["5", "9", "10", "11", "13", "14", "15"] : riskClass === "limited" ? ["52"] : ["none"]
|
|
148
|
+
};
|
|
149
|
+
return {
|
|
150
|
+
framework: "EU-AI-ACT",
|
|
151
|
+
version: "Regulation-2024-1689",
|
|
152
|
+
context: {
|
|
153
|
+
organization: ctx.organization,
|
|
154
|
+
systemName: ctx.systemName,
|
|
155
|
+
periodStart: ctx.periodStart,
|
|
156
|
+
periodEnd: ctx.periodEnd,
|
|
157
|
+
owner: ctx.owner
|
|
158
|
+
},
|
|
159
|
+
summary: summarize(findings),
|
|
160
|
+
findings,
|
|
161
|
+
payload,
|
|
162
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// src/governance/nist-ai-rmf.ts
|
|
167
|
+
async function nistAiRmfReport(ctx) {
|
|
168
|
+
const findings = [];
|
|
169
|
+
if (!ctx.owner?.email) {
|
|
170
|
+
findings.push({
|
|
171
|
+
id: "G-1.1",
|
|
172
|
+
severity: "high",
|
|
173
|
+
control: "NIST-AI-RMF:GOVERN-1.1",
|
|
174
|
+
summary: "No responsible owner recorded for the AI system.",
|
|
175
|
+
remediation: "Assign an accountable individual + email in GovernanceContext.owner."
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
if (ctx.datasets.length === 0) {
|
|
179
|
+
findings.push({
|
|
180
|
+
id: "G-1.3",
|
|
181
|
+
severity: "high",
|
|
182
|
+
control: "NIST-AI-RMF:GOVERN-1.3",
|
|
183
|
+
summary: "No versioned datasets recorded for the evaluation period.",
|
|
184
|
+
remediation: "Register each dataset with a Dataset manifest (content hash + provenance)."
|
|
185
|
+
});
|
|
186
|
+
} else {
|
|
187
|
+
for (const manifest of ctx.datasets) {
|
|
188
|
+
if (!manifest.contentHash || manifest.contentHash.length < 16) {
|
|
189
|
+
findings.push({
|
|
190
|
+
id: "G-1.3-hash",
|
|
191
|
+
severity: "medium",
|
|
192
|
+
control: "NIST-AI-RMF:GOVERN-1.3",
|
|
193
|
+
summary: `Dataset "${manifest.name}" has weak or missing content hash.`,
|
|
194
|
+
evidence: `contentHash="${manifest.contentHash}"`,
|
|
195
|
+
remediation: "Call dataset.manifest() to compute SHA-256; commit the manifest alongside releases."
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if (!ctx.redTeam) {
|
|
201
|
+
findings.push({
|
|
202
|
+
id: "M-2.6",
|
|
203
|
+
severity: "high",
|
|
204
|
+
control: "NIST-AI-RMF:MEASURE-2.6",
|
|
205
|
+
summary: "No red-team evaluation attached to the report period.",
|
|
206
|
+
remediation: "Run redTeamDataset() against the system and attach the RedTeamReport to context.redTeam."
|
|
207
|
+
});
|
|
208
|
+
} else if (ctx.redTeam.overallPassRate < 0.8) {
|
|
209
|
+
findings.push({
|
|
210
|
+
id: "M-2.6-rate",
|
|
211
|
+
severity: "high",
|
|
212
|
+
control: "NIST-AI-RMF:MEASURE-2.6",
|
|
213
|
+
summary: `Red-team pass rate ${(ctx.redTeam.overallPassRate * 100).toFixed(1)}% below 80% threshold.`,
|
|
214
|
+
evidence: JSON.stringify(ctx.redTeam.passRateByCategory),
|
|
215
|
+
remediation: "Harden the failing categories; rerun the battery."
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
const runs = await ctx.traceStore.listRuns({
|
|
219
|
+
since: Date.parse(ctx.periodStart),
|
|
220
|
+
until: Date.parse(ctx.periodEnd)
|
|
221
|
+
});
|
|
222
|
+
if (runs.length === 0) {
|
|
223
|
+
findings.push({
|
|
224
|
+
id: "M-2.1",
|
|
225
|
+
severity: "critical",
|
|
226
|
+
control: "NIST-AI-RMF:MEASURE-2.1",
|
|
227
|
+
summary: "No eval runs recorded for the reporting period.",
|
|
228
|
+
remediation: "Emit traces for every deployment-relevant evaluation."
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
if (!ctx.judgeCalibration || ctx.judgeCalibration.length === 0) {
|
|
232
|
+
findings.push({
|
|
233
|
+
id: "M-2.11",
|
|
234
|
+
severity: "medium",
|
|
235
|
+
control: "NIST-AI-RMF:MEASURE-2.11",
|
|
236
|
+
summary: "No judge-vs-human calibration recorded.",
|
|
237
|
+
remediation: "Build a human golden set; run calibrateJudge() before trusting LLM judge scores."
|
|
238
|
+
});
|
|
239
|
+
} else {
|
|
240
|
+
const weak = ctx.judgeCalibration.filter((c) => Number.isFinite(c.pearson) && c.pearson < 0.6);
|
|
241
|
+
if (weak.length > 0) {
|
|
242
|
+
findings.push({
|
|
243
|
+
id: "M-2.11-weak",
|
|
244
|
+
severity: "medium",
|
|
245
|
+
control: "NIST-AI-RMF:MEASURE-2.11",
|
|
246
|
+
summary: `${weak.length} judge(s) show weak agreement with humans (Pearson < 0.6).`,
|
|
247
|
+
remediation: "Retrain or replace the underperforming judges."
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
if (!ctx.outcomeStore) {
|
|
252
|
+
findings.push({
|
|
253
|
+
id: "MN-1.1",
|
|
254
|
+
severity: "medium",
|
|
255
|
+
control: "NIST-AI-RMF:MANAGE-1.1",
|
|
256
|
+
summary: "No deployment outcomes captured \u2014 meta-eval correlation cannot be computed.",
|
|
257
|
+
remediation: "Attach an OutcomeStore and ingest production outcome metrics."
|
|
258
|
+
});
|
|
259
|
+
} else {
|
|
260
|
+
const outcomes = await ctx.outcomeStore.list({
|
|
261
|
+
since: Date.parse(ctx.periodStart),
|
|
262
|
+
until: Date.parse(ctx.periodEnd)
|
|
263
|
+
});
|
|
264
|
+
if (outcomes.length === 0) {
|
|
265
|
+
findings.push({
|
|
266
|
+
id: "MN-1.1-empty",
|
|
267
|
+
severity: "medium",
|
|
268
|
+
control: "NIST-AI-RMF:MANAGE-1.1",
|
|
269
|
+
summary: "OutcomeStore present but no outcomes captured for the period."
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
const hashChecks = [];
|
|
274
|
+
for (const manifest of ctx.datasets) {
|
|
275
|
+
hashChecks.push({ name: manifest.name, ok: /^[0-9a-f]{64}$/.test(manifest.contentHash) });
|
|
276
|
+
}
|
|
277
|
+
const payload = {
|
|
278
|
+
controlsEvaluated: [
|
|
279
|
+
"GOVERN-1.1",
|
|
280
|
+
"GOVERN-1.3",
|
|
281
|
+
"MEASURE-2.1",
|
|
282
|
+
"MEASURE-2.6",
|
|
283
|
+
"MEASURE-2.11",
|
|
284
|
+
"MANAGE-1.1"
|
|
285
|
+
],
|
|
286
|
+
runCount: runs.length,
|
|
287
|
+
redTeamPassRate: ctx.redTeam?.overallPassRate ?? null,
|
|
288
|
+
datasetHashChecks: hashChecks
|
|
289
|
+
};
|
|
290
|
+
return {
|
|
291
|
+
framework: "NIST-AI-RMF",
|
|
292
|
+
version: "1.0.0",
|
|
293
|
+
context: {
|
|
294
|
+
organization: ctx.organization,
|
|
295
|
+
systemName: ctx.systemName,
|
|
296
|
+
periodStart: ctx.periodStart,
|
|
297
|
+
periodEnd: ctx.periodEnd,
|
|
298
|
+
owner: ctx.owner
|
|
299
|
+
},
|
|
300
|
+
summary: summarize(findings),
|
|
301
|
+
findings,
|
|
302
|
+
payload,
|
|
303
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// src/governance/soc2.ts
|
|
308
|
+
async function soc2Report(ctx) {
|
|
309
|
+
const findings = [];
|
|
310
|
+
const start = Date.parse(ctx.periodStart);
|
|
311
|
+
const end = Date.parse(ctx.periodEnd);
|
|
312
|
+
const runs = await ctx.traceStore.listRuns({ since: start, until: end });
|
|
313
|
+
const failureRate = runs.length > 0 ? runs.filter((r) => r.outcome?.pass === false).length / runs.length : null;
|
|
314
|
+
if (failureRate !== null && failureRate > 0.2) {
|
|
315
|
+
findings.push({
|
|
316
|
+
id: "CC7.1-fail-rate",
|
|
317
|
+
severity: "medium",
|
|
318
|
+
control: "SOC2:CC7.1",
|
|
319
|
+
summary: `System failure rate ${(failureRate * 100).toFixed(1)}% over the period exceeds 20%.`,
|
|
320
|
+
remediation: "Investigate failure clusters (failureClusterView) + prioritize remediation."
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
if (runs.length === 0) {
|
|
324
|
+
findings.push({
|
|
325
|
+
id: "CC7.1-coverage",
|
|
326
|
+
severity: "high",
|
|
327
|
+
control: "SOC2:CC7.1",
|
|
328
|
+
summary: "No telemetry runs recorded for the period \u2014 monitoring regime is incomplete."
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
const aborted = runs.filter((r) => r.status === "aborted");
|
|
332
|
+
if (aborted.length > runs.length * 0.05 && aborted.length >= 3) {
|
|
333
|
+
findings.push({
|
|
334
|
+
id: "CC7.2-abort",
|
|
335
|
+
severity: "medium",
|
|
336
|
+
control: "SOC2:CC7.2",
|
|
337
|
+
summary: `${aborted.length} run(s) aborted \u2014 investigate pattern.`,
|
|
338
|
+
remediation: "Use the bisector + failureClusterView to localize the trigger."
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
const incidentEvents = await ctx.traceStore.events({
|
|
342
|
+
kind: "policy_violation",
|
|
343
|
+
since: start,
|
|
344
|
+
until: end
|
|
345
|
+
});
|
|
346
|
+
const errorEvents = await ctx.traceStore.events({ kind: "error", since: start, until: end });
|
|
347
|
+
const totalIncidents = incidentEvents.length + errorEvents.length;
|
|
348
|
+
if (totalIncidents > 0) {
|
|
349
|
+
findings.push({
|
|
350
|
+
id: "CC7.3-resolution",
|
|
351
|
+
severity: "low",
|
|
352
|
+
control: "SOC2:CC7.3",
|
|
353
|
+
summary: `${totalIncidents} incident-class event(s) recorded; resolution tracking is informal.`,
|
|
354
|
+
remediation: 'Emit a resolution event (kind="log" with payload.resolves=<eventId>) per remediated incident.'
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
const modelFingerprints = new Set(runs.map((r) => r.modelFingerprint).filter(Boolean));
|
|
358
|
+
const promptHashes = new Set(runs.map((r) => r.promptSha).filter(Boolean));
|
|
359
|
+
const codeSha = new Set(runs.map((r) => r.codeSha).filter(Boolean));
|
|
360
|
+
if (codeSha.size === 0) {
|
|
361
|
+
findings.push({
|
|
362
|
+
id: "CC7.4-code",
|
|
363
|
+
severity: "high",
|
|
364
|
+
control: "SOC2:CC7.4",
|
|
365
|
+
summary: "No codeSha recorded on runs \u2014 cannot attribute scores to a specific release.",
|
|
366
|
+
remediation: "Populate Run.codeSha with the git SHA of the system at run time."
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
if (promptHashes.size === 0) {
|
|
370
|
+
findings.push({
|
|
371
|
+
id: "CC7.4-prompt",
|
|
372
|
+
severity: "medium",
|
|
373
|
+
control: "SOC2:CC7.4",
|
|
374
|
+
summary: "No promptSha recorded \u2014 prompt changes are untracked."
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
const payload = {
|
|
378
|
+
controls: ["CC7.1", "CC7.2", "CC7.3", "CC7.4"],
|
|
379
|
+
runCount: runs.length,
|
|
380
|
+
failureRate,
|
|
381
|
+
abortedCount: aborted.length,
|
|
382
|
+
incidentEventCount: totalIncidents,
|
|
383
|
+
distinctReleases: {
|
|
384
|
+
codeShas: codeSha.size,
|
|
385
|
+
promptHashes: promptHashes.size,
|
|
386
|
+
modelFingerprints: modelFingerprints.size
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
return {
|
|
390
|
+
framework: "SOC2",
|
|
391
|
+
version: "2017-Common-Criteria",
|
|
392
|
+
context: {
|
|
393
|
+
organization: ctx.organization,
|
|
394
|
+
systemName: ctx.systemName,
|
|
395
|
+
periodStart: ctx.periodStart,
|
|
396
|
+
periodEnd: ctx.periodEnd,
|
|
397
|
+
owner: ctx.owner
|
|
398
|
+
},
|
|
399
|
+
summary: summarize(findings),
|
|
400
|
+
findings,
|
|
401
|
+
payload,
|
|
402
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
export {
|
|
407
|
+
renderMarkdown,
|
|
408
|
+
summarize,
|
|
409
|
+
classifyEuAiRisk,
|
|
410
|
+
euAiActReport,
|
|
411
|
+
nistAiRmfReport,
|
|
412
|
+
soc2Report
|
|
413
|
+
};
|
|
414
|
+
//# sourceMappingURL=chunk-KKHDIONI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/governance/types.ts","../src/governance/eu-ai-act.ts","../src/governance/nist-ai-rmf.ts","../src/governance/soc2.ts"],"sourcesContent":["/**\n * Governance reporting — shared types.\n *\n * The framework collects a `GovernanceContext` (traces + outcomes +\n * dataset manifests + red-team results + judge calibration) and each\n * specific template (NIST AI RMF, SOC2, EU AI Act) renders a\n * structured report from it.\n *\n * Reports are machine-readable JSON first; human-readable Markdown is a\n * pure transform on top. External auditors consume the Markdown; CI\n * consumes the JSON.\n */\n\nimport type { DatasetManifest } from '../dataset'\nimport type { CalibrationResult } from '../judge-calibration'\nimport type { OutcomeStore } from '../meta-eval/outcome-store'\nimport type { RedTeamReport } from '../red-team'\nimport type { TraceStore } from '../trace/store'\n\nexport interface GovernanceContext {\n /** Legal / org identity for the report. */\n organization: string\n /** System / agent identifier. */\n systemName: string\n /** ISO8601 period the report covers. */\n periodStart: string\n periodEnd: string\n /** Versioned dataset manifests used during the period. */\n datasets: DatasetManifest[]\n traceStore: TraceStore\n outcomeStore?: OutcomeStore\n /** Cached red-team results for the period, if available. */\n redTeam?: RedTeamReport\n /** Judge-vs-human calibration results, if measured. */\n judgeCalibration?: CalibrationResult[]\n /** Responsible owner for the system — role + name + email. */\n owner: { role: string; name: string; email: string }\n}\n\nexport interface GovernanceFinding {\n id: string\n severity: 'info' | 'low' | 'medium' | 'high' | 'critical'\n /** Control reference the finding maps to (e.g. \"NIST-AI-RMF:MEASURE-2.1\"). */\n control: string\n summary: string\n evidence?: string\n remediation?: string\n}\n\nexport interface GovernanceReport {\n framework: 'NIST-AI-RMF' | 'SOC2' | 'EU-AI-ACT'\n version: string\n context: Pick<\n GovernanceContext,\n 'organization' | 'systemName' | 'periodStart' | 'periodEnd' | 'owner'\n >\n summary: {\n findings: number\n byeverity: Record<GovernanceFinding['severity'], number>\n overall: 'compliant' | 'compliant-with-findings' | 'non-compliant'\n }\n findings: GovernanceFinding[]\n /** Framework-specific structured payload (mapped controls, risk class, etc.). */\n payload: Record<string, unknown>\n generatedAt: string\n}\n\nexport function renderMarkdown(report: GovernanceReport): string {\n const sevEmoji: Record<GovernanceFinding['severity'], string> = {\n info: 'ℹ︎',\n low: '·',\n medium: '!',\n high: '!!',\n critical: '‼',\n }\n const lines: string[] = []\n lines.push(`# ${report.framework} report — ${report.context.systemName}`)\n lines.push('')\n lines.push(`- Organization: **${report.context.organization}**`)\n lines.push(`- Period: ${report.context.periodStart} → ${report.context.periodEnd}`)\n lines.push(\n `- Owner: ${report.context.owner.role} ${report.context.owner.name} <${report.context.owner.email}>`,\n )\n lines.push(`- Generated: ${report.generatedAt}`)\n lines.push('')\n lines.push(`## Summary — ${report.summary.overall}`)\n lines.push('')\n lines.push(`${report.summary.findings} finding(s).`)\n for (const [sev, n] of Object.entries(report.summary.byeverity) as Array<\n [GovernanceFinding['severity'], number]\n >) {\n if (n > 0) lines.push(`- ${sevEmoji[sev]} ${sev}: ${n}`)\n }\n lines.push('')\n lines.push('## Findings')\n lines.push('')\n for (const f of report.findings) {\n lines.push(`### ${sevEmoji[f.severity]} ${f.id} — ${f.control}`)\n lines.push('')\n lines.push(f.summary)\n if (f.evidence) {\n lines.push('')\n lines.push(`**Evidence:** ${f.evidence}`)\n }\n if (f.remediation) {\n lines.push('')\n lines.push(`**Remediation:** ${f.remediation}`)\n }\n lines.push('')\n }\n return lines.join('\\n')\n}\n\nexport function summarize(findings: GovernanceFinding[]): GovernanceReport['summary'] {\n const byeverity: GovernanceReport['summary']['byeverity'] = {\n info: 0,\n low: 0,\n medium: 0,\n high: 0,\n critical: 0,\n }\n for (const f of findings) byeverity[f.severity]++\n const overall: GovernanceReport['summary']['overall'] =\n byeverity.critical + byeverity.high > 0\n ? 'non-compliant'\n : byeverity.medium + byeverity.low > 0\n ? 'compliant-with-findings'\n : 'compliant'\n return { findings: findings.length, byeverity, overall }\n}\n","/**\n * EU AI Act — risk-class classification + compliance checklist.\n *\n * Classification is declarative: caller supplies the domain/use-case\n * signals (biometric? critical infrastructure? education? employment?\n * access to services?) and we map to the Act's risk tiers:\n * - \"unacceptable\" (prohibited)\n * - \"high\" (Annex III — strict obligations)\n * - \"limited\" (transparency obligations)\n * - \"minimal\" (voluntary codes of conduct)\n *\n * Then the compliance checklist enumerates Article 9 (risk mgmt),\n * 10 (data + data governance), 11 (technical documentation), 13\n * (transparency), 14 (human oversight), 15 (accuracy + robustness)\n * requirements and flags gaps.\n */\n\nimport type { GovernanceContext, GovernanceFinding, GovernanceReport } from './types'\nimport { summarize } from './types'\n\nexport type EuRiskClass = 'unacceptable' | 'high' | 'limited' | 'minimal'\n\nexport interface UseCaseSignals {\n /** Used for biometric identification in public spaces? (Art. 5 — unacceptable). */\n biometricPublic?: boolean\n /** Social scoring by public authorities? (Art. 5). */\n socialScoring?: boolean\n /** Subliminal manipulation? (Art. 5). */\n subliminal?: boolean\n /** Annex III sector: critical infrastructure / education / employment /\n * access to essential services / law enforcement / migration /\n * administration of justice / democratic processes? */\n annexIII?: boolean\n /** Interacts directly with natural persons (chatbot, agent)? — limited risk. */\n chatbot?: boolean\n /** Generates synthetic media (image/audio/video/text deepfakes)? — limited risk. */\n generatesSyntheticMedia?: boolean\n}\n\nexport function classifyEuAiRisk(signals: UseCaseSignals): EuRiskClass {\n if (signals.biometricPublic || signals.socialScoring || signals.subliminal) return 'unacceptable'\n if (signals.annexIII) return 'high'\n if (signals.chatbot || signals.generatesSyntheticMedia) return 'limited'\n return 'minimal'\n}\n\nexport async function euAiActReport(\n ctx: GovernanceContext,\n signals: UseCaseSignals,\n): Promise<GovernanceReport> {\n const riskClass = classifyEuAiRisk(signals)\n const findings: GovernanceFinding[] = []\n\n if (riskClass === 'unacceptable') {\n findings.push({\n id: 'EU-ART-5',\n severity: 'critical',\n control: 'EU-AI-ACT:Article-5',\n summary: 'Use case matches a prohibited practice under Article 5.',\n remediation: 'Discontinue or substantially redesign the use case.',\n })\n }\n\n if (riskClass === 'high') {\n // Article 9 — risk management\n if (!ctx.redTeam) {\n findings.push({\n id: 'EU-ART-9',\n severity: 'high',\n control: 'EU-AI-ACT:Article-9',\n summary:\n 'High-risk system lacks documented adversarial-testing evidence (Art. 9 risk mgmt).',\n remediation: 'Run redTeamDataset() + attach the report.',\n })\n }\n // Article 10 — data + data governance\n if (ctx.datasets.length === 0) {\n findings.push({\n id: 'EU-ART-10',\n severity: 'high',\n control: 'EU-AI-ACT:Article-10',\n summary: 'No training/eval datasets recorded with provenance (Art. 10).',\n })\n }\n // Article 11 — technical documentation (traces + runs)\n const runs = await ctx.traceStore.listRuns({\n since: Date.parse(ctx.periodStart),\n until: Date.parse(ctx.periodEnd),\n })\n if (runs.length === 0) {\n findings.push({\n id: 'EU-ART-11',\n severity: 'high',\n control: 'EU-AI-ACT:Article-11',\n summary: 'No eval runs recorded (Art. 11 technical documentation).',\n })\n }\n // Article 13 — transparency to users\n if (!signals.chatbot && !signals.generatesSyntheticMedia) {\n // High-risk but not a chatbot — transparency may still apply; flag informational\n } else {\n findings.push({\n id: 'EU-ART-13',\n severity: 'info',\n control: 'EU-AI-ACT:Article-13',\n summary:\n 'Chatbot/synthetic-media transparency obligations apply; verify user-facing disclosures.',\n })\n }\n // Article 14 — human oversight\n if (!ctx.owner?.email) {\n findings.push({\n id: 'EU-ART-14',\n severity: 'high',\n control: 'EU-AI-ACT:Article-14',\n summary: 'No designated human overseer (Art. 14).',\n remediation: 'Populate GovernanceContext.owner with the responsible individual.',\n })\n }\n // Article 15 — accuracy + robustness\n if (!ctx.outcomeStore) {\n findings.push({\n id: 'EU-ART-15',\n severity: 'medium',\n control: 'EU-AI-ACT:Article-15',\n summary: 'No post-deployment outcome measurement; accuracy + robustness are un-attested.',\n remediation: 'Attach an OutcomeStore + run correlationStudy() over the reporting period.',\n })\n }\n }\n\n if (riskClass === 'limited') {\n findings.push({\n id: 'EU-ART-52',\n severity: 'info',\n control: 'EU-AI-ACT:Article-52',\n summary: 'Transparency obligations apply: disclose AI nature + synthetic content labeling.',\n remediation: 'Ensure user-facing surfaces label AI-generated content.',\n })\n }\n\n const payload = {\n riskClass,\n signals,\n articlesReviewed:\n riskClass === 'high'\n ? ['5', '9', '10', '11', '13', '14', '15']\n : riskClass === 'limited'\n ? ['52']\n : ['none'],\n }\n\n return {\n framework: 'EU-AI-ACT',\n version: 'Regulation-2024-1689',\n context: {\n organization: ctx.organization,\n systemName: ctx.systemName,\n periodStart: ctx.periodStart,\n periodEnd: ctx.periodEnd,\n owner: ctx.owner,\n },\n summary: summarize(findings),\n findings,\n payload,\n generatedAt: new Date().toISOString(),\n }\n}\n","/**\n * NIST AI RMF 1.0 — Govern / Map / Measure / Manage mapping.\n *\n * Each subcategory derives its status from concrete framework state:\n * MEASURE 2.x: do we have a calibration regime? contamination controls?\n * MEASURE 2.7: are red-team results available?\n * MANAGE 1.x: are outcome metrics captured? correlation measured?\n * GOVERN 1.x: dataset + prompt provenance recorded?\n *\n * We ship the mapping and the derivation rules; consumers supply the\n * GovernanceContext.\n */\n\nimport type { GovernanceContext, GovernanceFinding, GovernanceReport } from './types'\nimport { summarize } from './types'\n\nexport async function nistAiRmfReport(ctx: GovernanceContext): Promise<GovernanceReport> {\n const findings: GovernanceFinding[] = []\n\n // GOVERN 1.1 — \"Accountable individual identified\"\n if (!ctx.owner?.email) {\n findings.push({\n id: 'G-1.1',\n severity: 'high',\n control: 'NIST-AI-RMF:GOVERN-1.1',\n summary: 'No responsible owner recorded for the AI system.',\n remediation: 'Assign an accountable individual + email in GovernanceContext.owner.',\n })\n }\n\n // GOVERN 1.3 — \"Inventory + lifecycle tracking\"\n if (ctx.datasets.length === 0) {\n findings.push({\n id: 'G-1.3',\n severity: 'high',\n control: 'NIST-AI-RMF:GOVERN-1.3',\n summary: 'No versioned datasets recorded for the evaluation period.',\n remediation: 'Register each dataset with a Dataset manifest (content hash + provenance).',\n })\n } else {\n // Validate content hashes are stable\n for (const manifest of ctx.datasets) {\n if (!manifest.contentHash || manifest.contentHash.length < 16) {\n findings.push({\n id: 'G-1.3-hash',\n severity: 'medium',\n control: 'NIST-AI-RMF:GOVERN-1.3',\n summary: `Dataset \"${manifest.name}\" has weak or missing content hash.`,\n evidence: `contentHash=\"${manifest.contentHash}\"`,\n remediation:\n 'Call dataset.manifest() to compute SHA-256; commit the manifest alongside releases.',\n })\n }\n }\n }\n\n // MEASURE 2.6 — \"Safety + adversarial testing\"\n if (!ctx.redTeam) {\n findings.push({\n id: 'M-2.6',\n severity: 'high',\n control: 'NIST-AI-RMF:MEASURE-2.6',\n summary: 'No red-team evaluation attached to the report period.',\n remediation:\n 'Run redTeamDataset() against the system and attach the RedTeamReport to context.redTeam.',\n })\n } else if (ctx.redTeam.overallPassRate < 0.8) {\n findings.push({\n id: 'M-2.6-rate',\n severity: 'high',\n control: 'NIST-AI-RMF:MEASURE-2.6',\n summary: `Red-team pass rate ${(ctx.redTeam.overallPassRate * 100).toFixed(1)}% below 80% threshold.`,\n evidence: JSON.stringify(ctx.redTeam.passRateByCategory),\n remediation: 'Harden the failing categories; rerun the battery.',\n })\n }\n\n // MEASURE 2.1 — \"Test results against defined metrics\"\n const runs = await ctx.traceStore.listRuns({\n since: Date.parse(ctx.periodStart),\n until: Date.parse(ctx.periodEnd),\n })\n if (runs.length === 0) {\n findings.push({\n id: 'M-2.1',\n severity: 'critical',\n control: 'NIST-AI-RMF:MEASURE-2.1',\n summary: 'No eval runs recorded for the reporting period.',\n remediation: 'Emit traces for every deployment-relevant evaluation.',\n })\n }\n\n // MEASURE 2.11 — \"Calibration + validation regime\"\n if (!ctx.judgeCalibration || ctx.judgeCalibration.length === 0) {\n findings.push({\n id: 'M-2.11',\n severity: 'medium',\n control: 'NIST-AI-RMF:MEASURE-2.11',\n summary: 'No judge-vs-human calibration recorded.',\n remediation:\n 'Build a human golden set; run calibrateJudge() before trusting LLM judge scores.',\n })\n } else {\n const weak = ctx.judgeCalibration.filter((c) => Number.isFinite(c.pearson) && c.pearson < 0.6)\n if (weak.length > 0) {\n findings.push({\n id: 'M-2.11-weak',\n severity: 'medium',\n control: 'NIST-AI-RMF:MEASURE-2.11',\n summary: `${weak.length} judge(s) show weak agreement with humans (Pearson < 0.6).`,\n remediation: 'Retrain or replace the underperforming judges.',\n })\n }\n }\n\n // MANAGE 1.1 — \"Outcomes tracked post-deployment\"\n if (!ctx.outcomeStore) {\n findings.push({\n id: 'MN-1.1',\n severity: 'medium',\n control: 'NIST-AI-RMF:MANAGE-1.1',\n summary: 'No deployment outcomes captured — meta-eval correlation cannot be computed.',\n remediation: 'Attach an OutcomeStore and ingest production outcome metrics.',\n })\n } else {\n const outcomes = await ctx.outcomeStore.list({\n since: Date.parse(ctx.periodStart),\n until: Date.parse(ctx.periodEnd),\n })\n if (outcomes.length === 0) {\n findings.push({\n id: 'MN-1.1-empty',\n severity: 'medium',\n control: 'NIST-AI-RMF:MANAGE-1.1',\n summary: 'OutcomeStore present but no outcomes captured for the period.',\n })\n }\n }\n\n // Validate that dataset manifests carry strong SHA-256-shaped content hashes.\n const hashChecks: Array<{ name: string; ok: boolean }> = []\n for (const manifest of ctx.datasets) {\n // We don't persist the scenarios here; the check is that the caller's\n // manifest already carries a hash in the expected hex format.\n hashChecks.push({ name: manifest.name, ok: /^[0-9a-f]{64}$/.test(manifest.contentHash) })\n }\n\n const payload = {\n controlsEvaluated: [\n 'GOVERN-1.1',\n 'GOVERN-1.3',\n 'MEASURE-2.1',\n 'MEASURE-2.6',\n 'MEASURE-2.11',\n 'MANAGE-1.1',\n ],\n runCount: runs.length,\n redTeamPassRate: ctx.redTeam?.overallPassRate ?? null,\n datasetHashChecks: hashChecks,\n }\n\n return {\n framework: 'NIST-AI-RMF',\n version: '1.0.0',\n context: {\n organization: ctx.organization,\n systemName: ctx.systemName,\n periodStart: ctx.periodStart,\n periodEnd: ctx.periodEnd,\n owner: ctx.owner,\n },\n summary: summarize(findings),\n findings,\n payload,\n generatedAt: new Date().toISOString(),\n }\n}\n","/**\n * SOC 2 — Common Criteria 7 (system operations + change management)\n * audit trail derived from the trace corpus.\n *\n * This is NOT a formal SOC2 report — that requires an external\n * auditor. What we ship is the machine-readable *evidence* package\n * that an auditor consumes: run counts, deploy events, access log\n * summary, anomaly tracking, response-time SLOs.\n */\n\nimport type { GovernanceContext, GovernanceFinding, GovernanceReport } from './types'\nimport { summarize } from './types'\n\nexport async function soc2Report(ctx: GovernanceContext): Promise<GovernanceReport> {\n const findings: GovernanceFinding[] = []\n const start = Date.parse(ctx.periodStart)\n const end = Date.parse(ctx.periodEnd)\n const runs = await ctx.traceStore.listRuns({ since: start, until: end })\n\n // CC7.1 — \"Monitoring to detect anomalies\"\n const failureRate =\n runs.length > 0 ? runs.filter((r) => r.outcome?.pass === false).length / runs.length : null\n if (failureRate !== null && failureRate > 0.2) {\n findings.push({\n id: 'CC7.1-fail-rate',\n severity: 'medium',\n control: 'SOC2:CC7.1',\n summary: `System failure rate ${(failureRate * 100).toFixed(1)}% over the period exceeds 20%.`,\n remediation: 'Investigate failure clusters (failureClusterView) + prioritize remediation.',\n })\n }\n if (runs.length === 0) {\n findings.push({\n id: 'CC7.1-coverage',\n severity: 'high',\n control: 'SOC2:CC7.1',\n summary: 'No telemetry runs recorded for the period — monitoring regime is incomplete.',\n })\n }\n\n // CC7.2 — \"Anomaly investigation\"\n const aborted = runs.filter((r) => r.status === 'aborted')\n if (aborted.length > runs.length * 0.05 && aborted.length >= 3) {\n findings.push({\n id: 'CC7.2-abort',\n severity: 'medium',\n control: 'SOC2:CC7.2',\n summary: `${aborted.length} run(s) aborted — investigate pattern.`,\n remediation: 'Use the bisector + failureClusterView to localize the trigger.',\n })\n }\n\n // CC7.3 — \"Response to incidents\" — require an event tag for resolved incidents\n const incidentEvents = await ctx.traceStore.events({\n kind: 'policy_violation',\n since: start,\n until: end,\n })\n const errorEvents = await ctx.traceStore.events({ kind: 'error', since: start, until: end })\n const totalIncidents = incidentEvents.length + errorEvents.length\n if (totalIncidents > 0) {\n // No formal resolution tracking yet — flag medium by default\n findings.push({\n id: 'CC7.3-resolution',\n severity: 'low',\n control: 'SOC2:CC7.3',\n summary: `${totalIncidents} incident-class event(s) recorded; resolution tracking is informal.`,\n remediation:\n 'Emit a resolution event (kind=\"log\" with payload.resolves=<eventId>) per remediated incident.',\n })\n }\n\n // CC7.4 — \"Configuration change tracking\"\n const modelFingerprints = new Set(runs.map((r) => r.modelFingerprint).filter(Boolean) as string[])\n const promptHashes = new Set(runs.map((r) => r.promptSha).filter(Boolean) as string[])\n const codeSha = new Set(runs.map((r) => r.codeSha).filter(Boolean) as string[])\n if (codeSha.size === 0) {\n findings.push({\n id: 'CC7.4-code',\n severity: 'high',\n control: 'SOC2:CC7.4',\n summary: 'No codeSha recorded on runs — cannot attribute scores to a specific release.',\n remediation: 'Populate Run.codeSha with the git SHA of the system at run time.',\n })\n }\n if (promptHashes.size === 0) {\n findings.push({\n id: 'CC7.4-prompt',\n severity: 'medium',\n control: 'SOC2:CC7.4',\n summary: 'No promptSha recorded — prompt changes are untracked.',\n })\n }\n\n const payload = {\n controls: ['CC7.1', 'CC7.2', 'CC7.3', 'CC7.4'],\n runCount: runs.length,\n failureRate,\n abortedCount: aborted.length,\n incidentEventCount: totalIncidents,\n distinctReleases: {\n codeShas: codeSha.size,\n promptHashes: promptHashes.size,\n modelFingerprints: modelFingerprints.size,\n },\n }\n\n return {\n framework: 'SOC2',\n version: '2017-Common-Criteria',\n context: {\n organization: ctx.organization,\n systemName: ctx.systemName,\n periodStart: ctx.periodStart,\n periodEnd: ctx.periodEnd,\n owner: ctx.owner,\n },\n summary: summarize(findings),\n findings,\n payload,\n generatedAt: new Date().toISOString(),\n }\n}\n"],"mappings":"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","names":[]}
|