@gsengai/core 0.1.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli.ts","../src/store.ts","../src/hash.ts","../src/canonical-json.ts","../src/errors.ts","../src/export.ts","../src/gsengai-audit.ts"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n// Minimal zero-dep audit CLI. Deliberately unpolished: parseArgs,\n// two formats, plain output. Rich help, colour, and config files are non-goals.\nimport { existsSync } from \"node:fs\";\nimport { parseArgs } from \"node:util\";\nimport { createEvidenceStore } from \"./store\";\nimport type { ExportFilter, Modality } from \"./types\";\n\nconst USAGE = `gsengai-audit — export audit artifacts from an gsengai evidence store.\nSupports compliance with EU AI Act Article 50 and California SB 942. Exports\ncontain hashes and metadata only; raw content is never stored or exported.\nNot legal advice.\n\nUsage:\n gsengai-audit export --store <evidence.db> --format csv|report --out <path>\n [--system-id <id>] [--modality text|image|audio|video]\n [--since <ISO timestamp>] [--until <ISO timestamp>]\n\nFormats:\n csv streaming CSV of evidence records (fixed schema-v1 column order)\n report human-readable Markdown audit report (integrity, summary, records)\n\nTip: render the report Markdown to PDF/HTML yourself before handing it to\ncounsel (e.g. pandoc report.md -o report.pdf).`;\n\n/** Where CLI output goes; injectable for tests. */\nexport interface CliIo {\n log(message: string): void;\n error(message: string): void;\n}\n\nconst MODALITIES: ReadonlySet<string> = new Set([\"text\", \"image\", \"audio\", \"video\"]);\n\nfunction parseCliArgs(argv: string[]) {\n return parseArgs({\n args: argv,\n allowPositionals: true,\n options: {\n store: { type: \"string\" },\n format: { type: \"string\" },\n out: { type: \"string\" },\n \"system-id\": { type: \"string\" },\n modality: { type: \"string\" },\n since: { type: \"string\" },\n until: { type: \"string\" },\n help: { type: \"boolean\" },\n },\n } as const);\n}\n\n/**\n * The function the `gsengai-audit` bin calls. Returns the process exit code.\n * Read-only over the store: it opens, exports, and closes.\n */\nexport async function runAuditCli(argv: string[], io: CliIo = console): Promise<number> {\n let parsed: ReturnType<typeof parseCliArgs>;\n try {\n parsed = parseCliArgs(argv);\n } catch (err) {\n io.error(err instanceof Error ? err.message : String(err));\n io.error(USAGE);\n return 1;\n }\n const { values, positionals } = parsed;\n\n if (values.help === true) {\n io.log(USAGE);\n return 0;\n }\n if (positionals[0] !== \"export\" || positionals.length !== 1) {\n io.error(\"gsengai-audit: the only supported command is 'export'\");\n io.error(USAGE);\n return 1;\n }\n const storePath = values.store;\n const format = values.format;\n const outPath = values.out;\n if (storePath === undefined || format === undefined || outPath === undefined) {\n io.error(\"gsengai-audit: --store, --format, and --out are required\");\n io.error(USAGE);\n return 1;\n }\n if (format !== \"csv\" && format !== \"report\") {\n io.error(`gsengai-audit: unknown --format '${format}' (expected csv or report)`);\n return 1;\n }\n // createEvidenceStore would create an empty DB at a mistyped path; refuse instead.\n if (!existsSync(storePath)) {\n io.error(`gsengai-audit: no evidence store found at '${storePath}'`);\n return 1;\n }\n const modality = values.modality;\n if (modality !== undefined && !MODALITIES.has(modality)) {\n io.error(`gsengai-audit: unknown --modality '${modality}' (expected text|image|audio|video)`);\n return 1;\n }\n const filter: ExportFilter = {};\n if (values[\"system-id\"] !== undefined) {\n filter.systemId = values[\"system-id\"];\n }\n if (modality !== undefined) {\n filter.modality = modality as Modality;\n }\n if (values.since !== undefined) {\n filter.since = values.since;\n }\n if (values.until !== undefined) {\n filter.until = values.until;\n }\n\n const store = createEvidenceStore({ path: storePath });\n try {\n if (format === \"csv\") {\n const result = await store.exportCsv(outPath, filter);\n io.log(`CSV export: ${result.records} record(s) -> ${result.path}`);\n } else {\n const report = store.buildAuditReport({ path: outPath, filter });\n io.log(`Audit report: ${report.records} record(s) -> ${outPath}`);\n if (report.integrity.ok) {\n io.log(`Integrity: chain verified (${report.integrity.checked} record(s) checked)`);\n } else {\n // Never suppressed: the break is in the report AND on stderr.\n io.error(\n `WARNING: evidence hash chain BROKEN at seq ${report.integrity.brokenAtSeq} — ` +\n \"the Integrity section of the report records the break.\",\n );\n }\n }\n } catch (err) {\n io.error(`gsengai-audit: export failed: ${err instanceof Error ? err.message : String(err)}`);\n return 1;\n } finally {\n store.close();\n }\n return 0;\n}\n","// SPDX-License-Identifier: Apache-2.0\nimport { randomUUID } from \"node:crypto\";\nimport { once } from \"node:events\";\nimport { createWriteStream, writeFileSync } from \"node:fs\";\nimport Database from \"better-sqlite3\";\nimport { canonicalJson } from \"./canonical-json\";\nimport { NotImplementedError } from \"./errors\";\nimport { CSV_COLUMNS, recordToCsvRow, renderAuditReport } from \"./export\";\nimport { HASH_VERSION, hashText, sha256Hex } from \"./hash\";\nimport type {\n AppendEvidenceInput,\n AuditReport,\n AuditReportOptions,\n ChainVerification,\n CreateEvidenceStoreOptions,\n EvidenceRecord,\n EvidenceStore,\n ExportFilter,\n Modality,\n} from \"./types\";\n\nconst MODALITIES: ReadonlySet<string> = new Set([\"text\", \"image\", \"audio\", \"video\"]);\n\n// Append-only law (PRD C1): no UPDATE/DELETE path exists in this API, and the\n// triggers below abort raw SQL attempts as defense in depth. Never remove them.\nconst SCHEMA_SQL = `\nCREATE TABLE IF NOT EXISTS evidence (\n seq INTEGER PRIMARY KEY,\n id TEXT NOT NULL UNIQUE,\n ts TEXT NOT NULL,\n modality TEXT NOT NULL CHECK (modality IN ('text','image','audio','video')),\n model TEXT NOT NULL,\n system_id TEXT NOT NULL,\n prompt_hash TEXT,\n output_hash TEXT NOT NULL,\n output_hash_normalized TEXT,\n hash_version INTEGER NOT NULL,\n marking_methods TEXT NOT NULL,\n manifest_ref TEXT,\n disclosure_context TEXT,\n prev_hash TEXT,\n record_hash TEXT NOT NULL\n);\nCREATE INDEX IF NOT EXISTS idx_evidence_output_hash\n ON evidence (output_hash);\nCREATE INDEX IF NOT EXISTS idx_evidence_output_hash_normalized\n ON evidence (output_hash_normalized);\nCREATE TRIGGER IF NOT EXISTS evidence_append_only_update\nBEFORE UPDATE ON evidence\nBEGIN\n SELECT RAISE(ABORT, 'gsengai evidence store is append-only: UPDATE is not permitted');\nEND;\nCREATE TRIGGER IF NOT EXISTS evidence_append_only_delete\nBEFORE DELETE ON evidence\nBEGIN\n SELECT RAISE(ABORT, 'gsengai evidence store is append-only: DELETE is not permitted');\nEND;\n`;\n\ninterface EvidenceRow {\n seq: number;\n id: string;\n ts: string;\n modality: string;\n model: string;\n system_id: string;\n prompt_hash: string | null;\n output_hash: string;\n output_hash_normalized: string | null;\n hash_version: number;\n marking_methods: string;\n manifest_ref: string | null;\n disclosure_context: string | null;\n prev_hash: string | null;\n record_hash: string;\n}\n\nfunction rowToRecord(row: EvidenceRow): EvidenceRecord {\n return {\n id: row.id,\n ts: row.ts,\n modality: row.modality as Modality,\n model: row.model,\n system_id: row.system_id,\n prompt_hash: row.prompt_hash,\n output_hash: row.output_hash,\n output_hash_normalized: row.output_hash_normalized,\n hash_version: row.hash_version,\n marking_methods: JSON.parse(row.marking_methods) as string[],\n manifest_ref: row.manifest_ref,\n disclosure_context: row.disclosure_context,\n prev_hash: row.prev_hash,\n record_hash: row.record_hash,\n };\n}\n\n/** `record_hash = sha256(canonicalJson(all fields above))` per PRD §4. */\nfunction computeRecordHash(body: Omit<EvidenceRecord, \"record_hash\">): string {\n return sha256Hex(canonicalJson(body));\n}\n\nfunction requireNonEmptyString(value: unknown, name: string): string {\n if (typeof value !== \"string\" || value.length === 0) {\n throw new TypeError(`gsengai: ${name} must be a non-empty string`);\n }\n return value;\n}\n\nfunction resolveOutputHashes(input: AppendEvidenceInput): {\n output_hash: string;\n output_hash_normalized: string | null;\n hash_version: number;\n} {\n const provided = [input.outputText, input.outputBytes, input.outputHashes].filter(\n (v) => v !== undefined,\n );\n if (provided.length !== 1) {\n throw new TypeError(\"gsengai: provide exactly one of outputText, outputBytes, or outputHashes\");\n }\n if (input.outputText !== undefined) {\n // Raw text is hashed here, in memory, and goes no further (PRD C4).\n const hashes = hashText(input.outputText);\n return {\n output_hash: hashes.outputHash,\n output_hash_normalized: hashes.outputHashNormalized,\n hash_version: hashes.hashVersion,\n };\n }\n if (input.outputBytes !== undefined) {\n // Raw bytes (e.g. the signed image file) are hashed here, in memory, and go\n // no further (PRD C4). Text normalization does not apply (ADR-0018).\n return {\n output_hash: sha256Hex(input.outputBytes),\n output_hash_normalized: null,\n hash_version: HASH_VERSION,\n };\n }\n const given = input.outputHashes as NonNullable<typeof input.outputHashes>;\n return {\n output_hash: requireNonEmptyString(given.outputHash, \"outputHashes.outputHash\"),\n output_hash_normalized: given.outputHashNormalized ?? null,\n hash_version: given.hashVersion ?? HASH_VERSION,\n };\n}\n\nfunction normalizeInstant(value: string, name: string): string {\n const ms = Date.parse(requireNonEmptyString(value, name));\n if (Number.isNaN(ms)) {\n throw new TypeError(`gsengai: ${name} must be a Date.parse-able timestamp`);\n }\n return new Date(ms).toISOString();\n}\n\n/**\n * Translate an ExportFilter into a WHERE clause (PRD C3). `since`/`until` are\n * normalized to ISO 8601 UTC, so lexicographic comparison against the stored\n * `ts` (always `toISOString()` output) is a correct instant comparison; both\n * bounds are inclusive.\n */\nfunction buildFilter(filter: ExportFilter | undefined): {\n where: string;\n params: Record<string, string>;\n normalized: ExportFilter | undefined;\n} {\n if (filter === undefined) {\n return { where: \"\", params: {}, normalized: undefined };\n }\n const conditions: string[] = [];\n const params: Record<string, string> = {};\n const normalized: ExportFilter = {};\n if (filter.systemId !== undefined) {\n normalized.systemId = requireNonEmptyString(filter.systemId, \"filter.systemId\");\n conditions.push(\"system_id = @systemId\");\n params.systemId = normalized.systemId;\n }\n if (filter.modality !== undefined) {\n if (!MODALITIES.has(filter.modality)) {\n throw new TypeError(\n \"gsengai: filter.modality must be one of 'text' | 'image' | 'audio' | 'video'\",\n );\n }\n normalized.modality = filter.modality;\n conditions.push(\"modality = @modality\");\n params.modality = normalized.modality;\n }\n if (filter.since !== undefined) {\n normalized.since = normalizeInstant(filter.since, \"filter.since\");\n conditions.push(\"ts >= @since\");\n params.since = normalized.since;\n }\n if (filter.until !== undefined) {\n normalized.until = normalizeInstant(filter.until, \"filter.until\");\n conditions.push(\"ts <= @until\");\n params.until = normalized.until;\n }\n if (conditions.length === 0) {\n return { where: \"\", params: {}, normalized: undefined };\n }\n return { where: ` WHERE ${conditions.join(\" AND \")}`, params, normalized };\n}\n\nexport function createEvidenceStore(options: CreateEvidenceStoreOptions): EvidenceStore {\n if (options.storeRawContent) {\n throw new NotImplementedError(\n \"gsengai: storeRawContent is not implemented in the MVP. Only hashes and metadata persist \" +\n \"(PRD C4); encrypted raw-content capture is planned post-MVP.\",\n );\n }\n const storePath = requireNonEmptyString(options.path, \"path\");\n const db = new Database(storePath);\n db.pragma(\"journal_mode = WAL\");\n db.exec(SCHEMA_SQL);\n\n const insertStmt = db.prepare(`\n INSERT INTO evidence (\n id, ts, modality, model, system_id, prompt_hash, output_hash,\n output_hash_normalized, hash_version, marking_methods, manifest_ref,\n disclosure_context, prev_hash, record_hash\n ) VALUES (\n @id, @ts, @modality, @model, @system_id, @prompt_hash, @output_hash,\n @output_hash_normalized, @hash_version, @marking_methods, @manifest_ref,\n @disclosure_context, @prev_hash, @record_hash\n )\n `);\n const lastHashStmt = db.prepare(\"SELECT record_hash FROM evidence ORDER BY seq DESC LIMIT 1\");\n const allStmt = db.prepare(\"SELECT * FROM evidence ORDER BY seq\");\n const byOutputHashStmt = db.prepare(\"SELECT * FROM evidence WHERE output_hash = ? ORDER BY seq\");\n const byTextStmt = db.prepare(\n \"SELECT * FROM evidence WHERE output_hash = @raw OR output_hash_normalized = @normalized ORDER BY seq\",\n );\n const countStmt = db.prepare(\"SELECT COUNT(*) AS n FROM evidence\");\n\n function filteredRows(filter: ExportFilter | undefined): {\n rows: IterableIterator<EvidenceRow>;\n normalized: ExportFilter | undefined;\n } {\n const { where, params, normalized } = buildFilter(filter);\n const stmt = db.prepare(`SELECT * FROM evidence${where} ORDER BY seq`);\n return { rows: stmt.iterate(params) as IterableIterator<EvidenceRow>, normalized };\n }\n\n function verifyChainImpl(): ChainVerification {\n let checked = 0;\n let prevHash: string | null = null;\n for (const row of allStmt.iterate() as IterableIterator<EvidenceRow>) {\n checked += 1;\n const { record_hash, ...body } = rowToRecord(row);\n if (body.prev_hash !== prevHash || computeRecordHash(body) !== record_hash) {\n return { ok: false, checked, brokenAtSeq: row.seq };\n }\n prevHash = record_hash;\n }\n return { ok: true, checked };\n }\n\n // Reading the chain head and inserting must be atomic so prev_hash linkage\n // stays correct under concurrent writers.\n const runAppend = db.transaction(\n (body: Omit<EvidenceRecord, \"prev_hash\" | \"record_hash\">): EvidenceRecord => {\n const last = lastHashStmt.get() as { record_hash: string } | undefined;\n const chained: Omit<EvidenceRecord, \"record_hash\"> = {\n ...body,\n prev_hash: last ? last.record_hash : null,\n };\n const record: EvidenceRecord = { ...chained, record_hash: computeRecordHash(chained) };\n insertStmt.run({ ...record, marking_methods: JSON.stringify(record.marking_methods) });\n return record;\n },\n );\n\n return {\n append(input: AppendEvidenceInput): EvidenceRecord {\n const modality = input.modality;\n if (typeof modality !== \"string\" || !MODALITIES.has(modality)) {\n throw new TypeError(\n \"gsengai: modality must be one of 'text' | 'image' | 'audio' | 'video'\",\n );\n }\n const body: Omit<EvidenceRecord, \"prev_hash\" | \"record_hash\"> = {\n id: randomUUID(),\n ts: new Date().toISOString(),\n modality,\n model: requireNonEmptyString(input.model, \"model\"),\n system_id: requireNonEmptyString(input.systemId, \"systemId\"),\n prompt_hash: input.promptHash ?? null,\n ...resolveOutputHashes(input),\n marking_methods: input.markingMethods ?? [\"logging\"],\n manifest_ref: input.manifestRef ?? null,\n disclosure_context: input.disclosureContext ?? null,\n };\n return runAppend.immediate(body);\n },\n\n findByOutputHash(hash: string): EvidenceRecord[] {\n const rows = byOutputHashStmt.all(hash) as EvidenceRow[];\n return rows.map(rowToRecord);\n },\n\n findByText(text: string): EvidenceRecord[] {\n // §5: lookups apply the recorded algorithm version. v1 is the only version,\n // so the v1 normalization is applied here; a future v2 must dispatch on\n // each record's hash_version instead of silently using the latest.\n const hashes = hashText(text);\n const rows = byTextStmt.all({\n raw: hashes.outputHash,\n normalized: hashes.outputHashNormalized,\n }) as EvidenceRow[];\n return rows.map(rowToRecord);\n },\n\n async exportJsonl(path: string): Promise<{ records: number; path: string }> {\n const out = createWriteStream(path, { encoding: \"utf8\" });\n let records = 0;\n try {\n for (const row of allStmt.iterate() as IterableIterator<EvidenceRow>) {\n const line = `${canonicalJson(rowToRecord(row))}\\n`;\n if (!out.write(line)) {\n await once(out, \"drain\");\n }\n records += 1;\n }\n } finally {\n out.end();\n }\n await once(out, \"close\");\n return { records, path };\n },\n\n async exportCsv(\n path: string,\n filter?: ExportFilter,\n ): Promise<{ records: number; path: string }> {\n const { rows } = filteredRows(filter);\n const out = createWriteStream(path, { encoding: \"utf8\" });\n let records = 0;\n try {\n if (!out.write(`${CSV_COLUMNS.join(\",\")}\\n`)) {\n await once(out, \"drain\");\n }\n for (const row of rows) {\n const line = `${recordToCsvRow(rowToRecord(row))}\\n`;\n if (!out.write(line)) {\n await once(out, \"drain\");\n }\n records += 1;\n }\n } finally {\n out.end();\n }\n await once(out, \"close\");\n return { records, path };\n },\n\n buildAuditReport(opts: AuditReportOptions = {}): AuditReport {\n const { rows, normalized } = filteredRows(opts.filter);\n const records = [...rows].map(rowToRecord);\n // Whole-store verification, independent of the filter: the report always\n // renders and the outcome is always surfaced (verified or BROKEN).\n const integrity = verifyChainImpl();\n const markdown = renderAuditReport({\n records,\n integrity,\n storePath,\n filter: normalized,\n generatedAt: new Date().toISOString(),\n });\n if (opts.path !== undefined) {\n writeFileSync(opts.path, markdown, \"utf8\");\n }\n return { markdown, records: records.length, integrity, path: opts.path ?? null };\n },\n\n verifyChain: verifyChainImpl,\n\n count(): number {\n const row = countStmt.get() as { n: number };\n return row.n;\n },\n\n close(): void {\n db.close();\n },\n };\n}\n","// SPDX-License-Identifier: Apache-2.0\nimport { createHash } from \"node:crypto\";\n\n/** Version of the text normalization spec (PRD §5) used for `output_hash_normalized`. */\nexport const HASH_VERSION = 1;\n\n/** SHA-256 hex digest of a UTF-8 string or raw bytes. */\nexport function sha256Hex(data: string | Uint8Array): string {\n const hash = createHash(\"sha256\");\n if (typeof data === \"string\") {\n hash.update(data, \"utf8\");\n } else {\n hash.update(data);\n }\n return hash.digest(\"hex\");\n}\n\n/**\n * Text normalization spec v1 (PRD §5), applied in exactly this order:\n * 1. Unicode NFC normalize\n * 2. toLowerCase()\n * 3. Collapse every whitespace run to a single space (U+0020)\n * 4. Trim\n */\nexport function normalizeText(raw: string): string {\n return raw.normalize(\"NFC\").toLowerCase().replace(/\\s+/gu, \" \").trim();\n}\n\nexport interface TextHashes {\n /** SHA-256 of the raw output text. */\n outputHash: string;\n /** SHA-256 of the normalized text (spec §5). */\n outputHashNormalized: string;\n hashVersion: number;\n}\n\n/** Hash a generated text per PRD §5. The text is only read in memory — never persisted. */\nexport function hashText(raw: string): TextHashes {\n return {\n outputHash: sha256Hex(raw),\n outputHashNormalized: sha256Hex(normalizeText(raw)),\n hashVersion: HASH_VERSION,\n };\n}\n","// SPDX-License-Identifier: Apache-2.0\nimport { sha256Hex } from \"./hash\";\n\n/**\n * Canonical JSON (PRD §4): recursively key-sorted objects, arrays kept in order,\n * no insignificant whitespace, UTF-8. Deterministic for any JSON-serializable value.\n * Non-serializable values (cycles, BigInt) throw, as with `JSON.stringify`.\n */\nexport function canonicalJson(value: unknown): string {\n const out = JSON.stringify(value, (_key, val: unknown) => {\n if (val !== null && typeof val === \"object\" && !Array.isArray(val)) {\n const record = val as Record<string, unknown>;\n const sorted: Record<string, unknown> = {};\n for (const key of Object.keys(record).sort()) {\n sorted[key] = record[key];\n }\n return sorted;\n }\n return val;\n });\n if (out === undefined) {\n throw new TypeError(\"canonicalJson: value does not serialize to JSON\");\n }\n return out;\n}\n\n/** `sha256(canonicalJson(messagesOrInput))` — the prompt fingerprint of PRD §4. */\nexport function hashPrompt(messagesOrInput: unknown): string {\n return sha256Hex(canonicalJson(messagesOrInput));\n}\n","// SPDX-License-Identifier: Apache-2.0\n\n/** Thrown for features that exist in the API surface but are deliberately not implemented in the MVP. */\nexport class NotImplementedError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"NotImplementedError\";\n }\n}\n","// SPDX-License-Identifier: Apache-2.0\n// Audit export helpers (PRD C3): CSV serialization and the human-readable\n// Markdown audit report. Both emit hashes and metadata only — the store's\n// privacy posture (PRD C4) extends to every export path, canary-enforced.\nimport type { ChainVerification, EvidenceRecord, ExportFilter } from \"./types\";\n\n/** Fixed CSV column order — matches the §4 record schema exactly. */\nexport const CSV_COLUMNS = [\n \"id\",\n \"ts\",\n \"modality\",\n \"model\",\n \"system_id\",\n \"prompt_hash\",\n \"output_hash\",\n \"output_hash_normalized\",\n \"hash_version\",\n \"marking_methods\",\n \"manifest_ref\",\n \"disclosure_context\",\n \"prev_hash\",\n \"record_hash\",\n] as const;\n\n/** RFC 4180 field escaping: quote when the value contains `\"`, `,`, or line breaks. */\nfunction csvField(value: string | number | null): string {\n if (value === null) {\n return \"\";\n }\n const s = String(value);\n return /[\",\\r\\n]/.test(s) ? `\"${s.replaceAll('\"', '\"\"')}\"` : s;\n}\n\nexport function recordToCsvRow(record: EvidenceRecord): string {\n return CSV_COLUMNS.map((column) =>\n csvField(\n column === \"marking_methods\" ? JSON.stringify(record.marking_methods) : record[column],\n ),\n ).join(\",\");\n}\n\n/**\n * Canonical limits & disclaimer block, verbatim from PRD §9. Never paraphrase,\n * never improvise legal language.\n */\nexport const PRD_S9_LIMITS = `> **What this does NOT do**\n>\n> - It does not make you compliant. It supports compliance with EU AI Act Article 50 and California SB 942; compliance depends on your system, your deployment context, and your processes.\n> - It does not embed imperceptible watermarks (MVP). It implements the signed-metadata and logging/fingerprinting layers of a multi-layer marking strategy.\n> - It cannot prevent downstream metadata stripping — manifests can be removed by re-encoding, screenshots, or platform uploads. That is exactly why the logging layer exists.\n> - It does not detect third-party AI content.\n> - It is not legal advice. Consult qualified counsel about your obligations.`;\n\n/** Everything the report renderer needs; assembled by the store. */\nexport interface AuditReportContext {\n /** Records after the filter, in chain (seq) order. */\n records: EvidenceRecord[];\n /** Whole-store verification result — reported even (especially) when broken. */\n integrity: ChainVerification;\n storePath: string;\n /** Normalized filter actually applied, or undefined for the full store. */\n filter: ExportFilter | undefined;\n /** ISO 8601 UTC generation timestamp. */\n generatedAt: string;\n}\n\n/** Markdown table cell: pipes and line breaks would break the table structure. */\nfunction cell(value: string | null): string {\n if (value === null || value === \"\") {\n return \"\";\n }\n return value.replaceAll(\"|\", \"\\\\|\").replaceAll(/\\r?\\n/gu, \" \");\n}\n\nfunction code(value: string | null): string {\n return value === null || value === \"\" ? \"\" : `\\`${cell(value)}\\``;\n}\n\nfunction describeFilter(filter: ExportFilter | undefined): string {\n const parts: string[] = [];\n if (filter?.systemId !== undefined) {\n parts.push(`system_id = \\`${cell(filter.systemId)}\\``);\n }\n if (filter?.modality !== undefined) {\n parts.push(`modality = \\`${filter.modality}\\``);\n }\n if (filter?.since !== undefined) {\n parts.push(`ts ≥ \\`${filter.since}\\``);\n }\n if (filter?.until !== undefined) {\n parts.push(`ts ≤ \\`${filter.until}\\``);\n }\n return parts.length === 0 ? \"none (full store)\" : parts.join(\" · \");\n}\n\n/** Count records per label, rendered most-frequent first (ties: label order). */\nfunction breakdown(records: EvidenceRecord[], label: (r: EvidenceRecord) => string): string {\n const counts = new Map<string, number>();\n for (const record of records) {\n const key = label(record);\n counts.set(key, (counts.get(key) ?? 0) + 1);\n }\n const rows = [...counts.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]));\n return rows.map(([key, n]) => `| ${cell(key)} | ${n} |`).join(\"\\n\");\n}\n\nfunction integritySection(integrity: ChainVerification): string {\n if (integrity.ok) {\n return (\n `**Chain verified** — ${integrity.checked} record(s) checked; ` +\n \"the tamper-evident hash chain is intact.\"\n );\n }\n return (\n `**⚠ BROKEN at seq ${integrity.brokenAtSeq}** — tamper-evident hash-chain verification ` +\n `FAILED at record seq ${integrity.brokenAtSeq} (${integrity.checked} record(s) checked up ` +\n \"to and including the break). Records at and after this point cannot be trusted as \" +\n \"appended; investigate the store before relying on this log.\"\n );\n}\n\n/**\n * Render the audit report (PRD C3) — the document a team hands its counsel or\n * a supervisory authority. It records what a system generated, marked, and\n * disclosed; it does not itself determine compliance (PRD §9). Integrity is\n * always reported, never suppressed: a broken chain renders prominently.\n */\nexport function renderAuditReport(ctx: AuditReportContext): string {\n const { records, integrity } = ctx;\n const firstTs = records[0]?.ts ?? null;\n const lastTs = records.at(-1)?.ts ?? null;\n\n const recordRows =\n records.length === 0\n ? \"_No records match the filter._\"\n : [\n \"| id | ts | modality | model | system_id | output_hash | output_hash_normalized | marking_methods | manifest_ref | disclosure_context |\",\n \"| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |\",\n ...records.map(\n (r) =>\n `| ${code(r.id)} | ${r.ts} | ${r.modality} | ${cell(r.model)} | ${cell(r.system_id)} | ` +\n `${code(r.output_hash)} | ${code(r.output_hash_normalized)} | ` +\n `${cell(r.marking_methods.join(\" + \"))} | ${code(r.manifest_ref)} | ${cell(r.disclosure_context)} |`,\n ),\n ].join(\"\\n\");\n\n const summaryBreakdowns =\n records.length === 0\n ? \"\"\n : `\n### By modality\n\n| Modality | Records |\n| --- | --- |\n${breakdown(records, (r) => r.modality)}\n\n### By model\n\n| Model | Records |\n| --- | --- |\n${breakdown(records, (r) => r.model)}\n\n### By system_id\n\n| system_id | Records |\n| --- | --- |\n${breakdown(records, (r) => r.system_id)}\n\n### By marking methods\n\n| Marking methods | Records |\n| --- | --- |\n${breakdown(records, (r) => r.marking_methods.join(\" + \"))}\n`;\n\n return `# Evidence audit report (gsengai)\n\n- Generated: ${ctx.generatedAt} (UTC)\n- Store: \\`${cell(ctx.storePath)}\\`\n- Filter: ${describeFilter(ctx.filter)}\n- Record schema: v1 (PRD §4) — hashes and metadata only; raw content is never stored or exported\n\n## Integrity\n\n${integritySection(integrity)}\n\nChain verification always covers the entire store, regardless of the export filter above.\n\n## Summary\n\n- Records (after filter): ${records.length}\n- First record: ${firstTs ?? \"—\"}\n- Last record: ${lastTs ?? \"—\"}\n${summaryBreakdowns}\n## Records\n\n${recordRows}\n\n## What this evidence is — and is not\n\nThis report is a machine-generated record of the outputs an AI system generated, marked,\nand disclosed, as captured in an append-only, hash-chained evidence store. It contains\ncryptographic hashes and metadata only — raw prompts and outputs are never stored and\nnever exported. It supports compliance with EU AI Act Article 50 and California SB 942;\nit is not, by itself, a determination of compliance.\n\n${PRD_S9_LIMITS}\n`;\n}\n","#!/usr/bin/env node\n// SPDX-License-Identifier: Apache-2.0\n// Bin entry for `gsengai-audit` — all logic lives in (and is tested via) runAuditCli.\nimport { runAuditCli } from \"./cli\";\n\nrunAuditCli(process.argv.slice(2)).then(\n (code) => {\n process.exitCode = code;\n },\n (err) => {\n console.error(err);\n process.exitCode = 1;\n },\n);\n"],"mappings":";;;AAGA,SAAS,kBAAkB;AAC3B,SAAS,iBAAiB;;;ACH1B,SAAS,kBAAkB;AAC3B,SAAS,YAAY;AACrB,SAAS,mBAAmB,qBAAqB;AACjD,OAAO,cAAc;;;ACHrB,SAAS,kBAAkB;AAGpB,IAAM,eAAe;AAGrB,SAAS,UAAU,MAAmC;AAC3D,QAAM,OAAO,WAAW,QAAQ;AAChC,MAAI,OAAO,SAAS,UAAU;AAC5B,SAAK,OAAO,MAAM,MAAM;AAAA,EAC1B,OAAO;AACL,SAAK,OAAO,IAAI;AAAA,EAClB;AACA,SAAO,KAAK,OAAO,KAAK;AAC1B;AASO,SAAS,cAAc,KAAqB;AACjD,SAAO,IAAI,UAAU,KAAK,EAAE,YAAY,EAAE,QAAQ,SAAS,GAAG,EAAE,KAAK;AACvE;AAWO,SAAS,SAAS,KAAyB;AAChD,SAAO;AAAA,IACL,YAAY,UAAU,GAAG;AAAA,IACzB,sBAAsB,UAAU,cAAc,GAAG,CAAC;AAAA,IAClD,aAAa;AAAA,EACf;AACF;;;ACnCO,SAAS,cAAc,OAAwB;AACpD,QAAM,MAAM,KAAK,UAAU,OAAO,CAAC,MAAM,QAAiB;AACxD,QAAI,QAAQ,QAAQ,OAAO,QAAQ,YAAY,CAAC,MAAM,QAAQ,GAAG,GAAG;AAClE,YAAM,SAAS;AACf,YAAM,SAAkC,CAAC;AACzC,iBAAW,OAAO,OAAO,KAAK,MAAM,EAAE,KAAK,GAAG;AAC5C,eAAO,GAAG,IAAI,OAAO,GAAG;AAAA,MAC1B;AACA,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT,CAAC;AACD,MAAI,QAAQ,QAAW;AACrB,UAAM,IAAI,UAAU,iDAAiD;AAAA,EACvE;AACA,SAAO;AACT;;;ACrBO,IAAM,sBAAN,cAAkC,MAAM;AAAA,EAC7C,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;;;ACDO,IAAM,cAAc;AAAA,EACzB;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;AAGA,SAAS,SAAS,OAAuC;AACvD,MAAI,UAAU,MAAM;AAClB,WAAO;AAAA,EACT;AACA,QAAM,IAAI,OAAO,KAAK;AACtB,SAAO,WAAW,KAAK,CAAC,IAAI,IAAI,EAAE,WAAW,KAAK,IAAI,CAAC,MAAM;AAC/D;AAEO,SAAS,eAAe,QAAgC;AAC7D,SAAO,YAAY;AAAA,IAAI,CAAC,WACtB;AAAA,MACE,WAAW,oBAAoB,KAAK,UAAU,OAAO,eAAe,IAAI,OAAO,MAAM;AAAA,IACvF;AAAA,EACF,EAAE,KAAK,GAAG;AACZ;AAMO,IAAM,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAsB7B,SAAS,KAAK,OAA8B;AAC1C,MAAI,UAAU,QAAQ,UAAU,IAAI;AAClC,WAAO;AAAA,EACT;AACA,SAAO,MAAM,WAAW,KAAK,KAAK,EAAE,WAAW,WAAW,GAAG;AAC/D;AAEA,SAAS,KAAK,OAA8B;AAC1C,SAAO,UAAU,QAAQ,UAAU,KAAK,KAAK,KAAK,KAAK,KAAK,CAAC;AAC/D;AAEA,SAAS,eAAe,QAA0C;AAChE,QAAM,QAAkB,CAAC;AACzB,MAAI,QAAQ,aAAa,QAAW;AAClC,UAAM,KAAK,iBAAiB,KAAK,OAAO,QAAQ,CAAC,IAAI;AAAA,EACvD;AACA,MAAI,QAAQ,aAAa,QAAW;AAClC,UAAM,KAAK,gBAAgB,OAAO,QAAQ,IAAI;AAAA,EAChD;AACA,MAAI,QAAQ,UAAU,QAAW;AAC/B,UAAM,KAAK,eAAU,OAAO,KAAK,IAAI;AAAA,EACvC;AACA,MAAI,QAAQ,UAAU,QAAW;AAC/B,UAAM,KAAK,eAAU,OAAO,KAAK,IAAI;AAAA,EACvC;AACA,SAAO,MAAM,WAAW,IAAI,sBAAsB,MAAM,KAAK,QAAK;AACpE;AAGA,SAAS,UAAU,SAA2B,OAA8C;AAC1F,QAAM,SAAS,oBAAI,IAAoB;AACvC,aAAW,UAAU,SAAS;AAC5B,UAAM,MAAM,MAAM,MAAM;AACxB,WAAO,IAAI,MAAM,OAAO,IAAI,GAAG,KAAK,KAAK,CAAC;AAAA,EAC5C;AACA,QAAM,OAAO,CAAC,GAAG,OAAO,QAAQ,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC;AACzF,SAAO,KAAK,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM,KAAK,KAAK,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,IAAI;AACpE;AAEA,SAAS,iBAAiB,WAAsC;AAC9D,MAAI,UAAU,IAAI;AAChB,WACE,6BAAwB,UAAU,OAAO;AAAA,EAG7C;AACA,SACE,0BAAqB,UAAU,WAAW,yEAClB,UAAU,WAAW,KAAK,UAAU,OAAO;AAIvE;AAQO,SAAS,kBAAkB,KAAiC;AACjE,QAAM,EAAE,SAAS,UAAU,IAAI;AAC/B,QAAM,UAAU,QAAQ,CAAC,GAAG,MAAM;AAClC,QAAM,SAAS,QAAQ,GAAG,EAAE,GAAG,MAAM;AAErC,QAAM,aACJ,QAAQ,WAAW,IACf,mCACA;AAAA,IACE;AAAA,IACA;AAAA,IACA,GAAG,QAAQ;AAAA,MACT,CAAC,MACC,KAAK,KAAK,EAAE,EAAE,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,QAAQ,MAAM,KAAK,EAAE,KAAK,CAAC,MAAM,KAAK,EAAE,SAAS,CAAC,MAChF,KAAK,EAAE,WAAW,CAAC,MAAM,KAAK,EAAE,sBAAsB,CAAC,MACvD,KAAK,EAAE,gBAAgB,KAAK,KAAK,CAAC,CAAC,MAAM,KAAK,EAAE,YAAY,CAAC,MAAM,KAAK,EAAE,kBAAkB,CAAC;AAAA,IACpG;AAAA,EACF,EAAE,KAAK,IAAI;AAEjB,QAAM,oBACJ,QAAQ,WAAW,IACf,KACA;AAAA;AAAA;AAAA;AAAA;AAAA,EAKN,UAAU,SAAS,CAAC,MAAM,EAAE,QAAQ,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMrC,UAAU,SAAS,CAAC,MAAM,EAAE,KAAK,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMlC,UAAU,SAAS,CAAC,MAAM,EAAE,SAAS,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMtC,UAAU,SAAS,CAAC,MAAM,EAAE,gBAAgB,KAAK,KAAK,CAAC,CAAC;AAAA;AAGxD,SAAO;AAAA;AAAA,eAEM,IAAI,WAAW;AAAA,aACjB,KAAK,IAAI,SAAS,CAAC;AAAA,YACpB,eAAe,IAAI,MAAM,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,EAKpC,iBAAiB,SAAS,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,4BAMD,QAAQ,MAAM;AAAA,kBACxB,WAAW,QAAG;AAAA,iBACf,UAAU,QAAG;AAAA,EAC5B,iBAAiB;AAAA;AAAA;AAAA,EAGjB,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUV,aAAa;AAAA;AAEf;;;AJ3LA,IAAM,aAAkC,oBAAI,IAAI,CAAC,QAAQ,SAAS,SAAS,OAAO,CAAC;AAInF,IAAM,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;AAoDnB,SAAS,YAAY,KAAkC;AACrD,SAAO;AAAA,IACL,IAAI,IAAI;AAAA,IACR,IAAI,IAAI;AAAA,IACR,UAAU,IAAI;AAAA,IACd,OAAO,IAAI;AAAA,IACX,WAAW,IAAI;AAAA,IACf,aAAa,IAAI;AAAA,IACjB,aAAa,IAAI;AAAA,IACjB,wBAAwB,IAAI;AAAA,IAC5B,cAAc,IAAI;AAAA,IAClB,iBAAiB,KAAK,MAAM,IAAI,eAAe;AAAA,IAC/C,cAAc,IAAI;AAAA,IAClB,oBAAoB,IAAI;AAAA,IACxB,WAAW,IAAI;AAAA,IACf,aAAa,IAAI;AAAA,EACnB;AACF;AAGA,SAAS,kBAAkB,MAAmD;AAC5E,SAAO,UAAU,cAAc,IAAI,CAAC;AACtC;AAEA,SAAS,sBAAsB,OAAgB,MAAsB;AACnE,MAAI,OAAO,UAAU,YAAY,MAAM,WAAW,GAAG;AACnD,UAAM,IAAI,UAAU,YAAY,IAAI,6BAA6B;AAAA,EACnE;AACA,SAAO;AACT;AAEA,SAAS,oBAAoB,OAI3B;AACA,QAAM,WAAW,CAAC,MAAM,YAAY,MAAM,aAAa,MAAM,YAAY,EAAE;AAAA,IACzE,CAAC,MAAM,MAAM;AAAA,EACf;AACA,MAAI,SAAS,WAAW,GAAG;AACzB,UAAM,IAAI,UAAU,0EAA0E;AAAA,EAChG;AACA,MAAI,MAAM,eAAe,QAAW;AAElC,UAAM,SAAS,SAAS,MAAM,UAAU;AACxC,WAAO;AAAA,MACL,aAAa,OAAO;AAAA,MACpB,wBAAwB,OAAO;AAAA,MAC/B,cAAc,OAAO;AAAA,IACvB;AAAA,EACF;AACA,MAAI,MAAM,gBAAgB,QAAW;AAGnC,WAAO;AAAA,MACL,aAAa,UAAU,MAAM,WAAW;AAAA,MACxC,wBAAwB;AAAA,MACxB,cAAc;AAAA,IAChB;AAAA,EACF;AACA,QAAM,QAAQ,MAAM;AACpB,SAAO;AAAA,IACL,aAAa,sBAAsB,MAAM,YAAY,yBAAyB;AAAA,IAC9E,wBAAwB,MAAM,wBAAwB;AAAA,IACtD,cAAc,MAAM,eAAe;AAAA,EACrC;AACF;AAEA,SAAS,iBAAiB,OAAe,MAAsB;AAC7D,QAAM,KAAK,KAAK,MAAM,sBAAsB,OAAO,IAAI,CAAC;AACxD,MAAI,OAAO,MAAM,EAAE,GAAG;AACpB,UAAM,IAAI,UAAU,YAAY,IAAI,sCAAsC;AAAA,EAC5E;AACA,SAAO,IAAI,KAAK,EAAE,EAAE,YAAY;AAClC;AAQA,SAAS,YAAY,QAInB;AACA,MAAI,WAAW,QAAW;AACxB,WAAO,EAAE,OAAO,IAAI,QAAQ,CAAC,GAAG,YAAY,OAAU;AAAA,EACxD;AACA,QAAM,aAAuB,CAAC;AAC9B,QAAM,SAAiC,CAAC;AACxC,QAAM,aAA2B,CAAC;AAClC,MAAI,OAAO,aAAa,QAAW;AACjC,eAAW,WAAW,sBAAsB,OAAO,UAAU,iBAAiB;AAC9E,eAAW,KAAK,uBAAuB;AACvC,WAAO,WAAW,WAAW;AAAA,EAC/B;AACA,MAAI,OAAO,aAAa,QAAW;AACjC,QAAI,CAAC,WAAW,IAAI,OAAO,QAAQ,GAAG;AACpC,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AACA,eAAW,WAAW,OAAO;AAC7B,eAAW,KAAK,sBAAsB;AACtC,WAAO,WAAW,WAAW;AAAA,EAC/B;AACA,MAAI,OAAO,UAAU,QAAW;AAC9B,eAAW,QAAQ,iBAAiB,OAAO,OAAO,cAAc;AAChE,eAAW,KAAK,cAAc;AAC9B,WAAO,QAAQ,WAAW;AAAA,EAC5B;AACA,MAAI,OAAO,UAAU,QAAW;AAC9B,eAAW,QAAQ,iBAAiB,OAAO,OAAO,cAAc;AAChE,eAAW,KAAK,cAAc;AAC9B,WAAO,QAAQ,WAAW;AAAA,EAC5B;AACA,MAAI,WAAW,WAAW,GAAG;AAC3B,WAAO,EAAE,OAAO,IAAI,QAAQ,CAAC,GAAG,YAAY,OAAU;AAAA,EACxD;AACA,SAAO,EAAE,OAAO,UAAU,WAAW,KAAK,OAAO,CAAC,IAAI,QAAQ,WAAW;AAC3E;AAEO,SAAS,oBAAoB,SAAoD;AACtF,MAAI,QAAQ,iBAAiB;AAC3B,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AACA,QAAM,YAAY,sBAAsB,QAAQ,MAAM,MAAM;AAC5D,QAAM,KAAK,IAAI,SAAS,SAAS;AACjC,KAAG,OAAO,oBAAoB;AAC9B,KAAG,KAAK,UAAU;AAElB,QAAM,aAAa,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAU7B;AACD,QAAM,eAAe,GAAG,QAAQ,4DAA4D;AAC5F,QAAM,UAAU,GAAG,QAAQ,qCAAqC;AAChE,QAAM,mBAAmB,GAAG,QAAQ,2DAA2D;AAC/F,QAAM,aAAa,GAAG;AAAA,IACpB;AAAA,EACF;AACA,QAAM,YAAY,GAAG,QAAQ,oCAAoC;AAEjE,WAAS,aAAa,QAGpB;AACA,UAAM,EAAE,OAAO,QAAQ,WAAW,IAAI,YAAY,MAAM;AACxD,UAAM,OAAO,GAAG,QAAQ,yBAAyB,KAAK,eAAe;AACrE,WAAO,EAAE,MAAM,KAAK,QAAQ,MAAM,GAAoC,WAAW;AAAA,EACnF;AAEA,WAAS,kBAAqC;AAC5C,QAAI,UAAU;AACd,QAAI,WAA0B;AAC9B,eAAW,OAAO,QAAQ,QAAQ,GAAoC;AACpE,iBAAW;AACX,YAAM,EAAE,aAAa,GAAG,KAAK,IAAI,YAAY,GAAG;AAChD,UAAI,KAAK,cAAc,YAAY,kBAAkB,IAAI,MAAM,aAAa;AAC1E,eAAO,EAAE,IAAI,OAAO,SAAS,aAAa,IAAI,IAAI;AAAA,MACpD;AACA,iBAAW;AAAA,IACb;AACA,WAAO,EAAE,IAAI,MAAM,QAAQ;AAAA,EAC7B;AAIA,QAAM,YAAY,GAAG;AAAA,IACnB,CAAC,SAA4E;AAC3E,YAAM,OAAO,aAAa,IAAI;AAC9B,YAAM,UAA+C;AAAA,QACnD,GAAG;AAAA,QACH,WAAW,OAAO,KAAK,cAAc;AAAA,MACvC;AACA,YAAM,SAAyB,EAAE,GAAG,SAAS,aAAa,kBAAkB,OAAO,EAAE;AACrF,iBAAW,IAAI,EAAE,GAAG,QAAQ,iBAAiB,KAAK,UAAU,OAAO,eAAe,EAAE,CAAC;AACrF,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AAAA,IACL,OAAO,OAA4C;AACjD,YAAM,WAAW,MAAM;AACvB,UAAI,OAAO,aAAa,YAAY,CAAC,WAAW,IAAI,QAAQ,GAAG;AAC7D,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AACA,YAAM,OAA0D;AAAA,QAC9D,IAAI,WAAW;AAAA,QACf,KAAI,oBAAI,KAAK,GAAE,YAAY;AAAA,QAC3B;AAAA,QACA,OAAO,sBAAsB,MAAM,OAAO,OAAO;AAAA,QACjD,WAAW,sBAAsB,MAAM,UAAU,UAAU;AAAA,QAC3D,aAAa,MAAM,cAAc;AAAA,QACjC,GAAG,oBAAoB,KAAK;AAAA,QAC5B,iBAAiB,MAAM,kBAAkB,CAAC,SAAS;AAAA,QACnD,cAAc,MAAM,eAAe;AAAA,QACnC,oBAAoB,MAAM,qBAAqB;AAAA,MACjD;AACA,aAAO,UAAU,UAAU,IAAI;AAAA,IACjC;AAAA,IAEA,iBAAiB,MAAgC;AAC/C,YAAM,OAAO,iBAAiB,IAAI,IAAI;AACtC,aAAO,KAAK,IAAI,WAAW;AAAA,IAC7B;AAAA,IAEA,WAAW,MAAgC;AAIzC,YAAM,SAAS,SAAS,IAAI;AAC5B,YAAM,OAAO,WAAW,IAAI;AAAA,QAC1B,KAAK,OAAO;AAAA,QACZ,YAAY,OAAO;AAAA,MACrB,CAAC;AACD,aAAO,KAAK,IAAI,WAAW;AAAA,IAC7B;AAAA,IAEA,MAAM,YAAY,MAA0D;AAC1E,YAAM,MAAM,kBAAkB,MAAM,EAAE,UAAU,OAAO,CAAC;AACxD,UAAI,UAAU;AACd,UAAI;AACF,mBAAW,OAAO,QAAQ,QAAQ,GAAoC;AACpE,gBAAM,OAAO,GAAG,cAAc,YAAY,GAAG,CAAC,CAAC;AAAA;AAC/C,cAAI,CAAC,IAAI,MAAM,IAAI,GAAG;AACpB,kBAAM,KAAK,KAAK,OAAO;AAAA,UACzB;AACA,qBAAW;AAAA,QACb;AAAA,MACF,UAAE;AACA,YAAI,IAAI;AAAA,MACV;AACA,YAAM,KAAK,KAAK,OAAO;AACvB,aAAO,EAAE,SAAS,KAAK;AAAA,IACzB;AAAA,IAEA,MAAM,UACJ,MACA,QAC4C;AAC5C,YAAM,EAAE,KAAK,IAAI,aAAa,MAAM;AACpC,YAAM,MAAM,kBAAkB,MAAM,EAAE,UAAU,OAAO,CAAC;AACxD,UAAI,UAAU;AACd,UAAI;AACF,YAAI,CAAC,IAAI,MAAM,GAAG,YAAY,KAAK,GAAG,CAAC;AAAA,CAAI,GAAG;AAC5C,gBAAM,KAAK,KAAK,OAAO;AAAA,QACzB;AACA,mBAAW,OAAO,MAAM;AACtB,gBAAM,OAAO,GAAG,eAAe,YAAY,GAAG,CAAC,CAAC;AAAA;AAChD,cAAI,CAAC,IAAI,MAAM,IAAI,GAAG;AACpB,kBAAM,KAAK,KAAK,OAAO;AAAA,UACzB;AACA,qBAAW;AAAA,QACb;AAAA,MACF,UAAE;AACA,YAAI,IAAI;AAAA,MACV;AACA,YAAM,KAAK,KAAK,OAAO;AACvB,aAAO,EAAE,SAAS,KAAK;AAAA,IACzB;AAAA,IAEA,iBAAiB,OAA2B,CAAC,GAAgB;AAC3D,YAAM,EAAE,MAAM,WAAW,IAAI,aAAa,KAAK,MAAM;AACrD,YAAM,UAAU,CAAC,GAAG,IAAI,EAAE,IAAI,WAAW;AAGzC,YAAM,YAAY,gBAAgB;AAClC,YAAM,WAAW,kBAAkB;AAAA,QACjC;AAAA,QACA;AAAA,QACA;AAAA,QACA,QAAQ;AAAA,QACR,cAAa,oBAAI,KAAK,GAAE,YAAY;AAAA,MACtC,CAAC;AACD,UAAI,KAAK,SAAS,QAAW;AAC3B,sBAAc,KAAK,MAAM,UAAU,MAAM;AAAA,MAC3C;AACA,aAAO,EAAE,UAAU,SAAS,QAAQ,QAAQ,WAAW,MAAM,KAAK,QAAQ,KAAK;AAAA,IACjF;AAAA,IAEA,aAAa;AAAA,IAEb,QAAgB;AACd,YAAM,MAAM,UAAU,IAAI;AAC1B,aAAO,IAAI;AAAA,IACb;AAAA,IAEA,QAAc;AACZ,SAAG,MAAM;AAAA,IACX;AAAA,EACF;AACF;;;ADvXA,IAAM,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAuBd,IAAMA,cAAkC,oBAAI,IAAI,CAAC,QAAQ,SAAS,SAAS,OAAO,CAAC;AAEnF,SAAS,aAAa,MAAgB;AACpC,SAAO,UAAU;AAAA,IACf,MAAM;AAAA,IACN,kBAAkB;AAAA,IAClB,SAAS;AAAA,MACP,OAAO,EAAE,MAAM,SAAS;AAAA,MACxB,QAAQ,EAAE,MAAM,SAAS;AAAA,MACzB,KAAK,EAAE,MAAM,SAAS;AAAA,MACtB,aAAa,EAAE,MAAM,SAAS;AAAA,MAC9B,UAAU,EAAE,MAAM,SAAS;AAAA,MAC3B,OAAO,EAAE,MAAM,SAAS;AAAA,MACxB,OAAO,EAAE,MAAM,SAAS;AAAA,MACxB,MAAM,EAAE,MAAM,UAAU;AAAA,IAC1B;AAAA,EACF,CAAU;AACZ;AAMA,eAAsB,YAAY,MAAgB,KAAY,SAA0B;AACtF,MAAI;AACJ,MAAI;AACF,aAAS,aAAa,IAAI;AAAA,EAC5B,SAAS,KAAK;AACZ,OAAG,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AACzD,OAAG,MAAM,KAAK;AACd,WAAO;AAAA,EACT;AACA,QAAM,EAAE,QAAQ,YAAY,IAAI;AAEhC,MAAI,OAAO,SAAS,MAAM;AACxB,OAAG,IAAI,KAAK;AACZ,WAAO;AAAA,EACT;AACA,MAAI,YAAY,CAAC,MAAM,YAAY,YAAY,WAAW,GAAG;AAC3D,OAAG,MAAM,uDAAuD;AAChE,OAAG,MAAM,KAAK;AACd,WAAO;AAAA,EACT;AACA,QAAM,YAAY,OAAO;AACzB,QAAM,SAAS,OAAO;AACtB,QAAM,UAAU,OAAO;AACvB,MAAI,cAAc,UAAa,WAAW,UAAa,YAAY,QAAW;AAC5E,OAAG,MAAM,0DAA0D;AACnE,OAAG,MAAM,KAAK;AACd,WAAO;AAAA,EACT;AACA,MAAI,WAAW,SAAS,WAAW,UAAU;AAC3C,OAAG,MAAM,oCAAoC,MAAM,4BAA4B;AAC/E,WAAO;AAAA,EACT;AAEA,MAAI,CAAC,WAAW,SAAS,GAAG;AAC1B,OAAG,MAAM,8CAA8C,SAAS,GAAG;AACnE,WAAO;AAAA,EACT;AACA,QAAM,WAAW,OAAO;AACxB,MAAI,aAAa,UAAa,CAACA,YAAW,IAAI,QAAQ,GAAG;AACvD,OAAG,MAAM,sCAAsC,QAAQ,qCAAqC;AAC5F,WAAO;AAAA,EACT;AACA,QAAM,SAAuB,CAAC;AAC9B,MAAI,OAAO,WAAW,MAAM,QAAW;AACrC,WAAO,WAAW,OAAO,WAAW;AAAA,EACtC;AACA,MAAI,aAAa,QAAW;AAC1B,WAAO,WAAW;AAAA,EACpB;AACA,MAAI,OAAO,UAAU,QAAW;AAC9B,WAAO,QAAQ,OAAO;AAAA,EACxB;AACA,MAAI,OAAO,UAAU,QAAW;AAC9B,WAAO,QAAQ,OAAO;AAAA,EACxB;AAEA,QAAM,QAAQ,oBAAoB,EAAE,MAAM,UAAU,CAAC;AACrD,MAAI;AACF,QAAI,WAAW,OAAO;AACpB,YAAM,SAAS,MAAM,MAAM,UAAU,SAAS,MAAM;AACpD,SAAG,IAAI,eAAe,OAAO,OAAO,iBAAiB,OAAO,IAAI,EAAE;AAAA,IACpE,OAAO;AACL,YAAM,SAAS,MAAM,iBAAiB,EAAE,MAAM,SAAS,OAAO,CAAC;AAC/D,SAAG,IAAI,iBAAiB,OAAO,OAAO,iBAAiB,OAAO,EAAE;AAChE,UAAI,OAAO,UAAU,IAAI;AACvB,WAAG,IAAI,8BAA8B,OAAO,UAAU,OAAO,qBAAqB;AAAA,MACpF,OAAO;AAEL,WAAG;AAAA,UACD,8CAA8C,OAAO,UAAU,WAAW;AAAA,QAE5E;AAAA,MACF;AAAA,IACF;AAAA,EACF,SAAS,KAAK;AACZ,OAAG,MAAM,iCAAiC,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,EAAE;AAC5F,WAAO;AAAA,EACT,UAAE;AACA,UAAM,MAAM;AAAA,EACd;AACA,SAAO;AACT;;;AMlIA,YAAY,QAAQ,KAAK,MAAM,CAAC,CAAC,EAAE;AAAA,EACjC,CAACC,UAAS;AACR,YAAQ,WAAWA;AAAA,EACrB;AAAA,EACA,CAAC,QAAQ;AACP,YAAQ,MAAM,GAAG;AACjB,YAAQ,WAAW;AAAA,EACrB;AACF;","names":["MODALITIES","code"]}