/*
 * 文件定位：storagePool / baseToolStorage / shell.scriptExecution core。
 * 核心目的：把脚本执行做成 runtime-governed shell 原语，兼容旧 dry-run 计划并支持真实 provider 调用。
 */
import { describeShellWorkspaceWrite, shellWorkspaceWriteGuardMessage, } from "../../_shared/workspaceWriteGuard.js";
export const shellScriptExecutionDescriptor = {
    toolId: "shell.scriptExecution",
    capability: "execute-script",
    layer: "agent_executionEngine.basic_toolLayer.baseTools.shellBase.shellExecution",
    defaultDispatch: "dry-run",
    defaultLanguage: "sh",
    defaultTimeoutMs: 30_000,
    maxTimeoutMs: 600_000,
    maxScriptBytes: 65_536,
    permissionsRequired: ["shell:execute"],
    unsafeSideEffects: false,
    requiresTapApproval: true,
    tapOwnsApproval: true,
};
function stringValue(value) {
    return typeof value === "string" ? value : undefined;
}
function isBlank(value) {
    return typeof value !== "string" || value.trim().length === 0;
}
function cleanList(values) {
    if (!Array.isArray(values)) {
        return [];
    }
    return [...new Set(values.map((value) => stringValue(value)?.trim() ?? "").filter(Boolean))];
}
function dryRunEnabled(context) {
    return context?.dryRun !== false;
}
function guardRejected(context) {
    return context?.guard?.accepted === false || context?.guard?.allowed === false;
}
function guardAllowsRealExecution(context) {
    return context?.guard?.allowed === true || context?.guard?.accepted === true;
}
function auditEvent(type, context, metadata) {
    return {
        type,
        toolId: shellScriptExecutionDescriptor.toolId,
        invocationId: stringValue(context?.invocationId)?.trim() || `${shellScriptExecutionDescriptor.toolId}:dry-run`,
        dryRun: dryRunEnabled(context),
        metadata: {
            ...(context?.auditMetadata ?? {}),
            ...(metadata ?? {}),
        },
    };
}
function failure(code, message, boundary) {
    return {
        ok: false,
        error: {
            code,
            message,
            boundary,
            safeForRuntimeInspection: true,
            internalDetailExposed: false,
        },
        events: ["basicTool.shell.scriptExecution.rejected"],
    };
}
function toolFailure(code, message, boundary, context) {
    return {
        ok: false,
        toolId: shellScriptExecutionDescriptor.toolId,
        error: {
            code,
            message,
            boundary,
            safeForRuntimeInspection: true,
            internalDetailExposed: false,
        },
        audit: [auditEvent("agentCore.basicTool.shell.scriptExecution.rejected", context, { code, boundary })],
        events: ["basicTool.shell.scriptExecution.rejected"],
    };
}
function resolveScopes(requestedScopes, allowedScopes) {
    const requested = cleanList(requestedScopes);
    const allowed = cleanList(allowedScopes);
    const denied = requested.filter((scope) => !allowed.includes(scope));
    if (denied.length > 0) {
        return failure("SCOPE_DENIED", `shell.scriptExecution scope ${denied[0]} is outside runtime governance`, "scope");
    }
    return requested;
}
function normalizeScript(value) {
    if (isBlank(value)) {
        return failure("MISSING_SCRIPT", "shell.scriptExecution requires script", "input");
    }
    const script = stringValue(value) ?? "";
    if (script.includes("\0")) {
        return failure("INVALID_SCRIPT", "shell.scriptExecution script must be a safe string", "input");
    }
    if (Buffer.byteLength(script, "utf8") > shellScriptExecutionDescriptor.maxScriptBytes) {
        return failure("SCRIPT_TOO_LARGE", "shell.scriptExecution script exceeds first-round resource limits", "resource");
    }
    return script;
}
function normalizeLanguage(value) {
    if (value !== undefined && typeof value !== "string") {
        return failure("INVALID_LANGUAGE", "shell.scriptExecution language is not supported", "input");
    }
    const language = value ?? shellScriptExecutionDescriptor.defaultLanguage;
    if (!["sh", "bash", "zsh", "fish", "powershell", "unknown"].includes(language)) {
        return failure("INVALID_LANGUAGE", "shell.scriptExecution language is not supported", "input");
    }
    return language;
}
function normalizeCwd(value) {
    if (value === undefined) {
        return undefined;
    }
    if (typeof value !== "string") {
        return failure("INVALID_CWD", "shell.scriptExecution cwd must be a safe path string", "input");
    }
    const cwd = value.trim();
    if (cwd.length === 0 || cwd.includes("\0")) {
        return failure("INVALID_CWD", "shell.scriptExecution cwd must be a safe path string", "input");
    }
    return cwd;
}
function normalizeTimeout(value) {
    if (value !== undefined && typeof value !== "number") {
        return failure("INVALID_TIMEOUT", "shell.scriptExecution timeoutMs must be between 1 and 600000", "resource");
    }
    const timeoutMs = value ?? shellScriptExecutionDescriptor.defaultTimeoutMs;
    if (!Number.isInteger(timeoutMs) || timeoutMs <= 0 || timeoutMs > shellScriptExecutionDescriptor.maxTimeoutMs) {
        return failure("INVALID_TIMEOUT", "shell.scriptExecution timeoutMs must be between 1 and 600000", "resource");
    }
    return timeoutMs;
}
function normalizeStdin(value) {
    if (value === undefined) {
        return undefined;
    }
    if (typeof value !== "string" || value.includes("\0")) {
        return failure("INVALID_STDIN", "shell.scriptExecution stdin must be a safe string", "input");
    }
    return value;
}
function previewScript(script) {
    const compact = script.replace(/\s+/gu, " ").trim();
    return compact.length <= 160 ? compact : compact.slice(0, 157) + "...";
}
function scriptCommand(language, script) {
    if (language === "powershell") {
        return { command: "pwsh", args: ["-NoProfile", "-Command", script] };
    }
    const command = language === "unknown" ? "sh" : language;
    return { command, args: ["-c", script] };
}
function normalizeShellScriptExecution(request) {
    const runtimeId = stringValue(request.context?.runtimeId)?.trim() ?? "";
    if (isBlank(runtimeId)) {
        return failure("MISSING_RUNTIME_ID", "shell.scriptExecution requires context.runtimeId for audit", "input");
    }
    if (guardRejected(request.context)) {
        return failure("GOVERNANCE_REJECTED", request.context?.guard?.reason ?? "shell.scriptExecution was rejected by runtime governance", "governance");
    }
    const acceptedScopes = resolveScopes(request.context?.requestedScopes, request.context?.allowedScopes);
    if (!Array.isArray(acceptedScopes)) {
        return acceptedScopes;
    }
    const script = normalizeScript(request.script);
    if (typeof script !== "string") {
        return script;
    }
    const workspaceWriteReason = describeShellWorkspaceWrite(script);
    if (workspaceWriteReason !== undefined) {
        return failure("WORKSPACE_WRITE_REQUIRES_CODE_TOOL", shellWorkspaceWriteGuardMessage(workspaceWriteReason), "governance");
    }
    const language = normalizeLanguage(request.language);
    if (typeof language !== "string") {
        return language;
    }
    const cwd = normalizeCwd(request.cwd);
    if (cwd !== undefined && typeof cwd !== "string") {
        return cwd;
    }
    const timeoutMs = normalizeTimeout(request.timeoutMs);
    if (typeof timeoutMs !== "number") {
        return timeoutMs;
    }
    const stdin = normalizeStdin(request.stdin);
    if (stdin !== undefined && typeof stdin !== "string") {
        return stdin;
    }
    const invocationId = stringValue(request.context?.invocationId)?.trim() || `${runtimeId}:shell.scriptExecution:${language}`;
    const command = scriptCommand(language, script);
    return {
        runtimeId,
        invocationId,
        script,
        language,
        command: command.command,
        args: command.args,
        cwd,
        timeoutMs,
        stdin,
        acceptedScopes,
    };
}
function isScriptExecutionFailure(value) {
    return "ok" in value && !value.ok;
}
export function planShellScriptExecution(request = {}) {
    if (request.context?.dryRun === false) {
        return failure("REAL_SCRIPT_EXECUTION_NOT_ALLOWED", "first-round shell.scriptExecution only creates a dry-run script plan", "contract");
    }
    const normalized = normalizeShellScriptExecution({
        ...request,
        context: {
            ...request.context,
            dryRun: true,
        },
    });
    if (isScriptExecutionFailure(normalized)) {
        return normalized;
    }
    return {
        ok: true,
        plan: {
            toolId: "shell.scriptExecution",
            capability: "execute-script",
            runtimeId: normalized.runtimeId,
            invocationId: normalized.invocationId,
            language: normalized.language,
            cwd: normalized.cwd,
            timeoutMs: normalized.timeoutMs,
            scriptPreview: previewScript(normalized.script),
            scriptLineCount: normalized.script.split(/\r?\n/u).length,
            scriptBytes: Buffer.byteLength(normalized.script, "utf8"),
            requiredPermissions: ["shell:script:dry-run"],
            requiresTapApproval: true,
            dispatch: "dry-run",
            dryRun: true,
            wouldSpawnProcess: true,
            wouldWriteTempScript: false,
            unsafeSideEffects: false,
            acceptedScopes: normalized.acceptedScopes,
            outputEnvelope: {
                stdoutPreview: "",
                stderrPreview: "",
                started: false,
            },
            audit: {
                guard: "shell-script-dry-run-approval",
                event: "basicTool.shell.scriptExecution.planned",
                metadata: {
                    ...(request.context?.auditMetadata ?? {}),
                    ...(request.metadata ?? {}),
                },
            },
        },
        events: ["basicTool.shell.scriptExecution.planned"],
    };
}
function dryRunOutput(normalized) {
    return {
        kind: "agentCore.basicTool.shell.scriptExecution",
        invocationId: normalized.invocationId,
        scriptPreview: previewScript(normalized.script),
        scriptLineCount: normalized.script.split(/\r?\n/u).length,
        scriptBytes: Buffer.byteLength(normalized.script, "utf8"),
        language: normalized.language,
        command: normalized.command,
        args: normalized.args,
        cwd: normalized.cwd,
        timeoutMs: normalized.timeoutMs,
        dryRun: true,
        providerCalled: false,
        stdout: "",
        stderr: "",
        permissionsRequired: shellScriptExecutionDescriptor.permissionsRequired,
        unsafeSideEffects: false,
    };
}
export async function executeShellScript(request = {}) {
    const normalized = normalizeShellScriptExecution(request);
    if (isScriptExecutionFailure(normalized)) {
        return toolFailure(normalized.error.code, normalized.error.message, normalized.error.boundary, request.context);
    }
    if (dryRunEnabled(request.context)) {
        return {
            ok: true,
            toolId: shellScriptExecutionDescriptor.toolId,
            output: dryRunOutput(normalized),
            audit: [
                auditEvent("agentCore.basicTool.shell.scriptExecution.dryRun", request.context, {
                    language: normalized.language,
                    timeoutMs: normalized.timeoutMs,
                }),
            ],
            events: ["basicTool.shell.scriptExecution.dryRun"],
        };
    }
    if (!guardAllowsRealExecution(request.context)) {
        return toolFailure("GOVERNANCE_REJECTED", "shell.scriptExecution requires an allowed runtime governance guard when dryRun is false", "governance", request.context);
    }
    if (request.provider === undefined) {
        return toolFailure("PROVIDER_UNAVAILABLE", "shell.scriptExecution requires a runtime-provided shell executor when dryRun is false", "provider", request.context);
    }
    try {
        const providerResult = await request.provider({
            script: normalized.script,
            language: normalized.language,
            command: normalized.command,
            args: normalized.args,
            cwd: normalized.cwd,
            timeoutMs: normalized.timeoutMs,
            stdin: normalized.stdin,
        }, request.context ?? {});
        return {
            ok: true,
            toolId: shellScriptExecutionDescriptor.toolId,
            output: {
                kind: "agentCore.basicTool.shell.scriptExecution",
                invocationId: normalized.invocationId,
                scriptPreview: previewScript(normalized.script),
                scriptLineCount: normalized.script.split(/\r?\n/u).length,
                scriptBytes: Buffer.byteLength(normalized.script, "utf8"),
                language: normalized.language,
                command: normalized.command,
                args: normalized.args,
                cwd: normalized.cwd,
                timeoutMs: normalized.timeoutMs,
                dryRun: false,
                providerCalled: true,
                exitCode: providerResult.exitCode,
                stdout: providerResult.stdout,
                stderr: providerResult.stderr,
                permissionsRequired: shellScriptExecutionDescriptor.permissionsRequired,
                unsafeSideEffects: false,
            },
            audit: [
                auditEvent("agentCore.basicTool.shell.scriptExecution.provider", request.context, {
                    language: normalized.language,
                    exitCode: providerResult.exitCode,
                }),
            ],
            events: ["basicTool.shell.scriptExecution.providerCalled"],
        };
    }
    catch (error) {
        return toolFailure("PROVIDER_REJECTED", error instanceof Error ? error.message : "shell.scriptExecution provider rejected the invocation", "provider", request.context);
    }
}
