import { analysisErrorProjectionSchema } from "../contracts/errorSchemas.js"; import type { AnalysisError } from "../domain/analysisErrorBase.js"; import { projectAnalysisError } from "../domain/analysisErrorProjection.js"; import { jsonValueSchema, type JsonValue } from "../domain/jsonValue.js"; import { ProviderAdapterError } from "../domain/providerAdapterError.js"; import { err, type Result } from "../domain/result.js"; import type { AnalysisExecution } from "./AnalysisProvider.js"; import type { BinarySession } from "./binary/BinarySession.js"; import { analysisErrorWithCleanupFailure } from "../domain/analysisErrorCleanup.js"; /** Await one-shot cleanup and retain either the outcome or both failures. */ export const withSessionCleanup = async ( session: BinarySession, operation: () => Promise, onCleanupFailure: (value: Value, error: AnalysisError) => Value, ): Promise => { let value: Value; try { value = await operation(); } catch (cause: unknown) { const closed = await session.close(); if (!closed.ok) throw new AggregateError( [cause, closed.error], "Analysis and provider cleanup both failed", { cause }, ); throw cause; } const closed = await session.close(); return closed.ok ? value : onCleanupFailure(value, closed.error); }; /** Keep the primary CLI failure or completed Evidence alongside cleanup failure. */ export const directAnalysisCleanupFailure = ( output: JsonValue, error: AnalysisError, ): JsonValue => { const cleanup = projectAnalysisError(error); if (typeof output === "object" && output !== null && !Array.isArray(output)) { const { error: label, ...projection } = output; const primary = analysisErrorProjectionSchema.safeParse(projection); if (typeof label === "string" && primary.success) return { error: label, ...primary.data, code: "cleanup_incomplete", retryable: false, remediation: cleanup.remediation, details: { // Primary fields describe the original failure; the later close // assessment, including its resources, belongs to cleanup_error. resources: error.cleanupResources.slice(), ...primary.data.details, cleanup: "incomplete", execution_failure: primary.data.details !== undefined && "execution_failure" in primary.data.details ? (primary.data.details.execution_failure ?? primary.data.code) : primary.data.code, cleanup_error: cleanup, }, }; } return { error: "Analysis failed", ...cleanup, code: "cleanup_incomplete", retryable: false, details: { ...cleanup.details, cleanup: "incomplete", partial_observation: output, }, }; }; /** Preserve managed observations or the original typed failure in diagnostics. */ export const managedAnalysisCleanupFailure = ( result: Result, error: AnalysisError, ): Result => { if (!result.ok) return err(analysisErrorWithCleanupFailure(result.error, error)); const cleanup = projectAnalysisError(error); return err( new ProviderAdapterError( error instanceof ProviderAdapterError ? error.providerId : "rea", "close_binary", { cause: error, remediationAction: cleanup.remediation.action, cleanup: { reason: cleanup.message, resources: error.cleanupResources }, diagnostics: { cleanup_error: cleanup, partial_observation: jsonValueSchema.parse(result.value), }, }, ), ); };