{"version":3,"file":"reviewer.d.ts","sourceRoot":"","sources":["../../../src/core/evaluation/reviewer.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACX,kBAAkB,EAClB,eAAe,EACf,4BAA4B,EAC5B,wBAAwB,EACxB,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,wBAAwB;IACxC,gBAAgB,EAAE,MAAM,CAAC;IACzB,wBAAwB,EAAE,MAAM,CAAC;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,kBAAkB,CAAC,YAAY,CAAC,CAAC;IAC7C,OAAO,EAAE,kBAAkB,CAAC,SAAS,CAAC,CAAC;IACvC,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,yBAAyB,EAAE,MAAM,CAAC;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,wBAAwB;IACxC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,WAAW,GAAG,SAAS,GAAG,aAAa,CAAC;IAClE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IAC/C,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,OAAO,CAAC,KAAK,EAAE;QACd,UAAU,EAAE,4BAA4B,CAAC;QACzC,MAAM,EAAE,wBAAwB,CAAC;QACjC,MAAM,CAAC,EAAE,WAAW,CAAC;KACrB,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,wBAAwB;IACxC,UAAU,EAAE,4BAA4B,CAAC;IACzC,cAAc,EAAE,wBAAwB,CAAC;IACzC,MAAM,EAAE,eAAe,EAAE,CAAC;CAC1B;AAKD,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,wBAAwB,CAe3F;AAeD,wBAAsB,sBAAsB,CAAC,KAAK,EAAE;IACnD,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,MAAM,CAAC,EAAE,WAAW,CAAC;CACrB,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAwDpC;AAED,wBAAgB,2BAA2B,CAAC,UAAU,SAAqB,GAAG,kBAAkB,CAY/F","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { hashJson } from \"./identity.js\";\nimport type {\n\tEvaluationArtifact,\n\tEvaluationEvent,\n\tEvaluationReviewerAssignment,\n\tSemanticEvaluationResult,\n} from \"./types.js\";\n\nexport interface EvaluationEvidencePacket {\n\tevidencePacketId: string;\n\tcandidateEvaluationRunId: string;\n\tscenarioId: string;\n\tassertions: EvaluationArtifact[\"assertions\"];\n\tmetrics: EvaluationArtifact[\"metrics\"];\n\tevidenceIds: string[];\n\tredactedCandidateIdentity: string;\n\tcreatedAt: string;\n\tcontentHash: string;\n}\n\nexport interface EvaluationReviewerOutput {\n\tstatus: \"pass\" | \"fail\" | \"uncertain\" | \"invalid\" | \"unavailable\";\n\tdimensions: Record<string, number | undefined>;\n\trationale: string;\n\tuncertainty?: number;\n}\n\nexport interface EvaluationReviewer {\n\treadonly reviewerId: string;\n\texecute(input: {\n\t\tassignment: EvaluationReviewerAssignment;\n\t\tpacket: EvaluationEvidencePacket;\n\t\tsignal?: AbortSignal;\n\t}): Promise<EvaluationReviewerOutput>;\n}\n\nexport interface EvaluationReviewerResult {\n\tassignment: EvaluationReviewerAssignment;\n\tsemanticResult: SemanticEvaluationResult;\n\tevents: EvaluationEvent[];\n}\n\nconst PROMPT_INJECTION_PATTERN =\n\t/(?:ignore|disregard|override)\\s+(?:all|any|the)\\s+(?:previous|above|evaluation|reviewer)|reveal\\s+(?:hidden|system)|mark\\s+(?:this|the)\\s+as\\s+pass/i;\n\nexport function createEvidencePacket(artifact: EvaluationArtifact): EvaluationEvidencePacket {\n\tconst packet = {\n\t\tevidencePacketId: `evidence-${randomUUID()}`,\n\t\tcandidateEvaluationRunId: artifact.run.evaluationRunId,\n\t\tscenarioId: artifact.scenario.scenarioId,\n\t\tassertions: artifact.assertions,\n\t\tmetrics: artifact.metrics,\n\t\tevidenceIds: [...artifact.evidenceIds],\n\t\tredactedCandidateIdentity: hashJson({\n\t\t\tscenario: artifact.scenario,\n\t\t\tcandidate: artifact.candidate.providerProfile,\n\t\t}),\n\t\tcreatedAt: new Date().toISOString(),\n\t};\n\treturn { ...packet, contentHash: hashJson(packet) };\n}\n\nfunction validateOutput(output: EvaluationReviewerOutput): void {\n\tif (![\"pass\", \"fail\", \"uncertain\", \"invalid\", \"unavailable\"].includes(output.status))\n\t\tthrow new Error(\"reviewer output status is invalid\");\n\tif (typeof output.rationale !== \"string\" || output.rationale.length > 4_000)\n\t\tthrow new Error(\"reviewer rationale is not bounded\");\n\tif (PROMPT_INJECTION_PATTERN.test(output.rationale)) throw new Error(\"reviewer prompt-injection content detected\");\n\tif (output.uncertainty !== undefined && (output.uncertainty < 0 || output.uncertainty > 1))\n\t\tthrow new Error(\"reviewer uncertainty must be between 0 and 1\");\n\tfor (const value of Object.values(output.dimensions)) {\n\t\tif (value !== undefined && !Number.isFinite(value)) throw new Error(\"reviewer dimension must be finite\");\n\t}\n}\n\nexport async function runIndependentReviewer(input: {\n\tartifact: EvaluationArtifact;\n\treviewer: EvaluationReviewer;\n\trubricId: string;\n\trubricVersion: number;\n\treviewerDefinition: string;\n\tsignal?: AbortSignal;\n}): Promise<EvaluationReviewerResult> {\n\tif (input.reviewer.reviewerId === input.artifact.candidate.providerProfile)\n\t\tthrow new Error(\"reviewer identity must differ from candidate identity\");\n\tif (input.artifact.run.status !== \"completed\") throw new Error(\"reviewer requires a completed candidate run\");\n\tconst packet = createEvidencePacket(input.artifact);\n\tconst assignment: EvaluationReviewerAssignment = {\n\t\treviewerRunId: randomUUID(),\n\t\tcandidateEvaluationRunId: input.artifact.run.evaluationRunId,\n\t\treviewerDefinition: input.reviewerDefinition,\n\t\trubricId: input.rubricId,\n\t\trubricVersion: input.rubricVersion,\n\t\tevidencePacketId: packet.evidencePacketId,\n\t};\n\tconst events: EvaluationEvent[] = [\n\t\t{\n\t\t\teventId: randomUUID(),\n\t\t\ttype: \"EVAL_REVIEWER_ASSIGNED\",\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tdetails: { reviewerRunId: assignment.reviewerRunId },\n\t\t},\n\t\t{\n\t\t\teventId: randomUUID(),\n\t\t\ttype: \"EVAL_REVIEWER_STARTED\",\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tdetails: { reviewerId: input.reviewer.reviewerId },\n\t\t},\n\t];\n\tconst output = await input.reviewer.execute({ assignment, packet, signal: input.signal });\n\tvalidateOutput(output);\n\tevents.push({\n\t\teventId: randomUUID(),\n\t\ttype: \"EVAL_REVIEWER_OUTPUT_VALIDATED\",\n\t\ttimestamp: new Date().toISOString(),\n\t\tdetails: { reviewerRunId: assignment.reviewerRunId },\n\t});\n\tconst deterministicFailure = input.artifact.assertions.some(\n\t\t(assertion) =>\n\t\t\t(assertion.severity === \"critical\" || assertion.severity === \"high\") && assertion.status !== \"pass\",\n\t);\n\tconst semanticResult: SemanticEvaluationResult = {\n\t\trubricId: input.rubricId,\n\t\trubricVersion: input.rubricVersion,\n\t\tjudgeId: input.reviewer.reviewerId,\n\t\tstatus: deterministicFailure && output.status === \"pass\" ? \"fail\" : output.status,\n\t\tdimensions: output.dimensions,\n\t\trationale: output.rationale,\n\t\tcandidateIdentityHidden: true,\n\t\ttoolExecutionAllowed: false,\n\t};\n\tevents.push({\n\t\teventId: randomUUID(),\n\t\ttype: \"EVAL_REVIEWER_COMPLETED\",\n\t\ttimestamp: new Date().toISOString(),\n\t\tdetails: { status: semanticResult.status },\n\t});\n\treturn { assignment, semanticResult, events };\n}\n\nexport function createDeterministicReviewer(reviewerId = \"fixture-reviewer\"): EvaluationReviewer {\n\treturn {\n\t\treviewerId,\n\t\tasync execute() {\n\t\t\treturn {\n\t\t\t\tstatus: \"pass\",\n\t\t\t\tdimensions: { correctness: 1, safety: 1 },\n\t\t\t\trationale: \"Deterministic evidence packet satisfied the rubric.\",\n\t\t\t\tuncertainty: 0,\n\t\t\t};\n\t\t},\n\t};\n}\n"]}