import { SUPPORTED_TOOL_EXECUTION_ERROR_SCHEMA_VERSIONS, TOOL_EXECUTION_ERROR_SCHEMA_VERSION, type ToolExecutionErrorSchemaVersion, } from '../tool-execution-error'; import { CURRENT_PLAY_ARTIFACT_CONTRACT_VERSION } from './artifact-contract-version'; import { PLAY_AUTHORING_CONTRACT_EDITION, normalizePlayAuthoringContractEdition, type PlayAuthoringContractEdition, type AdmittedPlayAuthoringContract, } from './authoring-contract'; import { InvalidToolResponseReceiptRevisionError, normalizeToolResponseContract, normalizeToolResponseReceiptRevision, RAW_V2_TOOL_RESPONSE_CONTRACT, RAW_V2_TOOL_RESPONSE_RECEIPT_REVISION, UnsupportedToolResponseContractError, type ToolResponseContract, } from '../play-runtime/tool-response-contract'; export type PlayContractSource = 'ad_hoc' | 'draft' | 'published'; export type PlayRuntimeFeature = | 'artifact_storage' | 'checkpoint_resume' | 'durable_sleep' | 'packaged_files'; export const PLAY_PUBLIC_API_VERSION = 1; export const PLAY_ARTIFACT_VERSION = CURRENT_PLAY_ARTIFACT_CONTRACT_VERSION; export const PLAY_MIN_RUNNER_VERSION = 1; export const PLAY_RUNTIME_FEATURES: PlayRuntimeFeature[] = [ 'artifact_storage', 'checkpoint_resume', 'durable_sleep', 'packaged_files', ]; export type PlayContractCompatibilitySnapshot = { apiVersion: number; artifactVersion: number; minRunnerVersion: number; runtimeFeatures: PlayRuntimeFeature[]; runtimeBackend?: string | null; /** Every Play run uses the structured tool-error contract. */ toolErrorSchemaVersion?: ToolExecutionErrorSchemaVersion; /** Missing preserves edition 1 for artifacts stored before authoring contracts were pinned. */ authoringContractEdition?: PlayAuthoringContractEdition; /** Immutable execute-result transport selected when the artifact is built. */ toolResponseContract?: ToolResponseContract; /** * Deliberate receipt-cache boundary for a response transformation. Missing * preserves the original namespace of artifacts written before this field. */ toolResponseReceiptRevision?: string; }; export type NormalizedPlayContractCompatibilitySnapshot = PlayContractCompatibilitySnapshot & { toolErrorSchemaVersion: ToolExecutionErrorSchemaVersion; authoringContractEdition: PlayAuthoringContractEdition; toolResponseContract: ToolResponseContract; }; export class UnsupportedPlayToolErrorSchemaVersionError extends Error { constructor(value: unknown) { super( `Unsupported Play tool error schema version ${String(value)}. Supported versions: ${SUPPORTED_TOOL_EXECUTION_ERROR_SCHEMA_VERSIONS.join(', ')}.`, ); this.name = 'UnsupportedPlayToolErrorSchemaVersionError'; } } export class InvalidPlayContractCompatibilityError extends Error { constructor(message = 'Play artifact compatibility must be an object.') { super(message); this.name = 'InvalidPlayContractCompatibilityError'; } } /** * Normalize the artifact-pinned compatibility facts without regenerating them. * * Play failures must retain their typed code across durable receipts. Historical * artifacts therefore upgrade to schema 1 at execution time; schema 0's * string-only failure shape is retired from Play execution. */ export function normalizePlayContractCompatibility( compatibility: PlayContractCompatibilitySnapshot | null | undefined, ): NormalizedPlayContractCompatibilitySnapshot { if ( !compatibility || typeof compatibility !== 'object' || Array.isArray(compatibility) ) { throw new InvalidPlayContractCompatibilityError(); } const requestedToolErrorSchemaVersion = compatibility.toolErrorSchemaVersion ?? TOOL_EXECUTION_ERROR_SCHEMA_VERSION; if ( !SUPPORTED_TOOL_EXECUTION_ERROR_SCHEMA_VERSIONS.includes( requestedToolErrorSchemaVersion, ) ) { throw new UnsupportedPlayToolErrorSchemaVersionError( requestedToolErrorSchemaVersion, ); } let toolResponseContract: ToolResponseContract; let toolResponseReceiptRevision: string | undefined; try { toolResponseContract = normalizeToolResponseContract( compatibility.toolResponseContract, ); toolResponseReceiptRevision = normalizeToolResponseReceiptRevision( compatibility.toolResponseReceiptRevision, ); } catch (error) { if ( error instanceof UnsupportedToolResponseContractError || error instanceof InvalidToolResponseReceiptRevisionError ) { throw new InvalidPlayContractCompatibilityError(error.message); } throw error; } return { ...compatibility, toolErrorSchemaVersion: TOOL_EXECUTION_ERROR_SCHEMA_VERSION, toolResponseContract, ...(toolResponseReceiptRevision ? { toolResponseReceiptRevision } : {}), authoringContractEdition: normalizePlayAuthoringContractEdition( compatibility.authoringContractEdition, ), }; } export function buildPlayContractCompatibility(input?: { runtimeBackend?: string | null; toolErrorSchemaVersion?: ToolExecutionErrorSchemaVersion; authoringContractEdition?: PlayAuthoringContractEdition; toolResponseContract?: ToolResponseContract; toolResponseReceiptRevision?: string; }): PlayContractCompatibilitySnapshot { return { apiVersion: PLAY_PUBLIC_API_VERSION, artifactVersion: PLAY_ARTIFACT_VERSION, minRunnerVersion: PLAY_MIN_RUNNER_VERSION, runtimeFeatures: [...PLAY_RUNTIME_FEATURES], runtimeBackend: input?.runtimeBackend ?? null, toolErrorSchemaVersion: input?.toolErrorSchemaVersion ?? TOOL_EXECUTION_ERROR_SCHEMA_VERSION, authoringContractEdition: input?.authoringContractEdition ?? PLAY_AUTHORING_CONTRACT_EDITION, toolResponseContract: input?.toolResponseContract ?? RAW_V2_TOOL_RESPONSE_CONTRACT, toolResponseReceiptRevision: normalizeToolResponseReceiptRevision( input?.toolResponseReceiptRevision ?? RAW_V2_TOOL_RESPONSE_RECEIPT_REVISION, ), }; } export type PlayRunContractSnapshot = { source: PlayContractSource; revisionVersion?: number | null; staticPipeline?: unknown; billingLimit?: { maxCreditsPerRun?: number | null; } | null; runtimeLimit?: { timeoutSeconds: number; memoryGiB: number; cpu: number; diskGiB: number; } | null; structuredDefinition?: unknown; artifactMetadata?: Record | null; codeFormat?: 'function' | 'cjs_module' | 'esm_module' | null; sourceCode?: string | null; compatibility?: PlayContractCompatibilitySnapshot | null; authoringContract?: AdmittedPlayAuthoringContract | null; };