/**
 * Adapter that implements {@link RunnerStepExecutor} on top of a
 * {@link ProcessBackend}. Relay owns command construction (CLI flags, env,
 * cwd, timeout); the backend only provides "where to run" — create an
 * isolated environment, exec the command, destroy.
 *
 * The WorkflowRunner synthesizes one of these when a caller passes
 * `processBackend` without an explicit `executor`, so every existing
 * `executor.executeAgentStep(...)` call site transparently flows through
 * the backend (e.g. a cloud sandbox) without any further plumbing.
 */
import { buildCommand } from './process-spawner.js';
function shellEscape(value) {
    if (value === '')
        return "''";
    if (/^[A-Za-z0-9_\/.:,=+@%-]+$/.test(value))
        return value;
    return `'${value.replace(/'/g, `'\\''`)}'`;
}
function commandToShell(argv) {
    return argv.map(shellEscape).join(' ');
}
export function createProcessBackendExecutor(backend, options = {}) {
    const baseEnv = options.env ?? {};
    return {
        async executeAgentStep(step, agentDef, resolvedTask, timeoutMs) {
            if (agentDef.cli === 'api') {
                throw new Error(`processBackend cannot execute cli "api" agents — api agents call the Anthropic API directly. ` +
                    `Route agent "${agentDef.name}" through a subprocess CLI (claude, codex, etc.) or omit processBackend.`);
            }
            const extraArgs = agentDef.constraints?.model ? ['--model', agentDef.constraints.model] : [];
            const argv = buildCommand(agentDef.cli, extraArgs, resolvedTask);
            const commandString = commandToShell(argv);
            const env = await backend.createEnvironment(step.name);
            try {
                const execOpts = {};
                if (agentDef.cwd)
                    execOpts.cwd = agentDef.cwd;
                if (Object.keys(baseEnv).length > 0)
                    execOpts.env = baseEnv;
                // timeoutSeconds is ceil-rounded from the caller's timeoutMs; a 500ms
                // timeout becomes 1s because the backend protocol uses seconds.
                if (timeoutMs && timeoutMs > 0) {
                    execOpts.timeoutSeconds = Math.max(1, Math.ceil(timeoutMs / 1000));
                }
                const result = await env.exec(commandString, execOpts);
                if (result.exitCode !== 0) {
                    const tail = result.output.slice(-2000);
                    throw new Error(`Agent step "${step.name}" exited with code ${result.exitCode}: ${tail}`);
                }
                return result.output;
            }
            finally {
                await env.destroy().catch(() => undefined);
            }
        },
        async executeDeterministicStep(step, resolvedCommand, cwd) {
            const env = await backend.createEnvironment(step.name);
            try {
                const execOpts = { cwd };
                if (Object.keys(baseEnv).length > 0)
                    execOpts.env = baseEnv;
                if (step.timeoutMs && step.timeoutMs > 0) {
                    execOpts.timeoutSeconds = Math.max(1, Math.ceil(step.timeoutMs / 1000));
                }
                return await env.exec(resolvedCommand, execOpts);
            }
            finally {
                await env.destroy().catch(() => undefined);
            }
        },
    };
}
//# sourceMappingURL=process-backend-executor.js.map