diff --git a/src/server/promptCompletion.ts b/src/server/promptCompletion.ts index 81306aeb23..59fa48e6ce 100644 --- a/src/server/promptCompletion.ts +++ b/src/server/promptCompletion.ts @@ -4,13 +4,13 @@ import { z } from "zod"; import { uniqueSorted } from "../domain/canonicalOrdering.js"; import type { JsonValue } from "../domain/jsonValue.js"; import type { AnalysisOperationPort } from "../application/AnalysisProvider.js"; +import { parseProcessCaptureEvidence } from "../application/process/ProcessEvidence.js"; import type { BinarySessionPort } from "../application/binary/BinarySessionPort.js"; import type { PromptCompletionKind } from "../contracts/promptContracts.js"; import { addressedValueSchema } from "../domain/native/nativeInventory.js"; import { currentMcpRequestSignal } from "./EvidenceMcpServer.js"; import { parseArtifactInventoryEvidence } from "../domain/artifactInventoryEvidence.js"; import { EvidenceIntegrityError } from "../domain/evidenceErrors.js"; -import { parseProcessCapture } from "../domain/process/processCaptureParsing.js"; const documentListSchema = z.array(z.string().min(1)); const procedureInventorySchema = z.array( @@ -216,13 +216,20 @@ const artifactInventoryValues = ( }; const isProcessCaptureEvidence = (evidence: LedgerEvidence): boolean => { - if (evidence.operation !== "capture_process_scenario") return false; - try { - parseProcessCapture(evidence.normalized_result); - return true; - } catch (cause: unknown) { - if (cause instanceof TypeError || cause instanceof z.ZodError) return false; - throw cause; + const parsed = parseProcessCaptureEvidence(evidence); + if (parsed.ok) return true; + switch (parsed.error.kind) { + case "wrong-workflow": + case "unsupported-provider": + return false; + case "invalid-evidence": + case "invalid-capture": + if ( + parsed.error.cause instanceof TypeError || + parsed.error.cause instanceof z.ZodError + ) + return false; + throw parsed.error.cause; } }; diff --git a/tests/boundary/mcp/promptCompletion.test.ts b/tests/boundary/mcp/promptCompletion.test.ts index 8d919c3c7a..77ea4b7574 100644 --- a/tests/boundary/mcp/promptCompletion.test.ts +++ b/tests/boundary/mcp/promptCompletion.test.ts @@ -21,6 +21,7 @@ import { } from "../../../src/domain/artifactInspection.js"; import { artifactInventoryResultSchema } from "../../../src/domain/artifactGraph.js"; import { jsonValueSchema } from "../../../src/domain/jsonValue.js"; +import { PROCESS_PROVIDER } from "../../../src/domain/process/processEvidenceProvider.js"; import { digestCanonicalValue } from "../../../src/domain/canonicalDigest.js"; import { createPromptCompletionSource } from "../../../src/server/promptCompletion.js"; import { observed } from "../../fixtures/analysisExecution.js"; @@ -89,6 +90,42 @@ describe("guided prompt completion from live analysis", () => { }); describe("guided prompt completion from investigation records", () => { + it("offers only captures admitted by the process comparison identity", async () => { + const session = createTestBinarySession(() => client([])); + const captureResult = jsonValueSchema.parse(EMPTY_PROCESS_CAPTURE_EXAMPLE); + const unsupportedProvider = createEvidence(undefined, fixtureProvider, { + predicateType: "rea.process-capture", + operation: "capture_process_scenario", + parameters: {}, + result: captureResult, + confidence: "observed", + authority: "controlled-replay", + environment: fixtureEnvironment, + }); + const wrongPredicate = createEvidence(undefined, PROCESS_PROVIDER, { + predicateType: "rea.other-process-data", + operation: "capture_process_scenario", + parameters: {}, + result: captureResult, + confidence: "observed", + authority: "controlled-replay", + environment: fixtureEnvironment, + }); + for (const evidence of [ + PROCESS_CAPTURE_REFERENCE, + unsupportedProvider, + wrongPredicate, + ]) + expect(session.recordEvidence(evidence).ok).toBe(true); + const completion = createPromptCompletionSource(session, session); + + expect(await completion.complete("capture", "ev_")).toEqual([ + PROCESS_CAPTURE_REFERENCE.evidence_id, + ]); + + await session.close(); + }); + it("excludes process captures missing canonical executable identity", async () => { const session = createTestBinarySession(() => client([])); const { @@ -101,7 +138,8 @@ describe("guided prompt completion from investigation records", () => { ...EMPTY_PROCESS_CAPTURE_EXAMPLE, manifest: { ...legacyManifest, executable_sha256 }, }; - const evidence = createEvidence(undefined, fixtureProvider, { + const evidence = createEvidence(undefined, PROCESS_PROVIDER, { + predicateType: "rea.process-capture", operation: "capture_process_scenario", parameters: {}, result: legacyCapture, @@ -121,7 +159,8 @@ describe("guided prompt completion from investigation records", () => { it("projects only validated typed identifiers from the evidence ledger", async () => { const session = createTestBinarySession(() => client([])); - const invalidCapture = createEvidence(undefined, fixtureProvider, { + const invalidCapture = createEvidence(undefined, PROCESS_PROVIDER, { + predicateType: "rea.process-capture", operation: "capture_process_scenario", parameters: {}, result: {},