/**
 * OpenAIAgentsAdapter -- Orchestrator adapter for the OpenAI Agents SDK.
 *
 * Hook surface: OpenAI Agents SDK Runner.run lifecycle. The adapter exposes
 * a wrapRunner(runner) method that returns a governance-wrapped runner.
 * Customers substitute their runner with the wrapped version:
 *
 *   const adapter = new OpenAIAgentsAdapter(config);
 *   adapter.initialize(orchConfig);
 *   const result = await adapter.wrapRunner(runner).run(agent, input);
 *
 * Handoff governance: The Agents SDK's agent-to-agent handoff is a
 * first-class primitive. Handoffs map onto the DelegationDecision contract.
 * A governance check fires at each handoff boundary before the SDK routes
 * control to the target agent.
 *
 * Tool-call governance: Distinct from the OpenAI LLM provider adapter
 * (src/llm-adapters/openai.ts). The Agents SDK adapter fires at handoff and
 * plan boundaries. The provider adapter fires at the model call level.
 * Both emit to the same audit chain with distinct event types:
 *   - Agents SDK layer: 'orchestrator:delegation-approved/denied' (handoff)
 *   - Provider layer:   'llm:request-intercepted' (model call)
 * No duplicate enforcement path is created.
 *
 * Session-state auditing: The Runner maintains session state across turns.
 * The adapter emits state transitions as part of the audit trail using
 * orchestrator:plan-step-begin / orchestrator:plan-step-end pairs.
 *
 * DEFERRED-OPENAI-AGENTS-V1-API: Verify Runner hook surface at implementation
 * time. As of 2026-04-22 the Agents SDK is at v1.x; the Runner.run lifecycle
 * hooks (on_handoff, on_tool_call, on_run_start, on_run_end) are confirmed
 * in the Python SDK. The TypeScript SDK hook surface needs verification.
 *
 * No live OpenAI Agents SDK installed. All framework surfaces are shims.
 * Tests use in-process fakes only.
 *
 * Spec: P3-ORCH-01, Section 2.7, AC 14-15
 * @connexum/ai-governance
 */
import * as crypto from 'crypto';
import { BaseOrchestratorAdapter } from './base.js';
import { OrchestratorError, } from './index.js';
import { ActionDecisionType } from '../adapters/index.js';
// ---------------------------------------------------------------------------
// GovernanceWrappedRunner -- shimmed Runner wrapper
// ---------------------------------------------------------------------------
/**
 * Governance-wrapped Runner returned by OpenAIAgentsAdapter.wrapRunner().
 *
 * DEFERRED-OPENAI-AGENTS-V1-API: The actual Runner class interface needs
 * verification against the SDK. This shim exposes the hooks as explicit
 * methods that the customer's integration code or a thin SDK wrapper calls.
 */
