/*
 * 文件定位：storagePool / baseToolStorage / shell.detachedExecution bestPractice。
 * 核心目的：比较三家来源实践，并给 baseTools 入口提供稳定的进程控制调用面。
 */
import { createShellBaseToolDefinition, createShellCoreHandler, injectRuntimeInvocationMetadata, jsonSchema } from "../../_shared/baseToolAdapter.js";
import { plainJsonRecord, readRecord, safeMetadata, trimmedString } from "../_shared/processControlJson.js";
import { anthropicShellDetachedExecutionPractice } from "./anthropic.js";
import { deepmindShellDetachedExecutionPractice } from "./deepmind.js";
import { openaiShellDetachedExecutionPractice } from "./openai.js";
import { planShellDetachedExecution, } from "./core.js";
import { shellDetachedExecutionDependencyDeclarations, } from "./dependencies.js";
export * from "./core.js";
export const shellDetachedExecutionProviderPractices = [
    anthropicShellDetachedExecutionPractice,
    openaiShellDetachedExecutionPractice,
    deepmindShellDetachedExecutionPractice,
];
export const shellDetachedExecutionBestPracticeDescriptor = {
    toolId: "shell.detachedExecution",
    bestPractice: "runtime-governed-process-control",
    sourcePriority: ["cli", "agent-sdk", "api-sdk", "praxis-native"],
    providerOrder: ["anthropic", "openai", "deepmind"],
    dependencies: shellDetachedExecutionDependencyDeclarations,
};
function orderedPractices(preferredProvider) {
    if (preferredProvider === undefined) {
        return shellDetachedExecutionProviderPractices;
    }
    return [
        ...shellDetachedExecutionProviderPractices.filter((practice) => practice.providerName === preferredProvider),
        ...shellDetachedExecutionProviderPractices.filter((practice) => practice.providerName !== preferredProvider),
    ];
}
export function selectShellDetachedExecutionPractice(dependencies = {}) {
    for (const practice of orderedPractices(dependencies.preferredProvider)) {
        const provider = practice.createProvider(dependencies);
        if (provider !== undefined) {
            return { providerName: practice.providerName, practice, provider };
        }
    }
    return {
        providerName: "praxis-native",
        practice: {
            providerName: "praxis-native",
            source: { kind: "praxis-native", label: "Praxis dry-run fallback" },
            directCliSupport: false,
            sideEffectPolicy: "runtime-governed",
            notes: ["No injected or host process-control provider is currently available; dry-run remains available."],
            createProvider: () => undefined,
        },
    };
}
function stringValue(value) {
    return typeof value === "string" ? value : undefined;
}
function requestRecord(input) {
    return readRecord(input) ?? {};
}
function bestPracticeRequest(input) {
    return requestRecord(input);
}
function runtimeContext(input) {
    return (readRecord(requestRecord(input).context) ?? {});
}
function realExecutionRequested(input) {
    return readRecord(requestRecord(input).context)?.dryRun === false;
}
function guardAllows(context) {
    return context.guard?.allowed === true || context.guard?.accepted === true;
}
function auditEvent(type, context, metadata) {
    return {
        type,
        toolId: "shell.detachedExecution",
        invocationId: trimmedString(context.invocationId) || "shell.detachedExecution:dry-run",
        dryRun: context.dryRun !== false,
        metadata: {
            ...safeMetadata(context.auditMetadata),
            ...(metadata ?? {}),
        },
    };
}
function failure(code, message, boundary, context) {
    return {
        ok: false,
        toolId: "shell.detachedExecution",
        error: { code, message, boundary, safeForRuntimeInspection: true, internalDetailExposed: false },
        audit: [auditEvent("agentCore.basicTool.shell.detachedExecution.rejected", context ?? {}, { code, boundary })],
        events: ["basicTool.shell.detachedExecution.rejected"],
    };
}
function convertPlanResult(result) {
    if (!result.ok) {
        return {
            ok: false,
            toolId: "shell.detachedExecution",
            error: {
                code: result.error.code,
                message: result.error.message,
                boundary: (result.error.boundary === "approval" ? "governance" : result.error.boundary),
                safeForRuntimeInspection: true,
                internalDetailExposed: false,
            },
            audit: result.audit,
            events: result.events,
        };
    }
    return { ok: true, toolId: "shell.detachedExecution", output: result.output, audit: result.audit, events: result.events };
}
function normalizedProviderRequest(request, plannedOutput) {
    const requestInput = requestRecord(request);
    return {
        target: plannedOutput.target,
        context: { ...(readRecord(requestInput.context) ?? {}), dryRun: false },
    };
}
function normalizeProviderResult(providerResult) {
    const resultRecord = readRecord(providerResult);
    if (resultRecord === undefined) {
        return undefined;
    }
    const resultEnvelope = plainJsonRecord(resultRecord.resultEnvelope);
    if (resultEnvelope === undefined || resultEnvelope.planned === true) {
        return undefined;
    }
    return { resultEnvelope, metadata: safeMetadata(resultRecord.metadata) };
}
export async function executeShellDetachedExecution(request = {}) {
    const normalizedRequest = bestPracticeRequest(request);
    if (!realExecutionRequested(normalizedRequest)) {
        return convertPlanResult(planShellDetachedExecution(normalizedRequest));
    }
    const context = runtimeContext(normalizedRequest);
    const runtimeId = stringValue(context.runtimeId)?.trim();
    if (runtimeId === undefined || runtimeId.length === 0) {
        return failure("MISSING_RUNTIME_ID", "shell.detachedExecution requires context.runtimeId for real execution audit", "input", context);
    }
    if (!guardAllows(context)) {
        return failure("GOVERNANCE_REJECTED", "shell.detachedExecution requires an allowed runtime governance guard when dryRun is false", "governance", context);
    }
    const planned = planShellDetachedExecution({ ...normalizedRequest, context: { ...runtimeContext(normalizedRequest), dryRun: true } });
    if (!planned.ok) {
        return convertPlanResult(planned);
    }
    const selection = selectShellDetachedExecutionPractice({ executor: normalizedRequest.executor, provider: normalizedRequest.provider, preferredProvider: normalizedRequest.preferredProvider });
    if (selection.provider === undefined) {
        return failure("PROVIDER_UNAVAILABLE", "shell.detachedExecution requires a runtime-provided process-control provider when dryRun is false", "provider", context);
    }
    try {
        const providerResult = normalizeProviderResult(await selection.provider(normalizedProviderRequest(request, planned.output), context));
        if (providerResult === undefined) {
            return failure("PROVIDER_REJECTED", "shell.detachedExecution provider must return a plain JSON runtime envelope", "provider", context);
        }
        return {
            ok: true,
            toolId: "shell.detachedExecution",
            output: {
                ...planned.output,
                dryRun: false,
                executionBlocked: false,
                providerCalled: true,
                resultEnvelope: providerResult.resultEnvelope,
            },
            audit: [auditEvent("agentCore.basicTool.shell.detachedExecution.provider", context, { providerName: selection.providerName, ...providerResult.metadata })],
            events: ["basicTool.shell.detachedExecution.providerCalled"],
        };
    }
    catch (error) {
        return failure("PROVIDER_REJECTED", "shell.detachedExecution provider rejected the invocation", "provider", context);
    }
}
export const shellDetachedExecutionBaseToolDefinition = createShellBaseToolDefinition({
    toolId: "shell.detachedExecution",
    title: "Detached Execution",
    description: "Plan or execute a detached shell launch through a governed runtime provider. Use this for long-lived GUI/browser launches such as Chrome, Firefox, Edge, or xdg-open when the process may outlive the agent session.",
    summary: "Use shell.detachedExecution when TAP/runtime has approved this process-control primitive.",
    storageGroup: "processControl",
    riskLevel: "risky",
    permissionHints: ["shell:execute", "shell:process:detached"],
    dependencies: shellDetachedExecutionDependencyDeclarations,
    inputSchema: jsonSchema("shell.detachedExecution.input", {
        type: "object",
        additionalProperties: true,
        properties: {
            target: {
                type: "object",
                additionalProperties: true,
                required: ["command"],
                properties: {
                    command: { type: "string", minLength: 1 },
                    workingDirectory: { type: "string" },
                    shell: { type: "string", enum: ["sh", "bash", "zsh"] },
                    launchId: { type: "string" },
                    pidFilePath: { type: "string" },
                    stdoutLogPath: { type: "string" },
                    stderrLogPath: { type: "string" },
                    restartPolicy: { type: "string", enum: ["none", "on-failure"] },
                },
            },
            context: { type: "object", additionalProperties: true },
            preferredProvider: { type: "string", enum: ["anthropic", "openai", "deepmind", "praxis-native"] },
        },
    }),
    outputSchema: jsonSchema("shell.detachedExecution.output", {
        type: "object",
        additionalProperties: true,
        required: ["kind", "dryRun", "executionBlocked"],
        properties: {
            kind: { const: "agentCore.basicTool.shell.detachedExecution" },
            dryRun: { type: "boolean" },
            executionBlocked: { type: "boolean" },
        },
    }),
});
export const shellDetachedExecutionHandler = createShellCoreHandler(shellDetachedExecutionBaseToolDefinition, async (request) => executeShellDetachedExecution({
    ...request.input,
    executor: request.executor,
    context: {
        ...(readRecord(request.input.context) ?? {}),
        runtimeId: trimmedString(readRecord(request.input.context)?.runtimeId) ?? request.runtimeId,
        ...({ sessionId: trimmedString(readRecord(request.input.context)?.sessionId) ?? request.sessionId }),
        invocationId: trimmedString(readRecord(request.input.context)?.invocationId) ?? request.toolCallId,
        auditMetadata: injectRuntimeInvocationMetadata(request.metadata, safeMetadata(readRecord(request.input.context)?.auditMetadata), request),
    },
}));
