"use strict";
/**
 * LangGraphAdapter -- Orchestrator adapter for the LangGraph framework.
 *
 * Hook surface: LangGraph's interrupt_before / interrupt_after on node
 * entry/exit. The adapter exposes a factory that produces a CheckpointSaver
 * subclass the customer plugs into their StateGraph:
 *
 *   const adapter = new LangGraphAdapter(config);
 *   adapter.initialize(orchConfig);
 *   const graph = workflow.compile({ checkpointer: adapter.toCheckpointSaver() });
 *
 * Edge traversal audit: each edge traversal emits orchestrator:role-edge-invoked
 * at DEBUG severity. Conditional edges include the branching decision in the
 * context payload.
 *
 * Command primitive support: LangGraph's Command (used for supervisor->worker
 * routing) maps onto the DelegationDecision contract. A DelegationDecision is
 * produced per Command invocation.
 *
 * No real @langchain/langgraph or @langchain/langgraph-checkpoint package is
 * installed. This adapter shims the CheckpointSaver abstract shape and can
 * be exercised fully in tests without the live SDK.
 *
 * Spec: P3-ORCH-01, Section 2.5, AC 10-11
 * @connexum/ai-governance
 */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.LangGraphAdapter = exports.LangGraphCheckpointSaver = void 0;
const crypto = __importStar(require("crypto"));
const base_1 = require("./base");
const index_1 = require("./index");
const index_2 = require("../adapters/index");
// ---------------------------------------------------------------------------
// LangGraphCheckpointSaver -- shimmed CheckpointSaver subclass
// ---------------------------------------------------------------------------
/**
 * Shimmed LangGraph CheckpointSaver.
 *
 * Mirrors the abstract CheckpointSaver from
 * @langchain/langgraph-checkpoint as of 2024-2026.
 *
 * Consumers integrate via:
 *   const saver = adapter.toCheckpointSaver();
 *   const graph = workflow.compile({ checkpointer: saver });
 *
 * The saver intercepts put() calls at policy boundaries (interrupt_before /
 * interrupt_after transitions) and dispatches governance checks to the adapter.
 */
