/*
 * 文件定位：Agent 执行引擎 / 基础工具原语层 / 基础工具集合 / Shell 基础工具 / 进程控制。
 * 核心目的：提供 Shell 基础工具 / 进程控制 中的“终止进程”基础能力原语。
 * 能力要求1：需要定义该能力的输入、输出、错误、权限需求和可观测事件。
 * 能力要求2：这些基础工具是 Agent 成立的底层能力，不是 TAP 的最终高级工具库。
 * 能力要求3：后续 TAP 可以基于这些原语构建更强的工具编排、审批、替换和专业能力库。
 * 边界：保留 Agent 基础工具原语，不替代 TAP 的高级工具系统。
 * 对接：需要被 runtime.execEngine 拉起，并和 mainLoop、stateEngine、事件暴露、工具调用策略接通。
 * 实现提示：先补稳定类型契约、最小可测行为和清晰错误边界，再接入真实执行逻辑。
 */
import { approvalRecord, cleanNumberList, cleanStringList, readRecord, safeMetadata, stringValue, trimmedString, } from "../_shared/processControlJson.js";
export const shellProcessTerminationDescriptor = {
    toolId: "shell.processTermination",
    capability: "terminate-shell-process",
    route: "agent_executionEngine.basic_toolLayer.baseTools.shellBase.processControl",
    defaultDryRun: true,
    tapOwnsApproval: true,
    permissionsRequired: ["shell:process:terminate"],
    unsafeSideEffects: true,
};
const supportedSignals = ["SIGTERM", "SIGINT", "SIGHUP", "SIGKILL"];
function dryRunEnabled(context) {
    return readRecord(context)?.dryRun !== false;
}
function invocationId(context) {
    return trimmedString(readRecord(context)?.invocationId) || "shell.processTermination:dry-run";
}
function auditEvent(type, context, processId, metadata) {
    return {
        type,
        toolId: shellProcessTerminationDescriptor.toolId,
        invocationId: invocationId(context),
        dryRun: dryRunEnabled(context),
        processId,
        metadata: {
            ...safeMetadata(readRecord(context)?.auditMetadata),
            ...(metadata ?? {}),
        },
    };
}
function failure(code, message, boundary, context, processId) {
    return {
        ok: false,
        toolId: shellProcessTerminationDescriptor.toolId,
        error: { code, message, boundary, publicSafe: true, internalDetailExposed: false },
        audit: [auditEvent("agentCore.basicTool.shell.processTermination.rejected", context, processId, { code })],
        events: ["basicTool.shell.processTermination.rejected"],
    };
}
function normalizeTarget(target, context) {
    const targetRecord = readRecord(target);
    if (targetRecord?.processId === undefined) {
        return failure("MISSING_PROCESS_ID", "shell.processTermination requires target.processId", "input", context);
    }
    if (typeof targetRecord.processId !== "number" || !Number.isSafeInteger(targetRecord.processId) || targetRecord.processId <= 0) {
        return failure("INVALID_PROCESS_ID", "shell.processTermination target.processId must be a positive integer", "input", context, typeof targetRecord.processId === "number" ? targetRecord.processId : undefined);
    }
    const processId = targetRecord.processId;
    if (targetRecord.force !== undefined && typeof targetRecord.force !== "boolean") {
        return failure("INVALID_FORCE", "shell.processTermination target.force must be boolean when provided", "input", context, processId);
    }
    if (targetRecord.signal !== undefined && typeof targetRecord.signal !== "string") {
        return failure("UNSUPPORTED_SIGNAL", "shell.processTermination target.signal is not supported by this primitive", "input", context, processId);
    }
    const signalValue = stringValue(targetRecord.signal) ?? (targetRecord.force === true ? "SIGKILL" : "SIGTERM");
    if (!supportedSignals.includes(signalValue)) {
        return failure("UNSUPPORTED_SIGNAL", "shell.processTermination target.signal is not supported by this primitive", "input", context, processId);
    }
    const signal = signalValue;
    const reason = trimmedString(targetRecord.reason);
    if (targetRecord.reason !== undefined && reason === undefined) {
        return failure("INVALID_REASON", "shell.processTermination target.reason must not be blank when provided", "input", context, processId);
    }
    return {
        processId,
        signal,
        reason,
        force: targetRecord.force === true || signal === "SIGKILL",
    };
}
function requiredPermissions(target) {
    const permissions = ["shell:process:terminate"];
    if (target.signal !== "SIGTERM") {
        permissions.push("shell:process:signal");
    }
    if (target.force) {
        permissions.push("shell:process:force");
    }
    return permissions;
}
function ensureProcessScope(target, context) {
    const allowedProcessIds = cleanNumberList(readRecord(context)?.allowedProcessIds);
    if (allowedProcessIds.length === 0 || allowedProcessIds.includes(target.processId)) {
        return undefined;
    }
    return failure("SCOPE_REJECTED", "shell.processTermination target process is outside the allowed process scope", "scope", context, target.processId);
}
function ensurePermissions(target, context) {
    const grantedPermissions = cleanStringList(readRecord(context)?.grantedPermissions);
    if (grantedPermissions.length === 0) {
        return undefined;
    }
    const missing = requiredPermissions(target).filter((permission) => !grantedPermissions.includes(permission));
    if (missing.length === 0) {
        return undefined;
    }
    return failure("PERMISSION_DENIED", `shell.processTermination is missing permissions: ${missing.join(", ")}`, "permission", context, target.processId);
}
function ensureApproval(target, context) {
    if (!target.force) {
        return undefined;
    }
    const approval = approvalRecord(context);
    if (approval?.accepted === true) {
        return undefined;
    }
    if (approval?.accepted === false) {
        return failure("APPROVAL_REJECTED", stringValue(approval.reason) ?? "shell.processTermination approval was rejected by TAP governance", "approval", context, target.processId);
    }
    return failure("APPROVAL_REQUIRED", "shell.processTermination forceful termination requires TAP approval", "approval", context, target.processId);
}
function ensureDryRunOnly(target, context) {
    if (dryRunEnabled(context)) {
        return undefined;
    }
    return failure("REAL_EXECUTION_BLOCKED", "shell.processTermination only returns a guarded dry-run termination envelope in the first implementation", "contract", context, target.processId);
}
export function planShellProcessTermination(request = {}) {
    const target = normalizeTarget(request.target, request.context);
    if ("ok" in target) {
        return target;
    }
    const scopeFailure = ensureProcessScope(target, request.context);
    if (scopeFailure !== undefined) {
        return scopeFailure;
    }
    const permissionFailure = ensurePermissions(target, request.context);
    if (permissionFailure !== undefined) {
        return permissionFailure;
    }
    const approvalFailure = ensureApproval(target, request.context);
    if (approvalFailure !== undefined) {
        return approvalFailure;
    }
    const realExecutionFailure = ensureDryRunOnly(target, request.context);
    if (realExecutionFailure !== undefined) {
        return realExecutionFailure;
    }
    return {
        ok: true,
        toolId: shellProcessTerminationDescriptor.toolId,
        output: {
            kind: "agentCore.basicTool.shell.processTermination",
            target,
            permissionsRequired: requiredPermissions(target),
            dryRun: true,
            executionBlocked: true,
            unsafeSideEffects: true,
            terminationEnvelope: {
                operation: "terminate-process",
                processId: target.processId,
                signal: target.signal,
                reason: target.reason,
                force: target.force,
            },
        },
        audit: [
            auditEvent("agentCore.basicTool.shell.processTermination.dryRun", request.context, target.processId, {
                signal: target.signal,
                force: target.force,
            }),
        ],
        events: ["basicTool.shell.processTermination.dryRun"],
    };
}
