"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.executeFrontierIssue = executeFrontierIssue;
const control_plane_config_1 = require("../../config/control-plane-config");
const canonical_workflow_state_1 = require("../../comments/canonical-workflow-state");
const branding_1 = require("../../core/branding");
const tracked_project_1 = require("../../core/tracked-project");
const catalog_1 = require("../../i18n/catalog");
const types_1 = require("../../platform/ticket-tracker/types");
const workflow_labels_1 = require("../../platform/ticket-tracker/workflow-labels");
const types_2 = require("../../platform/workflow-state/types");
const constants_1 = require("../../worker/constants");
const plan_progress_1 = require("../../workflows/plan-progress");
const recurring_1 = require("../../workflows/recurring");
const types_3 = require("../../workflows/types");
const workflow_state_read_support_1 = require("../../workflows/workflow-state-read-support");
const default_issue_workspace_manager_1 = require("../../workspaces/default-issue-workspace-manager");
const constants_2 = require("../constants");
const execution_ownership_1 = require("../execution-ownership");
const claim_workflow_execution_1 = require("./claim-workflow-execution");
const dependencies_1 = require("./dependencies");
const effects_1 = require("./effects");
const failure_recovery_1 = require("./failure-recovery");
const freshness_1 = require("./freshness");
const gate_1 = require("./gate");
const handoffs_1 = require("./handoffs");
const issue_branch_target_1 = require("./issue-branch-target");
const loop_1 = require("./loop");
const needs_human_1 = require("./needs-human");
const preflight_resolution_child_1 = require("./preflight-resolution-child");
const run_state_helpers_1 = require("./run-state-helpers");
const publish_1 = require("./publish");
const recurring_parent_1 = require("./recurring-parent");
const results_1 = require("./results");
const shared_1 = require("./shared");
const spawn_child_controller_1 = require("./spawn-child-controller");
const transitions_1 = require("./transitions");
async function executeFrontierIssue(context, executable) {
    const preparedExecution = await prepareIssueExecution(context, executable);
    if ("summary" in preparedExecution) {
        return preparedExecution.summary;
    }
    const preClaimExecution = await resolvePreClaimExit(preparedExecution);
    if ("summary" in preClaimExecution) {
        return preClaimExecution.summary;
    }
    const claimedExecution = await claimAndConfirmExecution(preClaimExecution);
    if ("summary" in claimedExecution) {
        return claimedExecution.summary;
    }
    try {
        const shortCircuitSummary = await runPreFreshnessShortCircuits(claimedExecution);
        if (shortCircuitSummary !== null) {
            return shortCircuitSummary;
        }
    }
    catch (error) {
        return (0, failure_recovery_1.handleClaimedIssueFailure)(claimedExecution.issueContext, claimedExecution.claimedIssue, claimedExecution.logger, claimedExecution.failureSeed, error, claimedExecution.workflowLabels);
    }
    return (0, loop_1.runClaimedIssueLoop)({
        context: claimedExecution.issueContext,
        logger: claimedExecution.logger,
        claimedIssue: claimedExecution.claimedIssue,
        workflowLabels: claimedExecution.workflowLabels,
        failureSeed: claimedExecution.failureSeed,
        continuationContext: null,
        dependencies: {
            syncPlannedIssueDependencies,
        },
    });
}
async function prepareIssueExecution(context, executable) {
    const freshnessSnapshot = await (0, freshness_1.loadFreshnessSnapshot)(context.freshTicketTracker, executable.issue.id);
    const issueContext = {
        ...context,
        config: (0, control_plane_config_1.resolveIssueEffectiveConfigFromComments)(context.config, freshnessSnapshot.comments, undefined, {
            issueBody: freshnessSnapshot.latestIssue.body,
        }),
    };
    const snapshot = freshnessSnapshot.latestIssue.workflowState;
    const freshExecutable = snapshot.status === types_2.WORKFLOW_STATUSES.aiRunning
        ? {
            issue: freshnessSnapshot.latestIssue,
            snapshot: (0, shared_1.cloneWorkflowStateSnapshot)(snapshot, types_2.WORKFLOW_STATUSES.aiRunning),
        }
        : (0, types_2.isRunnableWorkflowState)(snapshot)
            ? {
                issue: freshnessSnapshot.latestIssue,
                snapshot,
            }
            : null;
    if (freshExecutable === null) {
        return {
            summary: buildInterruptedStateChangedSummary(issueContext, freshnessSnapshot.latestIssue, null, "run.orchestrator.interrupted.stateChanged.summary"),
        };
    }
    const { logger } = (0, shared_1.buildIssueLogContext)(issueContext.config, freshExecutable.issue, issueContext.logHomeDirectory);
    const branchName = freshnessSnapshot.latestIssue.sourceBranchName ??
        (0, default_issue_workspace_manager_1.resolveIssueBranchName)(issueContext.config.branchPrefix, freshnessSnapshot.latestIssue);
    const executionOwnership = (0, execution_ownership_1.createExecutionOwnership)({
        workerIdentity: issueContext.workerIdentity,
        pollIntervalSec: issueContext.config.pollIntervalSec,
    });
    const preparation = await (0, gate_1.prepareWorkerRun)({
        context: issueContext,
        snapshot: freshnessSnapshot,
    });
    const existingPullRequest = preparation.workflowState === null
        ? null
        : (0, results_1.filterPullRequestByBase)(await issueContext.repositoryHost.findPullRequestByBranch(branchName), (0, issue_branch_target_1.resolveIssueRunBaseBranch)(preparation.workflowState));
    return {
        issueContext,
        freshExecutable,
        logger,
        workflowLabels: issueContext.config.ticketTracker.workflowLabels,
        branchName,
        existingPullRequest,
        executionOwnership,
        preparation,
        scannedInputCursor: (0, shared_1.resolvePreparationScannedInputCursor)(preparation),
    };
}
async function resolvePreClaimExit(preparedExecution) {
    await preparedExecution.logger.appendRunStart({
        runKind: preparedExecution.preparation.kind === "run_worker"
            ? preparedExecution.preparation.workflow.mode
            : preparedExecution.preparation.kind === "requeue_for_replan"
                ? "preflight_replan"
                : preparedExecution.preparation.kind ===
                    "preflight_resolution_required"
                    ? "preflight_resolution"
                    : "preflight_handoff",
        issue: preparedExecution.preparation.latestIssue,
        currentLabels: (0, workflow_labels_1.projectWorkflowLabelNames)(preparedExecution.workflowLabels, preparedExecution.freshExecutable.issue.workflowState),
        pullRequestUrl: preparedExecution.existingPullRequest?.url ?? null,
        workerId: preparedExecution.executionOwnership.workerId,
        runId: preparedExecution.executionOwnership.runId,
    });
    if (preparedExecution.preparation.kind === "handoff_to_human") {
        return {
            summary: await (0, handoffs_1.handOffIssueToHuman)({
                context: preparedExecution.issueContext,
                executable: preparedExecution.freshExecutable,
                logger: preparedExecution.logger,
                gate: preparedExecution.preparation,
                workflowLabels: preparedExecution.workflowLabels,
                dependencies: {
                    publishTransitionSummary: publish_1.publishTransitionSummary,
                },
            }),
        };
    }
    if (preparedExecution.preparation.kind === "requeue_for_replan") {
        return {
            summary: await (0, needs_human_1.publishPreflightReplanTransition)({
                context: preparedExecution.issueContext,
                executable: preparedExecution.freshExecutable,
                logger: preparedExecution.logger,
                branchName: preparedExecution.branchName,
                responseLanguage: preparedExecution.issueContext.config.i18n.defaultLanguage,
                preparation: preparedExecution.preparation,
                dependencies: {
                    collectExistingPullRequestResolution: effects_1.collectExistingPullRequestResolution,
                    publishTransitionSummary: publish_1.publishTransitionSummary,
                },
            }),
        };
    }
    if (preparedExecution.preparation.kind === "preflight_resolution_required") {
        return {
            summary: await (0, preflight_resolution_child_1.resolvePreflightResolutionIssue)({
                issueContext: preparedExecution.issueContext,
                logger: preparedExecution.logger,
                preparation: preparedExecution.preparation,
            }),
        };
    }
    return preparedExecution;
}
async function claimAndConfirmExecution(preparedExecution) {
    const claimedExecutionCursor = (0, recurring_1.hasActiveRecurringParentSchedule)(preparedExecution.preparation.workflowState)
        ? (preparedExecution.preparation.workflowState?.lastScannedCommentCursor ??
            null)
        : preparedExecution.scannedInputCursor;
    const claimedIssue = await (0, claim_workflow_execution_1.claimWorkflowExecution)({
        context: preparedExecution.issueContext,
        executable: {
            issue: preparedExecution.freshExecutable.issue,
            snapshot: preparedExecution.freshExecutable.snapshot,
            publishStartVisibleUpdate: !(0, recurring_1.hasActiveRecurringParentSchedule)(preparedExecution.preparation.workflowState),
        },
        logger: preparedExecution.logger,
        responseLanguage: preparedExecution.issueContext.config.i18n.defaultLanguage,
        comments: preparedExecution.preparation.comments,
        workflowState: preparedExecution.preparation.workflowState,
        planningIntent: preparedExecution.preparation.planningIntent,
        previousConsecutiveAiRuns: preparedExecution.preparation.currentConsecutiveAiRuns,
        currentConsecutiveAiRuns: preparedExecution.preparation.currentConsecutiveAiRuns,
        lastScannedCommentCursor: claimedExecutionCursor,
        executionOwnership: preparedExecution.executionOwnership,
    });
    const claimStillOwned = await (0, claim_workflow_execution_1.confirmClaimedExecutionOwnership)({
        context: preparedExecution.issueContext,
        issueId: claimedIssue.originalIssue.id,
        claimedIssue,
    });
    if (!claimStillOwned) {
        const latestSnapshot = await (0, freshness_1.loadFreshnessSnapshot)(preparedExecution.issueContext.freshTicketTracker, claimedIssue.originalIssue.id);
        return {
            summary: buildInterruptedStateChangedSummary(preparedExecution.issueContext, latestSnapshot.latestIssue, preparedExecution.existingPullRequest?.url ?? null, "run.orchestrator.interrupted.claimed.summary"),
        };
    }
    return {
        ...preparedExecution,
        claimedIssue,
        failureSeed: (0, transitions_1.createFailureSeed)(preparedExecution.issueContext.config, claimedIssue.originalIssue, claimedIssue.previousConsecutiveAiRuns, preparedExecution.preparation.currentTotalAiRunHours, preparedExecution.preparation.workflow.mode ??
            (0, run_state_helpers_1.inferWorkflowRunMode)(preparedExecution.preparation.workflowState), preparedExecution.preparation.workflowTemplateName, preparedExecution.preparation.workflowState),
    };
}
async function runPreFreshnessShortCircuits(claimedExecution) {
    const recurringParentSummary = await (0, recurring_parent_1.maybeHandleRecurringParentIssue)({
        context: claimedExecution.issueContext,
        executable: claimedExecution.freshExecutable,
        workflowState: claimedExecution.preparation.workflowState,
        logger: claimedExecution.logger,
        workflowLabels: claimedExecution.workflowLabels,
        claimedIssue: claimedExecution.claimedIssue,
        assignees: claimedExecution.freshExecutable.issue.assignees,
        pullRequestUrl: claimedExecution.existingPullRequest?.url ?? null,
        scannedInputCursor: claimedExecution.scannedInputCursor,
    });
    if (recurringParentSummary !== null) {
        return recurringParentSummary;
    }
    return maybeHandleControllerChildIssueControlPlane(claimedExecution);
}
async function maybeHandleControllerChildIssueControlPlane(claimedExecution) {
    if (claimedExecution.preparation.latestIssue.kind !== types_1.TRACKER_ITEM_KINDS.issue) {
        return null;
    }
    const workflowState = resolveControllerChildIssueControlPlaneWorkflowState(claimedExecution);
    if (workflowState === null ||
        workflowState.workflowSurface !== "issue" ||
        (0, recurring_1.hasActiveRecurringParentSchedule)(workflowState)) {
        return null;
    }
    return (0, spawn_child_controller_1.publishControllerChildIssues)({
        context: claimedExecution.issueContext,
        claimedIssue: claimedExecution.claimedIssue,
        logger: claimedExecution.logger,
        workflowLabels: claimedExecution.workflowLabels,
        workerExecution: buildControllerChildIssueControlPlaneExecution({
            claimedIssue: claimedExecution.claimedIssue,
            preparation: claimedExecution.preparation,
            responseLanguage: claimedExecution.issueContext.config.i18n.defaultLanguage,
            workflowState,
            branchName: claimedExecution.branchName,
            pullRequestUrl: claimedExecution.existingPullRequest?.url ?? null,
            facts: await resolveControllerChildIssueControlPlaneFacts({
                context: claimedExecution.issueContext,
                issue: claimedExecution.preparation.latestIssue,
                workflowState,
                branchName: claimedExecution.branchName,
                pullRequest: claimedExecution.existingPullRequest,
            }),
            scannedInputCursor: claimedExecution.scannedInputCursor,
        }),
    });
}
function resolveControllerChildIssueControlPlaneWorkflowState(claimedExecution) {
    const preparedWorkflowState = claimedExecution.preparation.workflowState;
    if (preparedWorkflowState?.pendingPlanningIntent === undefined &&
        (0, plan_progress_1.hasControllerChildIssueControlPlaneAction)(preparedWorkflowState)) {
        return preparedWorkflowState;
    }
    const canonicalState = (0, canonical_workflow_state_1.resolveCanonicalWorkflowStateComment)(claimedExecution.preparation.comments, claimedExecution.issueContext.config.i18n.defaultLanguage);
    if (canonicalState.kind === "found" &&
        canonicalState.workflowState.pendingPlanningIntent === undefined &&
        (0, plan_progress_1.hasControllerChildIssueControlPlaneAction)(canonicalState.workflowState)) {
        return canonicalState.workflowState;
    }
    return null;
}
function buildControllerChildIssueControlPlaneExecution(input) {
    return {
        issue: input.claimedIssue.originalIssue,
        workerId: input.claimedIssue.executionOwnership.workerId,
        workerName: `${branding_1.CLI_COMMAND_NAME}-control-plane`,
        authProfileId: "control-plane",
        authProfileKind: "control-plane",
        workspaceHeadCommitOidBeforeRun: "",
        runMode: input.preparation.workflow.mode,
        workerOutcome: constants_1.WORKER_OUTCOMES.proceed,
        result: (0, types_3.buildWorkflowResultFieldsWithDefaults)({
            resultSummary: "Controller child issues reconciled.",
            evidence: [
                "Controller child issue state was reconciled against the workflow plan.",
            ],
        }),
        commitMessage: null,
        consecutiveAiRuns: input.preparation.currentConsecutiveAiRuns,
        responseLanguage: input.responseLanguage,
        facts: input.facts,
        branchName: input.branchName,
        pullRequestUrl: input.pullRequestUrl,
        scannedInputCursor: input.scannedInputCursor,
        workflowTemplateName: input.preparation.workflowTemplateName,
        workflowState: input.workflowState,
        plannedIssueTitle: null,
        mergeContext: input.preparation.workflow.mergeContext ?? null,
        planningIntent: input.preparation.planningIntent,
        workflowStartApproval: null,
        startedWithPlanningIntent: input.preparation.workflowState?.pendingPlanningIntent ?? null,
        startedWithWorkflowStartApprovalIntent: input.preparation.workflowState?.pendingWorkflowStartApprovalIntent ??
            null,
        continuation: null,
        needsHumanReason: null,
        incrementsConsecutiveAiRuns: false,
        recordsRunOutcome: false,
    };
}
async function resolveControllerChildIssueControlPlaneFacts(input) {
    const workspacePath = (0, default_issue_workspace_manager_1.getIssueWorkspacePath)((0, tracked_project_1.toTrackedProjectRef)(input.context.config), input.issue.id, input.context.logHomeDirectory);
    const baseBranch = (0, issue_branch_target_1.resolveIssueRunBaseBranch)(input.workflowState);
    try {
        return (await (0, effects_1.collectRoutingFacts)(input.context, workspacePath, input.branchName, baseBranch)).facts;
    }
    catch {
        return (0, results_1.buildRoutingResolution)(false, (0, results_1.filterPullRequestByBase)(input.pullRequest, baseBranch)).facts;
    }
}
async function syncPlannedIssueDependencies(input) {
    return (0, dependencies_1.syncPlannedIssueDependencies)({
        context: input.context,
        issue: input.issue,
        workflowState: input.workflowState,
        resolvePersistedIssueWorkflowState: workflow_state_read_support_1.resolvePersistedIssueWorkflowState,
        handOffDependencyIssueToHuman: (handoffInput) => (0, handoffs_1.handOffDependencyIssueToHuman)({
            ...handoffInput,
            dependencies: {
                publishTransitionSummary: publish_1.publishTransitionSummary,
            },
        }),
    });
}
function buildInterruptedStateChangedSummary(context, issue, prUrl, summaryKey) {
    return (0, results_1.buildRunCliSummary)({
        issue,
        outcome: constants_2.RUN_ISSUE_OUTCOMES.interrupted,
        responseLanguage: context.config.i18n.defaultLanguage,
        summary: (0, catalog_1.translate)(context.config.i18n.defaultLanguage, summaryKey, {
            displayId: issue.displayId,
        }),
        commentUrl: null,
        prUrl,
        nextStep: (0, catalog_1.translate)(context.config.i18n.defaultLanguage, "run.orchestrator.interrupted.next"),
        interruptReason: "state-changed",
    });
}
