import { InvalidToolExecutionEvidenceError, ToolExecutionEvidenceConflictError, ToolExecutionEvidenceUnavailableError, normalizeListToolExecutionEvidenceQuery, normalizeListToolExecutionEvidenceResult, normalizeToolExecutionEvidenceBundle, type ListToolExecutionEvidenceQuery, type ListToolExecutionEvidenceResult, type PrepareToolExecutionEvidenceResult, type ToolExecutionEvidenceBundle, type ToolExecutionEvidenceRepository, } from '@qinglong/runtime-core/tool-execution-evidence'; import type { PostgresClient, PostgresPool, PostgresQueryable, } from '@qinglong/runtime-core'; import { POSTGRES_DEFINITION_RETRYABLE_SQL_STATES, POSTGRES_DEFINITION_TRANSACTION_ATTEMPTS, configurePostgresDefinitionTransaction, postgresRequiredInteger, postgresRequiredJsonObject, postgresRequiredString, postgresSqlState, rollbackPostgresDefinitionTransaction, } from '../repository/definitionRepositorySupport'; type Row = Record; const TRACE_ID_PATTERN = /^[0-9a-f]{32}$/; const SPAN_ID_PATTERN = /^[0-9a-f]{16}$/; const AUDIT_EVENT_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/; const EVIDENCE_SELECT = ` trace.trace_json AS "traceJson", receipt.audit_json AS "auditJson", receipt.receipt_json AS "receiptJson", trace.trace_id AS "storedTraceId", trace.span_id AS "storedSpanId", trace.parent_span_id AS "storedParentSpanId", trace.project_id AS "traceProjectId", trace.run_id AS "traceRunId", trace.step_run_id AS "traceStepRunId", trace.invocation_plan_digest AS "traceInvocationPlanDigest", trace.binding_digest AS "traceBindingDigest", trace.adapter_digest AS "storedAdapterDigest", trace.redaction_contract_digest AS "storedRedactionContractDigest", trace.audit_contract_digest AS "storedAuditContractDigest", trace.created_at_ms AS "traceCreatedAtMs", trace.trace_digest AS "storedTraceDigest", receipt.event_id AS "storedEventId", receipt.project_id AS "receiptProjectId", receipt.run_id AS "receiptRunId", receipt.step_run_id AS "receiptStepRunId", receipt.trace_id AS "receiptTraceId", receipt.span_id AS "receiptSpanId", receipt.trace_digest AS "receiptTraceDigest", receipt.invocation_plan_digest AS "receiptInvocationPlanDigest", receipt.binding_digest AS "receiptBindingDigest", receipt.audit_record_digest AS "storedAuditRecordDigest", receipt.created_at_ms AS "receiptCreatedAtMs", receipt.receipt_digest AS "storedReceiptDigest" `; function unavailable( cause?: unknown, ): ToolExecutionEvidenceUnavailableError { return new ToolExecutionEvidenceUnavailableError({ cause: cause instanceof Error ? cause : undefined, }); } function identity(value: unknown, pattern: RegExp, label: string): string { if (typeof value !== 'string' || !pattern.test(value)) { throw new InvalidToolExecutionEvidenceError(`${label} is invalid`); } return value; } function requiredText(row: Row, key: string): string { return postgresRequiredString(row[key], unavailable); } function requiredInteger(row: Row, key: string): number { const value = postgresRequiredInteger(row[key], unavailable); if (value < 0) throw unavailable(); return value; } function nullableText(row: Row, key: string): string | null { const value = row[key]; if (value === null) return null; return postgresRequiredString(value, unavailable); } function jsonObject(row: Row, key: string): Readonly> { return postgresRequiredJsonObject(row[key], unavailable); } function bundleFromRow( row: Row, ): Readonly { let bundle: Readonly; try { bundle = normalizeToolExecutionEvidenceBundle({ schema: 'qinglong/tool-execution-evidence-bundle@v1', trace: jsonObject(row, 'traceJson'), audit: jsonObject(row, 'auditJson'), receipt: jsonObject(row, 'receiptJson'), } as unknown as ToolExecutionEvidenceBundle); } catch { throw unavailable(); } const trace = bundle.trace; const receipt = bundle.receipt; if ( requiredText(row, 'storedTraceId') !== trace.traceId || requiredText(row, 'storedSpanId') !== trace.spanId || nullableText(row, 'storedParentSpanId') !== trace.parentSpanId || requiredText(row, 'traceProjectId') !== trace.projectId || requiredText(row, 'traceRunId') !== trace.runId || requiredText(row, 'traceStepRunId') !== trace.stepRunId || requiredText(row, 'traceInvocationPlanDigest') !== trace.invocationPlanDigest || requiredText(row, 'traceBindingDigest') !== trace.bindingDigest || requiredText(row, 'storedAdapterDigest') !== trace.adapterDigest || requiredText(row, 'storedRedactionContractDigest') !== trace.redactionContractDigest || requiredText(row, 'storedAuditContractDigest') !== trace.auditContractDigest || requiredInteger(row, 'traceCreatedAtMs') !== trace.createdAtMs || requiredText(row, 'storedTraceDigest') !== trace.traceDigest || requiredText(row, 'storedEventId') !== receipt.eventId || requiredText(row, 'receiptProjectId') !== receipt.projectId || requiredText(row, 'receiptRunId') !== receipt.runId || requiredText(row, 'receiptStepRunId') !== receipt.stepRunId || requiredText(row, 'receiptTraceId') !== receipt.traceId || requiredText(row, 'receiptSpanId') !== receipt.spanId || requiredText(row, 'receiptTraceDigest') !== receipt.traceDigest || requiredText(row, 'receiptInvocationPlanDigest') !== receipt.invocationPlanDigest || requiredText(row, 'receiptBindingDigest') !== receipt.bindingDigest || requiredText(row, 'storedAuditRecordDigest') !== receipt.auditRecordDigest || requiredInteger(row, 'receiptCreatedAtMs') !== receipt.createdAtMs || requiredText(row, 'storedReceiptDigest') !== receipt.receiptDigest ) { throw unavailable(); } return bundle; } function constraintError(error: unknown): boolean { const state = postgresSqlState(error); return state === '23503' || state === '23505' || state === '23514'; } function mapStorageError(error: unknown): Error { if ( error instanceof InvalidToolExecutionEvidenceError || error instanceof ToolExecutionEvidenceConflictError || error instanceof ToolExecutionEvidenceUnavailableError ) { return error; } return constraintError(error) ? new ToolExecutionEvidenceConflictError() : unavailable(error); } async function findRows( queryable: PostgresQueryable, where: string, values: readonly unknown[], limit = 2, ): Promise { const result = await queryable.query( `SELECT ${EVIDENCE_SELECT} FROM "ql3"."tool_execution_trace_anchors" AS trace JOIN "ql3"."tool_execution_audit_receipts" AS receipt ON receipt.trace_id = trace.trace_id AND receipt.span_id = trace.span_id WHERE ${where} LIMIT ${limit}`, values, ); return result.rows; } async function insertAudit( client: PostgresClient, bundle: Readonly, ): Promise { const audit = bundle.audit; await client.query( `INSERT INTO "ql3"."security_audit_events" ( event_id, request_id, operation_id, project_id, subject_type, subject_id, authentication_id, outcome, reasons, project_version, binding_version, occurred_at_ms ) VALUES ( $1::uuid, $2, $3, $4, $5, $6, $7, $8, $9::jsonb, $10, $11, $12 )`, [ audit.eventId, audit.requestId, audit.operationId, audit.projectId, audit.subject?.type ?? null, audit.subject?.id ?? null, audit.authenticationId, audit.outcome, JSON.stringify(audit.reasons), audit.fence?.projectVersion ?? null, audit.fence?.bindingVersion ?? null, audit.occurredAtMs, ], ); } async function insertTraceAndReceipt( client: PostgresClient, bundle: Readonly, ): Promise { const trace = bundle.trace; await client.query( `INSERT INTO "ql3"."tool_execution_trace_anchors" ( trace_id, span_id, parent_span_id, project_id, run_id, step_run_id, invocation_plan_digest, binding_digest, adapter_digest, redaction_contract_digest, audit_contract_digest, created_at_ms, trace_digest, trace_json ) VALUES ( $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14::jsonb )`, [ trace.traceId, trace.spanId, trace.parentSpanId, trace.projectId, trace.runId, trace.stepRunId, trace.invocationPlanDigest, trace.bindingDigest, trace.adapterDigest, trace.redactionContractDigest, trace.auditContractDigest, trace.createdAtMs, trace.traceDigest, JSON.stringify(trace), ], ); const receipt = bundle.receipt; await client.query( `INSERT INTO "ql3"."tool_execution_audit_receipts" ( event_id, project_id, run_id, step_run_id, trace_id, span_id, trace_digest, invocation_plan_digest, binding_digest, audit_record_digest, created_at_ms, receipt_digest, audit_json, receipt_json ) VALUES ( $1::uuid, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13::jsonb, $14::jsonb )`, [ receipt.eventId, receipt.projectId, receipt.runId, receipt.stepRunId, receipt.traceId, receipt.spanId, receipt.traceDigest, receipt.invocationPlanDigest, receipt.bindingDigest, receipt.auditRecordDigest, receipt.createdAtMs, receipt.receiptDigest, JSON.stringify(bundle.audit), JSON.stringify(receipt), ], ); } export class PostgresToolExecutionEvidenceRepository implements ToolExecutionEvidenceRepository { constructor(private readonly pool: PostgresPool) { if ( !pool || typeof pool.query !== 'function' || typeof pool.connect !== 'function' ) { throw unavailable(); } } async findByTrace( traceIdValue: string, spanIdValue: string, ): Promise | null> { const traceId = identity(traceIdValue, TRACE_ID_PATTERN, 'traceId'); const spanId = identity(spanIdValue, SPAN_ID_PATTERN, 'spanId'); try { const rows = await findRows( this.pool, 'trace.trace_id = $1 AND trace.span_id = $2', [traceId, spanId], ); if (rows.length > 1) throw unavailable(); return rows[0] ? bundleFromRow(rows[0]) : null; } catch (error) { throw mapStorageError(error); } } async findByAuditEventId( eventIdValue: string, ): Promise | null> { const eventId = identity( eventIdValue, AUDIT_EVENT_ID_PATTERN, 'audit eventId', ); try { const rows = await findRows( this.pool, 'receipt.event_id = $1::uuid', [eventId], ); if (rows.length > 1) throw unavailable(); return rows[0] ? bundleFromRow(rows[0]) : null; } catch (error) { throw mapStorageError(error); } } async listByRun( queryValue: ListToolExecutionEvidenceQuery, ): Promise { const query = normalizeListToolExecutionEvidenceQuery(queryValue); try { const result = await this.pool.query( `SELECT ${EVIDENCE_SELECT} FROM "ql3"."tool_execution_trace_anchors" AS trace JOIN "ql3"."tool_execution_audit_receipts" AS receipt ON receipt.trace_id = trace.trace_id AND receipt.span_id = trace.span_id WHERE trace.run_id = $1 AND ( $2::char(32) IS NULL OR trace.created_at_ms > $3 OR (trace.created_at_ms = $3 AND trace.trace_id > $2) OR (trace.created_at_ms = $3 AND trace.trace_id = $2 AND trace.span_id > $4) ) ORDER BY trace.created_at_ms, trace.trace_id, trace.span_id LIMIT $5`, [ query.runId, query.after?.traceId ?? null, query.after?.createdAtMs ?? 0, query.after?.spanId ?? '', query.limit + 1, ], ); const truncated = result.rows.length > query.limit; const bundles = result.rows.slice(0, query.limit).map(bundleFromRow); const last = bundles.at(-1); return normalizeListToolExecutionEvidenceResult( { bundles, truncated, ...(truncated && last ? { next: { createdAtMs: last.trace.createdAtMs, traceId: last.trace.traceId, spanId: last.trace.spanId, }, } : {}), }, query, ); } catch (error) { throw mapStorageError(error); } } async prepare( bundleValue: ToolExecutionEvidenceBundle, ): Promise> { const bundle = normalizeToolExecutionEvidenceBundle(bundleValue); for ( let attempt = 0; attempt < POSTGRES_DEFINITION_TRANSACTION_ATTEMPTS; attempt += 1 ) { let client: PostgresClient; try { client = await this.pool.connect(); } catch (error) { throw unavailable(error); } let began = false; try { await configurePostgresDefinitionTransaction(client); began = true; const existing = await findRows( client, 'receipt.event_id = $1::uuid OR (trace.trace_id = $2 AND trace.span_id = $3)', [ bundle.receipt.eventId, bundle.trace.traceId, bundle.trace.spanId, ], 3, ); if (existing.length > 1) { throw new ToolExecutionEvidenceConflictError(); } if (existing[0]) { const stored = bundleFromRow(existing[0]); if (JSON.stringify(stored) !== JSON.stringify(bundle)) { throw new ToolExecutionEvidenceConflictError(); } await client.query('COMMIT'); began = false; return Object.freeze({ status: 'existing', bundle: stored }); } const step = await client.query( `SELECT step.kind, step.status, run.project_id AS "projectId" FROM "ql3"."step_runs" AS step JOIN "ql3"."runs" AS run ON run.id = step.run_id WHERE step.id = $1 AND step.run_id = $2 LIMIT 2 FOR SHARE OF step, run`, [bundle.trace.stepRunId, bundle.trace.runId], ); if ( step.rows.length !== 1 || requiredText(step.rows[0]!, 'kind') !== 'tool' || !['ready', 'waiting_approval'].includes( requiredText(step.rows[0]!, 'status'), ) || requiredText(step.rows[0]!, 'projectId') !== bundle.trace.projectId ) { throw new ToolExecutionEvidenceConflictError(); } await insertAudit(client, bundle); await insertTraceAndReceipt(client, bundle); await client.query('COMMIT'); began = false; return Object.freeze({ status: 'created', bundle }); } catch (error) { if (began) await rollbackPostgresDefinitionTransaction(client); const state = postgresSqlState(error); if ( ((state && POSTGRES_DEFINITION_RETRYABLE_SQL_STATES.has(state)) || state === '23505') && attempt + 1 < POSTGRES_DEFINITION_TRANSACTION_ATTEMPTS ) { continue; } throw mapStorageError(error); } finally { client.release(); } } throw unavailable(); } }