open-multi-agent-kit 0.95.2 → 0.96.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.
Files changed (70) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +15 -0
  3. package/dist/approvals/approval-receipt-store.d.ts.map +1 -1
  4. package/dist/approvals/approval-receipt-store.js +25 -0
  5. package/dist/approvals/approval-receipt-store.js.map +1 -1
  6. package/dist/core/agent-session.d.ts +2 -2
  7. package/dist/core/agent-session.d.ts.map +1 -1
  8. package/dist/core/agent-session.js +41 -17
  9. package/dist/core/agent-session.js.map +1 -1
  10. package/dist/core/provider-resilience.d.ts +7 -0
  11. package/dist/core/provider-resilience.d.ts.map +1 -1
  12. package/dist/core/provider-resilience.js +11 -0
  13. package/dist/core/provider-resilience.js.map +1 -1
  14. package/dist/core/subagent-orchestration.d.ts.map +1 -1
  15. package/dist/core/subagent-orchestration.js +4 -2
  16. package/dist/core/subagent-orchestration.js.map +1 -1
  17. package/dist/core/tools/diagnostics.d.ts.map +1 -1
  18. package/dist/core/tools/diagnostics.js +8 -2
  19. package/dist/core/tools/diagnostics.js.map +1 -1
  20. package/dist/guardrails/evidence-protocol.d.ts +10 -0
  21. package/dist/guardrails/evidence-protocol.d.ts.map +1 -0
  22. package/dist/guardrails/evidence-protocol.js +34 -0
  23. package/dist/guardrails/evidence-protocol.js.map +1 -0
  24. package/dist/guardrails/evidence-system.d.ts +3 -1
  25. package/dist/guardrails/evidence-system.d.ts.map +1 -1
  26. package/dist/guardrails/evidence-system.js +22 -14
  27. package/dist/guardrails/evidence-system.js.map +1 -1
  28. package/dist/guardrails/replay-ledger-lock-owner.d.ts.map +1 -1
  29. package/dist/guardrails/replay-ledger-lock-owner.js +3 -1
  30. package/dist/guardrails/replay-ledger-lock-owner.js.map +1 -1
  31. package/dist/guardrails/replay-payload-hash.d.ts +6 -0
  32. package/dist/guardrails/replay-payload-hash.d.ts.map +1 -0
  33. package/dist/guardrails/replay-payload-hash.js +24 -0
  34. package/dist/guardrails/replay-payload-hash.js.map +1 -0
  35. package/dist/index.d.ts +2 -1
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +1 -0
  38. package/dist/index.js.map +1 -1
  39. package/dist/types/evidence.d.ts +11 -3
  40. package/dist/types/evidence.d.ts.map +1 -1
  41. package/dist/types/evidence.js.map +1 -1
  42. package/docs/book-to-skill.md +79 -0
  43. package/docs/compaction.md +10 -0
  44. package/docs/docs.json +8 -0
  45. package/docs/index.md +3 -0
  46. package/docs/models.md +12 -0
  47. package/docs/provider-resilience.md +37 -56
  48. package/docs/providers.md +4 -0
  49. package/docs/run-protocol.md +72 -0
  50. package/docs/sdk.md +15 -2
  51. package/docs/sessions.md +6 -0
  52. package/docs/settings.md +2 -0
  53. package/docs/skills.md +9 -1
  54. package/docs/usage.md +1 -1
  55. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  56. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  57. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  58. package/examples/extensions/gondolin/package-lock.json +2 -2
  59. package/examples/extensions/gondolin/package.json +1 -1
  60. package/examples/extensions/sandbox/package-lock.json +2 -2
  61. package/examples/extensions/sandbox/package.json +1 -1
  62. package/examples/extensions/with-deps/package-lock.json +2 -2
  63. package/examples/extensions/with-deps/package.json +1 -1
  64. package/npm-shrinkwrap.json +37 -15
  65. package/package.json +8 -5
  66. package/dist/core/context-budget-compressors.d.ts +0 -50
  67. package/dist/core/context-budget-compressors.d.ts.map +0 -1
  68. package/dist/core/context-budget-compressors.js +0 -166
  69. package/dist/core/context-budget-compressors.js.map +0 -1
  70. package/dist/core/extensions/loader.js.bak-sharedjiti-20260812-111246 +0 -553
