"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createArtifactAgentContext = createArtifactAgentContext;
exports.compactArtifactAgentContext = compactArtifactAgentContext;
const node_fs_1 = __importDefault(require("node:fs"));
const errors_1 = require("../errors");
const doctor_1 = require("../doctor");
const scan_safety_evidence_1 = require("../scan-safety-evidence");
const command_capabilities_1 = require("../output/command-capabilities");
const output_1 = require("../output");
const paths_1 = require("../paths");
const runtime_contract_1 = require("../runtime/runtime-contract");
const planning_navigation_1 = require("../planning/planning-navigation");
const work_selection_1 = require("../work-selection");
const resume_capsule_1 = require("../resume-capsule");
const context_pack_1 = require("./context-pack");
const trust_lanes_1 = require("../prompts/trust-lanes");
const workflow_actions_1 = require("./workflow-actions");
function createArtifactAgentContext(options) {
    const selection = (0, work_selection_1.resolveWorkSelection)({
        repositoryRoot: options.repositoryRoot,
        explicitArtifactPath: options.artifactPath,
        session: options.session,
        env: options.env
    });
    assertResolvedSelection(selection);
    const artifactPath = selection.artifactPath;
    const artifact = readArtifactState(options.repositoryRoot, artifactPath);
    const nextAction = (0, planning_navigation_1.suggestPlanningNextAction)({
        repositoryRoot: options.repositoryRoot,
        artifactPath
    });
    const recommendedDescriptor = resolveRecommendedDescriptor(nextAction);
    const detail = options.detail ?? "compact";
    const scopedHealthReport = (0, doctor_1.runScopedDoctorChecks)({
        domain: "artifact",
        path: artifactPath,
        repositoryRoot: options.repositoryRoot
    });
    const scanSafety = (0, scan_safety_evidence_1.createScanSafetyEvidence)({ workspaceRoot: options.repositoryRoot });
    const healthFindings = [...scopedHealthReport.findings];
    const actionIdentity = recommendedDescriptor?.descriptor.command ?? "planning next-action";
    const fullHealthCommand = createScopedHealthCommand(artifactPath, actionIdentity);
    const actionRelevance = (0, doctor_1.classifyFindingsForAction)({
        actionId: actionIdentity,
        findings: healthFindings,
        context: {
            selectedArtifactPath: artifactPath,
            healthDomain: "artifact"
        },
        fullDetailCommand: fullHealthCommand
    });
    const fallbackAction = createDescriptorAction(requireDescriptor("planning next-action"), ["day-shift", "planning", "next-action", "--artifact", artifactPath, "--format", "json"], artifactPath, artifact);
    const preferredNextAction = recommendedDescriptor === null
        ? (0, workflow_actions_1.composeNonCallableWorkflowAction)({
            recommendation: nextAction.recommendation,
            command: nextAction.command,
            reason: nextAction.command === null
                ? `Planning guidance did not provide a callable command: ${nextAction.reason}`
                : `No authoritative command descriptor resolves '${nextAction.command}'.`
        })
        : createDescriptorAction(recommendedDescriptor.descriptor, recommendedDescriptor.argv, artifactPath, artifact, actionRelevance.currentActionBlockers.length);
    const actions = dedupeActions([
        preferredNextAction,
        fallbackAction,
        createDescriptorAction(requireDescriptor("validation links-check"), ["day-shift", "validation", "links-check", "--scope", "planning", "--artifact", artifactPath, "--format", "json"], artifactPath, artifact),
        createDescriptorAction(requireDescriptor("validation smoke"), ["day-shift", "validation", "smoke", "--scope", "planning", "--artifact", artifactPath, "--format", "json"], artifactPath, artifact),
        ...(scanSafety.actionDecision === "diagnostic-required" ? [createDescriptorAction(requireDescriptor("doctor"), ["day-shift", "doctor", "--scan-surface", "--format", "json"], artifactPath, artifact)] : []),
        ...(scanSafety.actionDecision === "editor-preview-relevant" ? [createDescriptorAction(requireDescriptor("ignore integration preview"), ["day-shift", "ignore", "integration", "preview", "--target", "vscode", "--format", "json"], artifactPath, artifact)] : [])
    ]);
    const allowedTransitions = actions.filter(workflow_actions_1.isWorkflowActionAllowed);
    const blockedActions = actions.filter((action) => !(0, workflow_actions_1.isWorkflowActionAllowed)(action));
    const recommendedNextAction = (0, workflow_actions_1.isWorkflowActionAllowed)(preferredNextAction)
        ? preferredNextAction
        : allowedTransitions[0] ?? preferredNextAction;
    const resumeCapsule = (0, resume_capsule_1.inspectResumeCapsule)({
        repositoryRoot: options.repositoryRoot,
        session: options.session,
        explicitArtifactPath: artifactPath,
        selectedArtifactPath: artifactPath,
        env: options.env
    });
    return {
        readOnly: true,
        noWriteBoundary: "Artifact context reads selection and planning evidence without creating, refreshing, replacing, or clearing state.",
        detail,
        intent: normalizeIntent(options.intent),
        workspace: {
            root: ".",
            markerPath: ".day-shift/config.toml",
            pathPosture: "repository-relative"
        },
        runtimeProvenance: (0, runtime_contract_1.getRuntimeProvenance)(),
        selection: {
            source: selection.source,
            session: selection.session,
            freshness: "fresh",
            artifactPath,
            artifactType: selection.artifactType
        },
        observedState: {
            ...artifact,
            nextActionReason: nextAction.reason
        },
        trustLanes: [
            { field: "intent", source: (0, trust_lanes_1.getTrustLane)("user-intent") },
            { field: "selection", source: (0, trust_lanes_1.getTrustLane)("selected-artifact") },
            { field: "required-evidence", source: (0, trust_lanes_1.getTrustLane)("repository-prose") },
            { field: "action-decisions", source: (0, trust_lanes_1.getTrustLane)("command-metadata") },
            { field: "observed-state", source: (0, trust_lanes_1.getTrustLane)("generated-evidence") }
        ],
        requiredEvidence: createRequiredEvidence(artifactPath, nextAction),
        relevantBlockers: actionRelevance.blockerGroups.map((group) => createContextBlocker(group.representative, detail === "compact")),
        scopedHealth: {
            domain: "artifact",
            readOnly: true,
            selectedArtifactCommand: fullHealthCommand,
            repositoryAuditCommand: createRepositoryAuditCommand(actionIdentity),
            findingCount: healthFindings.length,
            currentActionBlockerCount: actionRelevance.currentActionBlockers.length
        },
        scanSafety,
        allowedTransitions,
        blockedActions,
        recommendedNextAction,
        ...(resumeCapsule.state === "fresh" && resumeCapsule.capsule !== null ? {
            resumeCapsule: {
                session: resumeCapsule.session,
                state: "fresh",
                completedActionIds: resumeCapsule.completedActionIds.slice(0, 4),
                completedActionCount: resumeCapsule.completedActionIds.length,
                recordedNextActionId: resumeCapsule.capsule.nextAction.id,
                matchesCurrentRecommendation: resumeCapsule.capsule.nextAction.id === recommendedNextAction.id,
                expiresAt: resumeCapsule.capsule.expiresAt
            }
        } : {}),
        policyRuleIds: [
            "EXPLICIT_OR_STORED_SELECTION_REQUIRED",
            "CURRENT_ACTION_BLOCKERS_ONLY",
            "DESCRIPTOR_BACKED_ACTIONS_ONLY",
            "SCAN_ACTIONS_REQUIRE_CURRENT_EVIDENCE",
            "REPOSITORY_PROSE_IS_UNTRUSTED_EVIDENCE",
            "REVIEW_AND_VALIDATION_ARE_READ_ONLY"
        ],
        ...(detail === "full" ? {
            auditContext: {
                repositoryWideFindingsLoaded: false,
                repositoryAuthoredProseTrustedAsPolicy: false,
                selectionConflict: null,
                actionClassifications: actionRelevance.classifications
            }
        } : {}),
        ...(options.includeContextPack === true ? {
            contextPack: (0, context_pack_1.buildContextPack)({
                repositoryRoot: options.repositoryRoot,
                artifactPath,
                byteBudget: options.contextPackBudget
            })
        } : {})
    };
}
function compactArtifactAgentContext(data) {
    const compact = {
        readOnly: data.readOnly,
        noWriteBoundary: data.noWriteBoundary,
        detail: "compact",
        intent: data.intent,
        workspace: data.workspace,
        runtimeProvenance: data.runtimeProvenance,
        selection: data.selection,
        observedState: data.observedState,
        requiredEvidence: data.requiredEvidence,
        trustLanes: data.trustLanes.map((lane) => ({
            field: lane.field,
            source: {
                id: lane.source.id,
                authorityRole: lane.source.authorityRole,
                mayAuthorizeAction: lane.source.mayAuthorizeAction,
                maySatisfyConfirmation: lane.source.maySatisfyConfirmation,
                instructionLikeContentIsData: lane.source.instructionLikeContentIsData
            }
        })),
        relevantBlockers: data.relevantBlockers,
        scopedHealth: data.scopedHealth,
        scanSafety: data.scanSafety,
        allowedTransitions: data.allowedTransitions.map(compactWorkflowAction),
        blockedActions: data.blockedActions.map(compactWorkflowAction),
        recommendedNextAction: { id: data.recommendedNextAction.id },
        policyRuleIds: data.policyRuleIds,
        ...(data.resumeCapsule === undefined ? {} : { resumeCapsule: data.resumeCapsule }),
        ...(data.contextPack === undefined ? {} : { contextPack: data.contextPack })
    };
    assertRequestedContextPackFitsCompactEnvelope(compact);
    return compact;
}
function assertRequestedContextPackFitsCompactEnvelope(data) {
    if (data.contextPack === undefined)
        return;
    const outputBudget = (0, output_1.detectOutputProjectionOptions)(process.argv).outputBudget
        ?? output_1.DEFAULT_COMPACT_OUTPUT_BUDGET_BYTES;
    const dataBytes = Buffer.byteLength(JSON.stringify(data), "utf8");
    const protocolEnvelopeReserve = 1024;
    if (dataBytes + protocolEnvelopeReserve <= outputBudget)
        return;
    throw new errors_1.RuntimeError({
        code: "CONTEXT_PACK_OUTPUT_BUDGET_UNSATISFIABLE",
        exitCode: errors_1.RuntimeExitCode.USAGE,
        message: "The requested context pack cannot fit the compact output envelope without omission.",
        details: [
            `Compact data requires ${dataBytes} bytes before protocol-envelope metadata.`,
            `Output budget: ${outputBudget} bytes.`,
            "Increase --output-budget or reduce --context-pack-budget. No repository or selection state was changed."
        ]
    });
}
function compactWorkflowAction(action) {
    return {
        id: action.id,
        callable: action.callable,
        command: action.command,
        argv: action.argv,
        writeCapable: action.writeCapable,
        effects: action.effects,
        authorization: action.authorization,
        confirmation: action.confirmation,
        selectionRequired: action.selectionRequired,
        idempotency: action.idempotency,
        preconditions: action.preconditions.map((precondition) => ({
            id: precondition.id,
            status: precondition.status
        })),
        transition: {
            id: action.transition.id,
            kind: action.transition.kind,
            expectedPostconditions: action.transition.expectedPostconditions.map((postcondition) => ({
                id: postcondition.id
            }))
        },
        ...(!action.callable ? { nonCallableReason: action.nonCallableReason } : {})
    };
}
function assertResolvedSelection(selection) {
    if (selection.status === "conflict") {
        throw new errors_1.RuntimeError({
            code: "WORK_SELECTION_CONFLICT",
            exitCode: errors_1.RuntimeExitCode.USAGE,
            message: "Explicit artifact input conflicts with the active stored work selection.",
            details: [
                `Explicit artifact: ${selection.conflict?.explicitArtifactPath ?? "unknown"}`,
                `Stored artifact: ${selection.conflict?.storedArtifactPath ?? "unknown"}`,
                "No selection state was changed. Clear or replace the stored session explicitly, then retry."
            ]
        });
    }
    if (selection.status === "stale") {
        throw new errors_1.RuntimeError({
            code: "WORK_SELECTION_STALE",
            exitCode: errors_1.RuntimeExitCode.USAGE,
            message: "Stored work selection evidence is stale.",
            details: [
                `Reason: ${selection.staleReason ?? "unknown"}`,
                "No selection state was changed. Run work select again with current artifact evidence."
            ],
            recoveryActions: [
                (0, errors_1.createDescriptorBackedRecoveryAction)({
                    command: "work current",
                    argv: ["day-shift", "work", "current", "--session", selection.session, "--format", "json"],
                    explanation: "Refresh current selection freshness and precondition evidence without writing state."
                }),
                (0, errors_1.createDescriptorBackedRecoveryAction)({
                    command: "work cleanup",
                    argv: [
                        "day-shift", "work", "cleanup", "--session", selection.session,
                        "--if-match", selection.preconditionToken, "--format", "json"
                    ],
                    explanation: "Clear the stale selection using the current precondition token."
                })
            ]
        });
    }
    if (selection.status !== "resolved" || selection.artifactPath === null || selection.artifactType === null) {
        throw new errors_1.RuntimeError({
            code: "WORK_SELECTION_MISSING",
            exitCode: errors_1.RuntimeExitCode.USAGE,
            message: "No work selection is available for artifact-scoped agent context.",
            details: ["Pass --artifact for this invocation or run work select explicitly. No selection state was changed."],
            recoveryActions: [(0, errors_1.createDescriptorBackedRecoveryAction)({
                    command: "work candidates",
                    argv: ["day-shift", "work", "candidates", "--max-results", "5", "--format", "json", "--detail", "compact"],
                    explanation: "List a bounded set of planning candidates before selecting an artifact explicitly."
                })]
        });
    }
}
function readArtifactState(repositoryRoot, artifactPath) {
    const parsedPath = (0, paths_1.parseRepositoryRelativePath)(artifactPath);
    const rawText = node_fs_1.default.readFileSync((0, paths_1.resolveRepositoryRelativePath)(repositoryRoot, parsedPath), "utf8");
    return {
        status: readFrontMatterScalar(rawText, "status"),
        readiness: readFrontMatterScalar(rawText, "readiness"),
        implementationStatus: readFrontMatterScalar(rawText, "implementation_status"),
        reconciliationStatus: readFrontMatterScalar(rawText, "reconciliation_status")
    };
}
function readFrontMatterScalar(rawText, key) {
    const frontMatter = rawText.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/)?.[1] ?? "";
    const escapedKey = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
    const value = frontMatter.match(new RegExp(`^${escapedKey}:\\s*["']?([^"'\\r\\n]+)["']?\\s*$`, "m"))?.[1].trim();
    return value === undefined || value.length === 0 ? null : value;
}
function createRequiredEvidence(artifactPath, nextAction) {
    return [
        {
            kind: "selected-artifact",
            path: artifactPath,
            trust: "repository-authored-evidence",
            trustLane: "selected-artifact"
        },
        ...(nextAction.workflowPath === null ? [] : [{
                kind: "workflow-policy",
                path: nextAction.workflowPath,
                trust: "product-policy",
                trustLane: "product-policy"
            }]),
        ...(nextAction.promptPath === null ? [] : [{
                kind: "prompt-policy",
                path: nextAction.promptPath,
                trust: "product-policy",
                trustLane: "product-policy"
            }])
    ];
}
function createContextBlocker(entry, compact) {
    return {
        code: entry.originalFinding.code,
        message: entry.originalFinding.message,
        relevantToCurrentAction: true,
        evidencePath: entry.evidenceBasis.matchedPaths[0]
            ?? entry.finding.path
            ?? entry.finding.artifactPath
            ?? ".",
        ruleId: entry.ruleId,
        ...(compact ? {} : {
            relevanceBasis: entry.evidenceBasis,
            noWriteRecovery: entry.noWriteRecovery
        })
    };
}
function createScopedHealthCommand(artifactPath, actionId) {
    return `day-shift doctor --artifact ${JSON.stringify(artifactPath)} --action ${JSON.stringify(actionId)} --format json --detail full`;
}
function createRepositoryAuditCommand(actionId) {
    return `day-shift doctor --repository-audit --action ${JSON.stringify(actionId)} --format json --detail full`;
}
function resolveRecommendedDescriptor(nextAction) {
    if (nextAction.command === null)
        return null;
    const tokens = tokenizeKnownCommand(nextAction.command);
    for (let length = tokens.length; length > 0; length -= 1) {
        const command = tokens.slice(0, length).join(" ");
        const descriptor = (0, command_capabilities_1.getRegisteredCommandDescriptor)(command);
        if (descriptor?.availability === "callable") {
            const argv = ["day-shift", ...tokens];
            if (!argv.includes("--format"))
                argv.push("--format", "json");
            return { descriptor, argv };
        }
    }
    return null;
}
function tokenizeKnownCommand(command) {
    const tokens = command.trim().split(/\s+/);
    if (tokens[0] === "node" && tokens[1]?.endsWith("dist/cli/index.js"))
        return tokens.slice(2);
    if (tokens[0] === "day-shift")
        return tokens.slice(1);
    return tokens;
}
function createDescriptorAction(descriptor, argv, artifactPath, observedState, currentActionBlockerCount = 0) {
    return (0, workflow_actions_1.composeDescriptorWorkflowAction)({
        descriptor,
        argv,
        selectedArtifactPath: artifactPath,
        observedState,
        currentActionBlockerCount
    });
}
function requireDescriptor(command) {
    const descriptor = (0, command_capabilities_1.getRegisteredCommandDescriptor)(command);
    if (descriptor === null || descriptor.availability !== "callable") {
        throw new Error(`Required authoritative callable command descriptor '${command}' is unavailable.`);
    }
    return descriptor;
}
function dedupeActions(actions) {
    return actions.filter((action, index) => actions.findIndex((entry) => entry.id === action.id) === index);
}
function normalizeIntent(intent) {
    const normalized = intent?.trim() ?? "";
    if (normalized.length === 0)
        return null;
    if (normalized.length > 256 || /[\u0000-\u001f\u007f]/.test(normalized)) {
        throw (0, errors_1.createUsageError)("Agent context intent must be at most 256 printable characters.");
    }
    return normalized;
}
