import { appendEvent, appendToolCall } from "../features/observability/index.js";
import { assessToolHookCapability } from "../features/policy/index.js";
import { extractCommandFromArgs } from "../features/policy/path-extractor.js";
import { evaluateMemberToolUse } from "../features/policy/index.js";
import { normalizeSeedToolName } from "../features/policy/tool-surface.js";
import { createRunStore, getActiveRun, getRun, updateRun } from "../features/run-state/index.js";
import { findTeamMemberBySession, loadTeamRuntime } from "../features/team-mode/index.js";
import { evaluateSeedCommandPolicy } from "../features/tool-policy/index.js";
import { listWorkspaceLeases } from "../features/workspace-state/index.js";
import { nowIso, sanitizeJson } from "../shared/index.js";
import { getSeedCommandSession } from "./command-mode.js";
const safeMemberCoordinationTools = [
    "seed_team_inbox",
    "seed_team_ack",
    "seed_team_broadcast",
    "seed_team_status",
    "seed_team_roster",
    "seed_team_conversation",
    "seed_team_send",
    "seed_team_meeting_close",
    "seed_team_meeting_create",
    "seed_team_meeting_post",
    "seed_team_meeting_status",
    "seed_team_member_request",
    "seed_team_task_list",
    "seed_context_bundle_list",
    "seed_context_bundle_read",
    "seed_context_metrics",
    "seed_orchestrator_next",
    "seed_orchestrator_record_decision",
    "seed_orchestrator_tick",
    "seed_session_info",
];
const readOnlyControlSeedTools = new Set([
    "seed_task_get",
    "seed_task_list",
    "seed_team_inbox",
    "seed_team_status",
    "seed_team_roster",
    "seed_team_conversation",
    "seed_team_meeting_status",
    "seed_team_list",
    "seed_team_task_list",
    "seed_workspace_status",
    "seed_trace_read",
    "seed_continuation_read",
    "seed_context_bundle_list",
    "seed_context_bundle_read",
    "seed_context_metrics",
    "seed_orchestrator_next",
    "seed_model_route",
    "seed_run_telemetry",
    "seed_artifact_list",
    "seed_artifact_read",
    "seed_report_check",
    "seed_session_list",
    "seed_session_read",
    "seed_session_search",
    "seed_session_info",
    "seed_tool_policy_check",
    "seed_command_status",
    "seed_background_output",
    "seed_background_notifications",
    "seed_skill_recommend",
    "seed_skill_validate",
]);
export function createToolExecuteBeforeHandler(ctx) {
    const store = createRunStore(ctx.input.directory, ctx.config);
    return async function toolExecuteBefore(input, output) {
        const active = await getActiveRun(store);
        if (!active)
            return;
        const run = await getRun(store, active.run_id);
        const runtime = await loadTeamRuntime(store, active.run_id);
        const member = findTeamMemberBySession(runtime, input.sessionID);
        const isRootSession = !run?.root_session_id || run.root_session_id === input.sessionID;
        await enforceReadOnlyCommandMode(ctx, store, active.run_id, input, output);
        if (run && isRootSession) {
            await enforceRootShellPolicy(store, run, input, output, ctx.config);
            await enforceCoordinatorDirectWorkPolicy(store, run, input, output);
        }
        if (member && member.role !== "lead" && !isRootSession) {
            const activeLeases = await listWorkspaceLeases(store, active.run_id);
            const decision = evaluateMemberToolUse({
                toolName: input.tool,
                args: output.args,
                memberName: String(member.name),
                memberToolPolicy: member.tool_policy,
                memberMcpPolicy: member.mcp_policy,
                memberSkillPolicy: member.skill_policy,
                memberReadPaths: member.read_paths ?? [],
                memberWritePaths: member.write_paths ?? [],
                activeLeases,
                allowRecursiveDelegation: member.spawn_policy?.allowed === true &&
                    (member.spawn_policy.max_depth ?? 0) > 0 &&
                    member.spawn_policy.requires_lab_representative_approval === false,
                safeCoordinationTools: safeMemberCoordinationTools,
            });
            if (!decision.allowed) {
                const timestamp = nowIso();
                const eventType = decision.code === "tool_denied" ||
                    decision.code === "tool_not_allowed" ||
                    decision.code === "recursive_delegation_denied" ||
                    decision.code === "seed_command_exec_reserved"
                    ? "team_member_tool_policy_blocked"
                    : "team_member_workspace_policy_blocked";
                await appendEvent(store, active.run_id, {
                    type: eventType,
                    actor: String(member.name),
                    session_id: input.sessionID,
                    summary: `${member.name} blocked from ${input.tool}: ${decision.reason}`,
                    metadata: {
                        role_id: member.name,
                        tool: input.tool,
                        reason: decision.reason,
                        code: decision.code,
                        classification: decision.capability.tool,
                        file_effect: decision.capability.file_effect,
                        requested_paths: decision.extracted_paths.map((entry) => entry.path),
                        extracted_paths: decision.extracted_paths,
                        matched_lease_ids: decision.matched_lease_ids,
                        allowed_tools: member.tool_policy?.allowed_tools ?? [],
                        denied_tools: member.tool_policy?.denied_tools ?? [],
                        read_paths: member.read_paths ?? [],
                        write_paths: member.write_paths ?? [],
                    },
                });
                await appendToolCall(store, active.run_id, {
                    tool_call_id: input.callID,
                    timestamp_start: timestamp,
                    timestamp_end: timestamp,
                    run_id: active.run_id,
                    session_id: input.sessionID,
                    agent: String(member.name),
                    tool_name: input.tool,
                    args_summary: JSON.stringify(sanitizeJson(output.args)).slice(0, 500),
                    status: "failed",
                    evidence_refs: [],
                    error: `SEED team member policy blocked ${input.tool}: ${decision.reason}`,
                });
                throw new Error(`SEED team member policy blocked ${input.tool}: ${decision.reason}`);
            }
        }
        await appendToolCall(store, active.run_id, {
            tool_call_id: input.callID,
            timestamp_start: nowIso(),
            run_id: active.run_id,
            session_id: input.sessionID,
            agent: member ? String(member.name) : undefined,
            tool_name: input.tool,
            args_summary: JSON.stringify(sanitizeJson(output.args)).slice(0, 500),
            status: "started",
            evidence_refs: [],
        });
    };
}
async function enforceReadOnlyCommandMode(ctx, store, runId, input, output) {
    const commandState = getSeedCommandSession(ctx, input.sessionID);
    if (commandState?.mode !== "readonly_control")
        return;
    if (isAllowedReadOnlyControlTool(input.tool, output.args))
        return;
    const timestamp = nowIso();
    const capability = assessToolHookCapability({ toolName: input.tool, args: output.args });
    const reason = `SEED read-only status command blocked mutating tool ${input.tool}. /seed-status must not change runtime state; use /seed-lab-meeting to pause for feedback, or start a new /seed run for new work.`;
    await appendEvent(store, runId, {
        type: "readonly_control_tool_blocked",
        actor: "seed-command-guard",
        session_id: input.sessionID,
        summary: reason,
        metadata: {
            tool: input.tool,
            command: commandState.command,
            command_source: commandState.source,
            capability,
        },
    });
    await appendToolCall(store, runId, {
        tool_call_id: input.callID,
        timestamp_start: timestamp,
        timestamp_end: timestamp,
        run_id: runId,
        session_id: input.sessionID,
        tool_name: input.tool,
        args_summary: JSON.stringify(sanitizeJson(output.args)).slice(0, 500),
        status: "failed",
        evidence_refs: [],
        error: reason,
    });
    throw new Error(reason);
}
function isAllowedReadOnlyControlTool(toolName, args) {
    const normalized = normalizeSeedToolName(toolName);
    if (readOnlyControlSeedTools.has(normalized))
        return true;
    const capability = assessToolHookCapability({ toolName, args });
    return capability.tool.kind === "read_only" && (capability.file_effect === "none" || capability.file_effect === "read");
}
async function enforceRootShellPolicy(store, run, input, output, config) {
    const capability = assessToolHookCapability({ toolName: input.tool, args: output.args });
    if (capability.tool.kind !== "shell")
        return;
    const command = extractCommandFromArgs(output.args);
    const decision = evaluateSeedCommandPolicy({
        command: command ? [command] : [],
        requireProfessorHandoff: config.safety.require_professor_handoff_for_destructive_actions,
    });
    if (decision.action === "allow")
        return;
    const timestamp = nowIso();
    await appendEvent(store, run.run_id, {
        type: decision.action === "ask" ? "root_shell_policy_approval_required" : "root_shell_policy_blocked",
        actor: run.active_agent,
        session_id: input.sessionID,
        summary: `${input.tool}: ${decision.reason}`,
        metadata: {
            tool: input.tool,
            command_summary: decision.command_summary,
            action: decision.action,
            matches: decision.matches,
            require_professor_handoff: decision.require_professor_handoff,
        },
    });
    if (decision.action === "ask") {
        await appendEvent(store, run.run_id, {
            type: "professor_handoff_required",
            actor: run.active_agent,
            session_id: input.sessionID,
            summary: `Root shell command should ask professor approval before execution: ${decision.command_summary}`,
            metadata: {
                source: "tool_execute_before",
                tool: input.tool,
                command_summary: decision.command_summary,
                matches: decision.matches,
            },
        });
        return;
    }
    await appendToolCall(store, run.run_id, {
        tool_call_id: input.callID,
        timestamp_start: timestamp,
        timestamp_end: timestamp,
        run_id: run.run_id,
        session_id: input.sessionID,
        agent: run.active_agent,
        tool_name: input.tool,
        args_summary: JSON.stringify(sanitizeJson(output.args)).slice(0, 500),
        status: "failed",
        evidence_refs: [],
        error: `SEED root shell policy denied ${input.tool}: ${decision.reason}`,
    });
    throw new Error(`SEED root shell policy denied ${input.tool}: ${decision.reason}`);
}
async function enforceCoordinatorDirectWorkPolicy(store, run, input, output) {
    if (run.run_mode !== "coordinator")
        return;
    if (input.tool === "seed_coordinator_direct_work")
        return;
    const capability = assessToolHookCapability({ toolName: input.tool, args: output.args });
    const isDirectNativeWrite = capability.tool.kind === "write_file" ||
        (capability.tool.kind === "shell" && capability.file_effect !== "none" && capability.file_effect !== "read");
    if (!isDirectNativeWrite)
        return;
    if (run.coordinator_direct_work_reason && !run.coordinator_direct_work_used_at) {
        await updateRun(store, run.run_id, { coordinator_direct_work_used_at: nowIso() });
        await appendEvent(store, run.run_id, {
            type: "coordinator_direct_work_exception_used",
            actor: run.active_agent,
            session_id: input.sessionID,
            summary: `${input.tool}: ${run.coordinator_direct_work_reason}`,
            metadata: {
                tool: input.tool,
                reason: run.coordinator_direct_work_reason,
                file_effect: capability.file_effect,
                paths: capability.paths,
            },
        });
        return;
    }
    const reason = "Lab-Representative is in coordinator mode; direct native edit/write/bash work requires seed_coordinator_direct_work with a concrete reason.";
    const timestamp = nowIso();
    await appendEvent(store, run.run_id, {
        type: "coordinator_direct_work_blocked",
        actor: run.active_agent,
        session_id: input.sessionID,
        summary: `${input.tool}: ${reason}`,
        metadata: {
            tool: input.tool,
            file_effect: capability.file_effect,
            paths: capability.paths,
            run_mode: run.run_mode,
        },
    });
    await appendToolCall(store, run.run_id, {
        tool_call_id: input.callID,
        timestamp_start: timestamp,
        timestamp_end: timestamp,
        run_id: run.run_id,
        session_id: input.sessionID,
        agent: run.active_agent,
        tool_name: input.tool,
        args_summary: JSON.stringify(sanitizeJson(output.args)).slice(0, 500),
        status: "failed",
        evidence_refs: [],
        error: `SEED coordinator policy blocked ${input.tool}: ${reason}`,
    });
    throw new Error(`SEED coordinator policy blocked ${input.tool}: ${reason}`);
}
//# sourceMappingURL=tool-execute-before.js.map