@@ -1 +1 @@
1
- {"version":3,"file":"diagnostics.js","sourceRoot":"","sources":["../../../src/core/tools/diagnostics.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Compiler-backed diagnostics tool (LSP-class value via project-native checkers).\n *\n * Routes per language to the checker the project already uses and normalizes\n * output to `SEVERITY path:line:col message`. Fail-soft by construction: a\n * missing checker, missing project marker, or checker crash reports `skipped`\n * for that language instead of throwing. Results are TTL-cached (5 s) since\n * project-wide checks are expensive.\n *\n * | language | checker (first found) | scope |\n * |------------|------------------------------------------|-------------------|\n * | typescript | `tsc --noEmit -p <nearest tsconfig>` | project |\n * | python | `pyright --outputjson` / `ruff check` | file or project |\n * | go | `go vet ./...` | module |\n * | rust | `cargo check --message-format short` | workspace |\n */\nimport { spawn } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { AgentTool } from \"omk-agent-core\";\nimport { type Static, Type } from \"typebox\";\nimport type { ToolDefinition } from \"../extensions/types.ts\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.ts\";\n\nconst diagnosticsSchema = Type.Object({\n\tpath: Type.Optional(Type.String({ description: \"Restrict diagnostics to this file (relative to cwd)\" })),\n\tlanguage: Type.Optional(\n\t\tType.Union(\n\t\t\t[\n\t\t\t\tType.Literal(\"auto\"),\n\t\t\t\tType.Literal(\"typescript\"),\n\t\t\t\tType.Literal(\"python\"),\n\t\t\t\tType.Literal(\"go\"),\n\t\t\t\tType.Literal(\"rust\"),\n\t\t\t],\n\t\t\t{\n\t\t\t\tdescription: \"Language to check; auto detects from path extension and project markers\",\n\t\t\t},\n\t\t),\n\t),\n\ttimeout: Type.Optional(Type.Number({ description: \"Per-language timeout in seconds (default 90)\" })),\n});\n\nexport type DiagnosticsToolInput = Static<typeof diagnosticsSchema>;\n\nexport interface DiagnosticLine {\n\treadonly language: string;\n\treadonly severity: \"error\" | \"warning\" | \"info\";\n\treadonly path: string;\n\treadonly line: number;\n\treadonly column: number;\n\treadonly message: string;\n}\n\nexport interface DiagnosticsToolDetails {\n\treadonly diagnostics: readonly DiagnosticLine[];\n\treadonly skipped: readonly { language: string; reason: string }[];\n\treadonly counts: Record<string, number>;\n}\n\nconst CACHE_TTL_MS = 5_000;\nconst MAX_DIAGNOSTICS = 50;\nconst DEFAULT_TIMEOUT_MS = 90_000;\n\nconst cache = new Map<string, { at: number; result: LanguageResult }>();\n\ninterface LanguageResult {\n\treadonly lines: readonly DiagnosticLine[];\n\treadonly skippedReason?: string;\n}\n\nfunction commandExists(command: string): Promise<boolean> {\n\treturn new Promise((resolve) => {\n\t\tconst probe = spawn(\"sh\", [\"-c\", `command -v ${command}`], { stdio: \"ignore\" });\n\t\tprobe.on(\"close\", (code) => resolve(code === 0));\n\t\tprobe.on(\"error\", () => resolve(false));\n\t});\n}\n\nfunction run(\n\tcommand: string,\n\targs: readonly string[],\n\tcwd: string,\n\ttimeoutMs: number,\n): Promise<{ code: number | null; out: string }> {\n\treturn new Promise((resolve) => {\n\t\tconst child = spawn(command, args, { cwd, stdio: [\"ignore\", \"pipe\", \"pipe\"], windowsHide: true });\n\t\tlet out = \"\";\n\t\tconst timer = setTimeout(() => {\n\t\t\tchild.kill(\"SIGKILL\");\n\t\t}, timeoutMs);\n\t\tchild.stdout.on(\"data\", (d: Buffer) => {\n\t\t\tif (out.length < 4 * 1024 * 1024) out += d.toString(\"utf8\");\n\t\t});\n\t\tchild.stderr.on(\"data\", (d: Buffer) => {\n\t\t\tif (out.length < 4 * 1024 * 1024) out += d.toString(\"utf8\");\n\t\t});\n\t\tchild.on(\"close\", (code) => {\n\t\t\tclearTimeout(timer);\n\t\t\tresolve({ code, out });\n\t\t});\n\t\tchild.on(\"error\", () => {\n\t\t\tclearTimeout(timer);\n\t\t\tresolve({ code: null, out });\n\t\t});\n\t});\n}\n\nfunction nearestMarker(cwd: string, names: readonly string[]): string | undefined {\n\t// ponytail: project markers live at the session root in this harness; walk up\n\t// at most 4 levels for nested worktrees.\n\tlet dir = cwd;\n\tfor (let level = 0; level <= 4; level++) {\n\t\tfor (const name of names) {\n\t\t\tif (existsSync(join(dir, name))) return dir;\n\t\t}\n\t\tconst parent = join(dir, \"..\");\n\t\tif (parent === dir) return undefined;\n\t\tdir = parent;\n\t}\n\treturn undefined;\n}\n\nconst TS_ERROR = /^(?<path>.+?)\\((?<line>\\d+),(?<col>\\d+)\\): error TS\\d+: (?<msg>.+)$/;\n\nfunction parseTsc(out: string, language: string): DiagnosticLine[] {\n\tconst lines: DiagnosticLine[] = [];\n\tfor (const raw of out.split(\"\\n\")) {\n\t\tconst match = TS_ERROR.exec(raw.trim());\n\t\tif (match?.groups) {\n\t\t\tlines.push({\n\t\t\t\tlanguage,\n\t\t\t\tseverity: \"error\",\n\t\t\t\tpath: match.groups.path,\n\t\t\t\tline: Number(match.groups.line),\n\t\t\t\tcolumn: Number(match.groups.col),\n\t\t\t\tmessage: match.groups.msg.trim(),\n\t\t\t});\n\t\t}\n\t}\n\treturn lines;\n}\n\nconst PYRIGHT_ISSUE = /^(?<path>.+?):(?<line>\\d+):(?<col>\\d+) - (?<sev>error|warning|information): (?<msg>.+)$/;\n\nfunction parsePyright(out: string): DiagnosticLine[] {\n\tconst lines: DiagnosticLine[] = [];\n\t// pyright --outputjson wraps issues in JSON; fall back to the text form when absent.\n\ttry {\n\t\tconst parsed = JSON.parse(out) as {\n\t\t\tgeneralDiagnostics?: {\n\t\t\t\tfile: string;\n\t\t\t\tseverity: string;\n\t\t\t\trange: { start: { line: number; character: number } };\n\t\t\t\tmessage: string;\n\t\t\t}[];\n\t\t};\n\t\tfor (const issue of parsed.generalDiagnostics ?? []) {\n\t\t\tlines.push({\n\t\t\t\tlanguage: \"python\",\n\t\t\t\tseverity: issue.severity === \"error\" ? \"error\" : issue.severity === \"warning\" ? \"warning\" : \"info\",\n\t\t\t\tpath: issue.file,\n\t\t\t\tline: issue.range.start.line + 1,\n\t\t\t\tcolumn: issue.range.start.character + 1,\n\t\t\t\tmessage: issue.message,\n\t\t\t});\n\t\t}\n\t\treturn lines;\n\t} catch {\n\t\tfor (const raw of out.split(\"\\n\")) {\n\t\t\tconst match = PYRIGHT_ISSUE.exec(raw.trim());\n\t\t\tif (match?.groups) {\n\t\t\t\tlines.push({\n\t\t\t\t\tlanguage: \"python\",\n\t\t\t\t\tseverity: match.groups.sev === \"error\" ? \"error\" : match.groups.sev === \"warning\" ? \"warning\" : \"info\",\n\t\t\t\t\tpath: match.groups.path,\n\t\t\t\t\tline: Number(match.groups.line),\n\t\t\t\t\tcolumn: Number(match.groups.col),\n\t\t\t\t\tmessage: match.groups.msg.trim(),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\treturn lines;\n\t}\n}\n\nconst RUFF_ISSUE = /^(?<path>.+?):(?<line>\\d+):(?<col>\\d+): (?<msg>.+)$/;\n\nfunction parseRuff(out: string): DiagnosticLine[] {\n\tconst lines: DiagnosticLine[] = [];\n\tfor (const raw of out.split(\"\\n\")) {\n\t\tconst match = RUFF_ISSUE.exec(raw.trim());\n\t\tif (match?.groups) {\n\t\t\tlines.push({\n\t\t\t\tlanguage: \"python\",\n\t\t\t\tseverity: \"warning\",\n\t\t\t\tpath: match.groups.path,\n\t\t\t\tline: Number(match.groups.line),\n\t\t\t\tcolumn: Number(match.groups.col),\n\t\t\t\tmessage: match.groups.msg.trim(),\n\t\t\t});\n\t\t}\n\t}\n\treturn lines;\n}\n\nconst GO_VET = /^(?:#\\s+\\S+\\s+)?(?<path>.+?\\.go):(?<line>\\d+):(?<col>\\d+): (?<msg>.+)$/;\n\nfunction parseGoVet(out: string): DiagnosticLine[] {\n\tconst lines: DiagnosticLine[] = [];\n\tfor (const raw of out.split(\"\\n\")) {\n\t\tconst match = GO_VET.exec(raw.trim());\n\t\tif (match?.groups) {\n\t\t\tlines.push({\n\t\t\t\tlanguage: \"go\",\n\t\t\t\tseverity: \"error\",\n\t\t\t\tpath: match.groups.path,\n\t\t\t\tline: Number(match.groups.line),\n\t\t\t\tcolumn: Number(match.groups.col),\n\t\t\t\tmessage: match.groups.msg.trim(),\n\t\t\t});\n\t\t}\n\t}\n\treturn lines;\n}\n\nconst CARGO_SHORT = /^(?<path>.+?):(?<line>\\d+):(?<col>\\d+): (?<msg>.+)$/;\n\nfunction parseCargo(out: string): DiagnosticLine[] {\n\tconst lines: DiagnosticLine[] = [];\n\tfor (const raw of out.split(\"\\n\")) {\n\t\tconst match = CARGO_SHORT.exec(raw.trim());\n\t\tif (match?.groups) {\n\t\t\tconst sev = match.groups.msg.startsWith(\"warning\") ? \"warning\" : \"error\";\n\t\t\tlines.push({\n\t\t\t\tlanguage: \"rust\",\n\t\t\t\tseverity: sev,\n\t\t\t\tpath: match.groups.path,\n\t\t\t\tline: Number(match.groups.line),\n\t\t\t\tcolumn: Number(match.groups.col),\n\t\t\t\tmessage: match.groups.msg.trim(),\n\t\t\t});\n\t\t}\n\t}\n\treturn lines;\n}\n\nasync function checkTypeScript(cwd: string, path: string | undefined, timeoutMs: number): Promise<LanguageResult> {\n\tconst root = nearestMarker(cwd, [\"tsconfig.json\", \"tsconfig.build.json\"]);\n\tif (!root) return { lines: [], skippedReason: \"no tsconfig found\" };\n\tconst tsc = join(cwd, \"node_modules\", \".bin\", \"tsc\");\n\tconst localBin = existsSync(tsc) ? tsc : join(root, \"node_modules\", \".bin\", \"tsc\");\n\tconst bin = existsSync(localBin) ? localBin : \"tsc\";\n\tconst config = existsSync(join(root, \"tsconfig.json\")) ? \"tsconfig.json\" : \"tsconfig.build.json\";\n\tconst { code, out } = await run(bin, [\"--noEmit\", \"-p\", config, \"--pretty\", \"false\"], root, timeoutMs);\n\tif (code === null) return { lines: [], skippedReason: \"tsc spawn failed\" };\n\treturn { lines: parseTsc(out, \"typescript\").filter((d) => !path || d.path.includes(path)) };\n}\n\nasync function checkPython(cwd: string, path: string | undefined, timeoutMs: number): Promise<LanguageResult> {\n\tconst target = path ?? \".\";\n\tif (await commandExists(\"pyright\")) {\n\t\tconst { code, out } = await run(\"pyright\", [\"--outputjson\", target], cwd, timeoutMs);\n\t\tif (code === null) return { lines: [], skippedReason: \"pyright spawn failed\" };\n\t\treturn { lines: parsePyright(out) };\n\t}\n\tif (await commandExists(\"ruff\")) {\n\t\tconst { code, out } = await run(\"ruff\", [\"check\", \"--no-cache\", target], cwd, timeoutMs);\n\t\tif (code === null) return { lines: [], skippedReason: \"ruff spawn failed\" };\n\t\treturn { lines: parseRuff(out) };\n\t}\n\treturn { lines: [], skippedReason: \"pyright/ruff not installed\" };\n}\n\nasync function checkGo(cwd: string, path: string | undefined, timeoutMs: number): Promise<LanguageResult> {\n\tconst root = nearestMarker(cwd, [\"go.mod\"]);\n\tif (!root) return { lines: [], skippedReason: \"no go.mod found\" };\n\tif (!(await commandExists(\"go\"))) return { lines: [], skippedReason: \"go not installed\" };\n\tconst { code, out } = await run(\"go\", [\"vet\", \"./...\"], root, timeoutMs);\n\tif (code === null) return { lines: [], skippedReason: \"go vet spawn failed\" };\n\treturn { lines: parseGoVet(out).filter((d) => !path || d.path.includes(path)) };\n}\n\nasync function checkRust(cwd: string, path: string | undefined, timeoutMs: number): Promise<LanguageResult> {\n\tconst root = nearestMarker(cwd, [\"Cargo.toml\"]);\n\tif (!root) return { lines: [], skippedReason: \"no Cargo.toml found\" };\n\tif (!(await commandExists(\"cargo\"))) return { lines: [], skippedReason: \"cargo not installed\" };\n\tconst { code, out } = await run(\"cargo\", [\"check\", \"--quiet\", \"--message-format\", \"short\"], root, timeoutMs);\n\tif (code === null) return { lines: [], skippedReason: \"cargo spawn failed\" };\n\treturn { lines: parseCargo(out).filter((d) => !path || d.path.includes(path)) };\n}\n\nconst CHECKERS = {\n\ttypescript: checkTypeScript,\n\tpython: checkPython,\n\tgo: checkGo,\n\trust: checkRust,\n} as const;\n\ntype Language = keyof typeof CHECKERS;\n\nfunction detectLanguages(cwd: string, path: string | undefined): Language[] {\n\tif (path) {\n\t\tif (/\\.tsx?$/.test(path)) return [\"typescript\"];\n\t\tif (/\\.py$/.test(path)) return [\"python\"];\n\t\tif (/\\.go$/.test(path)) return [\"go\"];\n\t\tif (/\\.rs$/.test(path)) return [\"rust\"];\n\t}\n\tconst detected: Language[] = [];\n\tif (nearestMarker(cwd, [\"tsconfig.json\", \"tsconfig.build.json\"])) detected.push(\"typescript\");\n\tif (nearestMarker(cwd, [\"pyproject.toml\", \"setup.py\", \"setup.cfg\", \"requirements.txt\"])) detected.push(\"python\");\n\tif (nearestMarker(cwd, [\"go.mod\"])) detected.push(\"go\");\n\tif (nearestMarker(cwd, [\"Cargo.toml\"])) detected.push(\"rust\");\n\treturn detected.length > 0 ? detected : [\"typescript\", \"python\", \"go\", \"rust\"];\n}\n\nasync function checkLanguage(\n\tlanguage: Language,\n\tcwd: string,\n\tpath: string | undefined,\n\ttimeoutMs: number,\n): Promise<LanguageResult> {\n\tconst key = `${language}${cwd}${path ?? \"\"}`;\n\tconst cached = cache.get(key);\n\tif (cached && Date.now() - cached.at < CACHE_TTL_MS) return cached.result;\n\tconst result = await CHECKERS[language](cwd, path, timeoutMs);\n\tif (cache.size >= 16) {\n\t\tconst oldest = cache.keys().next().value;\n\t\tif (oldest !== undefined) cache.delete(oldest);\n\t}\n\tcache.set(key, { at: Date.now(), result });\n\treturn result;\n}\n\nexport function createDiagnosticsToolDefinition(\n\tcwd: string,\n): ToolDefinition<typeof diagnosticsSchema, DiagnosticsToolDetails> {\n\treturn {\n\t\tname: \"diagnostics\",\n\t\tlabel: \"diagnostics\",\n\t\tdescription:\n\t\t\t\"Run the project's own type/lint checkers (tsc, pyright/ruff, go vet, cargo check) and return normalized diagnostics. Missing checkers or missing project markers are reported as skipped, never as tool errors.\",\n\t\tpromptSnippet: \"Get compiler/lint diagnostics for the workspace or a file\",\n\t\tparameters: diagnosticsSchema,\n\t\tasync execute(_toolCallId, { path, language, timeout }: DiagnosticsToolInput) {\n\t\t\tconst timeoutMs = timeout !== undefined && timeout > 0 ? timeout * 1000 : DEFAULT_TIMEOUT_MS;\n\t\t\tconst languages = language && language !== \"auto\" ? [language] : detectLanguages(cwd, path);\n\t\t\tconst settled = await Promise.all(languages.map((lang) => checkLanguage(lang, cwd, path, timeoutMs)));\n\t\t\tconst diagnostics: DiagnosticLine[] = [];\n\t\t\tconst skipped: { language: string; reason: string }[] = [];\n\t\t\tfor (let index = 0; index < languages.length; index++) {\n\t\t\t\tconst result = settled[index];\n\t\t\t\tif (result.skippedReason) skipped.push({ language: languages[index], reason: result.skippedReason });\n\t\t\t\tdiagnostics.push(...result.lines);\n\t\t\t}\n\t\t\tconst capped = diagnostics.slice(0, MAX_DIAGNOSTICS);\n\t\t\tconst counts: Record<string, number> = {};\n\t\t\tfor (const d of diagnostics) counts[d.language] = (counts[d.language] ?? 0) + 1;\n\t\t\tconst text =\n\t\t\t\tcapped.length === 0 && skipped.length === 0\n\t\t\t\t\t? \"No diagnostics.\"\n\t\t\t\t\t: [\n\t\t\t\t\t\t\t...capped.map(\n\t\t\t\t\t\t\t\t(d) =>\n\t\t\t\t\t\t\t\t\t`${d.severity.toUpperCase()} ${d.path}:${d.line}:${d.column} ${d.message} (${d.language})`,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t...(diagnostics.length > capped.length ? [`... ${diagnostics.length - capped.length} more`] : []),\n\t\t\t\t\t\t\t...skipped.map((s) => `${s.language}: skipped (${s.reason})`),\n\t\t\t\t\t\t].join(\"\\n\");\n\t\t\treturn { content: [{ type: \"text\", text }], details: { diagnostics: capped, skipped, counts } };\n\t\t},\n\t};\n}\n\nexport function createDiagnosticsTool(cwd: string): AgentTool<typeof diagnosticsSchema> {\n\treturn wrapToolDefinition(createDiagnosticsToolDefinition(cwd));\n}\n"]}
1
+ {"version":3,"file":"diagnostics.js","sourceRoot":"","sources":["../../../src/core/tools/diagnostics.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Compiler-backed diagnostics tool (LSP-class value via project-native checkers).\n *\n * Routes per language to the checker the project already uses and normalizes\n * output to `SEVERITY path:line:col message`. Fail-soft by construction: a\n * missing checker, missing project marker, or checker crash reports `skipped`\n * for that language instead of throwing. Results are TTL-cached (5 s) since\n * project-wide checks are expensive.\n *\n * | language | checker (first found) | scope |\n * |------------|------------------------------------------|-------------------|\n * | typescript | `tsc --noEmit -p <nearest tsconfig>` | project |\n * | python | `pyright --outputjson` / `ruff check` | file or project |\n * | go | `go vet ./...` | module |\n * | rust | `cargo check --message-format short` | workspace |\n */\nimport { spawn } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { AgentTool } from \"omk-agent-core\";\nimport { type Static, Type } from \"typebox\";\nimport type { ToolDefinition } from \"../extensions/types.ts\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.ts\";\n\nconst diagnosticsSchema = Type.Object({\n\tpath: Type.Optional(Type.String({ description: \"Restrict diagnostics to this file (relative to cwd)\" })),\n\tlanguage: Type.Optional(\n\t\tType.Union(\n\t\t\t[\n\t\t\t\tType.Literal(\"auto\"),\n\t\t\t\tType.Literal(\"typescript\"),\n\t\t\t\tType.Literal(\"python\"),\n\t\t\t\tType.Literal(\"go\"),\n\t\t\t\tType.Literal(\"rust\"),\n\t\t\t],\n\t\t\t{\n\t\t\t\tdescription: \"Language to check; auto detects from path extension and project markers\",\n\t\t\t},\n\t\t),\n\t),\n\ttimeout: Type.Optional(Type.Number({ description: \"Per-language timeout in seconds (default 90)\" })),\n});\n\nexport type DiagnosticsToolInput = Static<typeof diagnosticsSchema>;\n\nexport interface DiagnosticLine {\n\treadonly language: string;\n\treadonly severity: \"error\" | \"warning\" | \"info\";\n\treadonly path: string;\n\treadonly line: number;\n\treadonly column: number;\n\treadonly message: string;\n}\n\nexport interface DiagnosticsToolDetails {\n\treadonly diagnostics: readonly DiagnosticLine[];\n\treadonly skipped: readonly { language: string; reason: string }[];\n\treadonly counts: Record<string, number>;\n}\n\nconst CACHE_TTL_MS = 5_000;\nconst MAX_DIAGNOSTICS = 50;\nconst DEFAULT_TIMEOUT_MS = 90_000;\n\nconst cache = new Map<string, { at: number; result: LanguageResult }>();\n\ninterface LanguageResult {\n\treadonly lines: readonly DiagnosticLine[];\n\treadonly skippedReason?: string;\n}\n\nfunction commandExists(command: string): Promise<boolean> {\n\treturn new Promise((resolve) => {\n\t\tconst probe = spawn(\"sh\", [\"-c\", `command -v ${command}`], { stdio: \"ignore\" });\n\t\tprobe.on(\"close\", (code) => resolve(code === 0));\n\t\tprobe.on(\"error\", () => resolve(false));\n\t});\n}\n\nfunction run(\n\tcommand: string,\n\targs: readonly string[],\n\tcwd: string,\n\ttimeoutMs: number,\n): Promise<{ code: number | null; out: string }> {\n\treturn new Promise((resolve) => {\n\t\tconst child = spawn(command, args, { cwd, stdio: [\"ignore\", \"pipe\", \"pipe\"], windowsHide: true });\n\t\tlet out = \"\";\n\t\tconst timer = setTimeout(() => {\n\t\t\tchild.kill(\"SIGKILL\");\n\t\t}, timeoutMs);\n\t\tchild.stdout.on(\"data\", (d: Buffer) => {\n\t\t\tif (out.length < 4 * 1024 * 1024) out += d.toString(\"utf8\");\n\t\t});\n\t\tchild.stderr.on(\"data\", (d: Buffer) => {\n\t\t\tif (out.length < 4 * 1024 * 1024) out += d.toString(\"utf8\");\n\t\t});\n\t\tchild.on(\"close\", (code) => {\n\t\t\tclearTimeout(timer);\n\t\t\tresolve({ code, out });\n\t\t});\n\t\tchild.on(\"error\", () => {\n\t\t\tclearTimeout(timer);\n\t\t\tresolve({ code: null, out });\n\t\t});\n\t});\n}\n\nfunction nearestMarker(cwd: string, names: readonly string[]): string | undefined {\n\t// ponytail: project markers live at the session root in this harness; walk up\n\t// at most 4 levels for nested worktrees.\n\tlet dir = cwd;\n\tfor (let level = 0; level <= 4; level++) {\n\t\tfor (const name of names) {\n\t\t\tif (existsSync(join(dir, name))) return dir;\n\t\t}\n\t\tconst parent = join(dir, \"..\");\n\t\tif (parent === dir) return undefined;\n\t\tdir = parent;\n\t}\n\treturn undefined;\n}\n\nconst TS_ERROR = /^(?<path>.+?)\\((?<line>\\d+),(?<col>\\d+)\\): error TS\\d+: (?<msg>.+)$/;\n\nfunction parseTsc(out: string, language: string): DiagnosticLine[] {\n\tconst lines: DiagnosticLine[] = [];\n\tfor (const raw of out.split(\"\\n\")) {\n\t\tconst match = TS_ERROR.exec(raw.trim());\n\t\tif (match?.groups) {\n\t\t\tlines.push({\n\t\t\t\tlanguage,\n\t\t\t\tseverity: \"error\",\n\t\t\t\tpath: match.groups.path,\n\t\t\t\tline: Number(match.groups.line),\n\t\t\t\tcolumn: Number(match.groups.col),\n\t\t\t\tmessage: match.groups.msg.trim(),\n\t\t\t});\n\t\t}\n\t}\n\treturn lines;\n}\n\nconst PYRIGHT_ISSUE = /^(?<path>.+?):(?<line>\\d+):(?<col>\\d+) - (?<sev>error|warning|information): (?<msg>.+)$/;\n\nfunction parsePyright(out: string): DiagnosticLine[] {\n\tconst lines: DiagnosticLine[] = [];\n\t// pyright --outputjson wraps issues in JSON; fall back to the text form when absent.\n\ttry {\n\t\tconst parsed = JSON.parse(out) as {\n\t\t\tgeneralDiagnostics?: {\n\t\t\t\tfile: string;\n\t\t\t\tseverity: string;\n\t\t\t\trange: { start: { line: number; character: number } };\n\t\t\t\tmessage: string;\n\t\t\t}[];\n\t\t};\n\t\tfor (const issue of parsed.generalDiagnostics ?? []) {\n\t\t\tlines.push({\n\t\t\t\tlanguage: \"python\",\n\t\t\t\tseverity: issue.severity === \"error\" ? \"error\" : issue.severity === \"warning\" ? \"warning\" : \"info\",\n\t\t\t\tpath: issue.file,\n\t\t\t\tline: issue.range.start.line + 1,\n\t\t\t\tcolumn: issue.range.start.character + 1,\n\t\t\t\tmessage: issue.message,\n\t\t\t});\n\t\t}\n\t\treturn lines;\n\t} catch {\n\t\tfor (const raw of out.split(\"\\n\")) {\n\t\t\tconst match = PYRIGHT_ISSUE.exec(raw.trim());\n\t\t\tif (match?.groups) {\n\t\t\t\tlines.push({\n\t\t\t\t\tlanguage: \"python\",\n\t\t\t\t\tseverity: match.groups.sev === \"error\" ? \"error\" : match.groups.sev === \"warning\" ? \"warning\" : \"info\",\n\t\t\t\t\tpath: match.groups.path,\n\t\t\t\t\tline: Number(match.groups.line),\n\t\t\t\t\tcolumn: Number(match.groups.col),\n\t\t\t\t\tmessage: match.groups.msg.trim(),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\treturn lines;\n\t}\n}\n\nconst RUFF_ISSUE = /^(?<path>.+?):(?<line>\\d+):(?<col>\\d+): (?<msg>.+)$/;\n\nfunction parseRuff(out: string): DiagnosticLine[] {\n\tconst lines: DiagnosticLine[] = [];\n\tfor (const raw of out.split(\"\\n\")) {\n\t\tconst match = RUFF_ISSUE.exec(raw.trim());\n\t\tif (match?.groups) {\n\t\t\tlines.push({\n\t\t\t\tlanguage: \"python\",\n\t\t\t\tseverity: \"warning\",\n\t\t\t\tpath: match.groups.path,\n\t\t\t\tline: Number(match.groups.line),\n\t\t\t\tcolumn: Number(match.groups.col),\n\t\t\t\tmessage: match.groups.msg.trim(),\n\t\t\t});\n\t\t}\n\t}\n\treturn lines;\n}\n\nconst GO_VET = /^(?:#\\s+\\S+\\s+)?(?<path>.+?\\.go):(?<line>\\d+):(?<col>\\d+): (?<msg>.+)$/;\n\nfunction parseGoVet(out: string): DiagnosticLine[] {\n\tconst lines: DiagnosticLine[] = [];\n\tfor (const raw of out.split(\"\\n\")) {\n\t\tconst match = GO_VET.exec(raw.trim());\n\t\tif (match?.groups) {\n\t\t\tlines.push({\n\t\t\t\tlanguage: \"go\",\n\t\t\t\tseverity: \"error\",\n\t\t\t\tpath: match.groups.path,\n\t\t\t\tline: Number(match.groups.line),\n\t\t\t\tcolumn: Number(match.groups.col),\n\t\t\t\tmessage: match.groups.msg.trim(),\n\t\t\t});\n\t\t}\n\t}\n\treturn lines;\n}\n\nconst CARGO_SHORT = /^(?<path>.+?):(?<line>\\d+):(?<col>\\d+): (?<msg>.+)$/;\n\nfunction parseCargo(out: string): DiagnosticLine[] {\n\tconst lines: DiagnosticLine[] = [];\n\tfor (const raw of out.split(\"\\n\")) {\n\t\tconst match = CARGO_SHORT.exec(raw.trim());\n\t\tif (match?.groups) {\n\t\t\tconst sev = match.groups.msg.startsWith(\"warning\") ? \"warning\" : \"error\";\n\t\t\tlines.push({\n\t\t\t\tlanguage: \"rust\",\n\t\t\t\tseverity: sev,\n\t\t\t\tpath: match.groups.path,\n\t\t\t\tline: Number(match.groups.line),\n\t\t\t\tcolumn: Number(match.groups.col),\n\t\t\t\tmessage: match.groups.msg.trim(),\n\t\t\t});\n\t\t}\n\t}\n\treturn lines;\n}\n\nasync function checkTypeScript(cwd: string, path: string | undefined, timeoutMs: number): Promise<LanguageResult> {\n\tconst root = nearestMarker(cwd, [\"tsconfig.json\", \"tsconfig.build.json\"]);\n\tif (!root) return { lines: [], skippedReason: \"no tsconfig found\" };\n\tconst tsc = join(cwd, \"node_modules\", \".bin\", \"tsc\");\n\tconst localBin = existsSync(tsc) ? tsc : join(root, \"node_modules\", \".bin\", \"tsc\");\n\tconst bin = existsSync(localBin) ? localBin : \"tsc\";\n\tconst config = existsSync(join(root, \"tsconfig.json\")) ? \"tsconfig.json\" : \"tsconfig.build.json\";\n\tconst { code, out } = await run(bin, [\"--noEmit\", \"-p\", config, \"--pretty\", \"false\"], root, timeoutMs);\n\tif (code === null) return { lines: [], skippedReason: \"tsc spawn failed\" };\n\treturn { lines: parseTsc(out, \"typescript\").filter((d) => !path || d.path.includes(path)) };\n}\n\nasync function checkPython(cwd: string, path: string | undefined, timeoutMs: number): Promise<LanguageResult> {\n\tconst target = path ?? \".\";\n\tif (await commandExists(\"pyright\")) {\n\t\tconst { code, out } = await run(\"pyright\", [\"--outputjson\", target], cwd, timeoutMs);\n\t\tif (code === null) return { lines: [], skippedReason: \"pyright spawn failed\" };\n\t\tconst lines = parsePyright(out);\n\t\t// pyright가 사용 가능한 출력을 내지 못했으면(깨진 설치·실행 오류) 조용히 0건으로\n\t\t// 보고하지 말고 ruff로 폴�백한다. `--outputjson`은 정상 실행 시 항상\n\t\t// generalDiagnostics JSON을 낸다 — 마커가 없으면 실행 실패로 간주한다.\n\t\tif (lines.length > 0 || out.includes('\"generalDiagnostics\"')) return { lines };\n\t}\n\tif (await commandExists(\"ruff\")) {\n\t\t// ruff의 기본 full 출력은 parseRuff가 읽지 못하므로 concise(path:line:col: msg)를 강제한다.\n\t\tconst { code, out } = await run(\n\t\t\t\"ruff\",\n\t\t\t[\"check\", \"--no-cache\", \"--output-format\", \"concise\", target],\n\t\t\tcwd,\n\t\t\ttimeoutMs,\n\t\t);\n\t\tif (code === null) return { lines: [], skippedReason: \"ruff spawn failed\" };\n\t\treturn { lines: parseRuff(out) };\n\t}\n\treturn { lines: [], skippedReason: \"pyright/ruff not installed\" };\n}\n\nasync function checkGo(cwd: string, path: string | undefined, timeoutMs: number): Promise<LanguageResult> {\n\tconst root = nearestMarker(cwd, [\"go.mod\"]);\n\tif (!root) return { lines: [], skippedReason: \"no go.mod found\" };\n\tif (!(await commandExists(\"go\"))) return { lines: [], skippedReason: \"go not installed\" };\n\tconst { code, out } = await run(\"go\", [\"vet\", \"./...\"], root, timeoutMs);\n\tif (code === null) return { lines: [], skippedReason: \"go vet spawn failed\" };\n\treturn { lines: parseGoVet(out).filter((d) => !path || d.path.includes(path)) };\n}\n\nasync function checkRust(cwd: string, path: string | undefined, timeoutMs: number): Promise<LanguageResult> {\n\tconst root = nearestMarker(cwd, [\"Cargo.toml\"]);\n\tif (!root) return { lines: [], skippedReason: \"no Cargo.toml found\" };\n\tif (!(await commandExists(\"cargo\"))) return { lines: [], skippedReason: \"cargo not installed\" };\n\tconst { code, out } = await run(\"cargo\", [\"check\", \"--quiet\", \"--message-format\", \"short\"], root, timeoutMs);\n\tif (code === null) return { lines: [], skippedReason: \"cargo spawn failed\" };\n\treturn { lines: parseCargo(out).filter((d) => !path || d.path.includes(path)) };\n}\n\nconst CHECKERS = {\n\ttypescript: checkTypeScript,\n\tpython: checkPython,\n\tgo: checkGo,\n\trust: checkRust,\n} as const;\n\ntype Language = keyof typeof CHECKERS;\n\nfunction detectLanguages(cwd: string, path: string | undefined): Language[] {\n\tif (path) {\n\t\tif (/\\.tsx?$/.test(path)) return [\"typescript\"];\n\t\tif (/\\.py$/.test(path)) return [\"python\"];\n\t\tif (/\\.go$/.test(path)) return [\"go\"];\n\t\tif (/\\.rs$/.test(path)) return [\"rust\"];\n\t}\n\tconst detected: Language[] = [];\n\tif (nearestMarker(cwd, [\"tsconfig.json\", \"tsconfig.build.json\"])) detected.push(\"typescript\");\n\tif (nearestMarker(cwd, [\"pyproject.toml\", \"setup.py\", \"setup.cfg\", \"requirements.txt\"])) detected.push(\"python\");\n\tif (nearestMarker(cwd, [\"go.mod\"])) detected.push(\"go\");\n\tif (nearestMarker(cwd, [\"Cargo.toml\"])) detected.push(\"rust\");\n\treturn detected.length > 0 ? detected : [\"typescript\", \"python\", \"go\", \"rust\"];\n}\n\nasync function checkLanguage(\n\tlanguage: Language,\n\tcwd: string,\n\tpath: string | undefined,\n\ttimeoutMs: number,\n): Promise<LanguageResult> {\n\tconst key = `${language}${cwd}${path ?? \"\"}`;\n\tconst cached = cache.get(key);\n\tif (cached && Date.now() - cached.at < CACHE_TTL_MS) return cached.result;\n\tconst result = await CHECKERS[language](cwd, path, timeoutMs);\n\tif (cache.size >= 16) {\n\t\tconst oldest = cache.keys().next().value;\n\t\tif (oldest !== undefined) cache.delete(oldest);\n\t}\n\tcache.set(key, { at: Date.now(), result });\n\treturn result;\n}\n\nexport function createDiagnosticsToolDefinition(\n\tcwd: string,\n): ToolDefinition<typeof diagnosticsSchema, DiagnosticsToolDetails> {\n\treturn {\n\t\tname: \"diagnostics\",\n\t\tlabel: \"diagnostics\",\n\t\tdescription:\n\t\t\t\"Run the project's own type/lint checkers (tsc, pyright/ruff, go vet, cargo check) and return normalized diagnostics. Missing checkers or missing project markers are reported as skipped, never as tool errors.\",\n\t\tpromptSnippet: \"Get compiler/lint diagnostics for the workspace or a file\",\n\t\tparameters: diagnosticsSchema,\n\t\tasync execute(_toolCallId, { path, language, timeout }: DiagnosticsToolInput) {\n\t\t\tconst timeoutMs = timeout !== undefined && timeout > 0 ? timeout * 1000 : DEFAULT_TIMEOUT_MS;\n\t\t\tconst languages = language && language !== \"auto\" ? [language] : detectLanguages(cwd, path);\n\t\t\tconst settled = await Promise.all(languages.map((lang) => checkLanguage(lang, cwd, path, timeoutMs)));\n\t\t\tconst diagnostics: DiagnosticLine[] = [];\n\t\t\tconst skipped: { language: string; reason: string }[] = [];\n\t\t\tfor (let index = 0; index < languages.length; index++) {\n\t\t\t\tconst result = settled[index];\n\t\t\t\tif (result.skippedReason) skipped.push({ language: languages[index], reason: result.skippedReason });\n\t\t\t\tdiagnostics.push(...result.lines);\n\t\t\t}\n\t\t\tconst capped = diagnostics.slice(0, MAX_DIAGNOSTICS);\n\t\t\tconst counts: Record<string, number> = {};\n\t\t\tfor (const d of diagnostics) counts[d.language] = (counts[d.language] ?? 0) + 1;\n\t\t\tconst text =\n\t\t\t\tcapped.length === 0 && skipped.length === 0\n\t\t\t\t\t? \"No diagnostics.\"\n\t\t\t\t\t: [\n\t\t\t\t\t\t\t...capped.map(\n\t\t\t\t\t\t\t\t(d) =>\n\t\t\t\t\t\t\t\t\t`${d.severity.toUpperCase()} ${d.path}:${d.line}:${d.column} ${d.message} (${d.language})`,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t...(diagnostics.length > capped.length ? [`... ${diagnostics.length - capped.length} more`] : []),\n\t\t\t\t\t\t\t...skipped.map((s) => `${s.language}: skipped (${s.reason})`),\n\t\t\t\t\t\t].join(\"\\n\");\n\t\t\treturn { content: [{ type: \"text\", text }], details: { diagnostics: capped, skipped, counts } };\n\t\t},\n\t};\n}\n\nexport function createDiagnosticsTool(cwd: string): AgentTool<typeof diagnosticsSchema> {\n\treturn wrapToolDefinition(createDiagnosticsToolDefinition(cwd));\n}\n"]}
@@ -0,0 +1,10 @@
1
+ import { type Observation } from "omk-protocol";
2
+ import type { EvidenceReceipt } from "../types/evidence.ts";
3
+ /**
4
+ * Projects an integrity-checked EvidenceReceipt v3 into immutable protocol facts.
5
+ *
6
+ * This verifies the receipt core digest. Ledger membership and trusted attestation
7
+ * remain separate integrity checks and are not implied by this adapter.
8
+ */
9
+ export declare function evidenceReceiptToObservation(receipt: EvidenceReceipt, attemptId: string): Observation;
10
+ //# sourceMappingURL=evidence-protocol.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evidence-protocol.d.ts","sourceRoot":"","sources":["../../src/guardrails/evidence-protocol.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAoB,MAAM,cAAc,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAG5D;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,GAAG,WAAW,CAuBrG","sourcesContent":["import { type Observation, PROTOCOL_VERSION } from \"omk-protocol\";\nimport type { EvidenceReceipt } from \"../types/evidence.ts\";\nimport { validateEvidenceReceipt } from \"./evidence-receipt.ts\";\n\n/**\n * Projects an integrity-checked EvidenceReceipt v3 into immutable protocol facts.\n *\n * This verifies the receipt core digest. Ledger membership and trusted attestation\n * remain separate integrity checks and are not implied by this adapter.\n */\nexport function evidenceReceiptToObservation(receipt: EvidenceReceipt, attemptId: string): Observation {\n\tif (attemptId.trim().length === 0) throw new Error(\"attemptId must be a non-empty string\");\n\tconst validated = validateEvidenceReceipt(receipt);\n\tconst { core, envelope } = validated;\n\treturn Object.freeze({\n\t\tschemaVersion: PROTOCOL_VERSION,\n\t\tobservationId: `evidence-receipt:${core.receiptId}`,\n\t\ttaskId: core.goalId,\n\t\tattemptId,\n\t\tobservedAt: core.finishedAt,\n\t\tkind: \"evidence_receipt.v3\",\n\t\tsource: Object.freeze({ kind: \"evidence_receipt\", id: core.receiptId }),\n\t\tfacts: Object.freeze({\n\t\t\treceiptSchemaVersion: core.schemaVersion,\n\t\t\tclaim: core.claim,\n\t\t\texitCode: core.exitCode,\n\t\t\ttimedOut: core.status === \"timeout\",\n\t\t\taborted: core.status === \"aborted\",\n\t\t\tdurationMs: core.durationMs,\n\t\t\texecutor: core.executor,\n\t\t}),\n\t\tevidenceRefs: Object.freeze([`evidence-receipt:${core.receiptId}#sha256:${envelope.coreSha256}`]),\n\t});\n}\n"]}
@@ -0,0 +1,34 @@
1
+ import { PROTOCOL_VERSION } from "omk-protocol";
2
+ import { validateEvidenceReceipt } from "./evidence-receipt.js";
3
+ /**
4
+ * Projects an integrity-checked EvidenceReceipt v3 into immutable protocol facts.
5
+ *
6
+ * This verifies the receipt core digest. Ledger membership and trusted attestation
7
+ * remain separate integrity checks and are not implied by this adapter.
8
+ */
9
+ export function evidenceReceiptToObservation(receipt, attemptId) {
10
+ if (attemptId.trim().length === 0)
11
+ throw new Error("attemptId must be a non-empty string");
12
+ const validated = validateEvidenceReceipt(receipt);
13
+ const { core, envelope } = validated;
14
+ return Object.freeze({
15
+ schemaVersion: PROTOCOL_VERSION,
16
+ observationId: `evidence-receipt:${core.receiptId}`,
17
+ taskId: core.goalId,
18
+ attemptId,
19
+ observedAt: core.finishedAt,
20
+ kind: "evidence_receipt.v3",
21
+ source: Object.freeze({ kind: "evidence_receipt", id: core.receiptId }),
22
+ facts: Object.freeze({
23
+ receiptSchemaVersion: core.schemaVersion,
24
+ claim: core.claim,
25
+ exitCode: core.exitCode,
26
+ timedOut: core.status === "timeout",
27
+ aborted: core.status === "aborted",
28
+ durationMs: core.durationMs,
29
+ executor: core.executor,
30
+ }),
31
+ evidenceRefs: Object.freeze([`evidence-receipt:${core.receiptId}#sha256:${envelope.coreSha256}`]),
32
+ });
33
+ }
34
+ //# sourceMappingURL=evidence-protocol.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evidence-protocol.js","sourceRoot":"","sources":["../../src/guardrails/evidence-protocol.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAElE,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAEhE;;;;;GAKG;AACH,MAAM,UAAU,4BAA4B,CAAC,OAAwB,EAAE,SAAiB,EAAe;IACtG,IAAI,SAAS,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;IAC3F,MAAM,SAAS,GAAG,uBAAuB,CAAC,OAAO,CAAC,CAAC;IACnD,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,SAAS,CAAC;IACrC,OAAO,MAAM,CAAC,MAAM,CAAC;QACpB,aAAa,EAAE,gBAAgB;QAC/B,aAAa,EAAE,oBAAoB,IAAI,CAAC,SAAS,EAAE;QACnD,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,SAAS;QACT,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,IAAI,EAAE,qBAAqB;QAC3B,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,kBAAkB,EAAE,EAAE,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC;QACvE,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC;YACpB,oBAAoB,EAAE,IAAI,CAAC,aAAa;YACxC,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,QAAQ,EAAE,IAAI,CAAC,MAAM,KAAK,SAAS;YACnC,OAAO,EAAE,IAAI,CAAC,MAAM,KAAK,SAAS;YAClC,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,QAAQ,EAAE,IAAI,CAAC,QAAQ;SACvB,CAAC;QACF,YAAY,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,oBAAoB,IAAI,CAAC,SAAS,WAAW,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;KACjG,CAAC,CAAC;AAAA,CACH","sourcesContent":["import { type Observation, PROTOCOL_VERSION } from \"omk-protocol\";\nimport type { EvidenceReceipt } from \"../types/evidence.ts\";\nimport { validateEvidenceReceipt } from \"./evidence-receipt.ts\";\n\n/**\n * Projects an integrity-checked EvidenceReceipt v3 into immutable protocol facts.\n *\n * This verifies the receipt core digest. Ledger membership and trusted attestation\n * remain separate integrity checks and are not implied by this adapter.\n */\nexport function evidenceReceiptToObservation(receipt: EvidenceReceipt, attemptId: string): Observation {\n\tif (attemptId.trim().length === 0) throw new Error(\"attemptId must be a non-empty string\");\n\tconst validated = validateEvidenceReceipt(receipt);\n\tconst { core, envelope } = validated;\n\treturn Object.freeze({\n\t\tschemaVersion: PROTOCOL_VERSION,\n\t\tobservationId: `evidence-receipt:${core.receiptId}`,\n\t\ttaskId: core.goalId,\n\t\tattemptId,\n\t\tobservedAt: core.finishedAt,\n\t\tkind: \"evidence_receipt.v3\",\n\t\tsource: Object.freeze({ kind: \"evidence_receipt\", id: core.receiptId }),\n\t\tfacts: Object.freeze({\n\t\t\treceiptSchemaVersion: core.schemaVersion,\n\t\t\tclaim: core.claim,\n\t\t\texitCode: core.exitCode,\n\t\t\ttimedOut: core.status === \"timeout\",\n\t\t\taborted: core.status === \"aborted\",\n\t\t\tdurationMs: core.durationMs,\n\t\t\texecutor: core.executor,\n\t\t}),\n\t\tevidenceRefs: Object.freeze([`evidence-receipt:${core.receiptId}#sha256:${envelope.coreSha256}`]),\n\t});\n}\n"]}
@@ -13,7 +13,9 @@ export declare class TaskContractBuilder {
13
13
  setClaim(claim: string): this;
14
14
  addRequiredEvidence(evidence: Omit<EvidenceItem, "timestamp" | "status">): this;
15
15
  setFinalRisk(risk: string): this;
16
+ /** @deprecated Use `evaluateTask()` from `omk-protocol`; verdicts are derived, not mutated. */
16
17
  setVerdict(verdict: "pass" | "fail" | "conditional"): this;
18
+ /** @deprecated Append a protocol Observation and re-evaluate instead of mutating evidence state. */
17
19
  updateEvidenceStatus(claim: string, status: EvidenceStatus, gapReason?: string): this;
18
20
  private touch;
19
21
  build(): TaskContract;
@@ -29,7 +31,7 @@ export declare class ReplayLedgerManager {
29
31
  private committedHead;
30
32
  constructor(goalId: string, ledgerPath: string, expectedHead?: ReplayLedgerHead);
31
33
  private refresh;
32
- append(event: Omit<ReplayEvent, "seq" | "timestamp" | "payloadHash" | "prevHash" | "eventHash">, expectedHead?: ReplayLedgerHead): ReplayEvent;
34
+ append(event: Omit<ReplayEvent, "seq" | "timestamp" | "payloadHashAlgorithm" | "payloadHash" | "prevHash" | "eventHash">, expectedHead?: ReplayLedgerHead): ReplayEvent;
33
35
  /** Compatibility no-op that still verifies the caller's expected committed head. */
34
36
  persist(expectedHead?: ReplayLedgerHead): void;
35
37
  load(expectedHead?: ReplayLedgerHead): void;
@@ -1 +1 @@
1
- {"version":3,"file":"evidence-system.d.ts","sourceRoot":"","sources":["../../src/guardrails/evidence-system.ts"],"names":[],"mappings":"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","sourcesContent":["// allow: SIZE_OK - evidence ledger guardrails: tamper-evident replay chain + fail-closed parsing.\nimport { createHash } from \"node:crypto\";\nimport { existsSync, mkdirSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport type {\n\tEvidenceCommandDescriptor,\n\tEvidenceItem,\n\tEvidenceReceiptMode,\n\tEvidenceStatus,\n\tMergeGateResult,\n\tMergeGateStatus,\n\tReplayEvent,\n\tReplayLedger,\n\tReplayLedgerHead,\n\tTaskContract,\n\tVerifiedReplayLedgerSnapshot,\n\tWorkspaceScope,\n} from \"../types/evidence.ts\";\nimport { redactCommandDescriptor } from \"./command-redaction.ts\";\nimport {\n\ttype CommandHmacBinder,\n\tlatestRelevantWorkspaceMutationSeq,\n\tparseVerifiedLedgerSnapshot,\n\treplayPayloadMatches,\n\tverifyCommandAttestation,\n\tverifyWorkspaceBinding,\n} from \"./evidence-attestation.ts\";\n\nexport { latestRelevantWorkspaceMutationSeq } from \"./evidence-attestation.ts\";\n\nimport {\n\tcomputeEvidenceCommandSha256,\n\tconstantTimeSha256Equal,\n\tevidenceReceiptReplayPayload,\n\tparseSha256Hex,\n\tvalidateEvidenceReceipt,\n} from \"./evidence-receipt.ts\";\nimport { ReplayLedgerStore, replayLedgerHeadsEqual } from \"./replay-ledger-store.ts\";\n\nfunction sha256(input: string): string {\n\treturn createHash(\"sha256\").update(input).digest(\"hex\");\n}\n\nfunction ensureDir(path: string): void {\n\tconst dir = dirname(path);\n\tif (!existsSync(dir)) {\n\t\tmkdirSync(dir, { recursive: true });\n\t}\n}\n\n// ============================================================================\n// TaskContract\n// ============================================================================\n\nexport class TaskContractBuilder {\n\tprivate goalId: string;\n\tprivate completionClaim: string = \"\";\n\tprivate requiredEvidence: EvidenceItem[] = [];\n\tprivate finalRisk: string = \"\";\n\tprivate verdict: \"pass\" | \"fail\" | \"conditional\" = \"fail\";\n\tprivate createdAt: string;\n\tprivate updatedAt: string;\n\n\tconstructor(goalId: string) {\n\t\tthis.goalId = goalId;\n\t\tconst now = new Date().toISOString();\n\t\tthis.createdAt = now;\n\t\tthis.updatedAt = now;\n\t}\n\n\tsetClaim(claim: string): this {\n\t\tthis.completionClaim = claim;\n\t\tthis.touch();\n\t\treturn this;\n\t}\n\n\taddRequiredEvidence(evidence: Omit<EvidenceItem, \"timestamp\" | \"status\">): this {\n\t\tthis.requiredEvidence.push({\n\t\t\t...evidence,\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tstatus: \"pending\",\n\t\t});\n\t\tthis.touch();\n\t\treturn this;\n\t}\n\n\tsetFinalRisk(risk: string): this {\n\t\tthis.finalRisk = risk;\n\t\tthis.touch();\n\t\treturn this;\n\t}\n\n\tsetVerdict(verdict: \"pass\" | \"fail\" | \"conditional\"): this {\n\t\tthis.verdict = verdict;\n\t\tthis.touch();\n\t\treturn this;\n\t}\n\n\tupdateEvidenceStatus(claim: string, status: EvidenceStatus, gapReason?: string): this {\n\t\tconst item = this.requiredEvidence.find((e) => e.claim === claim);\n\t\tif (item) {\n\t\t\titem.status = status;\n\t\t\titem.gapReason = gapReason;\n\t\t\titem.timestamp = new Date().toISOString();\n\t\t\tthis.touch();\n\t\t}\n\t\treturn this;\n\t}\n\n\tprivate touch(): void {\n\t\tthis.updatedAt = new Date().toISOString();\n\t}\n\n\tbuild(): TaskContract {\n\t\treturn structuredClone({\n\t\t\tgoalId: this.goalId,\n\t\t\tcompletionClaim: this.completionClaim,\n\t\t\trequiredEvidence: this.requiredEvidence,\n\t\t\tfinalRisk: this.finalRisk,\n\t\t\tverdict: this.verdict,\n\t\t\tcreatedAt: this.createdAt,\n\t\t\tupdatedAt: this.updatedAt,\n\t\t});\n\t}\n\n\t/** Parse and validate a serialized TaskContract. Fails closed on any shape violation. */\n\tstatic fromJSON(json: string): TaskContract {\n\t\tconst raw: unknown = JSON.parse(json);\n\t\tif (raw === null || typeof raw !== \"object\" || Array.isArray(raw)) {\n\t\t\tthrow new Error(\"TaskContract JSON must be an object\");\n\t\t}\n\t\tconst { goalId, completionClaim, finalRisk, verdict, createdAt, updatedAt, requiredEvidence } = raw as Record<\n\t\t\tstring,\n\t\t\tunknown\n\t\t>;\n\t\tif (\n\t\t\ttypeof goalId !== \"string\" ||\n\t\t\ttypeof completionClaim !== \"string\" ||\n\t\t\ttypeof finalRisk !== \"string\" ||\n\t\t\ttypeof createdAt !== \"string\" ||\n\t\t\ttypeof updatedAt !== \"string\"\n\t\t) {\n\t\t\tthrow new Error(\"TaskContract JSON has missing or mistyped string fields\");\n\t\t}\n\t\tif (verdict !== \"pass\" && verdict !== \"fail\" && verdict !== \"conditional\") {\n\t\t\tthrow new Error(`TaskContract verdict must be pass|fail|conditional, got ${String(verdict)}`);\n\t\t}\n\t\tif (!Array.isArray(requiredEvidence)) {\n\t\t\tthrow new Error(\"TaskContract requiredEvidence must be an array\");\n\t\t}\n\t\treturn {\n\t\t\tgoalId,\n\t\t\tcompletionClaim,\n\t\t\tfinalRisk,\n\t\t\tverdict,\n\t\t\tcreatedAt,\n\t\t\tupdatedAt,\n\t\t\trequiredEvidence: requiredEvidence.map((item, index) => parseEvidenceItem(item, index)),\n\t\t};\n\t}\n\n\tstatic toJSON(contract: TaskContract): string {\n\t\treturn JSON.stringify(contract, null, 2);\n\t}\n}\n\nconst EVIDENCE_STATUSES: ReadonlySet<string> = new Set([\"pending\", \"gathering\", \"satisfied\", \"failed\", \"waived\"]);\n\nfunction optionalStringField(value: unknown, field: string, index: number): string | undefined {\n\tif (value !== undefined && typeof value !== \"string\") {\n\t\tthrow new Error(`TaskContract evidence[${index}].${field} must be a string when present`);\n\t}\n\treturn value;\n}\n\nfunction parseEvidenceItem(raw: unknown, index: number): EvidenceItem {\n\tif (raw === null || typeof raw !== \"object\" || Array.isArray(raw)) {\n\t\tthrow new Error(`TaskContract evidence[${index}] must be an object`);\n\t}\n\tconst item = raw as Record<string, unknown>;\n\tconst { claim, category, timestamp, status } = item;\n\tif (typeof claim !== \"string\" || typeof category !== \"string\" || typeof timestamp !== \"string\") {\n\t\tthrow new Error(`TaskContract evidence[${index}] has missing or mistyped fields`);\n\t}\n\tif (typeof status !== \"string\" || !EVIDENCE_STATUSES.has(status)) {\n\t\tthrow new Error(`TaskContract evidence[${index}] has invalid status ${String(status)}`);\n\t}\n\tconst receiptSchemaVersion = item.receiptSchemaVersion;\n\tif (receiptSchemaVersion !== undefined && receiptSchemaVersion !== 3) {\n\t\tthrow new Error(`TaskContract evidence[${index}].receiptSchemaVersion must be 3 when present`);\n\t}\n\tconst receiptCommandSha256 = optionalStringField(item.receiptCommandSha256, \"receiptCommandSha256\", index);\n\treturn {\n\t\tclaim,\n\t\tcategory: category as EvidenceItem[\"category\"],\n\t\ttimestamp,\n\t\tstatus: status as EvidenceStatus,\n\t\tartifactPath: optionalStringField(item.artifactPath, \"artifactPath\", index),\n\t\tverificationCommand: optionalStringField(item.verificationCommand, \"verificationCommand\", index),\n\t\thash: optionalStringField(item.hash, \"hash\", index),\n\t\treceiptId: optionalStringField(item.receiptId, \"receiptId\", index),\n\t\treceiptSchemaVersion,\n\t\t...(receiptCommandSha256 !== undefined\n\t\t\t? { receiptCommandSha256: parseSha256Hex(receiptCommandSha256, \"receiptCommandSha256\") }\n\t\t\t: {}),\n\t\treceiptLaneId: optionalStringField(item.receiptLaneId, \"receiptLaneId\", index),\n\t\tgapReason: optionalStringField(item.gapReason, \"gapReason\", index),\n\t};\n}\n\n// ============================================================================\n// ReplayLedger\n// ============================================================================\n\n/** prevHash sentinel for the first event in a replay chain. */\nconst GENESIS_HASH = \"genesis\";\n\nfunction computeEventHash(event: Omit<ReplayEvent, \"eventHash\">): string {\n\treturn sha256(\n\t\tJSON.stringify([\n\t\t\tevent.seq,\n\t\t\tevent.type,\n\t\t\tevent.timestamp,\n\t\t\tevent.goalId,\n\t\t\tevent.laneId ?? null,\n\t\t\tevent.payloadHash,\n\t\t\tevent.prevHash,\n\t\t]),\n\t);\n}\n\nfunction parseReplayEventLine(line: string, lineNumber: number): ReplayEvent {\n\tlet raw: unknown;\n\ttry {\n\t\traw = JSON.parse(line);\n\t} catch {\n\t\tthrow new Error(`Replay ledger corrupted at line ${lineNumber}: invalid JSON`);\n\t}\n\tif (raw === null || typeof raw !== \"object\" || Array.isArray(raw)) {\n\t\tthrow new Error(`Replay ledger corrupted at line ${lineNumber}: event must be an object`);\n\t}\n\tconst event = raw as Record<string, unknown>;\n\tconst { seq, type, timestamp, goalId, laneId, payloadHash, prevHash, eventHash } = event;\n\tif (\n\t\ttypeof seq !== \"number\" ||\n\t\ttypeof type !== \"string\" ||\n\t\ttypeof timestamp !== \"string\" ||\n\t\ttypeof goalId !== \"string\" ||\n\t\ttypeof payloadHash !== \"string\" ||\n\t\ttypeof prevHash !== \"string\" ||\n\t\ttypeof eventHash !== \"string\" ||\n\t\t(laneId !== undefined && typeof laneId !== \"string\") ||\n\t\t!(\"payload\" in event)\n\t) {\n\t\tthrow new Error(`Replay ledger corrupted at line ${lineNumber}: event fields are missing or mistyped`);\n\t}\n\treturn {\n\t\tseq,\n\t\ttype: type as ReplayEvent[\"type\"],\n\t\ttimestamp,\n\t\tgoalId,\n\t\tlaneId,\n\t\tpayload: event.payload,\n\t\tpayloadHash,\n\t\tprevHash,\n\t\teventHash,\n\t};\n}\n\nfunction parseReplayLedgerBytes(bytes: Buffer, goalId: string): ReplayEvent[] {\n\tconst lines = bytes\n\t\t.toString(\"utf8\")\n\t\t.split(\"\\n\")\n\t\t.filter((line) => line.trim().length > 0);\n\tconst events: ReplayEvent[] = [];\n\tlet prevHash = GENESIS_HASH;\n\tfor (let index = 0; index < lines.length; index++) {\n\t\tconst event = parseReplayEventLine(lines[index], index + 1);\n\t\tif (event.goalId !== goalId) throw new Error(`Replay ledger goal mismatch at line ${index + 1}`);\n\t\tif (event.seq !== index + 1) {\n\t\t\tthrow new Error(`Replay ledger corrupted at line ${index + 1}: expected seq ${index + 1}, got ${event.seq}`);\n\t\t}\n\t\tif (event.prevHash !== prevHash) throw new Error(`Replay ledger chain broken at line ${index + 1}`);\n\t\tif (sha256(JSON.stringify(event.payload)) !== event.payloadHash) {\n\t\t\tthrow new Error(`Replay ledger payload tampered at line ${index + 1} (seq ${event.seq})`);\n\t\t}\n\t\tif (computeEventHash(event) !== event.eventHash) {\n\t\t\tthrow new Error(`Replay ledger event hash mismatch at line ${index + 1} (seq ${event.seq})`);\n\t\t}\n\t\tprevHash = event.eventHash;\n\t\tevents.push(event);\n\t}\n\treturn events;\n}\n\nexport class ReplayLedgerManager {\n\tprivate readonly goalId: string;\n\tprivate readonly ledgerPath: string;\n\tprivate readonly store: ReplayLedgerStore;\n\tprivate events: ReplayEvent[] = [];\n\tprivate committedHead: ReplayLedgerHead;\n\n\tconstructor(goalId: string, ledgerPath: string, expectedHead?: ReplayLedgerHead) {\n\t\tthis.goalId = goalId;\n\t\tthis.ledgerPath = ledgerPath;\n\t\tthis.committedHead = { fileIdentity: null, size: 0, lastSeq: 0, lastHash: GENESIS_HASH };\n\t\tthis.store = new ReplayLedgerStore(ledgerPath, (bytes) => {\n\t\t\tconst events = parseReplayLedgerBytes(bytes, goalId);\n\t\t\tconst last = events.at(-1);\n\t\t\treturn { lastSeq: last?.seq ?? 0, lastHash: last?.eventHash ?? GENESIS_HASH };\n\t\t});\n\t\tthis.refresh(expectedHead);\n\t}\n\n\tprivate refresh(expectedHead?: ReplayLedgerHead): void {\n\t\tconst snapshot = this.store.load(expectedHead);\n\t\tthis.events = parseReplayLedgerBytes(snapshot.bytes, this.goalId);\n\t\tthis.committedHead = snapshot.head;\n\t}\n\n\tappend(\n\t\tevent: Omit<ReplayEvent, \"seq\" | \"timestamp\" | \"payloadHash\" | \"prevHash\" | \"eventHash\">,\n\t\texpectedHead: ReplayLedgerHead = this.committedHead,\n\t): ReplayEvent {\n\t\tif (event.goalId !== this.goalId) throw new Error(\"Replay event goalId does not match the ledger\");\n\t\tif (!replayLedgerHeadsEqual(expectedHead, this.committedHead)) {\n\t\t\tthrow new Error(\"Replay manager expected-head CAS is stale\");\n\t\t}\n\t\tconst payloadHash = sha256(JSON.stringify(event.payload));\n\t\tconst chained: Omit<ReplayEvent, \"eventHash\"> = {\n\t\t\t...event,\n\t\t\tseq: expectedHead.lastSeq + 1,\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tpayloadHash,\n\t\t\tprevHash: expectedHead.lastHash,\n\t\t};\n\t\tconst fullEvent: ReplayEvent = { ...chained, eventHash: computeEventHash(chained) };\n\t\tthis.committedHead = this.store.append(\n\t\t\tBuffer.from(`${JSON.stringify(fullEvent)}\\n`, \"utf8\"),\n\t\t\tfullEvent.seq,\n\t\t\tfullEvent.eventHash,\n\t\t\texpectedHead,\n\t\t);\n\t\tthis.events.push(fullEvent);\n\t\treturn structuredClone(fullEvent);\n\t}\n\n\t/** Compatibility no-op that still verifies the caller's expected committed head. */\n\tpersist(expectedHead: ReplayLedgerHead = this.committedHead): void {\n\t\tthis.getVerifiedSnapshot(expectedHead);\n\t}\n\n\tload(expectedHead: ReplayLedgerHead = this.committedHead): void {\n\t\tthis.refresh(expectedHead);\n\t}\n\n\tgetCommittedHead(): ReplayLedgerHead {\n\t\treturn structuredClone(this.committedHead);\n\t}\n\n\tgetVerifiedSnapshot(expectedHead: ReplayLedgerHead = this.committedHead): VerifiedReplayLedgerSnapshot {\n\t\tthis.refresh(expectedHead);\n\t\treturn structuredClone({ events: this.events, head: this.committedHead });\n\t}\n\n\tgetEvents(): ReadonlyArray<ReplayEvent> {\n\t\treturn structuredClone(this.events);\n\t}\n\n\tgetLedger(): Readonly<ReplayLedger> {\n\t\treturn structuredClone({\n\t\t\tgoalId: this.goalId,\n\t\t\tevents: this.events,\n\t\t\tledgerPath: this.ledgerPath,\n\t\t\tlastPersistedSeq: this.committedHead.lastSeq,\n\t\t});\n\t}\n\n\treplay<T>(handler: (event: ReplayEvent) => T | undefined): T[] {\n\t\treturn this.events.map(handler).filter((result): result is T => result !== undefined);\n\t}\n\n\texportToFile(path: string): void {\n\t\tensureDir(path);\n\t\twriteFileSync(path, JSON.stringify(this.events, null, 2), \"utf8\");\n\t}\n}\n\n// ============================================================================\n// EvidenceGate\n// ============================================================================\n\nexport interface EvidenceGateOptions {\n\t/** Minimum number of satisfied evidence items required. */\n\tminEvidenceCount?: number;\n\t/** Require at least one artifact with a SHA-256 hash in explicit legacy mode. */\n\trequireHash?: boolean;\n\t/** Require at least one verification command in explicit legacy mode. */\n\trequireVerificationCommand?: boolean;\n\t/** Receipt policy. Defaults to prefer; legacy must be selected explicitly. */\n\treceiptMode?: EvidenceReceiptMode;\n\t/** Resolve a serialized or decoded receipt by ID. The gate validates the returned value. */\n\tresolveReceipt?: (receiptId: string) => unknown;\n\t/** Strict source: one expected-head, file-identity, suffix, and chain-verified snapshot. */\n\tresolveVerifiedLedgerSnapshot?: () => VerifiedReplayLedgerSnapshot;\n\t/** Prefer-mode migration source; strict mode requires resolveVerifiedLedgerSnapshot. */\n\tresolveLedgerEvent?: (seq: number) => ReplayEvent | undefined;\n\t/** Recapture the receipt's workspace scope (same kind and scope) for freshness comparison. */\n\tcaptureWorkspaceFingerprint?: (scope: WorkspaceScope) => unknown;\n\t/**\n\t * Optional freshness source: the latest replay-ledger sequence of a workspace mutation\n\t * relevant to the given scope, or null/undefined when none is known. When configured,\n\t * the receipt's verified ledger seq must strictly exceed this value; a throwing or\n\t * out-of-contract source is a hard block (positive safe integers only).\n\t */\n\tresolveLatestWorkspaceMutationSeq?: (scope: WorkspaceScope) => number | null | undefined;\n\t/** Ephemeral trust anchor used to verify the original command's keyed attestation. */\n\tcommandAttestationBinder?: Pick<CommandHmacBinder, \"verify\">;\n\t/** Resolve the original command inside the same trust boundary as the binder. */\n\tresolveAttestedCommand?: (receiptId: string) => EvidenceCommandDescriptor | undefined;\n}\n\ninterface ReceiptCheckIssue {\n\treadonly message: string;\n\treadonly hard: boolean;\n}\n\nfunction receiptCheckIssue(message: string, hard: boolean): ReceiptCheckIssue {\n\treturn { message, hard };\n}\n\nfunction receiptIssue(\n\titem: EvidenceItem,\n\tcontract: TaskContract,\n\toptions: EvidenceGateOptions,\n): ReceiptCheckIssue | undefined {\n\tif (!item.receiptId) {\n\t\treturn receiptCheckIssue(`Execution receipt is missing for evidence \"${item.claim}\".`, false);\n\t}\n\tconst strict = options.receiptMode === \"strict\";\n\tif (item.receiptSchemaVersion !== 3 && (item.receiptSchemaVersion !== undefined || strict)) {\n\t\treturn receiptCheckIssue(`Execution receipt schema metadata is invalid for evidence \"${item.claim}\".`, true);\n\t}\n\tif (!options.resolveReceipt) {\n\t\treturn receiptCheckIssue(`Execution receipt resolver is missing for evidence \"${item.claim}\".`, false);\n\t}\n\n\tlet rawReceipt: unknown;\n\ttry {\n\t\trawReceipt = options.resolveReceipt(item.receiptId);\n\t} catch {\n\t\treturn receiptCheckIssue(`Execution receipt resolver failed for evidence \"${item.claim}\".`, true);\n\t}\n\tif (rawReceipt === undefined) {\n\t\treturn receiptCheckIssue(`Execution receipt is missing for evidence \"${item.claim}\".`, false);\n\t}\n\n\ttry {\n\t\tconst receipt = validateEvidenceReceipt(rawReceipt);\n\t\tif (receipt.core.receiptId !== item.receiptId) {\n\t\t\treturn receiptCheckIssue(`Execution receipt ID does not match evidence \"${item.claim}\".`, true);\n\t\t}\n\t\tif (receipt.core.goalId !== contract.goalId) {\n\t\t\treturn receiptCheckIssue(`Execution receipt goal does not match evidence \"${item.claim}\".`, true);\n\t\t}\n\t\tif (receipt.core.claim !== item.claim) {\n\t\t\treturn receiptCheckIssue(`Execution receipt claim does not match evidence \"${item.claim}\".`, true);\n\t\t}\n\t\tif (receipt.core.status !== \"passed\" || receipt.core.exitCode !== 0) {\n\t\t\treturn receiptCheckIssue(\n\t\t\t\t`Execution receipt for evidence \"${item.claim}\" did not pass with exit code 0.`,\n\t\t\t\ttrue,\n\t\t\t);\n\t\t}\n\n\t\tif (item.receiptCommandSha256 === undefined) {\n\t\t\treturn receiptCheckIssue(`Execution receipt command binding is missing for evidence \"${item.claim}\".`, false);\n\t\t}\n\t\tconst receiptCommandSha256 = computeEvidenceCommandSha256(receipt.core.command);\n\t\tif (!constantTimeSha256Equal(item.receiptCommandSha256, receiptCommandSha256)) {\n\t\t\treturn receiptCheckIssue(`Execution receipt command does not match evidence \"${item.claim}\".`, true);\n\t\t}\n\t\tconst attestationIssue = verifyCommandAttestation({\n\t\t\treceiptId: receipt.core.receiptId,\n\t\t\tbinding: receipt.core.commandBinding,\n\t\t\tpersistedCommand: receipt.core.command,\n\t\t\tpersistedSummary: receipt.core.commandRedaction,\n\t\t\tbinder: options.commandAttestationBinder,\n\t\t\tresolveCommand: options.resolveAttestedCommand,\n\t\t\tredact: redactCommandDescriptor,\n\t\t});\n\t\tif (attestationIssue !== undefined) {\n\t\t\treturn receiptCheckIssue(\n\t\t\t\t`Execution receipt command attestation ${attestationIssue.detail} for evidence \"${item.claim}\".`,\n\t\t\t\tattestationIssue.hard,\n\t\t\t);\n\t\t}\n\n\t\tif (receipt.core.laneId !== undefined && item.receiptLaneId === undefined) {\n\t\t\treturn receiptCheckIssue(`Execution receipt lane binding is missing for evidence \"${item.claim}\".`, false);\n\t\t}\n\t\tif (item.receiptLaneId !== receipt.core.laneId) {\n\t\t\treturn receiptCheckIssue(`Execution receipt lane does not match evidence \"${item.claim}\".`, true);\n\t\t}\n\n\t\tconst workspaceIssue = verifyWorkspaceBinding(receipt.core.workspaceAfter, options.captureWorkspaceFingerprint);\n\t\tif (workspaceIssue !== undefined) {\n\t\t\treturn receiptCheckIssue(\n\t\t\t\t`Execution receipt ${workspaceIssue.detail} for evidence \"${item.claim}\".`,\n\t\t\t\tworkspaceIssue.hard,\n\t\t\t);\n\t\t}\n\n\t\tlet snapshot: VerifiedReplayLedgerSnapshot | undefined;\n\t\tif (options.resolveVerifiedLedgerSnapshot !== undefined) {\n\t\t\ttry {\n\t\t\t\tsnapshot = parseVerifiedLedgerSnapshot(\n\t\t\t\t\toptions.resolveVerifiedLedgerSnapshot(),\n\t\t\t\t\tcontract.goalId,\n\t\t\t\t\tparseReplayLedgerBytes,\n\t\t\t\t);\n\t\t\t} catch {\n\t\t\t\treturn receiptCheckIssue(\n\t\t\t\t\t`Execution receipt verified ledger snapshot failed for evidence \"${item.claim}\".`,\n\t\t\t\t\ttrue,\n\t\t\t\t);\n\t\t\t}\n\t\t} else if (strict) {\n\t\t\treturn receiptCheckIssue(\n\t\t\t\t`Execution receipt verified ledger snapshot is missing for evidence \"${item.claim}\".`,\n\t\t\t\tfalse,\n\t\t\t);\n\t\t}\n\t\tlet latestMutationSeq =\n\t\t\tsnapshot === undefined\n\t\t\t\t? null\n\t\t\t\t: latestRelevantWorkspaceMutationSeq(snapshot.events, receipt.core.workspaceAfter.scope);\n\t\tif (snapshot === undefined && options.resolveLatestWorkspaceMutationSeq !== undefined) {\n\t\t\tlet resolved: unknown;\n\t\t\ttry {\n\t\t\t\tresolved = options.resolveLatestWorkspaceMutationSeq(receipt.core.workspaceAfter.scope);\n\t\t\t} catch {\n\t\t\t\treturn receiptCheckIssue(\n\t\t\t\t\t`Execution receipt workspace-mutation freshness source failed for evidence \"${item.claim}\".`,\n\t\t\t\t\ttrue,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (resolved !== null && resolved !== undefined) {\n\t\t\t\tif (!Number.isSafeInteger(resolved) || (resolved as number) <= 0) {\n\t\t\t\t\treturn receiptCheckIssue(\n\t\t\t\t\t\t`Execution receipt workspace-mutation freshness source is malformed for evidence \"${item.claim}\".`,\n\t\t\t\t\t\ttrue,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tlatestMutationSeq = resolved as number;\n\t\t\t}\n\t\t}\n\n\t\tconst binding = receipt.envelope.ledgerBinding;\n\t\tif (!binding) {\n\t\t\treturn receiptCheckIssue(\n\t\t\t\t`Execution receipt ledger binding is missing for evidence \"${item.claim}\".`,\n\t\t\t\tlatestMutationSeq !== null,\n\t\t\t);\n\t\t}\n\t\tconst event = snapshot\n\t\t\t? snapshot.events.find((candidate) => candidate.seq === binding.seq)\n\t\t\t: options.resolveLedgerEvent?.(binding.seq);\n\t\tif (!event && snapshot === undefined && !options.resolveLedgerEvent) {\n\t\t\treturn receiptCheckIssue(\n\t\t\t\t`Execution receipt ledger resolver is missing for evidence \"${item.claim}\".`,\n\t\t\t\tlatestMutationSeq !== null,\n\t\t\t);\n\t\t}\n\t\tconst replayPayload = evidenceReceiptReplayPayload(receipt);\n\t\tif (\n\t\t\t!event ||\n\t\t\tevent.seq !== binding.seq ||\n\t\t\tevent.type !== \"evidence_receipt\" ||\n\t\t\tevent.goalId !== contract.goalId ||\n\t\t\tevent.laneId !== receipt.core.laneId ||\n\t\t\t!constantTimeSha256Equal(event.eventHash, binding.eventHash) ||\n\t\t\t!replayPayloadMatches(event.payload, replayPayload.receiptId, replayPayload.coreSha256)\n\t\t) {\n\t\t\treturn receiptCheckIssue(`Execution receipt ledger binding does not match evidence \"${item.claim}\".`, true);\n\t\t}\n\t\tif (latestMutationSeq !== null && event.seq <= latestMutationSeq) {\n\t\t\treturn receiptCheckIssue(\n\t\t\t\t`Execution receipt is stale: workspace mutation seq ${latestMutationSeq} does not precede receipt ledger seq ${event.seq} for evidence \"${item.claim}\".`,\n\t\t\t\ttrue,\n\t\t\t);\n\t\t}\n\t} catch {\n\t\treturn receiptCheckIssue(`Execution receipt is invalid for evidence \"${item.claim}\".`, true);\n\t}\n\treturn undefined;\n}\n\nexport class EvidenceGate {\n\tprivate options: EvidenceGateOptions;\n\n\tconstructor(options: EvidenceGateOptions = {}) {\n\t\tthis.options = {\n\t\t\tminEvidenceCount: 1,\n\t\t\trequireHash: true,\n\t\t\trequireVerificationCommand: true,\n\t\t\treceiptMode: \"prefer\",\n\t\t\t...options,\n\t\t};\n\t}\n\n\tcheck(contract: TaskContract): MergeGateResult {\n\t\tconst evidence = contract.requiredEvidence;\n\t\tconst satisfied = evidence.filter((e) => e.status === \"satisfied\");\n\t\tconst failed = evidence.filter((e) => e.status === \"failed\");\n\t\tconst pending = evidence.filter((e) => e.status === \"pending\" || e.status === \"gathering\");\n\t\tconst mode = this.options.receiptMode ?? \"prefer\";\n\t\tconst checks: string[] = [];\n\t\tlet hardReceiptFailure = false;\n\n\t\tif (satisfied.length < (this.options.minEvidenceCount ?? 1)) {\n\t\t\tchecks.push(`Only ${satisfied.length}/${this.options.minEvidenceCount} required evidence items satisfied.`);\n\t\t}\n\t\tif (failed.length > 0) {\n\t\t\tchecks.push(`${failed.length} evidence item(s) failed.`);\n\t\t}\n\n\t\tif (mode === \"legacy\") {\n\t\t\tif (this.options.requireHash) {\n\t\t\t\tconst hashed = satisfied.filter((e) => e.hash && e.hash.length > 0);\n\t\t\t\tif (hashed.length === 0) checks.push(\"No satisfied evidence has a SHA-256 hash.\");\n\t\t\t}\n\t\t\tif (this.options.requireVerificationCommand) {\n\t\t\t\tconst withCommand = satisfied.filter((e) => e.verificationCommand && e.verificationCommand.length > 0);\n\t\t\t\tif (withCommand.length === 0) checks.push(\"No satisfied evidence has a verification command.\");\n\t\t\t}\n\t\t} else {\n\t\t\tconst unsatisfied = evidence.filter((item) => item.status !== \"satisfied\");\n\t\t\tif (unsatisfied.length > 0) {\n\t\t\t\tchecks.push(`${unsatisfied.length} required evidence item(s) are not satisfied.`);\n\t\t\t}\n\t\t\tfor (const item of satisfied) {\n\t\t\t\tconst issue = receiptIssue(item, contract, this.options);\n\t\t\t\tif (issue) {\n\t\t\t\t\tchecks.push(issue.message);\n\t\t\t\t\thardReceiptFailure ||= issue.hard;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (contract.verdict === \"fail\" || (mode !== \"legacy\" && contract.verdict !== \"pass\")) {\n\t\t\tchecks.push(`Task contract verdict is '${contract.verdict}'.`);\n\t\t}\n\n\t\tlet status: MergeGateStatus;\n\t\tlet reason: string;\n\t\tlet suggestion: string | undefined;\n\t\tif (checks.length === 0) {\n\t\t\tstatus = \"open\";\n\t\t\treason = `Evidence gate passed: ${satisfied.length}/${evidence.length} evidence items satisfied.`;\n\t\t} else {\n\t\t\tconst contractFailure = mode === \"legacy\" ? contract.verdict === \"fail\" : contract.verdict !== \"pass\";\n\t\t\tconst hasHardFailure = failed.length > 0 || contractFailure || hardReceiptFailure || mode === \"strict\";\n\t\t\tstatus = hasHardFailure ? \"blocked\" : mode === \"prefer\" || pending.length > 0 ? \"conditional\" : \"blocked\";\n\t\t\treason = checks.join(\"; \");\n\t\t\tsuggestion = `Gather remaining evidence (${pending.length} pending) and re-run verification.`;\n\t\t}\n\n\t\treturn {\n\t\t\tgateId: \"evidence-gate\",\n\t\t\tstatus,\n\t\t\treason,\n\t\t\tsuggestion,\n\t\t\tevidenceChecked: evidence,\n\t\t};\n\t}\n}\n\n// ============================================================================\n// FailClosedMergeGate\n// ============================================================================\n\nexport class FailClosedMergeGate {\n\tprivate gates: EvidenceGate[];\n\n\tconstructor(gates: EvidenceGate[] = [new EvidenceGate()]) {\n\t\tthis.gates = gates;\n\t}\n\n\tcheck(contract: TaskContract): MergeGateResult {\n\t\tconst results = this.gates.map((gate) => gate.check(contract));\n\t\tconst blocked = results.filter((r) => r.status === \"blocked\");\n\t\tconst conditional = results.filter((r) => r.status === \"conditional\");\n\n\t\tif (blocked.length > 0) {\n\t\t\treturn {\n\t\t\t\tgateId: \"fail-closed-merge-gate\",\n\t\t\t\tstatus: \"blocked\",\n\t\t\t\treason: `Blocked by ${blocked.length} gate(s): ${blocked.map((b) => b.reason).join(\"; \")}`,\n\t\t\t\tsuggestion: \"Resolve all blocking conditions before merge.\",\n\t\t\t\tevidenceChecked: contract.requiredEvidence,\n\t\t\t};\n\t\t}\n\n\t\tif (conditional.length > 0) {\n\t\t\treturn {\n\t\t\t\tgateId: \"fail-closed-merge-gate\",\n\t\t\t\tstatus: \"conditional\",\n\t\t\t\treason: `Conditional pass: ${conditional.length} gate(s) have pending conditions.`,\n\t\t\t\tsuggestion: \"Complete pending evidence or waive with explicit approval.\",\n\t\t\t\tevidenceChecked: contract.requiredEvidence,\n\t\t\t};\n\t\t}\n\n\t\treturn {\n\t\t\tgateId: \"fail-closed-merge-gate\",\n\t\t\tstatus: \"open\",\n\t\t\treason: \"All merge gates passed. Evidence verified and contract verdict is acceptable.\",\n\t\t\tevidenceChecked: contract.requiredEvidence,\n\t\t};\n\t}\n}\n\n// ============================================================================\n// Verify Reporter v2\n// ============================================================================\n\n/** Escape pipes/newlines so untrusted claim or command text cannot restructure the report table. */\nfunction escapeTableCell(value: string): string {\n\treturn value.replace(/\\\\/g, \"\\\\\\\\\").replace(/\\|/g, \"\\\\|\").replace(/\\r?\\n/g, \" \");\n}\n\nexport interface VerifyReporterV2Options {\n\toutputDir: string;\n\tgoalId: string;\n}\n\nexport class VerifyReporterV2 {\n\tprivate options: VerifyReporterV2Options;\n\n\tconstructor(options: VerifyReporterV2Options) {\n\t\tthis.options = options;\n\t}\n\n\trender(contract: TaskContract, mergeGate: MergeGateResult): string {\n\t\tconst lines: string[] = [\n\t\t\t`## OMK Verification Report v2 — ${contract.goalId}`,\n\t\t\t\"\",\n\t\t\t\"### Contract\",\n\t\t\t\"\",\n\t\t\t`| Field | Value |`,\n\t\t\t`|-------|-------|`,\n\t\t\t`| Claim | ${escapeTableCell(contract.completionClaim)} |`,\n\t\t\t`| Verdict | ${contract.verdict} |`,\n\t\t\t`| Final Risk | ${escapeTableCell(contract.finalRisk)} |`,\n\t\t\t\"\",\n\t\t\t\"### Evidence\",\n\t\t\t\"\",\n\t\t\t`| Claim | Status | Artifact | Command |`,\n\t\t\t`|-------|--------|----------|----------|`,\n\t\t];\n\n\t\tfor (const ev of contract.requiredEvidence) {\n\t\t\tlines.push(\n\t\t\t\t`| ${escapeTableCell(ev.claim)} | ${ev.status} | ${escapeTableCell(ev.artifactPath ?? \"—\")} | ${escapeTableCell(ev.verificationCommand ?? \"—\")} |`,\n\t\t\t);\n\t\t}\n\n\t\tlines.push(\n\t\t\t\"\",\n\t\t\t\"### Merge Gate\",\n\t\t\t\"\",\n\t\t\t`| Gate | Status | Reason |`,\n\t\t\t`|------|--------|--------|`,\n\t\t\t`| ${mergeGate.gateId} | ${mergeGate.status} | ${escapeTableCell(mergeGate.reason)} |`,\n\t\t\t\"\",\n\t\t);\n\n\t\tif (mergeGate.suggestion) {\n\t\t\tlines.push(`**Suggestion:** ${mergeGate.suggestion}`, \"\");\n\t\t}\n\n\t\treturn lines.join(\"\\n\");\n\t}\n\n\twrite(contract: TaskContract, mergeGate: MergeGateResult): string {\n\t\tconst markdown = this.render(contract, mergeGate);\n\t\tconst path = join(this.options.outputDir, `${this.options.goalId}.verify.md`);\n\t\tensureDir(path);\n\t\twriteFileSync(path, markdown, \"utf-8\");\n\t\treturn path;\n\t}\n}\n"]}
1
+ {"version":3,"file":"evidence-system.d.ts","sourceRoot":"","sources":["../../src/guardrails/evidence-system.ts"],"names":[],"mappings":"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","sourcesContent":["// allow: SIZE_OK - evidence ledger guardrails: tamper-evident replay chain + fail-closed parsing.\nimport { createHash } from \"node:crypto\";\nimport { existsSync, mkdirSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport type {\n\tEvidenceCommandDescriptor,\n\tEvidenceItem,\n\tEvidenceReceiptMode,\n\tEvidenceStatus,\n\tMergeGateResult,\n\tMergeGateStatus,\n\tReplayEvent,\n\tReplayLedger,\n\tReplayLedgerHead,\n\tTaskContract,\n\tVerifiedReplayLedgerSnapshot,\n\tWorkspaceScope,\n} from \"../types/evidence.ts\";\nimport { redactCommandDescriptor } from \"./command-redaction.ts\";\nimport {\n\ttype CommandHmacBinder,\n\tlatestRelevantWorkspaceMutationSeq,\n\tparseVerifiedLedgerSnapshot,\n\treplayPayloadMatches,\n\tverifyCommandAttestation,\n\tverifyWorkspaceBinding,\n} from \"./evidence-attestation.ts\";\n\nexport { latestRelevantWorkspaceMutationSeq } from \"./evidence-attestation.ts\";\n\nimport {\n\tcomputeEvidenceCommandSha256,\n\tconstantTimeSha256Equal,\n\tevidenceReceiptReplayPayload,\n\tparseSha256Hex,\n\tvalidateEvidenceReceipt,\n} from \"./evidence-receipt.ts\";\nimport { ReplayLedgerStore, replayLedgerHeadsEqual } from \"./replay-ledger-store.ts\";\nimport {\n\tcomputeReplayPayloadHash,\n\tJCS_REPLAY_PAYLOAD_HASH_ALGORITHM,\n\tLEGACY_REPLAY_PAYLOAD_HASH_ALGORITHM,\n} from \"./replay-payload-hash.ts\";\n\nfunction sha256(input: string): string {\n\treturn createHash(\"sha256\").update(input).digest(\"hex\");\n}\n\nfunction ensureDir(path: string): void {\n\tconst dir = dirname(path);\n\tif (!existsSync(dir)) {\n\t\tmkdirSync(dir, { recursive: true });\n\t}\n}\n\n// ============================================================================\n// TaskContract\n// ============================================================================\n\nexport class TaskContractBuilder {\n\tprivate goalId: string;\n\tprivate completionClaim: string = \"\";\n\tprivate requiredEvidence: EvidenceItem[] = [];\n\tprivate finalRisk: string = \"\";\n\tprivate verdict: \"pass\" | \"fail\" | \"conditional\" = \"fail\";\n\tprivate createdAt: string;\n\tprivate updatedAt: string;\n\n\tconstructor(goalId: string) {\n\t\tthis.goalId = goalId;\n\t\tconst now = new Date().toISOString();\n\t\tthis.createdAt = now;\n\t\tthis.updatedAt = now;\n\t}\n\n\tsetClaim(claim: string): this {\n\t\tthis.completionClaim = claim;\n\t\tthis.touch();\n\t\treturn this;\n\t}\n\n\taddRequiredEvidence(evidence: Omit<EvidenceItem, \"timestamp\" | \"status\">): this {\n\t\tthis.requiredEvidence.push({\n\t\t\t...evidence,\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tstatus: \"pending\",\n\t\t});\n\t\tthis.touch();\n\t\treturn this;\n\t}\n\n\tsetFinalRisk(risk: string): this {\n\t\tthis.finalRisk = risk;\n\t\tthis.touch();\n\t\treturn this;\n\t}\n\n\t/** @deprecated Use `evaluateTask()` from `omk-protocol`; verdicts are derived, not mutated. */\n\tsetVerdict(verdict: \"pass\" | \"fail\" | \"conditional\"): this {\n\t\tthis.verdict = verdict;\n\t\tthis.touch();\n\t\treturn this;\n\t}\n\n\t/** @deprecated Append a protocol Observation and re-evaluate instead of mutating evidence state. */\n\tupdateEvidenceStatus(claim: string, status: EvidenceStatus, gapReason?: string): this {\n\t\tconst item = this.requiredEvidence.find((e) => e.claim === claim);\n\t\tif (item) {\n\t\t\titem.status = status;\n\t\t\titem.gapReason = gapReason;\n\t\t\titem.timestamp = new Date().toISOString();\n\t\t\tthis.touch();\n\t\t}\n\t\treturn this;\n\t}\n\n\tprivate touch(): void {\n\t\tthis.updatedAt = new Date().toISOString();\n\t}\n\n\tbuild(): TaskContract {\n\t\treturn structuredClone({\n\t\t\tgoalId: this.goalId,\n\t\t\tcompletionClaim: this.completionClaim,\n\t\t\trequiredEvidence: this.requiredEvidence,\n\t\t\tfinalRisk: this.finalRisk,\n\t\t\tverdict: this.verdict,\n\t\t\tcreatedAt: this.createdAt,\n\t\t\tupdatedAt: this.updatedAt,\n\t\t});\n\t}\n\n\t/** Parse and validate a serialized TaskContract. Fails closed on any shape violation. */\n\tstatic fromJSON(json: string): TaskContract {\n\t\tconst raw: unknown = JSON.parse(json);\n\t\tif (raw === null || typeof raw !== \"object\" || Array.isArray(raw)) {\n\t\t\tthrow new Error(\"TaskContract JSON must be an object\");\n\t\t}\n\t\tconst { goalId, completionClaim, finalRisk, verdict, createdAt, updatedAt, requiredEvidence } = raw as Record<\n\t\t\tstring,\n\t\t\tunknown\n\t\t>;\n\t\tif (\n\t\t\ttypeof goalId !== \"string\" ||\n\t\t\ttypeof completionClaim !== \"string\" ||\n\t\t\ttypeof finalRisk !== \"string\" ||\n\t\t\ttypeof createdAt !== \"string\" ||\n\t\t\ttypeof updatedAt !== \"string\"\n\t\t) {\n\t\t\tthrow new Error(\"TaskContract JSON has missing or mistyped string fields\");\n\t\t}\n\t\tif (verdict !== \"pass\" && verdict !== \"fail\" && verdict !== \"conditional\") {\n\t\t\tthrow new Error(`TaskContract verdict must be pass|fail|conditional, got ${String(verdict)}`);\n\t\t}\n\t\tif (!Array.isArray(requiredEvidence)) {\n\t\t\tthrow new Error(\"TaskContract requiredEvidence must be an array\");\n\t\t}\n\t\treturn {\n\t\t\tgoalId,\n\t\t\tcompletionClaim,\n\t\t\tfinalRisk,\n\t\t\tverdict,\n\t\t\tcreatedAt,\n\t\t\tupdatedAt,\n\t\t\trequiredEvidence: requiredEvidence.map((item, index) => parseEvidenceItem(item, index)),\n\t\t};\n\t}\n\n\tstatic toJSON(contract: TaskContract): string {\n\t\treturn JSON.stringify(contract, null, 2);\n\t}\n}\n\nconst EVIDENCE_STATUSES: ReadonlySet<string> = new Set([\"pending\", \"gathering\", \"satisfied\", \"failed\", \"waived\"]);\n\nfunction optionalStringField(value: unknown, field: string, index: number): string | undefined {\n\tif (value !== undefined && typeof value !== \"string\") {\n\t\tthrow new Error(`TaskContract evidence[${index}].${field} must be a string when present`);\n\t}\n\treturn value;\n}\n\nfunction parseEvidenceItem(raw: unknown, index: number): EvidenceItem {\n\tif (raw === null || typeof raw !== \"object\" || Array.isArray(raw)) {\n\t\tthrow new Error(`TaskContract evidence[${index}] must be an object`);\n\t}\n\tconst item = raw as Record<string, unknown>;\n\tconst { claim, category, timestamp, status } = item;\n\tif (typeof claim !== \"string\" || typeof category !== \"string\" || typeof timestamp !== \"string\") {\n\t\tthrow new Error(`TaskContract evidence[${index}] has missing or mistyped fields`);\n\t}\n\tif (typeof status !== \"string\" || !EVIDENCE_STATUSES.has(status)) {\n\t\tthrow new Error(`TaskContract evidence[${index}] has invalid status ${String(status)}`);\n\t}\n\tconst receiptSchemaVersion = item.receiptSchemaVersion;\n\tif (receiptSchemaVersion !== undefined && receiptSchemaVersion !== 3) {\n\t\tthrow new Error(`TaskContract evidence[${index}].receiptSchemaVersion must be 3 when present`);\n\t}\n\tconst receiptCommandSha256 = optionalStringField(item.receiptCommandSha256, \"receiptCommandSha256\", index);\n\treturn {\n\t\tclaim,\n\t\tcategory: category as EvidenceItem[\"category\"],\n\t\ttimestamp,\n\t\tstatus: status as EvidenceStatus,\n\t\tartifactPath: optionalStringField(item.artifactPath, \"artifactPath\", index),\n\t\tverificationCommand: optionalStringField(item.verificationCommand, \"verificationCommand\", index),\n\t\thash: optionalStringField(item.hash, \"hash\", index),\n\t\treceiptId: optionalStringField(item.receiptId, \"receiptId\", index),\n\t\treceiptSchemaVersion,\n\t\t...(receiptCommandSha256 !== undefined\n\t\t\t? { receiptCommandSha256: parseSha256Hex(receiptCommandSha256, \"receiptCommandSha256\") }\n\t\t\t: {}),\n\t\treceiptLaneId: optionalStringField(item.receiptLaneId, \"receiptLaneId\", index),\n\t\tgapReason: optionalStringField(item.gapReason, \"gapReason\", index),\n\t};\n}\n\n// ============================================================================\n// ReplayLedger\n// ============================================================================\n\n/** prevHash sentinel for the first event in a replay chain. */\nconst GENESIS_HASH = \"genesis\";\n\nfunction computeEventHash(event: Omit<ReplayEvent, \"eventHash\">): string {\n\tconst metadata = [event.seq, event.type, event.timestamp, event.goalId, event.laneId ?? null];\n\tconst material =\n\t\tevent.payloadHashAlgorithm === undefined\n\t\t\t? [...metadata, event.payloadHash, event.prevHash]\n\t\t\t: [...metadata, event.payloadHashAlgorithm, event.payloadHash, event.prevHash];\n\treturn sha256(JSON.stringify(material));\n}\n\nfunction parseReplayEventLine(line: string, lineNumber: number): ReplayEvent {\n\tlet raw: unknown;\n\ttry {\n\t\traw = JSON.parse(line);\n\t} catch {\n\t\tthrow new Error(`Replay ledger corrupted at line ${lineNumber}: invalid JSON`);\n\t}\n\tif (raw === null || typeof raw !== \"object\" || Array.isArray(raw)) {\n\t\tthrow new Error(`Replay ledger corrupted at line ${lineNumber}: event must be an object`);\n\t}\n\tconst event = raw as Record<string, unknown>;\n\tconst { seq, type, timestamp, goalId, laneId, payloadHashAlgorithm, payloadHash, prevHash, eventHash } = event;\n\tif (\n\t\ttypeof seq !== \"number\" ||\n\t\ttypeof type !== \"string\" ||\n\t\ttypeof timestamp !== \"string\" ||\n\t\ttypeof goalId !== \"string\" ||\n\t\ttypeof payloadHash !== \"string\" ||\n\t\ttypeof prevHash !== \"string\" ||\n\t\ttypeof eventHash !== \"string\" ||\n\t\t(laneId !== undefined && typeof laneId !== \"string\") ||\n\t\t!(\"payload\" in event)\n\t) {\n\t\tthrow new Error(`Replay ledger corrupted at line ${lineNumber}: event fields are missing or mistyped`);\n\t}\n\tif (\n\t\tpayloadHashAlgorithm !== undefined &&\n\t\tpayloadHashAlgorithm !== LEGACY_REPLAY_PAYLOAD_HASH_ALGORITHM &&\n\t\tpayloadHashAlgorithm !== JCS_REPLAY_PAYLOAD_HASH_ALGORITHM\n\t) {\n\t\tthrow new Error(`Replay ledger corrupted at line ${lineNumber}: unsupported payload hash algorithm`);\n\t}\n\tconst parsed: ReplayEvent = {\n\t\tseq,\n\t\ttype: type as ReplayEvent[\"type\"],\n\t\ttimestamp,\n\t\tgoalId,\n\t\tlaneId,\n\t\tpayload: event.payload,\n\t\tpayloadHash,\n\t\tprevHash,\n\t\teventHash,\n\t};\n\treturn payloadHashAlgorithm === undefined ? parsed : { ...parsed, payloadHashAlgorithm };\n}\n\nfunction parseReplayLedgerBytes(bytes: Buffer, goalId: string): ReplayEvent[] {\n\tconst lines = bytes\n\t\t.toString(\"utf8\")\n\t\t.split(\"\\n\")\n\t\t.filter((line) => line.trim().length > 0);\n\tconst events: ReplayEvent[] = [];\n\tlet prevHash = GENESIS_HASH;\n\tfor (let index = 0; index < lines.length; index++) {\n\t\tconst event = parseReplayEventLine(lines[index], index + 1);\n\t\tif (event.goalId !== goalId) throw new Error(`Replay ledger goal mismatch at line ${index + 1}`);\n\t\tif (event.seq !== index + 1) {\n\t\t\tthrow new Error(`Replay ledger corrupted at line ${index + 1}: expected seq ${index + 1}, got ${event.seq}`);\n\t\t}\n\t\tif (event.prevHash !== prevHash) throw new Error(`Replay ledger chain broken at line ${index + 1}`);\n\t\tconst payloadHashAlgorithm = event.payloadHashAlgorithm ?? LEGACY_REPLAY_PAYLOAD_HASH_ALGORITHM;\n\t\tif (computeReplayPayloadHash(event.payload, payloadHashAlgorithm) !== event.payloadHash) {\n\t\t\tthrow new Error(`Replay ledger payload tampered at line ${index + 1} (seq ${event.seq})`);\n\t\t}\n\t\tif (computeEventHash(event) !== event.eventHash) {\n\t\t\tthrow new Error(`Replay ledger event hash mismatch at line ${index + 1} (seq ${event.seq})`);\n\t\t}\n\t\tprevHash = event.eventHash;\n\t\tevents.push(event);\n\t}\n\treturn events;\n}\n\nexport class ReplayLedgerManager {\n\tprivate readonly goalId: string;\n\tprivate readonly ledgerPath: string;\n\tprivate readonly store: ReplayLedgerStore;\n\tprivate events: ReplayEvent[] = [];\n\tprivate committedHead: ReplayLedgerHead;\n\n\tconstructor(goalId: string, ledgerPath: string, expectedHead?: ReplayLedgerHead) {\n\t\tthis.goalId = goalId;\n\t\tthis.ledgerPath = ledgerPath;\n\t\tthis.committedHead = { fileIdentity: null, size: 0, lastSeq: 0, lastHash: GENESIS_HASH };\n\t\tthis.store = new ReplayLedgerStore(ledgerPath, (bytes) => {\n\t\t\tconst events = parseReplayLedgerBytes(bytes, goalId);\n\t\t\tconst last = events[events.length - 1];\n\t\t\treturn { lastSeq: last?.seq ?? 0, lastHash: last?.eventHash ?? GENESIS_HASH };\n\t\t});\n\t\tthis.refresh(expectedHead);\n\t}\n\n\tprivate refresh(expectedHead?: ReplayLedgerHead): void {\n\t\tconst snapshot = this.store.load(expectedHead);\n\t\tthis.events = parseReplayLedgerBytes(snapshot.bytes, this.goalId);\n\t\tthis.committedHead = snapshot.head;\n\t}\n\n\tappend(\n\t\tevent: Omit<ReplayEvent, \"seq\" | \"timestamp\" | \"payloadHashAlgorithm\" | \"payloadHash\" | \"prevHash\" | \"eventHash\">,\n\t\texpectedHead: ReplayLedgerHead = this.committedHead,\n\t): ReplayEvent {\n\t\tif (event.goalId !== this.goalId) throw new Error(\"Replay event goalId does not match the ledger\");\n\t\tif (!replayLedgerHeadsEqual(expectedHead, this.committedHead)) {\n\t\t\tthrow new Error(\"Replay manager expected-head CAS is stale\");\n\t\t}\n\t\tconst payloadHashAlgorithm = JCS_REPLAY_PAYLOAD_HASH_ALGORITHM;\n\t\tconst payloadHash = computeReplayPayloadHash(event.payload, payloadHashAlgorithm);\n\t\tconst chained: Omit<ReplayEvent, \"eventHash\"> = {\n\t\t\t...event,\n\t\t\tseq: expectedHead.lastSeq + 1,\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tpayloadHashAlgorithm,\n\t\t\tpayloadHash,\n\t\t\tprevHash: expectedHead.lastHash,\n\t\t};\n\t\tconst fullEvent: ReplayEvent = { ...chained, eventHash: computeEventHash(chained) };\n\t\tthis.committedHead = this.store.append(\n\t\t\tBuffer.from(`${JSON.stringify(fullEvent)}\\n`, \"utf8\"),\n\t\t\tfullEvent.seq,\n\t\t\tfullEvent.eventHash,\n\t\t\texpectedHead,\n\t\t);\n\t\tthis.events.push(fullEvent);\n\t\treturn structuredClone(fullEvent);\n\t}\n\n\t/** Compatibility no-op that still verifies the caller's expected committed head. */\n\tpersist(expectedHead: ReplayLedgerHead = this.committedHead): void {\n\t\tthis.getVerifiedSnapshot(expectedHead);\n\t}\n\n\tload(expectedHead: ReplayLedgerHead = this.committedHead): void {\n\t\tthis.refresh(expectedHead);\n\t}\n\n\tgetCommittedHead(): ReplayLedgerHead {\n\t\treturn structuredClone(this.committedHead);\n\t}\n\n\tgetVerifiedSnapshot(expectedHead: ReplayLedgerHead = this.committedHead): VerifiedReplayLedgerSnapshot {\n\t\tthis.refresh(expectedHead);\n\t\treturn structuredClone({ events: this.events, head: this.committedHead });\n\t}\n\n\tgetEvents(): ReadonlyArray<ReplayEvent> {\n\t\treturn structuredClone(this.events);\n\t}\n\n\tgetLedger(): Readonly<ReplayLedger> {\n\t\treturn structuredClone({\n\t\t\tgoalId: this.goalId,\n\t\t\tevents: this.events,\n\t\t\tledgerPath: this.ledgerPath,\n\t\t\tlastPersistedSeq: this.committedHead.lastSeq,\n\t\t});\n\t}\n\n\treplay<T>(handler: (event: ReplayEvent) => T | undefined): T[] {\n\t\treturn this.events.map(handler).filter((result): result is T => result !== undefined);\n\t}\n\n\texportToFile(path: string): void {\n\t\tensureDir(path);\n\t\twriteFileSync(path, JSON.stringify(this.events, null, 2), \"utf8\");\n\t}\n}\n\n// ============================================================================\n// EvidenceGate\n// ============================================================================\n\nexport interface EvidenceGateOptions {\n\t/** Minimum number of satisfied evidence items required. */\n\tminEvidenceCount?: number;\n\t/** Require at least one artifact with a SHA-256 hash in explicit legacy mode. */\n\trequireHash?: boolean;\n\t/** Require at least one verification command in explicit legacy mode. */\n\trequireVerificationCommand?: boolean;\n\t/** Receipt policy. Defaults to prefer; legacy must be selected explicitly. */\n\treceiptMode?: EvidenceReceiptMode;\n\t/** Resolve a serialized or decoded receipt by ID. The gate validates the returned value. */\n\tresolveReceipt?: (receiptId: string) => unknown;\n\t/** Strict source: one expected-head, file-identity, suffix, and chain-verified snapshot. */\n\tresolveVerifiedLedgerSnapshot?: () => VerifiedReplayLedgerSnapshot;\n\t/** Prefer-mode migration source; strict mode requires resolveVerifiedLedgerSnapshot. */\n\tresolveLedgerEvent?: (seq: number) => ReplayEvent | undefined;\n\t/** Recapture the receipt's workspace scope (same kind and scope) for freshness comparison. */\n\tcaptureWorkspaceFingerprint?: (scope: WorkspaceScope) => unknown;\n\t/**\n\t * Optional freshness source: the latest replay-ledger sequence of a workspace mutation\n\t * relevant to the given scope, or null/undefined when none is known. When configured,\n\t * the receipt's verified ledger seq must strictly exceed this value; a throwing or\n\t * out-of-contract source is a hard block (positive safe integers only).\n\t */\n\tresolveLatestWorkspaceMutationSeq?: (scope: WorkspaceScope) => number | null | undefined;\n\t/** Ephemeral trust anchor used to verify the original command's keyed attestation. */\n\tcommandAttestationBinder?: Pick<CommandHmacBinder, \"verify\">;\n\t/** Resolve the original command inside the same trust boundary as the binder. */\n\tresolveAttestedCommand?: (receiptId: string) => EvidenceCommandDescriptor | undefined;\n}\n\ninterface ReceiptCheckIssue {\n\treadonly message: string;\n\treadonly hard: boolean;\n}\n\nfunction receiptCheckIssue(message: string, hard: boolean): ReceiptCheckIssue {\n\treturn { message, hard };\n}\n\nfunction receiptIssue(\n\titem: EvidenceItem,\n\tcontract: TaskContract,\n\toptions: EvidenceGateOptions,\n): ReceiptCheckIssue | undefined {\n\tif (!item.receiptId) {\n\t\treturn receiptCheckIssue(`Execution receipt is missing for evidence \"${item.claim}\".`, false);\n\t}\n\tconst strict = options.receiptMode === \"strict\";\n\tif (item.receiptSchemaVersion !== 3 && (item.receiptSchemaVersion !== undefined || strict)) {\n\t\treturn receiptCheckIssue(`Execution receipt schema metadata is invalid for evidence \"${item.claim}\".`, true);\n\t}\n\tif (!options.resolveReceipt) {\n\t\treturn receiptCheckIssue(`Execution receipt resolver is missing for evidence \"${item.claim}\".`, false);\n\t}\n\n\tlet rawReceipt: unknown;\n\ttry {\n\t\trawReceipt = options.resolveReceipt(item.receiptId);\n\t} catch {\n\t\treturn receiptCheckIssue(`Execution receipt resolver failed for evidence \"${item.claim}\".`, true);\n\t}\n\tif (rawReceipt === undefined) {\n\t\treturn receiptCheckIssue(`Execution receipt is missing for evidence \"${item.claim}\".`, false);\n\t}\n\n\ttry {\n\t\tconst receipt = validateEvidenceReceipt(rawReceipt);\n\t\tif (receipt.core.receiptId !== item.receiptId) {\n\t\t\treturn receiptCheckIssue(`Execution receipt ID does not match evidence \"${item.claim}\".`, true);\n\t\t}\n\t\tif (receipt.core.goalId !== contract.goalId) {\n\t\t\treturn receiptCheckIssue(`Execution receipt goal does not match evidence \"${item.claim}\".`, true);\n\t\t}\n\t\tif (receipt.core.claim !== item.claim) {\n\t\t\treturn receiptCheckIssue(`Execution receipt claim does not match evidence \"${item.claim}\".`, true);\n\t\t}\n\t\tif (receipt.core.status !== \"passed\" || receipt.core.exitCode !== 0) {\n\t\t\treturn receiptCheckIssue(\n\t\t\t\t`Execution receipt for evidence \"${item.claim}\" did not pass with exit code 0.`,\n\t\t\t\ttrue,\n\t\t\t);\n\t\t}\n\n\t\tif (item.receiptCommandSha256 === undefined) {\n\t\t\treturn receiptCheckIssue(`Execution receipt command binding is missing for evidence \"${item.claim}\".`, false);\n\t\t}\n\t\tconst receiptCommandSha256 = computeEvidenceCommandSha256(receipt.core.command);\n\t\tif (!constantTimeSha256Equal(item.receiptCommandSha256, receiptCommandSha256)) {\n\t\t\treturn receiptCheckIssue(`Execution receipt command does not match evidence \"${item.claim}\".`, true);\n\t\t}\n\t\tconst attestationIssue = verifyCommandAttestation({\n\t\t\treceiptId: receipt.core.receiptId,\n\t\t\tbinding: receipt.core.commandBinding,\n\t\t\tpersistedCommand: receipt.core.command,\n\t\t\tpersistedSummary: receipt.core.commandRedaction,\n\t\t\tbinder: options.commandAttestationBinder,\n\t\t\tresolveCommand: options.resolveAttestedCommand,\n\t\t\tredact: redactCommandDescriptor,\n\t\t});\n\t\tif (attestationIssue !== undefined) {\n\t\t\treturn receiptCheckIssue(\n\t\t\t\t`Execution receipt command attestation ${attestationIssue.detail} for evidence \"${item.claim}\".`,\n\t\t\t\tattestationIssue.hard,\n\t\t\t);\n\t\t}\n\n\t\tif (receipt.core.laneId !== undefined && item.receiptLaneId === undefined) {\n\t\t\treturn receiptCheckIssue(`Execution receipt lane binding is missing for evidence \"${item.claim}\".`, false);\n\t\t}\n\t\tif (item.receiptLaneId !== receipt.core.laneId) {\n\t\t\treturn receiptCheckIssue(`Execution receipt lane does not match evidence \"${item.claim}\".`, true);\n\t\t}\n\n\t\tconst workspaceIssue = verifyWorkspaceBinding(receipt.core.workspaceAfter, options.captureWorkspaceFingerprint);\n\t\tif (workspaceIssue !== undefined) {\n\t\t\treturn receiptCheckIssue(\n\t\t\t\t`Execution receipt ${workspaceIssue.detail} for evidence \"${item.claim}\".`,\n\t\t\t\tworkspaceIssue.hard,\n\t\t\t);\n\t\t}\n\n\t\tlet snapshot: VerifiedReplayLedgerSnapshot | undefined;\n\t\tif (options.resolveVerifiedLedgerSnapshot !== undefined) {\n\t\t\ttry {\n\t\t\t\tsnapshot = parseVerifiedLedgerSnapshot(\n\t\t\t\t\toptions.resolveVerifiedLedgerSnapshot(),\n\t\t\t\t\tcontract.goalId,\n\t\t\t\t\tparseReplayLedgerBytes,\n\t\t\t\t);\n\t\t\t} catch {\n\t\t\t\treturn receiptCheckIssue(\n\t\t\t\t\t`Execution receipt verified ledger snapshot failed for evidence \"${item.claim}\".`,\n\t\t\t\t\ttrue,\n\t\t\t\t);\n\t\t\t}\n\t\t} else if (strict) {\n\t\t\treturn receiptCheckIssue(\n\t\t\t\t`Execution receipt verified ledger snapshot is missing for evidence \"${item.claim}\".`,\n\t\t\t\tfalse,\n\t\t\t);\n\t\t}\n\t\tlet latestMutationSeq =\n\t\t\tsnapshot === undefined\n\t\t\t\t? null\n\t\t\t\t: latestRelevantWorkspaceMutationSeq(snapshot.events, receipt.core.workspaceAfter.scope);\n\t\tif (snapshot === undefined && options.resolveLatestWorkspaceMutationSeq !== undefined) {\n\t\t\tlet resolved: unknown;\n\t\t\ttry {\n\t\t\t\tresolved = options.resolveLatestWorkspaceMutationSeq(receipt.core.workspaceAfter.scope);\n\t\t\t} catch {\n\t\t\t\treturn receiptCheckIssue(\n\t\t\t\t\t`Execution receipt workspace-mutation freshness source failed for evidence \"${item.claim}\".`,\n\t\t\t\t\ttrue,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (resolved !== null && resolved !== undefined) {\n\t\t\t\tif (!Number.isSafeInteger(resolved) || (resolved as number) <= 0) {\n\t\t\t\t\treturn receiptCheckIssue(\n\t\t\t\t\t\t`Execution receipt workspace-mutation freshness source is malformed for evidence \"${item.claim}\".`,\n\t\t\t\t\t\ttrue,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tlatestMutationSeq = resolved as number;\n\t\t\t}\n\t\t}\n\n\t\tconst binding = receipt.envelope.ledgerBinding;\n\t\tif (!binding) {\n\t\t\treturn receiptCheckIssue(\n\t\t\t\t`Execution receipt ledger binding is missing for evidence \"${item.claim}\".`,\n\t\t\t\tlatestMutationSeq !== null,\n\t\t\t);\n\t\t}\n\t\tconst event = snapshot\n\t\t\t? snapshot.events.find((candidate) => candidate.seq === binding.seq)\n\t\t\t: options.resolveLedgerEvent?.(binding.seq);\n\t\tif (!event && snapshot === undefined && !options.resolveLedgerEvent) {\n\t\t\treturn receiptCheckIssue(\n\t\t\t\t`Execution receipt ledger resolver is missing for evidence \"${item.claim}\".`,\n\t\t\t\tlatestMutationSeq !== null,\n\t\t\t);\n\t\t}\n\t\tconst replayPayload = evidenceReceiptReplayPayload(receipt);\n\t\tif (\n\t\t\t!event ||\n\t\t\tevent.seq !== binding.seq ||\n\t\t\tevent.type !== \"evidence_receipt\" ||\n\t\t\tevent.goalId !== contract.goalId ||\n\t\t\tevent.laneId !== receipt.core.laneId ||\n\t\t\t!constantTimeSha256Equal(event.eventHash, binding.eventHash) ||\n\t\t\t!replayPayloadMatches(event.payload, replayPayload.receiptId, replayPayload.coreSha256)\n\t\t) {\n\t\t\treturn receiptCheckIssue(`Execution receipt ledger binding does not match evidence \"${item.claim}\".`, true);\n\t\t}\n\t\tif (latestMutationSeq !== null && event.seq <= latestMutationSeq) {\n\t\t\treturn receiptCheckIssue(\n\t\t\t\t`Execution receipt is stale: workspace mutation seq ${latestMutationSeq} does not precede receipt ledger seq ${event.seq} for evidence \"${item.claim}\".`,\n\t\t\t\ttrue,\n\t\t\t);\n\t\t}\n\t} catch {\n\t\treturn receiptCheckIssue(`Execution receipt is invalid for evidence \"${item.claim}\".`, true);\n\t}\n\treturn undefined;\n}\n\nexport class EvidenceGate {\n\tprivate options: EvidenceGateOptions;\n\n\tconstructor(options: EvidenceGateOptions = {}) {\n\t\tthis.options = {\n\t\t\tminEvidenceCount: 1,\n\t\t\trequireHash: true,\n\t\t\trequireVerificationCommand: true,\n\t\t\treceiptMode: \"prefer\",\n\t\t\t...options,\n\t\t};\n\t}\n\n\tcheck(contract: TaskContract): MergeGateResult {\n\t\tconst evidence = contract.requiredEvidence;\n\t\tconst satisfied = evidence.filter((e) => e.status === \"satisfied\");\n\t\tconst failed = evidence.filter((e) => e.status === \"failed\");\n\t\tconst pending = evidence.filter((e) => e.status === \"pending\" || e.status === \"gathering\");\n\t\tconst mode = this.options.receiptMode ?? \"prefer\";\n\t\tconst checks: string[] = [];\n\t\tlet hardReceiptFailure = false;\n\n\t\tif (satisfied.length < (this.options.minEvidenceCount ?? 1)) {\n\t\t\tchecks.push(`Only ${satisfied.length}/${this.options.minEvidenceCount} required evidence items satisfied.`);\n\t\t}\n\t\tif (failed.length > 0) {\n\t\t\tchecks.push(`${failed.length} evidence item(s) failed.`);\n\t\t}\n\n\t\tif (mode === \"legacy\") {\n\t\t\tif (this.options.requireHash) {\n\t\t\t\tconst hashed = satisfied.filter((e) => e.hash && e.hash.length > 0);\n\t\t\t\tif (hashed.length === 0) checks.push(\"No satisfied evidence has a SHA-256 hash.\");\n\t\t\t}\n\t\t\tif (this.options.requireVerificationCommand) {\n\t\t\t\tconst withCommand = satisfied.filter((e) => e.verificationCommand && e.verificationCommand.length > 0);\n\t\t\t\tif (withCommand.length === 0) checks.push(\"No satisfied evidence has a verification command.\");\n\t\t\t}\n\t\t} else {\n\t\t\tconst unsatisfied = evidence.filter((item) => item.status !== \"satisfied\");\n\t\t\tif (unsatisfied.length > 0) {\n\t\t\t\tchecks.push(`${unsatisfied.length} required evidence item(s) are not satisfied.`);\n\t\t\t}\n\t\t\tfor (const item of satisfied) {\n\t\t\t\tconst issue = receiptIssue(item, contract, this.options);\n\t\t\t\tif (issue) {\n\t\t\t\t\tchecks.push(issue.message);\n\t\t\t\t\thardReceiptFailure ||= issue.hard;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (contract.verdict === \"fail\" || (mode !== \"legacy\" && contract.verdict !== \"pass\")) {\n\t\t\tchecks.push(`Task contract verdict is '${contract.verdict}'.`);\n\t\t}\n\n\t\tlet status: MergeGateStatus;\n\t\tlet reason: string;\n\t\tlet suggestion: string | undefined;\n\t\tif (checks.length === 0) {\n\t\t\tstatus = \"open\";\n\t\t\treason = `Evidence gate passed: ${satisfied.length}/${evidence.length} evidence items satisfied.`;\n\t\t} else {\n\t\t\tconst contractFailure = mode === \"legacy\" ? contract.verdict === \"fail\" : contract.verdict !== \"pass\";\n\t\t\tconst hasHardFailure = failed.length > 0 || contractFailure || hardReceiptFailure || mode === \"strict\";\n\t\t\tstatus = hasHardFailure ? \"blocked\" : mode === \"prefer\" || pending.length > 0 ? \"conditional\" : \"blocked\";\n\t\t\treason = checks.join(\"; \");\n\t\t\tsuggestion = `Gather remaining evidence (${pending.length} pending) and re-run verification.`;\n\t\t}\n\n\t\treturn {\n\t\t\tgateId: \"evidence-gate\",\n\t\t\tstatus,\n\t\t\treason,\n\t\t\tsuggestion,\n\t\t\tevidenceChecked: evidence,\n\t\t};\n\t}\n}\n\n// ============================================================================\n// FailClosedMergeGate\n// ============================================================================\n\nexport class FailClosedMergeGate {\n\tprivate gates: EvidenceGate[];\n\n\tconstructor(gates: EvidenceGate[] = [new EvidenceGate()]) {\n\t\tthis.gates = gates;\n\t}\n\n\tcheck(contract: TaskContract): MergeGateResult {\n\t\tconst results = this.gates.map((gate) => gate.check(contract));\n\t\tconst blocked = results.filter((r) => r.status === \"blocked\");\n\t\tconst conditional = results.filter((r) => r.status === \"conditional\");\n\n\t\tif (blocked.length > 0) {\n\t\t\treturn {\n\t\t\t\tgateId: \"fail-closed-merge-gate\",\n\t\t\t\tstatus: \"blocked\",\n\t\t\t\treason: `Blocked by ${blocked.length} gate(s): ${blocked.map((b) => b.reason).join(\"; \")}`,\n\t\t\t\tsuggestion: \"Resolve all blocking conditions before merge.\",\n\t\t\t\tevidenceChecked: contract.requiredEvidence,\n\t\t\t};\n\t\t}\n\n\t\tif (conditional.length > 0) {\n\t\t\treturn {\n\t\t\t\tgateId: \"fail-closed-merge-gate\",\n\t\t\t\tstatus: \"conditional\",\n\t\t\t\treason: `Conditional pass: ${conditional.length} gate(s) have pending conditions.`,\n\t\t\t\tsuggestion: \"Complete pending evidence or waive with explicit approval.\",\n\t\t\t\tevidenceChecked: contract.requiredEvidence,\n\t\t\t};\n\t\t}\n\n\t\treturn {\n\t\t\tgateId: \"fail-closed-merge-gate\",\n\t\t\tstatus: \"open\",\n\t\t\treason: \"All merge gates passed. Evidence verified and contract verdict is acceptable.\",\n\t\t\tevidenceChecked: contract.requiredEvidence,\n\t\t};\n\t}\n}\n\n// ============================================================================\n// Verify Reporter v2\n// ============================================================================\n\n/** Escape pipes/newlines so untrusted claim or command text cannot restructure the report table. */\nfunction escapeTableCell(value: string): string {\n\treturn value.replace(/\\\\/g, \"\\\\\\\\\").replace(/\\|/g, \"\\\\|\").replace(/\\r?\\n/g, \" \");\n}\n\nexport interface VerifyReporterV2Options {\n\toutputDir: string;\n\tgoalId: string;\n}\n\nexport class VerifyReporterV2 {\n\tprivate options: VerifyReporterV2Options;\n\n\tconstructor(options: VerifyReporterV2Options) {\n\t\tthis.options = options;\n\t}\n\n\trender(contract: TaskContract, mergeGate: MergeGateResult): string {\n\t\tconst lines: string[] = [\n\t\t\t`## OMK Verification Report v2 — ${contract.goalId}`,\n\t\t\t\"\",\n\t\t\t\"### Contract\",\n\t\t\t\"\",\n\t\t\t`| Field | Value |`,\n\t\t\t`|-------|-------|`,\n\t\t\t`| Claim | ${escapeTableCell(contract.completionClaim)} |`,\n\t\t\t`| Verdict | ${contract.verdict} |`,\n\t\t\t`| Final Risk | ${escapeTableCell(contract.finalRisk)} |`,\n\t\t\t\"\",\n\t\t\t\"### Evidence\",\n\t\t\t\"\",\n\t\t\t`| Claim | Status | Artifact | Command |`,\n\t\t\t`|-------|--------|----------|----------|`,\n\t\t];\n\n\t\tfor (const ev of contract.requiredEvidence) {\n\t\t\tlines.push(\n\t\t\t\t`| ${escapeTableCell(ev.claim)} | ${ev.status} | ${escapeTableCell(ev.artifactPath ?? \"—\")} | ${escapeTableCell(ev.verificationCommand ?? \"—\")} |`,\n\t\t\t);\n\t\t}\n\n\t\tlines.push(\n\t\t\t\"\",\n\t\t\t\"### Merge Gate\",\n\t\t\t\"\",\n\t\t\t`| Gate | Status | Reason |`,\n\t\t\t`|------|--------|--------|`,\n\t\t\t`| ${mergeGate.gateId} | ${mergeGate.status} | ${escapeTableCell(mergeGate.reason)} |`,\n\t\t\t\"\",\n\t\t);\n\n\t\tif (mergeGate.suggestion) {\n\t\t\tlines.push(`**Suggestion:** ${mergeGate.suggestion}`, \"\");\n\t\t}\n\n\t\treturn lines.join(\"\\n\");\n\t}\n\n\twrite(contract: TaskContract, mergeGate: MergeGateResult): string {\n\t\tconst markdown = this.render(contract, mergeGate);\n\t\tconst path = join(this.options.outputDir, `${this.options.goalId}.verify.md`);\n\t\tensureDir(path);\n\t\twriteFileSync(path, markdown, \"utf-8\");\n\t\treturn path;\n\t}\n}\n"]}
@@ -7,6 +7,7 @@ import { latestRelevantWorkspaceMutationSeq, parseVerifiedLedgerSnapshot, replay
7
7
  export { latestRelevantWorkspaceMutationSeq } from "./evidence-attestation.js";
8
8
  import { computeEvidenceCommandSha256, constantTimeSha256Equal, evidenceReceiptReplayPayload, parseSha256Hex, validateEvidenceReceipt, } from "./evidence-receipt.js";
9
9
  import { ReplayLedgerStore, replayLedgerHeadsEqual } from "./replay-ledger-store.js";
10
+ import { computeReplayPayloadHash, JCS_REPLAY_PAYLOAD_HASH_ALGORITHM, LEGACY_REPLAY_PAYLOAD_HASH_ALGORITHM, } from "./replay-payload-hash.js";
10
11
  function sha256(input) {
11
12
  return createHash("sha256").update(input).digest("hex");
12
13
  }
@@ -52,11 +53,13 @@ export class TaskContractBuilder {
52
53
  this.touch();
53
54
  return this;
54
55
  }
56
+ /** @deprecated Use `evaluateTask()` from `omk-protocol`; verdicts are derived, not mutated. */
55
57
  setVerdict(verdict) {
56
58
  this.verdict = verdict;
57
59
  this.touch();
58
60
  return this;
59
61
  }
62
+ /** @deprecated Append a protocol Observation and re-evaluate instead of mutating evidence state. */
60
63
  updateEvidenceStatus(claim, status, gapReason) {
61
64
  const item = this.requiredEvidence.find((e) => e.claim === claim);
62
65
  if (item) {
@@ -162,15 +165,11 @@ function parseEvidenceItem(raw, index) {
162
165
  /** prevHash sentinel for the first event in a replay chain. */
163
166
  const GENESIS_HASH = "genesis";
164
167
  function computeEventHash(event) {
165
- return sha256(JSON.stringify([
166
- event.seq,
167
- event.type,
168
- event.timestamp,
169
- event.goalId,
170
- event.laneId ?? null,
171
- event.payloadHash,
172
- event.prevHash,
173
- ]));
168
+ const metadata = [event.seq, event.type, event.timestamp, event.goalId, event.laneId ?? null];
169
+ const material = event.payloadHashAlgorithm === undefined
170
+ ? [...metadata, event.payloadHash, event.prevHash]
171
+ : [...metadata, event.payloadHashAlgorithm, event.payloadHash, event.prevHash];
172
+ return sha256(JSON.stringify(material));
174
173
  }
175
174
  function parseReplayEventLine(line, lineNumber) {
176
175
  let raw;
@@ -184,7 +183,7 @@ function parseReplayEventLine(line, lineNumber) {
184
183
  throw new Error(`Replay ledger corrupted at line ${lineNumber}: event must be an object`);
185
184
  }
186
185
  const event = raw;
187
- const { seq, type, timestamp, goalId, laneId, payloadHash, prevHash, eventHash } = event;
186
+ const { seq, type, timestamp, goalId, laneId, payloadHashAlgorithm, payloadHash, prevHash, eventHash } = event;
188
187
  if (typeof seq !== "number" ||
189
188
  typeof type !== "string" ||
190
189
  typeof timestamp !== "string" ||
@@ -196,7 +195,12 @@ function parseReplayEventLine(line, lineNumber) {
196
195
  !("payload" in event)) {
197
196
  throw new Error(`Replay ledger corrupted at line ${lineNumber}: event fields are missing or mistyped`);
198
197
  }
199
- return {
198
+ if (payloadHashAlgorithm !== undefined &&
199
+ payloadHashAlgorithm !== LEGACY_REPLAY_PAYLOAD_HASH_ALGORITHM &&
200
+ payloadHashAlgorithm !== JCS_REPLAY_PAYLOAD_HASH_ALGORITHM) {
201
+ throw new Error(`Replay ledger corrupted at line ${lineNumber}: unsupported payload hash algorithm`);
202
+ }
203
+ const parsed = {
200
204
  seq,
201
205
  type: type,
202
206
  timestamp,
@@ -207,6 +211,7 @@ function parseReplayEventLine(line, lineNumber) {
207
211
  prevHash,
208
212
  eventHash,
209
213
  };
214
+ return payloadHashAlgorithm === undefined ? parsed : { ...parsed, payloadHashAlgorithm };
210
215
  }
211
216
  function parseReplayLedgerBytes(bytes, goalId) {
212
217
  const lines = bytes
@@ -224,7 +229,8 @@ function parseReplayLedgerBytes(bytes, goalId) {
224
229
  }
225
230
  if (event.prevHash !== prevHash)
226
231
  throw new Error(`Replay ledger chain broken at line ${index + 1}`);
227
- if (sha256(JSON.stringify(event.payload)) !== event.payloadHash) {
232
+ const payloadHashAlgorithm = event.payloadHashAlgorithm ?? LEGACY_REPLAY_PAYLOAD_HASH_ALGORITHM;
233
+ if (computeReplayPayloadHash(event.payload, payloadHashAlgorithm) !== event.payloadHash) {
228
234
  throw new Error(`Replay ledger payload tampered at line ${index + 1} (seq ${event.seq})`);
229
235
  }
230
236
  if (computeEventHash(event) !== event.eventHash) {
@@ -247,7 +253,7 @@ export class ReplayLedgerManager {
247
253
  this.committedHead = { fileIdentity: null, size: 0, lastSeq: 0, lastHash: GENESIS_HASH };
248
254
  this.store = new ReplayLedgerStore(ledgerPath, (bytes) => {
249
255
  const events = parseReplayLedgerBytes(bytes, goalId);
250
- const last = events.at(-1);
256
+ const last = events[events.length - 1];
251
257
  return { lastSeq: last?.seq ?? 0, lastHash: last?.eventHash ?? GENESIS_HASH };
252
258
  });
253
259
  this.refresh(expectedHead);
@@ -263,11 +269,13 @@ export class ReplayLedgerManager {
263
269
  if (!replayLedgerHeadsEqual(expectedHead, this.committedHead)) {
264
270
  throw new Error("Replay manager expected-head CAS is stale");
265
271
  }
266
- const payloadHash = sha256(JSON.stringify(event.payload));
272
+ const payloadHashAlgorithm = JCS_REPLAY_PAYLOAD_HASH_ALGORITHM;
273
+ const payloadHash = computeReplayPayloadHash(event.payload, payloadHashAlgorithm);
267
274
  const chained = {
268
275
  ...event,
269
276
  seq: expectedHead.lastSeq + 1,
270
277
  timestamp: new Date().toISOString(),
278
+ payloadHashAlgorithm,
271
279
  payloadHash,
272
280
  prevHash: expectedHead.lastHash,
273
281
  };