/*
 * 文件定位：Agent 执行引擎 / 执行核心逻辑。
 * 核心目的：定义 EphemeralProcedure 这一临时 BaseTool 编排合同。
 * 边界：只描述已有 BaseTool 的一次性执行计划，不生产新工具，不接管 TAP。
 * 对接：mainLoop 解释模型决策后交给 runtime 通过 BaseTool registry/handler/executor 执行。
 * 实现提示：只校验和归一化计划，真实执行必须留在 runtime BaseTool mount 链。
 */
function isRecord(value) {
    return typeof value === "object" && value !== null && !Array.isArray(value);
}
function readString(value) {
    return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
}
function readStringArray(value) {
    return Array.isArray(value)
        ? [...new Set(value.map((item) => readString(item)).filter((item) => item !== undefined))]
        : [];
}
function readRiskLevel(value) {
    return value === "high" || value === "medium" || value === "low" ? value : "medium";
}
function readExecutionMode(value) {
    return value === "serial" || value === "parallel" || value === "mixed" ? value : "serial";
}
function readResourceHints(value) {
    if (!isRecord(value)) {
        return {};
    }
    return {
        timeoutMs: typeof value.timeoutMs === "number" && Number.isFinite(value.timeoutMs) ? value.timeoutMs : undefined,
        maxOutputBytes: typeof value.maxOutputBytes === "number" && Number.isFinite(value.maxOutputBytes) ? value.maxOutputBytes : undefined,
        maxSteps: typeof value.maxSteps === "number" && Number.isFinite(value.maxSteps) ? value.maxSteps : undefined,
        metadata: isRecord(value.metadata) ? value.metadata : undefined,
    };
}
function failure(code, message) {
    return {
        ok: false,
        error: { code, message, publicSafe: true },
        events: ["agentCore.execution.ephemeralProcedure.rejected"],
    };
}
export function normalizeEphemeralProcedurePlan(input) {
    if (!isRecord(input)) {
        return failure("MISSING_PROCEDURE_ID", "EphemeralProcedure requires a procedure object");
    }
    const procedureId = readString(input.procedureId);
    if (procedureId === undefined) {
        return failure("MISSING_PROCEDURE_ID", "EphemeralProcedure requires a procedureId");
    }
    const purpose = readString(input.purpose);
    if (purpose === undefined) {
        return failure("MISSING_PURPOSE", "EphemeralProcedure requires a purpose");
    }
    const rawSteps = Array.isArray(input.steps) ? input.steps : [];
    if (rawSteps.length === 0) {
        return failure("EMPTY_STEPS", "EphemeralProcedure requires at least one BaseTool step");
    }
    const resourceLimits = readResourceHints(input.resourceLimits);
    const maxSteps = resourceLimits.maxSteps ?? 128;
    if (rawSteps.length > maxSteps || rawSteps.length > 128) {
        return failure("TOO_MANY_STEPS", "EphemeralProcedure cannot exceed 128 BaseTool calls in one procedure package");
    }
    const seen = new Set();
    const steps = [];
    for (const [index, raw] of rawSteps.entries()) {
        if (!isRecord(raw)) {
            return failure("MISSING_BASE_TOOL", `EphemeralProcedure step ${index + 1} is not an object`);
        }
        const stepId = readString(raw.stepId) ?? `${procedureId}:step:${index + 1}`;
        if (seen.has(stepId)) {
            return failure("DUPLICATE_STEP_ID", `EphemeralProcedure step ${stepId} is duplicated`);
        }
        seen.add(stepId);
        const baseToolId = readString(raw.baseToolId);
        if (baseToolId === undefined) {
            return failure("MISSING_BASE_TOOL", `EphemeralProcedure step ${stepId} requires baseToolId`);
        }
        if (baseToolId.startsWith("tap.") || baseToolId.startsWith("tap/")) {
            return failure("TAP_NOT_ALLOWED", "EphemeralProcedure cannot create or invoke TAP capability in this layer");
        }
        steps.push({
            stepId,
            baseToolId,
            input: isRecord(raw.input) ? raw.input : {},
            dependsOn: readStringArray(raw.dependsOn),
            riskLevel: readRiskLevel(raw.riskLevel),
            resourceHints: readResourceHints(raw.resourceHints),
            outputRef: readString(raw.outputRef) ?? `${procedureId}:output:${stepId}`,
        });
    }
    for (const step of steps) {
        const unknown = step.dependsOn.find((dependency) => !seen.has(dependency));
        if (unknown !== undefined) {
            return failure("UNKNOWN_DEPENDENCY", `EphemeralProcedure step ${step.stepId} depends on unknown step ${unknown}`);
        }
    }
    const requiredBaseTools = [...new Set([
            ...readStringArray(input.requiredBaseTools),
            ...steps.map((step) => step.baseToolId),
        ])];
    return {
        ok: true,
        plan: {
            kind: "praxis.ephemeralProcedurePlan",
            procedureId,
            purpose,
            author: input.author === "runtime" ? "runtime" : "model",
            executionMode: readExecutionMode(input.executionMode),
            steps,
            dependencies: readStringArray(input.dependencies),
            requiredBaseTools,
            riskLevel: readRiskLevel(input.riskLevel),
            approval: {
                required: Boolean(isRecord(input.approval) ? input.approval.required : input.approvalRequired),
                reason: isRecord(input.approval) ? readString(input.approval.reason) : readString(input.approvalReason),
            },
            resourceLimits,
            expectedOutputs: Array.isArray(input.expectedOutputs)
                ? input.expectedOutputs.filter(isRecord).map((output, index) => ({
                    outputRef: readString(output.outputRef) ?? `${procedureId}:expected:${index + 1}`,
                    kind: readString(output.kind),
                    description: readString(output.description),
                }))
                : [],
            mergeObservationPolicy: readString(input.mergeObservationPolicy) ?? "append",
            metadata: isRecord(input.metadata) ? input.metadata : {},
        },
        events: ["agentCore.execution.ephemeralProcedure.accepted"],
    };
}
function cleanStepIdSet(stepIds) {
    return new Set(readStringArray(stepIds));
}
function partialStatus(input) {
    if (input.completed === input.total)
        return "completed";
    if (input.failed === input.total)
        return "failed";
    if (input.fallback > 0)
        return "partialFallback";
    if (input.failed > 0)
        return "partialFailed";
    if (input.completed > 0)
        return "partialCompleted";
    if (input.waiting > 0 && input.ready > 0)
        return "partialWaiting";
    if (input.waiting > 0)
        return "partialWaiting";
    return "ready";
}
export function createEphemeralProcedureExecutionState(input) {
    const running = cleanStepIdSet(input.runningStepIds);
    const completed = cleanStepIdSet(input.completedStepIds);
    const failed = cleanStepIdSet(input.failedStepIds);
    const fallback = cleanStepIdSet(input.fallbackStepIds);
    const approvalPending = cleanStepIdSet(input.approvalPendingStepIds);
    const states = input.plan.steps.map((step) => {
        const blockedBy = step.dependsOn.filter((dependency) => !completed.has(dependency));
        const status = completed.has(step.stepId) ? "completed" :
            failed.has(step.stepId) ? "failed" :
                fallback.has(step.stepId) ? "fallback" :
                    running.has(step.stepId) ? "running" :
                        approvalPending.has(step.stepId) || (input.plan.approval.required && step.riskLevel === "high") ? "waitingApproval" :
                            blockedBy.length > 0 ? "waitingDependency" :
                                "ready";
        return {
            stepId: step.stepId,
            baseToolId: step.baseToolId,
            status,
            dependsOn: step.dependsOn,
            blockedBy,
            canContinueInParallel: input.plan.executionMode !== "serial" && status === "ready",
            mustUseBaseToolRegistry: true,
            riskLevel: step.riskLevel,
            outputRef: step.outputRef,
            metadata: {},
        };
    });
    const readyStepIds = states.filter((step) => step.status === "ready").map((step) => step.stepId);
    const waitingStepIds = states
        .filter((step) => step.status === "waitingApproval" || step.status === "waitingDependency")
        .map((step) => step.stepId);
    const completedStepIds = states.filter((step) => step.status === "completed").map((step) => step.stepId);
    const failedStepIds = states.filter((step) => step.status === "failed").map((step) => step.stepId);
    const fallbackStepIds = states.filter((step) => step.status === "fallback").map((step) => step.stepId);
    return {
        procedureId: input.plan.procedureId,
        executionMode: input.plan.executionMode,
        partialStatus: partialStatus({
            total: states.length,
            ready: readyStepIds.length,
            waiting: waitingStepIds.length,
            completed: completedStepIds.length,
            failed: failedStepIds.length,
            fallback: fallbackStepIds.length,
        }),
        steps: states,
        readyStepIds,
        waitingStepIds,
        completedStepIds,
        failedStepIds,
        fallbackStepIds,
        registryInvocationRequired: true,
        metadata: input.metadata ?? {},
        publicSafe: true,
    };
}
