"use strict";
/**
 * CrewAIAdapter -- Orchestrator adapter for the CrewAI framework.
 *
 * Hook surface: CrewAI Enterprise REST + webhook events. The adapter exposes
 * a webhookHandler(event) method that customers wire into their webhook
 * endpoint. Internally the handler maps each event type to the matching
 * BaseOrchestratorAdapter lifecycle method.
 *
 * Supported webhook event types:
 *   start_crew        -> startWorkflow
 *   complete_crew     -> completeWorkflow
 *   before_task       -> preTask
 *   after_task        -> postTask
 *
 * Hierarchical delegation: CrewAI's Process.hierarchical pattern (manager
 * dispatching to worker or reviewer roles) maps onto the DelegationDecision
 * contract via preDelegation.
 *
 * Role mapping: manager / worker / reviewer are canonical CrewAI roles.
 * They map onto AgentIdentity.role. An unregistered role triggers:
 *   - orchestrator:unregistered-role CRITICAL audit event
 *   - RoleNotRegisteredError thrown at the dispatch point
 *
 * DEFERRED-CREWAI-WEBHOOK-SIG: Verify CrewAI Enterprise webhook signature
 * scheme (HMAC header, algorithm, tolerance window) at implementation time.
 * This adapter omits signature verification until the scheme is confirmed.
 * Customers must validate signatures in the outer HTTP handler layer.
 *
 * No live CrewAI SDK installed. All framework surfaces are shims.
 * Tests use fake webhook payloads only.
 *
 * Spec: P3-ORCH-01, Section 2.3, AC 6-7
 * @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.CrewAIAdapter = void 0;
const crypto = __importStar(require("crypto"));
const base_1 = require("./base");
const index_1 = require("./index");
const index_2 = require("../adapters/index");
// ---------------------------------------------------------------------------
// CrewAIAdapter
// ---------------------------------------------------------------------------
class CrewAIAdapter extends base_1.BaseOrchestratorAdapter {
    adapterId = 'crewai';
    orchestratorId = 'crewai';
    crewaiConfig;
    /** Track active crew run IDs and their start contexts for completeWorkflow. */
    activeRuns = new Map();
    constructor(config) {
        super();
        this.crewaiConfig = config;
    }
    // ---------------------------------------------------------------------------
    // webhookHandler -- primary integration point
    // ---------------------------------------------------------------------------
    /**
     * Entry point for CrewAI Enterprise webhook events.
     *
     * Customers wire this method into their webhook HTTP handler:
     *
     *   app.post('/crewai/webhook', async (req, res) => {
     *     await adapter.webhookHandler(req.body as CrewAIWebhookEvent);
     *     res.sendStatus(200);
     *   });
     *
     * The method maps each event type to the corresponding lifecycle hook and
     * throws a typed OrchestratorError on governance denial.
     *
     * DEFERRED-CREWAI-WEBHOOK-SIG: signature verification is deferred.
     * Customers must validate HMAC signatures in the outer HTTP handler.
     */
    async webhookHandler(event) {
        this.assertInitialized();
        switch (event.eventType) {
            case 'start_crew':
                await this.handleStartCrew(event);
                break;
            case 'complete_crew':
                await this.handleCompleteCrew(event);
                break;
            case 'before_task':
                await this.handleBeforeTask(event);
                break;
            case 'after_task':
                await this.handleAfterTask(event);
                break;
            default: {
                // Fail closed: unknown event types are rejected with an audit event
                const unknown = event;
                this.emitCanonical('orchestrator:framework-dispatch', {
                    crewRunId: unknown.crewRunId,
                    eventType: unknown.eventType,
                    reason: `Unknown CrewAI webhook event type "${unknown.eventType}" -- denied`,
                });
                throw new index_1.OrchestratorError({
                    message: `Unknown CrewAI webhook event type: "${unknown.eventType}". ` +
                        'Only start_crew, complete_crew, before_task, after_task are supported.',
                    code: 'CREWAI_UNKNOWN_EVENT_TYPE',
                    orchestratorId: this.orchestratorId,
                    adapterId: this.adapterId,
                });
            }
        }
    }
    // ---------------------------------------------------------------------------
    // Internal event handlers
    // ---------------------------------------------------------------------------
    async handleStartCrew(event) {
        const auditId = crypto.randomUUID();
        if (this.isCircuitOpen()) {
            const reason = 'Circuit breaker is open; crew start denied';
            this.emitCanonical('orchestrator:task-denied', {
                crewRunId: event.crewRunId,
                crewName: event.crewName,
                reason,
            });
            this.recordFailure();
            throw new index_1.OrchestratorError({
                message: reason,
                code: 'CREWAI_CIRCUIT_OPEN',
                orchestratorId: this.orchestratorId,
                adapterId: this.adapterId,
            });
        }
        // Crew name allowlist
        if (!this.crewaiConfig.allowedCrewNames.includes(event.crewName)) {
            const reason = `Crew "${event.crewName}" is not in the allowed crew list`;
            this.emitCanonical('orchestrator:task-denied', {
                crewRunId: event.crewRunId,
                crewName: event.crewName,
                reason,
            });
            this.recordFailure();
            throw new index_1.OrchestratorError({
                message: reason,
                code: 'CREWAI_CREW_NOT_ALLOWED',
                orchestratorId: this.orchestratorId,
                adapterId: this.adapterId,
            });
        }
        // Validate manager role is registered
        this.assertRoleRegistered(event.managerRole, event.crewRunId);
        const wfCtx = {
            workflowId: event.crewRunId,
            name: event.crewName,
            initiator: {
                name: event.managerAgent,
                role: event.managerRole,
                sessionId: event.crewRunId,
            },
            timestamp: event.timestamp,
            metadata: {
                hierarchical: event.hierarchical,
                ...event.metadata,
            },
        };
        this.activeRuns.set(event.crewRunId, wfCtx);
        const decision = await this.startWorkflow(wfCtx);
        if (decision.type !== 'ALLOW') {
            this.recordFailure();
            throw new index_1.OrchestratorError({
                message: `Crew start denied: ${decision.reason}`,
                code: 'CREWAI_START_DENIED',
                orchestratorId: this.orchestratorId,
                adapterId: this.adapterId,
            });
        }
        this.recordSuccess();
        return decision;
    }
    async handleCompleteCrew(event) {
        const wfCtx = this.activeRuns.get(event.crewRunId) ?? {
            workflowId: event.crewRunId,
            name: event.crewName,
            initiator: { name: 'crewai', sessionId: event.crewRunId },
            timestamp: event.timestamp,
        };
        const result = {
            success: event.success,
            summary: event.summary,
            durationMs: event.durationMs,
        };
        await this.completeWorkflow(wfCtx, result);
        this.activeRuns.delete(event.crewRunId);
    }
    async handleBeforeTask(event) {
        const auditId = crypto.randomUUID();
        // Validate agent role is registered
        this.assertRoleRegistered(event.agentRole, event.crewRunId);
        // Handle delegation path (hierarchical mode)
        if (event.isDelegated && event.delegatingAgent && event.delegatingRole) {
            // Validate delegating role
            this.assertRoleRegistered(event.delegatingRole, event.crewRunId);
            const delegationCtx = {
                delegationId: `${event.crewRunId}-${event.taskId}-del`,
                fromAgent: {
                    name: event.delegatingAgent,
                    role: event.delegatingRole,
                    sessionId: event.crewRunId,
                },
                toAgent: {
                    name: event.agentName,
                    role: event.agentRole,
                    sessionId: event.crewRunId,
                },
                task: event.taskDescription,
                permissions: [event.agentRole],
                timestamp: new Date(event.timestamp),
            };
            const delDecision = await this.preDelegation(delegationCtx);
            if (delDecision.type === index_2.ActionDecisionType.DENY) {
                this.recordFailure();
                throw new index_1.OrchestratorError({
                    message: `Hierarchical delegation denied: ${delDecision.reason}`,
                    code: 'CREWAI_DELEGATION_DENIED',
                    orchestratorId: this.orchestratorId,
                    adapterId: this.adapterId,
                });
            }
        }
        const taskCtx = {
            taskId: event.taskId,
            taskType: event.agentRole,
            description: event.taskDescription,
            assignedAgent: {
                name: event.agentName,
                role: event.agentRole,
                sessionId: event.crewRunId,
            },
            input: { crewRunId: event.crewRunId },
            timestamp: new Date(event.timestamp),
        };
        const decision = await this.preTask(taskCtx);
        if (decision.type === index_2.ActionDecisionType.DENY) {
            this.recordFailure();
            throw new index_1.OrchestratorError({
                message: `Task before_task denied: ${decision.reason}`,
                code: 'CREWAI_TASK_DENIED',
                orchestratorId: this.orchestratorId,
                adapterId: this.adapterId,
            });
        }
        this.recordSuccess();
        return decision;
    }
    async handleAfterTask(event) {
        const taskCtx = {
            taskId: event.taskId,
            taskType: event.agentRole,
            description: '',
            assignedAgent: {
                name: event.agentName,
                role: event.agentRole,
                sessionId: event.crewRunId,
            },
            input: {},
            timestamp: new Date(event.timestamp),
        };
        await this.postTask(taskCtx, {
            success: event.success,
            output: event.output ?? {},
        });
    }
    /**
     * Assert a CrewAI role is registered with this adapter.
     * Emits orchestrator:unregistered-role CRITICAL and throws
     * RoleNotRegisteredError if not registered.
     */
    assertRoleRegistered(role, crewRunId) {
        if (!this.roles.has(role)) {
            this.emitCanonical('orchestrator:unregistered-role', {
                role,
                crewRunId,
                registeredRoles: [...this.roles.keys()],
            });
            throw new index_1.RoleNotRegisteredError({
                message: `CrewAI role "${role}" is not registered with CrewAIAdapter. ` +
                    `Registered roles: [${[...this.roles.keys()].join(', ')}]. ` +
                    'Register the role in OrchestratorAdapterConfig.roles before running the crew.',
                role,
                orchestratorId: this.orchestratorId,
                adapterId: this.adapterId,
            });
        }
    }
    // ---------------------------------------------------------------------------
    // 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: 'CrewAI crew start approved', 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,
            durationMs: result.durationMs,
        });
    }
    // ---------------------------------------------------------------------------
    // 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,
            taskType: task.taskType,
            agentName: task.assignedAgent.name,
        });
        return {
            type: index_2.ActionDecisionType.ALLOW,
            reason: `Task "${task.taskId}" approved via CrewAIAdapter`,
            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 });
        const maxSteps = this.crewaiConfig.maxTaskSteps;
        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.crewaiConfig.sensitivePermissions.includes(p));
        if (restricted.length > 0) {
            const reason = `Hierarchical delegation includes restricted permissions: ${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,
            fromRole: d.fromAgent.role,
            to: d.toAgent.name,
            toRole: d.toAgent.role,
        });
        return {
            type: index_2.ActionDecisionType.ALLOW,
            reason: 'Hierarchical 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: 'CrewAI 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('crew') && message.includes('not found')) {
            return new index_1.OrchestratorError({
                message: `CrewAI crew not found: ${message}`,
                code: 'CREWAI_CREW_NOT_FOUND',
                orchestratorId: this.orchestratorId,
                adapterId: this.adapterId,
                raw,
            });
        }
        if (message.includes('timeout') || message.includes('Timeout')) {
            return new index_1.OrchestratorError({
                message: `CrewAI run timeout: ${message}`,
                code: 'CREWAI_TIMEOUT',
                orchestratorId: this.orchestratorId,
                adapterId: this.adapterId,
                raw,
            });
        }
        return new index_1.OrchestratorError({
            message,
            code: 'CREWAI_ERROR',
            orchestratorId: this.orchestratorId,
            adapterId: this.adapterId,
            raw,
        });
    }
    // ---------------------------------------------------------------------------
    // Capabilities
    // ---------------------------------------------------------------------------
    getOrchestratorCapabilities() {
        return {
            workflowGovernance: true,
            taskGovernance: true,
            planGovernance: true,
            delegationGovernance: true,
            roleEdgeGovernance: true,
            parallelExecution: false,
            nestedPlans: true,
            circuitBreaker: true,
        };
    }
}
exports.CrewAIAdapter = CrewAIAdapter;
//# sourceMappingURL=crewai.js.map