export class GovernanceWrappedRunner {
    adapter;
    constructor(adapter) {
        this.adapter = adapter;
    }
    /**
     * Called when a run starts (maps to Runner.run entry).
     * Governance check; throws OrchestratorError if denied.
     */
    async onRunStart(ctx) {
        return this.adapter.handleRunStart(ctx);
    }
    /**
     * Called when a run completes.
     * Audit hook.
     */
    async onRunEnd(ctx, result) {
        return this.adapter.handleRunEnd(ctx, result);
    }
    /**
     * Called before a handoff (agent-to-agent control transfer).
     * Governance check; throws OrchestratorError if denied.
     */
    async onHandoff(event) {
        return this.adapter.handleHandoff(event);
    }
    /**
     * Called before a tool/function call at the Agents SDK layer.
     * Distinct from the LLM provider adapter tool-use check.
     */
    async onToolCall(event) {
        return this.adapter.handleToolCall(event);
    }
    /**
     * Called when session state transitions between steps.
     * Emits orchestrator:plan-step-begin / plan-step-end pair.
     */
    async onStateTransition(event) {
        return this.adapter.handleStateTransition(event);
    }
}
// ---------------------------------------------------------------------------
// OpenAIAgentsAdapter
// ---------------------------------------------------------------------------
export class OpenAIAgentsAdapter extends BaseOrchestratorAdapter {
    adapterId = 'openai-agents';
    orchestratorId = 'openai-agents';
    agentsConfig;
    constructor(config) {
        super();
        this.agentsConfig = config;
    }
    // ---------------------------------------------------------------------------
    // wrapRunner -- public factory
    // ---------------------------------------------------------------------------
    /**
     * Return a GovernanceWrappedRunner wired to this adapter.
     * Consumers substitute this for their SDK Runner.
     */
    wrapRunner(_runner) {
        this.assertInitialized();
        return new GovernanceWrappedRunner(this);
    }
    // ---------------------------------------------------------------------------
    // Internal run lifecycle dispatchers
    // ---------------------------------------------------------------------------
    /**
     * Governance check on run start.
     * Validates the agent against the allowedAgents list.
     */
    async handleRunStart(ctx) {
        this.assertInitialized();
        const auditId = crypto.randomUUID();
        if (this.isCircuitOpen()) {
            const reason = 'Circuit breaker is open; run start denied';
            this.emitCanonical('orchestrator:task-denied', {
                runId: ctx.runId,
                agent: ctx.agent.name,
                reason,
            });
            this.recordFailure();
            throw new OrchestratorError({
                message: reason,
                code: 'OPENAI_AGENTS_CIRCUIT_OPEN',
                orchestratorId: this.orchestratorId,
                adapterId: this.adapterId,
            });
        }
        if (!this.agentsConfig.allowedAgents.includes(ctx.agent.name)) {
            const reason = `Agent "${ctx.agent.name}" is not in the allowed agents list`;
            this.emitCanonical('orchestrator:task-denied', {
                runId: ctx.runId,
                agent: ctx.agent.name,
                reason,
            });
            this.recordFailure();
            throw new OrchestratorError({
                message: reason,
                code: 'OPENAI_AGENTS_AGENT_NOT_ALLOWED',
                orchestratorId: this.orchestratorId,
                adapterId: this.adapterId,
            });
        }
        const wfCtx = {
            workflowId: ctx.runId,
            name: `Agent run: ${ctx.agent.name}`,
            initiator: {
                name: ctx.agent.name,
                role: ctx.agent.role,
                sessionId: ctx.runId,
            },
            timestamp: ctx.startedAt,
            metadata: ctx.metadata,
        };
        const decision = await this.startWorkflow(wfCtx);
        this.recordSuccess();
        return decision;
    }
    /** Audit hook on run completion. */
    async handleRunEnd(ctx, result) {
        this.assertInitialized();
        const wfCtx = {
            workflowId: ctx.runId,
            name: `Agent run: ${ctx.agent.name}`,
            initiator: {
                name: ctx.agent.name,
                role: ctx.agent.role,
                sessionId: ctx.runId,
            },
            timestamp: ctx.startedAt,
        };
        await this.completeWorkflow(wfCtx, {
            success: result.success,
            summary: result.error ?? 'Run complete',
            durationMs: result.durationMs,
        });
    }
    /**
     * Handoff governance.
     * Agent-to-agent handoffs are first-class primitives mapping onto
     * the DelegationDecision contract.
     *
     * Sensitive handoff targets require reviewer approval.
     * This is distinct from the LLM provider adapter: the provider adapter
     * fires at the model call; this adapter fires at the handoff boundary.
     */
    async handleHandoff(event) {
        this.assertInitialized();
        const auditId = crypto.randomUUID();
        const isSensitive = this.agentsConfig.sensitiveHandoffTargets.includes(event.toAgent.name);
        if (isSensitive) {
            const tier = 'human-reviewer';
            const queueId = this.config.reviewerQueues[tier];
            if (!queueId) {
                const reason = `Handoff to sensitive agent "${event.toAgent.name}" requires ` +
                    'human-reviewer tier but no queue binding exists';
                this.emitCanonical('orchestrator:delegation-denied', {
                    handoffId: event.handoffId,
                    from: event.fromAgent.name,
                    to: event.toAgent.name,
                    reason,
                });
                return {
                    type: ActionDecisionType.DENY,
                    reason,
                    restrictedPermissions: [event.toAgent.name],
                    auditId,
                };
            }
            this.emitCanonical('orchestrator:reviewer-routed', {
                handoffId: event.handoffId,
                from: event.fromAgent.name,
                to: event.toAgent.name,
                tier,
                queueId,
            });
            return {
                type: ActionDecisionType.REQUIRE_APPROVAL,
                reason: `Handoff to "${event.toAgent.name}" requires human-reviewer approval`,
                auditId,
                metadata: { reviewerTier: tier, handoffId: event.handoffId },
            };
        }
        this.emitCanonical('orchestrator:delegation-approved', {
            handoffId: event.handoffId,
            from: event.fromAgent.name,
            to: event.toAgent.name,
        });
        return {
            type: ActionDecisionType.ALLOW,
            reason: `Handoff from "${event.fromAgent.name}" to "${event.toAgent.name}" approved`,
            auditId,
        };
    }
    /**
     * Tool-call governance at the Agents SDK layer.
     * This fires at plan/handoff boundaries, not at the model call level.
     * The LLM provider adapter fires at model call level via a separate path.
     * Both emit to the same audit chain with distinct event types (AC 15).
     */
    async handleToolCall(event) {
        this.assertInitialized();
        const auditId = crypto.randomUUID();
        const isSensitive = this.agentsConfig.sensitiveTools.includes(event.toolName);
        if (isSensitive) {
            const tier = 'human-reviewer';
            const queueId = this.config.reviewerQueues[tier];
            if (!queueId) {
                const reason = `Tool "${event.toolName}" is sensitive but no human-reviewer queue binding exists`;
                this.emitCanonical('orchestrator:task-denied', {
                    toolCallId: event.toolCallId,
                    toolName: event.toolName,
                    reason,
                });
                return { type: ActionDecisionType.DENY, reason, auditId };
            }
            this.emitCanonical('orchestrator:reviewer-routed', {
                toolCallId: event.toolCallId,
                toolName: event.toolName,
                tier,
                queueId,
                layer: 'agents-sdk', // distinguishes from provider-layer event
            });
            return {
                type: ActionDecisionType.REQUIRE_APPROVAL,
                reason: `Tool "${event.toolName}" requires reviewer approval (Agents SDK layer)`,
                auditId,
                metadata: { reviewerTier: tier, layer: 'agents-sdk' },
            };
        }
        this.emitCanonical('orchestrator:task-approved', {
            toolCallId: event.toolCallId,
            toolName: event.toolName,
            layer: 'agents-sdk',
        });
        return {
            type: ActionDecisionType.ALLOW,
            reason: `Tool "${event.toolName}" approved at Agents SDK layer`,
            auditId,
        };
    }
    /**
     * Session state transition auditing.
     * Emits orchestrator:plan-step-begin / plan-step-end pair per transition.
     */
    async handleStateTransition(event) {
        this.assertInitialized();
        this.emitCanonical('orchestrator:plan-step-begin', {
            runId: event.runId,
            stepIndex: event.stepIndex,
            transition: event.transitionDescription,
            previousStateKeys: Object.keys(event.previousState),
        });
        this.emitCanonical('orchestrator:plan-step-end', {
            runId: event.runId,
            stepIndex: event.stepIndex,
            newStateKeys: Object.keys(event.newState),
        });
    }
    // ---------------------------------------------------------------------------
    // 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: 'OpenAI Agents run 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: ActionDecisionType.DENY, reason, auditId };
        }
        this.emitCanonical('orchestrator:task-approved', { taskId: task.taskId });
        return {
            type: ActionDecisionType.ALLOW,
            reason: `Task "${task.taskId}" approved via OpenAIAgentsAdapter`,
            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.agentsConfig.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: 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: ActionDecisionType.DENY, reason, auditId };
        }
        this.emitCanonical('orchestrator:plan-approved', { planId: plan.planId });
        return { type: 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.agentsConfig.sensitiveTools.includes(p) ||
            this.agentsConfig.sensitiveHandoffTargets.includes(p));
        if (restricted.length > 0) {
            const reason = `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: ActionDecisionType.DENY,
                reason,
                restrictedPermissions: restricted,
                auditId,
            };
        }
        this.emitCanonical('orchestrator:delegation-approved', {
            delegationId: d.delegationId,
            from: d.fromAgent.name,
            to: d.toAgent.name,
        });
        return { type: 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: 'OpenAI Agents role edge permitted', auditId };
    }
    // ---------------------------------------------------------------------------
    // Error translation
    // ---------------------------------------------------------------------------
    translateError(raw) {
        if (raw instanceof OrchestratorError)
            return raw;
        const message = raw instanceof Error ? raw.message : String(raw);
        if (message.includes('MaxTurnsExceeded') || message.includes('max_turns')) {
            return new OrchestratorError({
                message: `OpenAI Agents max turns exceeded: ${message}`,
                code: 'OPENAI_AGENTS_MAX_TURNS_EXCEEDED',
                orchestratorId: this.orchestratorId,
                adapterId: this.adapterId,
                raw,
            });
        }
        if (message.includes('handoff') && message.includes('denied')) {
            return new OrchestratorError({
                message: `OpenAI Agents handoff denied: ${message}`,
                code: 'OPENAI_AGENTS_HANDOFF_DENIED',
                orchestratorId: this.orchestratorId,
                adapterId: this.adapterId,
                raw,
            });
        }
        if (message.includes('timeout') || message.includes('Timeout')) {
            return new OrchestratorError({
                message: `OpenAI Agents run timeout: ${message}`,
                code: 'OPENAI_AGENTS_TIMEOUT',
                orchestratorId: this.orchestratorId,
                adapterId: this.adapterId,
                raw,
            });
        }
        return new OrchestratorError({
            message,
            code: 'OPENAI_AGENTS_ERROR',
            orchestratorId: this.orchestratorId,
            adapterId: this.adapterId,
            raw,
        });
    }
    // ---------------------------------------------------------------------------
    // Capabilities
    // ---------------------------------------------------------------------------
    getOrchestratorCapabilities() {
        return {
            workflowGovernance: true,
            taskGovernance: true,
            planGovernance: true,
            delegationGovernance: true,
            roleEdgeGovernance: true,
            parallelExecution: true,
            nestedPlans: false,
            circuitBreaker: true,
        };
    }
}
//# sourceMappingURL=openai-agents.js.map