class LangGraphCheckpointSaver {
    adapter;
    /** In-memory snapshot store (keyed by checkpointId). Production consumers
     *  replace this with a persistent store via subclassing. */
    snapshots = new Map();
    constructor(adapter) {
        this.adapter = adapter;
    }
    /**
     * Called by the LangGraph runtime before node execution (interrupt_before).
     * Governance check; throws if denied.
     */
    async beforeNode(event) {
        return this.adapter.handleNodeEntry(event);
    }
    /**
     * Called by the LangGraph runtime after node execution (interrupt_after).
     * Audit hook; persists snapshot.
     */
    async afterNode(event) {
        return this.adapter.handleNodeExit(event);
    }
    /**
     * Called when the LangGraph runtime traverses an edge.
     * Emits orchestrator:role-edge-invoked DEBUG event.
     */
    async onEdge(event) {
        return this.adapter.handleEdgeTraversal(event);
    }
    /**
     * Called when the supervisor issues a Command.
     * Maps onto DelegationDecision contract.
     */
    async onCommand(command) {
        return this.adapter.handleCommand(command);
    }
    /**
     * Persist a checkpoint snapshot (shimmed put() method).
     * In production this writes to a durable store.
     */
    async put(snapshot) {
        this.snapshots.set(snapshot.checkpointId, { ...snapshot });
        await this.adapter.handleCheckpointPut(snapshot);
    }
    /**
     * Retrieve a snapshot by checkpoint ID (shimmed get() method).
     */
    async get(checkpointId) {
        return this.snapshots.get(checkpointId);
    }
    /**
     * List all stored checkpoints (shimmed list() method).
     */
    async list() {
        return [...this.snapshots.values()];
    }
}
exports.LangGraphCheckpointSaver = LangGraphCheckpointSaver;
// ---------------------------------------------------------------------------
// LangGraphAdapter
// ---------------------------------------------------------------------------
class LangGraphAdapter extends base_1.BaseOrchestratorAdapter {
    adapterId = 'langgraph';
    orchestratorId = 'langgraph';
    langgraphConfig;
    constructor(config) {
        super();
        this.langgraphConfig = config;
    }
    // ---------------------------------------------------------------------------
    // toCheckpointSaver -- public factory
    // ---------------------------------------------------------------------------
    /**
     * Return a LangGraphCheckpointSaver wired to this adapter.
     * Consumers plug the returned saver into StateGraph.compile().
     */
    toCheckpointSaver() {
        this.assertInitialized();
        return new LangGraphCheckpointSaver(this);
    }
    // ---------------------------------------------------------------------------
    // Internal node lifecycle dispatchers
    // ---------------------------------------------------------------------------
    /**
     * Governance check on node entry (interrupt_before).
     * Dispatches per-role policy check for the node's role.
     * Throws RoleNotRegisteredError if the role is not in the registry.
     */
    async handleNodeEntry(event) {
        this.assertInitialized();
        const auditId = crypto.randomUUID();
        // Circuit breaker
        if (this.isCircuitOpen()) {
            const reason = 'Circuit breaker is open; node entry denied';
            this.emitCanonical('orchestrator:task-denied', {
                runId: event.runId,
                nodeId: event.nodeId,
                role: event.role,
                reason,
            });
            this.recordFailure();
            return { type: index_2.ActionDecisionType.DENY, reason, auditId };
        }
        // Role allowlist check
        if (!this.langgraphConfig.allowedRoles.includes(event.role)) {
            // Unregistered role: emit canonical event and throw
            this.emitCanonical('orchestrator:unregistered-role', {
                runId: event.runId,
                nodeId: event.nodeId,
                role: event.role,
                allowedRoles: this.langgraphConfig.allowedRoles,
            });
            throw new index_1.RoleNotRegisteredError({
                message: `Role "${event.role}" is not registered with LangGraphAdapter. ` +
                    `Allowed roles: [${this.langgraphConfig.allowedRoles.join(', ')}].`,
                role: event.role,
                orchestratorId: this.orchestratorId,
                adapterId: this.adapterId,
            });
        }
        this.emitCanonical('orchestrator:task-approved', {
            runId: event.runId,
            nodeId: event.nodeId,
            role: event.role,
        });
        this.recordSuccess();
        return {
            type: index_2.ActionDecisionType.ALLOW,
            reason: `Node "${event.nodeId}" with role "${event.role}" approved for execution`,
            auditId,
        };
    }
    /**
     * Audit hook on node exit (interrupt_after).
     * Emits orchestrator:plan-step-end. Optionally emits checkpoint event.
     */
    async handleNodeExit(event) {
        this.assertInitialized();
        this.emitCanonical('orchestrator:plan-step-end', {
            runId: event.runId,
            nodeId: event.nodeId,
            role: event.role,
            isFinal: event.isFinal ?? false,
        });
    }
    /**
     * Edge traversal audit.
     * Emits orchestrator:role-edge-invoked DEBUG with branching decision
     * context for conditional edges.
     */
    async handleEdgeTraversal(event) {
        this.assertInitialized();
        const auditId = crypto.randomUUID();
        this.emitCanonical('orchestrator:role-edge-invoked', {
            runId: event.runId,
            threadId: event.threadId,
            fromNodeId: event.fromNodeId,
            toNodeId: event.toNodeId,
            fromRole: event.fromRole,
            toRole: event.toRole,
            isConditional: event.isConditional,
            branchDecision: event.isConditional ? (event.branchDecision ?? null) : undefined,
        });
        return {
            type: 'ALLOW',
            reason: event.isConditional
                ? `Conditional edge ${event.fromRole} -> ${event.toRole} traversed`
                : `Edge ${event.fromRole} -> ${event.toRole} traversed`,
            auditId,
        };
    }
    /**
     * Command (supervisor->worker delegation) governance.
     * Maps LangGraph Command primitive onto DelegationDecision contract.
     * Sensitive command targets require reviewer approval.
     */
    async handleCommand(command) {
        this.assertInitialized();
        const auditId = crypto.randomUUID();
        const isSensitive = this.langgraphConfig.sensitiveCommandTargets.includes(command.goto);
        if (isSensitive) {
            const tier = 'human-reviewer';
            const queueId = this.config.reviewerQueues[tier];
            if (!queueId) {
                // Fail closed
                const reason = `Command to sensitive target "${command.goto}" requires ` +
                    `human-reviewer tier but no queue binding exists`;
                this.emitCanonical('orchestrator:delegation-denied', {
                    sourceNodeId: command.sourceNodeId,
                    goto: command.goto,
                    reason,
                });
                return {
                    type: index_2.ActionDecisionType.DENY,
                    reason,
                    restrictedPermissions: [command.goto],
                    auditId,
                };
            }
            this.emitCanonical('orchestrator:reviewer-routed', {
                sourceNodeId: command.sourceNodeId,
                goto: command.goto,
                tier,
                queueId,
            });
            return {
                type: index_2.ActionDecisionType.REQUIRE_APPROVAL,
                reason: `Command to "${command.goto}" is a sensitive target; routing to ${tier}`,
                auditId,
                metadata: { reviewerTier: tier, command },
            };
        }
        this.emitCanonical('orchestrator:delegation-approved', {
            sourceNodeId: command.sourceNodeId,
            sourceRole: command.sourceRole,
            goto: command.goto,
            targetRole: command.targetRole,
        });
        return {
            type: index_2.ActionDecisionType.ALLOW,
            reason: `Command to "${command.goto}" approved`,
            auditId,
        };
    }
    /**
     * Checkpoint persistence hook.
     * Emits a plan-step-begin event to record the policy boundary snapshot.
     */
    async handleCheckpointPut(snapshot) {
        this.assertInitialized();
        this.emitCanonical('orchestrator:plan-step-begin', {
            checkpointId: snapshot.checkpointId,
            nodeId: snapshot.nodeId,
            threadId: snapshot.threadId,
            stateKeys: Object.keys(snapshot.state),
        });
    }
    // ---------------------------------------------------------------------------
    // OrchestratorAdapter surface -- workflow lifecycle
    // ---------------------------------------------------------------------------
    async startWorkflow(ctx) {
        const auditId = crypto.randomUUID();
        this.emitCanonical('orchestrator:workflow-start', {
            workflowId: ctx.workflowId,
            name: ctx.name,
            initiator: ctx.initiator.name,
        });
        return { type: 'ALLOW', reason: 'LangGraph workflow started', auditId };
    }
    async stepWorkflow(ctx) {
        const auditId = crypto.randomUUID();
        this.emitCanonical('orchestrator:plan-step-begin', {
            workflowId: ctx.workflowId,
            stepId: ctx.stepId,
            stepIndex: ctx.stepIndex,
        });
        if (this.isCircuitOpen()) {
            this.emitCanonical('orchestrator:circuit-breaker-tripped', {
                workflowId: ctx.workflowId,
                stepId: ctx.stepId,
            });
            return { type: 'DENY', reason: 'Circuit breaker is open', auditId };
        }
        return { type: 'ALLOW', reason: 'Step permitted', auditId };
    }
    async completeWorkflow(ctx, result) {
        this.emitCanonical('orchestrator:workflow-complete', {
            workflowId: ctx.workflowId,
            success: result.success,
            summary: result.summary,
        });
    }
    // ---------------------------------------------------------------------------
    // Task / plan / delegation surfaces
    // ---------------------------------------------------------------------------
    async preTask(task) {
        this.assertInitialized();
        const auditId = crypto.randomUUID();
        if (this.isCircuitOpen()) {
            const reason = 'Circuit breaker is open; task denied';
            this.emitCanonical('orchestrator:task-denied', { taskId: task.taskId, reason });
            return { type: index_2.ActionDecisionType.DENY, reason, auditId };
        }
        this.emitCanonical('orchestrator:task-approved', { taskId: task.taskId });
        return {
            type: index_2.ActionDecisionType.ALLOW,
            reason: 'Task approved via LangGraph adapter',
            auditId,
        };
    }
    async postTask(task, result) {
        this.emitCanonical('orchestrator:plan-step-end', { taskId: task.taskId, result });
    }
    async prePlan(plan) {
        this.assertInitialized();
        const auditId = crypto.randomUUID();
        this.emitCanonical('orchestrator:plan-proposed', { planId: plan.planId });
        // Step-count limit check (if configured)
        const maxSteps = this.langgraphConfig.maxPlanSteps;
        if (maxSteps !== undefined && plan.steps.length > maxSteps) {
            const reason = `Plan has ${plan.steps.length} steps, exceeding the maximum of ${maxSteps}`;
            this.emitCanonical('orchestrator:plan-denied', { planId: plan.planId, reason });
            return { type: index_2.ActionDecisionType.DENY, reason, auditId };
        }
        try {
            this.validatePlanStructure(plan);
        }
        catch (err) {
            const reason = err instanceof Error ? err.message : String(err);
            this.emitCanonical('orchestrator:plan-denied', { planId: plan.planId, reason });
            return { type: index_2.ActionDecisionType.DENY, reason, auditId };
        }
        this.emitCanonical('orchestrator:plan-approved', { planId: plan.planId });
        return { type: index_2.ActionDecisionType.ALLOW, reason: 'Plan approved', auditId };
    }
    async postPlan(plan, result) {
        this.emitCanonical('orchestrator:workflow-complete', { planId: plan.planId, result });
    }
    async preDelegation(d) {
        this.assertInitialized();
        const auditId = crypto.randomUUID();
        const restricted = d.permissions.filter((p) => this.langgraphConfig.sensitiveCommandTargets.includes(p));
        if (restricted.length > 0) {
            const reason = `Delegation includes sensitive targets: ${restricted.join(', ')}`;
            this.emitCanonical('orchestrator:delegation-denied', {
                delegationId: d.delegationId,
                from: d.fromAgent.name,
                to: d.toAgent.name,
                restricted,
            });
            return {
                type: index_2.ActionDecisionType.DENY,
                reason,
                restrictedPermissions: restricted,
                auditId,
            };
        }
        this.emitCanonical('orchestrator:delegation-approved', {
            delegationId: d.delegationId,
            from: d.fromAgent.name,
            to: d.toAgent.name,
        });
        return { type: index_2.ActionDecisionType.ALLOW, reason: 'Delegation approved', auditId };
    }
    async postDelegation(d, result) {
        this.emitCanonical('orchestrator:delegation-approved', {
            delegationId: d.delegationId,
            result,
        });
    }
    async invokeRoleEdge(edge) {
        this.assertInitialized();
        const auditId = crypto.randomUUID();
        this.emitCanonical('orchestrator:role-edge-invoked', {
            edgeId: edge.edgeId,
            fromRole: edge.fromRole,
            toRole: edge.toRole,
        });
        return { type: 'ALLOW', reason: 'Role edge permitted', auditId };
    }
    // ---------------------------------------------------------------------------
    // Error translation
    // ---------------------------------------------------------------------------
    translateError(raw) {
        if (raw instanceof index_1.OrchestratorError)
            return raw;
        const message = raw instanceof Error ? raw.message : String(raw);
        if (message.includes('GraphRecursionError') || message.includes('recursion')) {
            return new index_1.OrchestratorError({
                message: `LangGraph recursion limit exceeded: ${message}`,
                code: 'LANGGRAPH_RECURSION_LIMIT',
                orchestratorId: this.orchestratorId,
                adapterId: this.adapterId,
                raw,
            });
        }
        if (message.includes('NodeInterrupt') || message.includes('interrupt')) {
            return new index_1.OrchestratorError({
                message: `LangGraph node interrupt: ${message}`,
                code: 'LANGGRAPH_NODE_INTERRUPT',
                orchestratorId: this.orchestratorId,
                adapterId: this.adapterId,
                raw,
            });
        }
        if (message.includes('InvalidUpdateError') || message.includes('InvalidUpdate')) {
            return new index_1.OrchestratorError({
                message: `LangGraph invalid state update: ${message}`,
                code: 'LANGGRAPH_INVALID_UPDATE',
                orchestratorId: this.orchestratorId,
                adapterId: this.adapterId,
                raw,
            });
        }
        return new index_1.OrchestratorError({
            message,
            code: 'LANGGRAPH_ERROR',
            orchestratorId: this.orchestratorId,
            adapterId: this.adapterId,
            raw,
        });
    }
    // ---------------------------------------------------------------------------
    // Capabilities
    // ---------------------------------------------------------------------------
    getOrchestratorCapabilities() {
        return {
            workflowGovernance: true,
            taskGovernance: true,
            planGovernance: true,
            delegationGovernance: true,
            roleEdgeGovernance: true,
            parallelExecution: true,
            nestedPlans: true,
            circuitBreaker: true,
        };
    }
}
exports.LangGraphAdapter = LangGraphAdapter;
//# sourceMappingURL=langgraph.js.map