import type { PlayRunnerBackend, PlayRunnerCallbacks, PlayRunnerPreparedExecution, PlayRunnerPrepareInput, } from '../types'; import type { PlayRunnerExecutionConfig, PlayRunnerResult, } from '@shared_libs/play-runtime/protocol'; import { daytonaPlayRunnerBackend } from './daytona'; import { DaytonaSandboxAcquisitionUnavailableError } from './daytona-lifecycle'; import { modalPlayRunnerBackend } from './modal'; import { canTransitionRuntimeSandboxPlacement, resolveRuntimeSandboxPlacementPolicy, RUNTIME_SANDBOX_PLACEMENT_POLICIES, type RuntimeSandboxPlacementPolicyId, type RuntimeSandboxPlacementFailureReason, type RuntimeSandboxPlacementPolicy, type RuntimeSandboxProvider, } from '@shared_libs/play-runtime/runtime-sandbox-placement-policy'; type DaytonaFallbackPreparedExecution = PlayRunnerPreparedExecution & { kind: 'runtime_sandbox_placement'; policyId: string; provider: RuntimeSandboxProvider; providerPrepared: PlayRunnerPreparedExecution | undefined; }; function isPrepared( prepared: PlayRunnerPreparedExecution | undefined, ): prepared is DaytonaFallbackPreparedExecution { return prepared?.kind === 'runtime_sandbox_placement'; } type RuntimeSandboxProviderAdapter = { backend: PlayRunnerBackend; classifyAcquisitionFailure: ( error: unknown, ) => RuntimeSandboxPlacementFailureReason | null; }; function classifyDaytonaAcquisitionFailure( error: unknown, ): RuntimeSandboxPlacementFailureReason | null { if (!(error instanceof DaytonaSandboxAcquisitionUnavailableError)) { return null; } if ( error.reason === 'daytona_total_cpu_limit_exceeded' || error.reason === 'daytona_acquisition_rate_limited' ) { return 'provider_capacity_exhausted'; } if (error.reason === 'daytona_sandbox_start_timeout') { return 'provider_start_timeout_before_execution'; } return null; } /** * Deep placement Implementation. Provider order alone never permits a retry: * the policy must also name the exact pre-execution transition and the source * Adapter must classify the thrown failure into that provider-neutral reason. */ export function createRuntimeSandboxPlacementBackend(input: { policy: RuntimeSandboxPlacementPolicy; adapters: Readonly< Partial> >; }): PlayRunnerBackend { const primaryProvider = input.policy.providers[0]; const primaryAdapter = input.adapters[primaryProvider]; if (!primaryAdapter) { throw new Error( `Runtime sandbox placement policy ${input.policy.id} has no Adapter for ${primaryProvider}.`, ); } return { async prepare( prepareInput: PlayRunnerPrepareInput, callbacks?: PlayRunnerCallbacks, ): Promise { const providerPrepared = await primaryAdapter.backend.prepare?.( prepareInput, callbacks, ); const prepared: DaytonaFallbackPreparedExecution = { kind: 'runtime_sandbox_placement', policyId: input.policy.id, provider: primaryProvider, providerPrepared, dispose: async () => await providerPrepared?.dispose?.(), }; return prepared; }, async execute( config: PlayRunnerExecutionConfig, callbacks?: PlayRunnerCallbacks, prepared?: PlayRunnerPreparedExecution, ): Promise { const policyPrepared = isPrepared(prepared) && prepared.policyId === input.policy.id ? prepared : null; for (const [index, provider] of input.policy.providers.entries()) { const adapter = input.adapters[provider]; if (!adapter) { throw new Error( `Runtime sandbox placement policy ${input.policy.id} has no Adapter for ${provider}.`, ); } try { return await adapter.backend.execute( config, callbacks, index === 0 && policyPrepared?.provider === provider ? policyPrepared.providerPrepared : undefined, ); } catch (error) { const nextProvider = input.policy.providers[index + 1]; const reason = adapter.classifyAcquisitionFailure(error); if ( !nextProvider || !reason || callbacks?.cancellationSignal?.aborted || !canTransitionRuntimeSandboxPlacement({ policy: input.policy, from: provider, to: nextProvider, stage: 'acquisition', reason, }) ) { throw error; } // Keep this one-line JSON event parseable by the lifecycle gate. It // is the durable proof that a completed Modal run was an allowed // pre-code Daytona transition, rather than a direct Modal launch. console.warn( '[play-runner.sandbox_provider_fallback]', JSON.stringify({ runId: config.context.runId ?? null, workflowId: config.context.workflowId ?? null, policyId: input.policy.id, from: provider, to: nextProvider, stage: 'acquisition', reason, }), ); } } throw new Error( `Runtime sandbox placement policy ${input.policy.id} exhausted without a result.`, ); }, }; } export function createRuntimeSandboxPlacementBackendForPolicy( policyId: RuntimeSandboxPlacementPolicyId | string, overrides: { daytona?: PlayRunnerBackend; modal?: PlayRunnerBackend; } = {}, ): PlayRunnerBackend { return createRuntimeSandboxPlacementBackend({ policy: resolveRuntimeSandboxPlacementPolicy(policyId), adapters: { daytona: { backend: overrides.daytona ?? daytonaPlayRunnerBackend, classifyAcquisitionFailure: classifyDaytonaAcquisitionFailure, }, modal: { backend: overrides.modal ?? modalPlayRunnerBackend, classifyAcquisitionFailure: () => null, }, }, }); } /** Compatibility factory retained for existing imports and tests. */ export function createDaytonaModalFallbackBackend( input: { daytona?: PlayRunnerBackend; modal?: PlayRunnerBackend; } = {}, ): PlayRunnerBackend { return createRuntimeSandboxPlacementBackendForPolicy( RUNTIME_SANDBOX_PLACEMENT_POLICIES.daytonaThenModalV1, input, ); } export const daytonaModalFallbackPlayRunnerBackend = createDaytonaModalFallbackBackend(); export const daytonaOnlyPlayRunnerBackend = createRuntimeSandboxPlacementBackendForPolicy( RUNTIME_SANDBOX_PLACEMENT_POLICIES.daytonaOnlyV1, ); export const modalOnlyPlayRunnerBackend = createRuntimeSandboxPlacementBackendForPolicy( RUNTIME_SANDBOX_PLACEMENT_POLICIES.modalOnlyV1, ); const DEFAULT_POLICY_BACKENDS: Readonly< Record > = { [RUNTIME_SANDBOX_PLACEMENT_POLICIES.daytonaOnlyV1]: daytonaOnlyPlayRunnerBackend, [RUNTIME_SANDBOX_PLACEMENT_POLICIES.daytonaThenModalV1]: daytonaModalFallbackPlayRunnerBackend, [RUNTIME_SANDBOX_PLACEMENT_POLICIES.modalOnlyV1]: modalOnlyPlayRunnerBackend, }; export function resolveDefaultRuntimeSandboxPlacementBackend( policyId: RuntimeSandboxPlacementPolicyId | string, ): PlayRunnerBackend { const policy = resolveRuntimeSandboxPlacementPolicy(policyId); return DEFAULT_POLICY_BACKENDS[policy.id]; }