{"version":3,"file":"evidence-archive.d.ts","sourceRoot":"","sources":["../../../src/core/context-runtime/evidence-archive.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAOH,MAAM,MAAM,YAAY,GAAG,aAAa,GAAG,MAAM,GAAG,KAAK,GAAG,YAAY,CAAC;AAEzE,MAAM,WAAW,cAAc;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,YAAY,CAAC;IACnB,oEAAoE;IACpE,MAAM,EAAE,MAAM,CAAC;IACf,kEAAkE;IAClE,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC/B,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,cAAc,EAAE,YAAY,GAAG,aAAa,GAAG,cAAc,GAAG,aAAa,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACpH,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAAC;IAC9D,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC1C;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAUlD;AAED,kFAAkF;AAClF,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAGhG;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE;IAC1C,IAAI,EAAE,YAAY,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;CACb,GAAG,cAAc,CAYjB;AAED,qBAAa,uBAAwB,YAAW,eAAe;IAC9D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqC;IAEvD,KAAK,CACV,MAAM,EAAE,IAAI,CAAC,cAAc,EAAE,YAAY,GAAG,aAAa,GAAG,cAAc,GAAG,aAAa,CAAC,GACzF,OAAO,CAAC,MAAM,CAAC,CAIjB;IAEK,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAElE;IAEK,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAE9C;IAED,IAAI,IAAI,IAAI,MAAM,CAEjB;CACD;AAID;;;;GAIG;AACH,qBAAa,iBAAkB,YAAW,eAAe;IACxD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAE9B,YAAY,IAAI,EAAE,MAAM,EAEvB;IAED,OAAO,CAAC,OAAO;IAIT,KAAK,CACV,MAAM,EAAE,IAAI,CAAC,cAAc,EAAE,YAAY,GAAG,aAAa,GAAG,cAAc,GAAG,aAAa,CAAC,GACzF,OAAO,CAAC,MAAM,CAAC,CAcjB;IAEK,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAUlE;IAEK,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAE9C;CACD","sourcesContent":["/**\n * Cold evidence archive (L3) for context virtualization.\n *\n * Large raw tool outputs, command logs, diagnostics, and file excerpts are\n * persisted here durably and replaced in the hot working set by a concise\n * synopsis + a deterministic evidence reference. This is not the Reliability\n * Kernel evidence ledger — it stores raw operational artifacts; verification\n * authority remains with the Evidence Store / Completion Gate.\n */\n\nimport { createHash } from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as fsp from \"node:fs/promises\";\nimport * as path from \"node:path\";\n\nexport type EvidenceKind = \"tool-result\" | \"file\" | \"log\" | \"diagnostic\";\n\nexport interface EvidenceRecord {\n\tevidenceId: string;\n\tkind: EvidenceKind;\n\t/** Tool name, file path, or command that produced this artifact. */\n\tsource: string;\n\t/** Redacted raw content (secrets scrubbed before persistence). */\n\tcontent: string;\n\tcontentHash: string;\n\tcontentBytes: number;\n\tcreatedAtMs: number;\n}\n\nexport interface EvidenceArchive {\n\tstore(record: Omit<EvidenceRecord, \"evidenceId\" | \"contentHash\" | \"contentBytes\" | \"createdAtMs\">): Promise<string>;\n\tload(evidenceId: string): Promise<EvidenceRecord | undefined>;\n\thas(evidenceId: string): Promise<boolean>;\n}\n\nexport function hashContent(content: string): string {\n\treturn createHash(\"sha256\").update(content, \"utf8\").digest(\"hex\");\n}\n\n/**\n * Conservative secret redaction. Never persist API keys, bearer tokens, GitHub\n * personal access tokens, or common credential assignments into cold evidence.\n * This is a best-effort scrub for raw operational artifacts only.\n */\nexport function redactSecrets(text: string): string {\n\treturn text\n\t\t.replace(/(sk-[A-Za-z0-9_-]{16,})/g, \"[REDACTED_API_KEY]\")\n\t\t.replace(/(ghp_[A-Za-z0-9]{20,})/g, \"[REDACTED_GITHUB_TOKEN]\")\n\t\t.replace(/(Bearer\\s+)[A-Za-z0-9._-]{16,}/g, \"$1[REDACTED]\")\n\t\t.replace(/(AKIA[0-9A-Z]{16})/g, \"[REDACTED_AWS_KEY]\")\n\t\t.replace(\n\t\t\t/((?:api[_-]?key|secret|password|token|credential)\\s*[=:]\\s*)([\"']?)[^\\s\"']{8,}\\2/gi,\n\t\t\t\"$1$2[REDACTED]$2\",\n\t\t);\n}\n\n/** Deterministic, content-addressed evidence id (not secret, not random-only). */\nexport function deriveEvidenceId(kind: EvidenceKind, source: string, contentHash: string): string {\n\tconst short = contentHash.slice(0, 16);\n\treturn `${kind}:${source.replace(/[^A-Za-z0-9._-]+/g, \"-\").slice(0, 64)}:${short}`;\n}\n\nexport function buildEvidenceRecord(input: {\n\tkind: EvidenceKind;\n\tsource: string;\n\tcontent: string;\n\tnow?: number;\n}): EvidenceRecord {\n\tconst content = redactSecrets(input.content);\n\tconst contentHash = hashContent(content);\n\treturn {\n\t\tevidenceId: deriveEvidenceId(input.kind, input.source, contentHash),\n\t\tkind: input.kind,\n\t\tsource: input.source,\n\t\tcontent,\n\t\tcontentHash,\n\t\tcontentBytes: Buffer.byteLength(content, \"utf8\"),\n\t\tcreatedAtMs: input.now ?? Date.now(),\n\t};\n}\n\nexport class InMemoryEvidenceArchive implements EvidenceArchive {\n\tprivate readonly records = new Map<string, EvidenceRecord>();\n\n\tasync store(\n\t\trecord: Omit<EvidenceRecord, \"evidenceId\" | \"contentHash\" | \"contentBytes\" | \"createdAtMs\">,\n\t): Promise<string> {\n\t\tconst built = buildEvidenceRecord(record);\n\t\tthis.records.set(built.evidenceId, built);\n\t\treturn built.evidenceId;\n\t}\n\n\tasync load(evidenceId: string): Promise<EvidenceRecord | undefined> {\n\t\treturn this.records.get(evidenceId);\n\t}\n\n\tasync has(evidenceId: string): Promise<boolean> {\n\t\treturn this.records.has(evidenceId);\n\t}\n\n\tget size(): number {\n\t\treturn this.records.size;\n\t}\n}\n\nconst SUFFIX = \".tmp\";\n\n/**\n * Atomic file-backed cold evidence store. Writes go temp-then-rename so a\n * process exit never leaves a partial artifact; reads fail closed to undefined\n * on corruption (never fabricate evidence).\n */\nexport class EvidenceFileStore implements EvidenceArchive {\n\tprivate readonly root: string;\n\n\tconstructor(root: string) {\n\t\tthis.root = root;\n\t}\n\n\tprivate resolve(evidenceId: string): string {\n\t\treturn path.join(this.root, `${evidenceId}.evidence.json`);\n\t}\n\n\tasync store(\n\t\trecord: Omit<EvidenceRecord, \"evidenceId\" | \"contentHash\" | \"contentBytes\" | \"createdAtMs\">,\n\t): Promise<string> {\n\t\tconst built = buildEvidenceRecord(record);\n\t\tawait fsp.mkdir(this.root, { recursive: true });\n\t\tconst target = this.resolve(built.evidenceId);\n\t\tconst tmp = `${target}${SUFFIX}`;\n\t\tawait fsp.writeFile(tmp, JSON.stringify(built), \"utf8\");\n\t\tconst fh = await fsp.open(tmp, \"r\");\n\t\ttry {\n\t\t\tawait fh.sync();\n\t\t} finally {\n\t\t\tawait fh.close();\n\t\t}\n\t\tawait fsp.rename(tmp, target);\n\t\treturn built.evidenceId;\n\t}\n\n\tasync load(evidenceId: string): Promise<EvidenceRecord | undefined> {\n\t\ttry {\n\t\t\tconst data = await fsp.readFile(this.resolve(evidenceId), \"utf8\");\n\t\t\tconst parsed = JSON.parse(data) as EvidenceRecord;\n\t\t\t// Fail closed on malformed artifacts.\n\t\t\tif (typeof parsed.evidenceId !== \"string\" || typeof parsed.content !== \"string\") return undefined;\n\t\t\treturn parsed;\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\tasync has(evidenceId: string): Promise<boolean> {\n\t\treturn fs.existsSync(this.resolve(evidenceId));\n\t}\n}\n"]}