export const pipelineProvenanceAnnotationKeys = { configRef: "unidesk.ai/owning-config-ref", effectiveConfigSha256: "unidesk.ai/effective-config-sha256", renderer: "unidesk.ai/source-artifact-renderer", mode: "unidesk.ai/source-artifact-mode", } as const; export interface PipelineProvenanceContract { readonly configRef: string; readonly effectiveConfigSha256: string; readonly renderer: string; readonly mode: string; } export function pipelineProvenanceAnnotations(provenance: PipelineProvenanceContract): Record { return { [pipelineProvenanceAnnotationKeys.configRef]: provenance.configRef, [pipelineProvenanceAnnotationKeys.effectiveConfigSha256]: provenance.effectiveConfigSha256, [pipelineProvenanceAnnotationKeys.renderer]: provenance.renderer, [pipelineProvenanceAnnotationKeys.mode]: provenance.mode, }; } export function withPipelineProvenanceAnnotations( manifest: Record, provenance: PipelineProvenanceContract, ): Record { const next = structuredClone(manifest); const metadata = asRecord(next.metadata, "manifest.metadata"); const annotations = metadata.annotations === undefined ? {} : asRecord(metadata.annotations, "manifest.metadata.annotations"); metadata.annotations = { ...annotations, ...pipelineProvenanceAnnotations(provenance) }; return next; } export function pipelineProvenanceFromManifest(manifest: Record): PipelineProvenanceContract | null { const metadata = isRecord(manifest.metadata) ? manifest.metadata : {}; const annotations = isRecord(metadata.annotations) ? metadata.annotations : {}; const configRef = annotations[pipelineProvenanceAnnotationKeys.configRef]; const effectiveConfigSha256 = annotations[pipelineProvenanceAnnotationKeys.effectiveConfigSha256]; const renderer = annotations[pipelineProvenanceAnnotationKeys.renderer]; const mode = annotations[pipelineProvenanceAnnotationKeys.mode]; if ([configRef, effectiveConfigSha256, renderer, mode].some((value) => typeof value !== "string" || value.length === 0)) return null; return { configRef, effectiveConfigSha256, renderer, mode } as PipelineProvenanceContract; } function asRecord(value: unknown, path: string): Record { if (!isRecord(value)) throw new Error(`${path} must be an object`); return value; } function isRecord(value: unknown): value is Record { return value !== null && typeof value === "object" && !Array.isArray(value); }