/**
 * Runtime-owned approval flow helpers.
 *
 * Keeps approval request shaping and post-approval grant application in one
 * place so composition code can stay thin.
 */
import { APPROVAL } from '../approvals/types.js';
import { enforceFilesystemPolicy, getFilesystemAccessDecision } from './filesystem-policy.js';
const RISKY_SHELL_OPERATION_RULES = [
    {
        label: 'Send message',
        reason: 'This can send email or another outbound message.',
        pattern: /\.Send\b/
    },
    {
        label: 'Delete item',
        reason: 'This can delete email, calendar items, files, or other records.',
        pattern: /\.Delete\b/
    }
];
function toRiskyShellOperation({ label, reason }) {
    return { label, reason };
}
export function getRiskyShellOperations(command) {
    return RISKY_SHELL_OPERATION_RULES.filter(({ pattern }) => pattern.test(command)).map(toRiskyShellOperation);
}
export function hasRiskyShellOperation(command) {
    return RISKY_SHELL_OPERATION_RULES.some(({ pattern }) => pattern.test(command));
}
/** Render an explanatory message for a granted filesystem approval choice. */
export function describeFilesystemApprovalChoice(request, choice) {
    switch (choice) {
        case APPROVAL.ALLOW_ONCE:
            return `User allowed ${request.payload.access} once: ${request.payload.path}`;
        case APPROVAL.ALLOW_FILE:
            return `User always allowed ${request.payload.access} for file: ${request.payload.fileGrantPath}`;
        case APPROVAL.ALLOW_FOLDER:
            return `User always allowed ${request.payload.access} for folder: ${request.payload.folderGrantPath}`;
        default:
            return undefined;
    }
}
/** Build the approval request payload for a filesystem access decision. */
export async function buildFilesystemApprovalRequest(absolutePath, access, policy) {
    const decision = await getFilesystemAccessDecision({
        path: absolutePath,
        policy,
        access
    });
    return {
        kind: access === 'read' ? 'read' : 'write',
        title: 'Review file access',
        payload: {
            path: absolutePath,
            access,
            allowPersistentGrant: decision.allowPersistentGrant === true,
            fileGrantPath: decision.fileGrantPath ?? decision.canonicalPath,
            folderGrantPath: decision.folderGrantPath ?? decision.canonicalPath,
            workspaceRoots: decision.canonicalWorkspaceRoots
        },
        alwaysRequireExplicitApproval: decision.outcome === 'require_approval'
    };
}
/** Check whether a filesystem call is allowed without prompting under the current policy. */
export async function isFilesystemInvocationSafe(absolutePath, access, policy, ctx) {
    const decision = await getFilesystemAccessDecision({
        path: absolutePath,
        policy,
        access
    });
    if (decision.outcome !== 'require_approval') {
        return true;
    }
    try {
        await enforceFilesystemPolicy({
            path: absolutePath,
            policy,
            access,
            sessionState: ctx?.sessionState,
            approvalGrants: ctx?.approvalGrants?.filesystem
        });
        return true;
    }
    catch {
        return false;
    }
}
/** Persist the user's filesystem-approval choice into settings, session state, and tool ctx. */
export function applyFilesystemApprovalChoice(choice, request, access, settingsManager, ctx) {
    let grant;
    if (choice === APPROVAL.ALLOW_ONCE) {
        grant = { path: request.payload.fileGrantPath, access, scope: 'file' };
    }
    else if (choice === APPROVAL.ALLOW_FILE) {
        grant = { path: request.payload.fileGrantPath, access, scope: 'file' };
        settingsManager.addFilesystemApproval(grant);
        ctx?.sessionState?.grantFilesystemAccess(grant.path, grant.access, grant.scope);
    }
    else if (choice === APPROVAL.ALLOW_FOLDER) {
        grant = { path: request.payload.folderGrantPath, access, scope: 'folder' };
        settingsManager.addFilesystemApproval(grant);
        ctx?.sessionState?.grantFilesystemAccess(grant.path, grant.access, grant.scope);
    }
    return {
        ...ctx,
        approvalGrants: {
            ...ctx?.approvalGrants,
            filesystem: grant
                ? [...(ctx?.approvalGrants?.filesystem ?? []), grant]
                : [...(ctx?.approvalGrants?.filesystem ?? [])]
        }
    };
}
/** Build the approval request payload for a shell command. */
export function buildShellApprovalRequest(params, toolName) {
    const command = String(params.command ?? '');
    const highRiskOperations = getRiskyShellOperations(command);
    return {
        kind: toolName,
        title: 'Review shell command',
        alwaysRequireExplicitApproval: true,
        payload: {
            command,
            description: typeof params.description === 'string' ? params.description : undefined,
            connections: Array.isArray(params.connections) ? params.connections : undefined,
            ...(highRiskOperations.length > 0 ? { highRiskOperations } : {})
        }
    };
}
/** Build the approval request payload for a task subagent spawn. */
export function buildTaskSpawnApprovalRequest(payload) {
    return {
        kind: 'task',
        title: 'Review subagent spawn',
        alwaysRequireExplicitApproval: true,
        payload
    };
}
/** Check whether the shell policy allows invocations without prompting. */
export function isShellInvocationSafe(policy, command) {
    if (command !== undefined && hasRiskyShellOperation(command)) {
        return false;
    }
    return policy?.enabled === true && policy.mutationBehavior === 'allow';
}
/** Record an approved shell command on the tool execution context. */
export function applyShellApprovalChoice(ctx, params) {
    return {
        ...ctx,
        approvalGrants: {
            ...ctx?.approvalGrants,
            shell: [...(ctx?.approvalGrants?.shell ?? []), { command: String(params.command ?? '') }]
        }
    };
}
/** Translate a task-spawn approval choice into a one-shot subagent spawn grant. */
export function applyTaskSpawnApprovalChoice(ctx, request, choice = APPROVAL.ACCEPT) {
    const runInBackground = choice === APPROVAL.RUN_BACKGROUND
        ? true
        : choice === APPROVAL.RUN_FOREGROUND
            ? false
            : request.payload.requestedRunInBackground;
    return {
        ...ctx,
        // Task spawn approval is a one-shot child launch authorization. Approved
        // spawns become full-worker child sessions; the approval does not create a
        // reusable grant for later task calls.
        subagentSpawnGrant: {
            fullWorker: true,
            runInBackground
        }
    };
}
//# sourceMappingURL=runtime-approval-flow.js.map