"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.prepareWorkerRun = prepareWorkerRun;
const canonical_workflow_state_1 = require("../../comments/canonical-workflow-state");
const control_plane_config_1 = require("../../config/control-plane-config");
const types_1 = require("../../platform/ticket-tracker/types");
const types_2 = require("../../worker/types");
const approval_instructions_1 = require("../../workflows/approval-instructions");
const config_override_validation_1 = require("../../workflows/config-override-validation");
const foreign_input_approval_1 = require("../../workflows/foreign-input-approval");
const plan_progress_1 = require("../../workflows/plan-progress");
const pre_plan_state_1 = require("../../workflows/pre-plan-state");
const recurring_1 = require("../../workflows/recurring");
const replan_comment_1 = require("../../workflows/replan-comment");
const run_context_1 = require("../../workflows/run-context");
const run_context_summaries_1 = require("../../workflows/run-context-summaries");
const types_3 = require("../../workflows/types");
const preflight_resolution_marker_1 = require("./preflight-resolution-marker");
const presentation_1 = require("./presentation");
const required_mcp_gate_1 = require("./required-mcp-gate");
const required_skill_gate_1 = require("./required-skill-gate");
const run_limit_policy_1 = require("./run-limit-policy");
const run_state_helpers_1 = require("./run-state-helpers");
const shared_1 = require("./shared");
const types_4 = require("./types");
const workflow_surface_1 = require("./workflow-surface");
const workflow_template_root_1 = require("./workflow-template-root");
async function prepareWorkerRun(input) {
    const continuationContext = input.continuationContext ?? null;
    const promptContext = await resolveWorkerPromptContext(input.context, input.snapshot);
    const routeGate = await resolveWorkerRouteGate({
        context: input.context,
        snapshot: input.snapshot,
        promptContext,
        continuationContext,
        options: input.options ?? {},
    });
    if ("kind" in routeGate) {
        return routeGate;
    }
    const runLimitGate = resolveRunLimitHandoffGate(routeGate);
    if (runLimitGate !== null) {
        return runLimitGate;
    }
    const requiredSkillGate = await resolveRequiredSkillGate({
        context: input.context,
        routeGate,
    });
    if (requiredSkillGate !== null) {
        return requiredSkillGate;
    }
    const requiredMcpGate = await resolveRequiredMcpGate({
        context: input.context,
        routeGate,
    });
    if (requiredMcpGate !== null) {
        return requiredMcpGate;
    }
    const preflightReplanGate = resolvePreflightReplanGate(routeGate);
    if (preflightReplanGate !== null) {
        return preflightReplanGate;
    }
    return await applyGateHumanPolicies({
        context: input.context,
        routeGate,
    });
}
function buildHandoffToHumanGate(input) {
    return {
        kind: "handoff_to_human",
        ...buildWorkerRunPreparationFields(input),
        summary: input.summary,
    };
}
function buildWorkerRunPreparationFields(input) {
    const aiRunAccounting = (0, run_state_helpers_1.resolveRouteGateAiRunAccounting)(input);
    return {
        latestIssue: input.snapshot.latestIssue,
        comments: input.snapshot.comments,
        promptIssue: input.promptContext.issue,
        promptComments: input.promptContext.promptComments,
        linkedIssue: input.promptContext.linkedIssue,
        currentConsecutiveAiRuns: aiRunAccounting.consecutiveAiRuns,
        currentTotalAiRunHours: aiRunAccounting.totalAiRunHours,
        workflow: input.workflow,
        workflowTemplateName: input.workflowTemplateName,
        workflowState: input.workflowState,
        planningIntent: input.planningIntent,
        needsHumanReason: input.needsHumanReason,
        ...(input.invalidWorkflowStatePolicy !== undefined
            ? { invalidWorkflowStatePolicy: input.invalidWorkflowStatePolicy }
            : {}),
        continuationContext: input.continuationContext,
    };
}
function buildRunWorkerGate(input) {
    return {
        kind: "run_worker",
        ...buildWorkerRunPreparationFields({ ...input, needsHumanReason: null }),
    };
}
async function resolveRequiredSkillGate(input) {
    if (input.routeGate.workflow.mode === types_2.WORKER_RUN_MODES.workflowSelect) {
        return null;
    }
    const requiredSkillGate = await (0, required_skill_gate_1.resolveRequiredSkillPreWorkerGate)({
        repoRoot: (0, workflow_template_root_1.getWorkflowTemplateRepoRoot)(input.context),
        effectiveConfig: input.routeGate.effectiveConfig,
        issue: input.routeGate.snapshot.latestIssue,
        linkedIssue: input.routeGate.promptContext.linkedIssue,
        workflow: input.routeGate.workflow,
        workflowTemplateName: input.routeGate.workflowTemplateName,
    });
    if (requiredSkillGate === null) {
        return null;
    }
    if (requiredSkillGate.kind === "handoff") {
        return buildHandoffToHumanGate({
            snapshot: input.routeGate.snapshot,
            workflow: input.routeGate.workflow,
            workflowTemplateName: input.routeGate.workflowTemplateName,
            workflowState: input.routeGate.workflowState,
            planningIntent: input.routeGate.planningIntent,
            needsHumanReason: types_4.WORKER_NEEDS_HUMAN_REASONS.question,
            continuationContext: input.routeGate.continuationContext,
            summary: requiredSkillGate.summary,
            promptContext: input.routeGate.promptContext,
        });
    }
    return {
        kind: "preflight_resolution_required",
        ...buildWorkerRunPreparationFields({
            ...input.routeGate,
            needsHumanReason: null,
        }),
        summary: requiredSkillGate.summary,
        resolution: requiredSkillGate.resolution,
    };
}
async function resolveRequiredMcpGate(input) {
    if (input.routeGate.workflow.mode === types_2.WORKER_RUN_MODES.workflowSelect) {
        return null;
    }
    const requiredMcpGate = await (0, required_mcp_gate_1.resolveRequiredMcpPreWorkerGate)({
        repoRoot: (0, workflow_template_root_1.getWorkflowTemplateRepoRoot)(input.context),
        effectiveConfig: input.routeGate.effectiveConfig,
        issue: input.routeGate.snapshot.latestIssue,
        linkedIssue: input.routeGate.promptContext.linkedIssue,
        workflow: input.routeGate.workflow,
        workflowTemplateName: input.routeGate.workflowTemplateName,
    });
    if (requiredMcpGate === null) {
        return null;
    }
    if (requiredMcpGate.kind === "handoff") {
        return buildHandoffToHumanGate({
            snapshot: input.routeGate.snapshot,
            workflow: input.routeGate.workflow,
            workflowTemplateName: input.routeGate.workflowTemplateName,
            workflowState: input.routeGate.workflowState,
            planningIntent: input.routeGate.planningIntent,
            needsHumanReason: types_4.WORKER_NEEDS_HUMAN_REASONS.question,
            continuationContext: input.routeGate.continuationContext,
            summary: requiredMcpGate.summary,
            promptContext: input.routeGate.promptContext,
        });
    }
    return {
        kind: "preflight_resolution_required",
        ...buildWorkerRunPreparationFields({
            ...input.routeGate,
            needsHumanReason: null,
        }),
        summary: requiredMcpGate.summary,
        resolution: requiredMcpGate.resolution,
    };
}
async function resolveWorkerRouteGate(input) {
    const responseLanguage = input.context.config.i18n.defaultLanguage;
    const workflowStateResolution = (0, canonical_workflow_state_1.resolveCanonicalWorkflowStateComment)(input.promptContext.workflowComments, responseLanguage);
    const workflowStateForDefaultRouting = workflowStateResolution.kind === "found"
        ? workflowStateResolution.workflowState
        : input.promptContext.workflowBootstrapState;
    const unavailableWorkerGate = buildUnavailableWorkerConfigOverrideGate({
        context: input.context,
        snapshot: input.snapshot,
        promptContext: input.promptContext,
        responseLanguage,
        workflowState: workflowStateForDefaultRouting,
        continuationContext: input.continuationContext,
    });
    if (unavailableWorkerGate !== null) {
        return unavailableWorkerGate;
    }
    const effectiveConfig = resolveWorkflowRunEffectiveConfig({
        context: input.context,
        workflowComments: input.promptContext.workflowComments,
        workflowBootstrapState: input.promptContext.workflowBootstrapState,
        responseLanguage,
    });
    const workflow = await (0, run_context_1.resolveWorkflowRunContext)((0, workflow_template_root_1.getWorkflowTemplateRepoRoot)(input.context), input.promptContext.workflowComments, responseLanguage, {
        maxNestedWorkflowDepth: input.context.config.maxNestedWorkflowDepth,
        workflowSurface: (0, workflow_surface_1.resolveWorkflowSurfaceForRun)({
            issue: input.snapshot.latestIssue,
            workflowState: workflowStateForDefaultRouting,
        }),
        ...(input.snapshot.latestIssue.kind === types_1.TRACKER_ITEM_KINDS.pullRequest
            ? {
                issueCompletion: effectiveConfig.issueCompletion,
            }
            : {}),
        workflowStartApprovalConfig: input.context.config,
        workflowStartApprovalTarget: input.snapshot.latestIssue.kind === types_1.TRACKER_ITEM_KINDS.pullRequest
            ? "pull_request"
            : "issue",
        bootstrapWorkflowState: input.promptContext.workflowBootstrapState,
        issueBody: input.snapshot.latestIssue.body,
        foreignInputApprovalAssignee: (0, shared_1.resolveSingleAssignee)(input.snapshot.latestIssue),
        baseBranchDefault: input.context.config.repositoryHost.baseBranch,
    });
    switch (workflow.kind) {
        case "handoff":
            return buildHandoffToHumanGate({
                snapshot: input.snapshot,
                workflow: null,
                workflowTemplateName: workflow.workflowTemplateName,
                workflowState: workflow.workflowState,
                planningIntent: null,
                needsHumanReason: workflowStateResolution.kind === "invalid"
                    ? null
                    : types_4.WORKER_NEEDS_HUMAN_REASONS.question,
                ...(workflowStateResolution.kind === "invalid"
                    ? { invalidWorkflowStatePolicy: "treat_as_missing" }
                    : {}),
                continuationContext: input.continuationContext,
                summary: workflow.summary,
                promptContext: input.promptContext,
            });
        case "plan": {
            return {
                snapshot: input.snapshot,
                promptContext: input.promptContext,
                responseLanguage,
                routeKind: "plan",
                workflow: workflow.workflow,
                workflowTemplateName: workflow.workflowTemplateName,
                workflowState: workflow.workflowState,
                effectiveConfig: (0, control_plane_config_1.resolveIssueEffectiveConfigForWorkflowState)(input.context.config, workflow.workflowState),
                planningIntent: workflow.planningIntent,
                continuationContext: input.continuationContext,
                preflightReplanReason: input.options.allowPreflightReplan !== false &&
                    workflow.requiresPreflightReplan
                    ? (0, replan_comment_1.buildTemplateDriftReplanReason)(responseLanguage)
                    : null,
            };
        }
        case "select":
            return {
                snapshot: input.snapshot,
                promptContext: input.promptContext,
                responseLanguage,
                routeKind: "select",
                workflow: workflow.workflow,
                workflowTemplateName: workflow.workflowTemplateName,
                workflowState: workflow.workflowState,
                effectiveConfig: (0, control_plane_config_1.resolveIssueEffectiveConfigForWorkflowState)(input.context.config, workflow.workflowState),
                planningIntent: workflow.planningIntent,
                continuationContext: input.continuationContext,
                preflightReplanReason: null,
            };
        case "execute":
            return {
                snapshot: input.snapshot,
                promptContext: input.promptContext,
                responseLanguage,
                routeKind: "execute",
                workflow: workflow.workflow,
                workflowTemplateName: workflow.workflowTemplateName,
                workflowState: workflow.workflowState,
                effectiveConfig: (0, control_plane_config_1.resolveIssueEffectiveConfigForWorkflowState)(input.context.config, workflow.workflowState),
                planningIntent: null,
                continuationContext: input.continuationContext,
                preflightReplanReason: null,
            };
    }
}
function buildUnavailableWorkerConfigOverrideGate(input) {
    const unavailableWorker = (0, config_override_validation_1.findUnavailableWorkerConfigOverride)({
        configWorkers: (0, control_plane_config_1.getConfiguredWorkerProviders)(input.context.config),
        patch: input.workflowState?.configOverrides,
    });
    if (unavailableWorker === null) {
        return null;
    }
    return buildHandoffToHumanGate({
        snapshot: input.snapshot,
        workflow: null,
        workflowTemplateName: null,
        workflowState: input.workflowState,
        planningIntent: null,
        needsHumanReason: types_4.WORKER_NEEDS_HUMAN_REASONS.question,
        continuationContext: input.continuationContext,
        summary: (0, run_context_summaries_1.buildUnavailableWorkerConfigOverrideSummary)(input.responseLanguage, unavailableWorker),
        promptContext: input.promptContext,
    });
}
function resolvePreflightReplanGate(routeGate) {
    if (routeGate.preflightReplanReason === null) {
        return null;
    }
    return {
        kind: "requeue_for_replan",
        ...buildWorkerRunPreparationFields({
            ...routeGate,
            needsHumanReason: null,
        }),
        summary: routeGate.preflightReplanReason,
        replanReason: routeGate.preflightReplanReason,
    };
}
async function applyGateHumanPolicies(input) {
    if (input.context.currentUserLogin &&
        (await isForeignInputApprovalRequiredForWorkerRun({
            context: input.context,
            issue: input.routeGate.promptContext.issue,
            comments: input.routeGate.promptContext.workflowComments,
            linkedIssue: input.routeGate.promptContext.linkedIssue,
            linkedIssueComments: input.routeGate.promptContext.linkedIssueComments,
            workflowState: input.routeGate.workflowState,
            responseLanguage: input.routeGate.responseLanguage,
        }))) {
        return buildHandoffToHumanGate({
            snapshot: input.routeGate.snapshot,
            workflow: input.routeGate.workflow,
            workflowTemplateName: input.routeGate.workflowTemplateName,
            workflowState: input.routeGate.workflowState,
            planningIntent: input.routeGate.planningIntent,
            needsHumanReason: "foreign_approval",
            continuationContext: input.routeGate.continuationContext,
            summary: (0, approval_instructions_1.buildForeignApprovalSummary)(input.routeGate.responseLanguage, input.routeGate.promptContext.issue.id),
            promptContext: input.routeGate.promptContext,
        });
    }
    return buildRunWorkerGate(input.routeGate);
}
function resolveRunLimitHandoffGate(routeGate) {
    if ((0, preflight_resolution_marker_1.isPreflightResolutionIssue)(routeGate.snapshot.latestIssue)) {
        return null;
    }
    const aiRunAccounting = (0, run_state_helpers_1.resolveRouteGateAiRunAccounting)(routeGate);
    const runLimit = (0, run_limit_policy_1.resolveAiRunLimitPolicy)({
        config: routeGate.effectiveConfig,
        consecutiveAiRuns: aiRunAccounting.consecutiveAiRuns,
        totalAiRunHours: aiRunAccounting.totalAiRunHours,
    });
    if (!runLimit.reached) {
        return null;
    }
    if ((0, plan_progress_1.hasControllerChildIssueControlPlaneAction)(routeGate.workflowState)) {
        return null;
    }
    if ((0, recurring_1.isRecurringParentWorkflowState)(routeGate.workflowState)) {
        return null;
    }
    if (routeGate.routeKind !== "select" &&
        routeGate.routeKind !== "plan" &&
        routeGate.routeKind !== "execute" &&
        !(0, run_state_helpers_1.isImplicitWorkflowRun)(routeGate.workflow.mode, routeGate.workflowState)) {
        return null;
    }
    return buildHandoffToHumanGate({
        snapshot: routeGate.snapshot,
        workflow: routeGate.workflow,
        workflowTemplateName: routeGate.workflowTemplateName,
        workflowState: routeGate.workflowState,
        planningIntent: routeGate.planningIntent,
        needsHumanReason: "run_limit",
        continuationContext: routeGate.continuationContext,
        summary: (0, presentation_1.buildAiRunLimitSummary)(routeGate.responseLanguage, {
            reachedKind: runLimit.reachedKind,
            consecutiveAiRuns: aiRunAccounting.consecutiveAiRuns,
            totalAiRunHours: aiRunAccounting.totalAiRunHours,
            maxRuns: routeGate.effectiveConfig.aiRunLimits.maxRuns,
            maxHours: routeGate.effectiveConfig.aiRunLimits.maxHours,
        }),
        promptContext: routeGate.promptContext,
    });
}
async function resolveWorkerPromptContext(context, snapshot) {
    if (snapshot.latestIssue.kind !== types_1.TRACKER_ITEM_KINDS.pullRequest) {
        return {
            issue: snapshot.latestIssue,
            promptComments: snapshot.comments,
            workflowComments: snapshot.comments,
            linkedIssue: null,
            linkedIssueComments: [],
            workflowBootstrapState: null,
        };
    }
    const linkedIssueId = (0, shared_1.resolveLinkedIssueIdFromPullRequest)({ body: snapshot.latestIssue.body }, snapshot.comments);
    const latestIssue = await (0, shared_1.hydratePullRequestIssueSourceBranchName)(context.ticketTracker, snapshot.latestIssue);
    if (linkedIssueId === null) {
        return {
            issue: latestIssue,
            promptComments: snapshot.comments,
            workflowComments: snapshot.comments,
            linkedIssue: null,
            linkedIssueComments: [],
            workflowBootstrapState: null,
        };
    }
    let linkedIssue = null;
    try {
        const resolvedIssue = await context.ticketTracker.getIssue(linkedIssueId);
        if (resolvedIssue.kind === types_1.TRACKER_ITEM_KINDS.issue) {
            linkedIssue = resolvedIssue;
        }
    }
    catch {
        linkedIssue = null;
    }
    if (linkedIssue === null) {
        return {
            issue: latestIssue,
            promptComments: snapshot.comments,
            workflowComments: snapshot.comments,
            linkedIssue: null,
            linkedIssueComments: [],
            workflowBootstrapState: null,
        };
    }
    const linkedIssueComments = await (0, shared_1.loadSortedIssueComments)(context.ticketTracker, linkedIssue.id, {
        timeline: "issue-only",
    });
    const workflowBootstrapState = resolvePullRequestWorkflowBootstrapState({
        comments: linkedIssueComments,
        responseLanguage: context.config.i18n.defaultLanguage,
    });
    return {
        issue: latestIssue,
        promptComments: [...linkedIssueComments, ...snapshot.comments].sort((left, right) => left.createdAt.localeCompare(right.createdAt)),
        workflowComments: snapshot.comments,
        linkedIssue,
        linkedIssueComments,
        workflowBootstrapState,
    };
}
function resolvePullRequestWorkflowBootstrapState(input) {
    const resolution = (0, canonical_workflow_state_1.resolveCanonicalWorkflowStateComment)(input.comments, input.responseLanguage);
    if (resolution.kind === "invalid") {
        throw new Error(resolution.summary);
    }
    if (resolution.kind !== "found") {
        return null;
    }
    return (0, pre_plan_state_1.buildSelectionTransitionWorkflowState)({
        previousState: null,
        workflowSurface: "pull_request",
        workflowTemplates: resolution.workflowState.workflowTemplates,
        baseBranch: resolution.workflowState.baseBranch,
        schedule: (0, types_3.createOffWorkflowScheduleState)(),
        configOverrides: resolution.workflowState.configOverrides,
        consecutiveAiRuns: resolution.workflowState.consecutiveAiRuns,
        totalAiRunHours: resolution.workflowState.totalAiRunHours,
        lastScannedCommentCursor: resolution.workflowState.lastScannedCommentCursor,
    });
}
function resolveWorkflowRunEffectiveConfig(input) {
    const resolution = (0, canonical_workflow_state_1.resolveCanonicalWorkflowStateComment)(input.workflowComments, input.responseLanguage);
    if (resolution.kind === "invalid") {
        return input.context.config;
    }
    const workflowState = resolution.kind === "found"
        ? resolution.workflowState
        : input.workflowBootstrapState;
    return (0, control_plane_config_1.resolveIssueEffectiveConfigForWorkflowState)(input.context.config, workflowState ?? null);
}
async function isForeignInputApprovalRequiredForWorkerRun(input) {
    const currentAssignee = (0, shared_1.resolveSingleAssignee)(input.issue);
    if (currentAssignee === null ||
        input.context.currentUserLogin !== currentAssignee) {
        return false;
    }
    const effectiveConfig = (0, control_plane_config_1.resolveIssueEffectiveConfigForWorkflowState)(input.context.config, input.workflowState);
    const hasCanonicalWorkflowState = (0, canonical_workflow_state_1.resolveCanonicalWorkflowStateComment)(input.comments, input.responseLanguage)
        .kind === "found";
    const preCanonicalApproval = await (0, foreign_input_approval_1.resolvePreCanonicalForeignInputApproval)({
        ticketTracker: input.context.ticketTracker,
        config: effectiveConfig,
        issue: input.issue,
        comments: input.comments,
        linkedIssue: input.linkedIssue,
        linkedIssueComments: input.linkedIssueComments,
        approvedAssignee: currentAssignee,
    });
    const approvalState = !hasCanonicalWorkflowState && preCanonicalApproval !== undefined
        ? preCanonicalApproval
        : (input.workflowState?.foreignInputApproval ?? preCanonicalApproval);
    const approvalInput = {
        ticketTracker: input.context.ticketTracker,
        config: effectiveConfig,
        issue: input.issue,
        comments: input.comments,
        linkedIssue: input.linkedIssue,
        linkedIssueComments: input.linkedIssueComments,
        scanStartCursor: input.workflowState?.lastScannedCommentCursor ?? null,
        currentAssignee,
    };
    if (approvalState !== undefined) {
        approvalInput.approvalState = approvalState;
    }
    const approvalRequirement = await (0, foreign_input_approval_1.resolveForeignApprovalRequirement)(approvalInput);
    return approvalRequirement.required;
}
