/**
 * OpenAI/Codex Governance Adapter
 *
 * Maps OpenAI function calling and tool_use formats to the
 * GovernanceAdapter interface. Since OpenAI's API does not provide
 * native pre/post-action hooks, this adapter operates as middleware:
 * callers wrap their function execution with wrapFunctionCall().
 *
 * Supports both legacy function_calling format and the newer tool_use
 * (tools array) format.
 *
 * No OpenAI SDK dependency. This maps governance to their wire format.
 *
 * @connexum/ai-governance
 */
import { BaseGovernanceAdapter, ActionDecisionType, } from '../adapters/index.js';
import { PromptInjectionDetector, buildInjectionAuditEvent, } from '../security/prompt-injection-detector.js';
// ---------------------------------------------------------------------------
// OpenAI Adapter
// ---------------------------------------------------------------------------
export class OpenAIAdapter extends BaseGovernanceAdapter {
    blockedFunctions = [];
    approvalRequiredFunctions = [];
    agentIdentity = { name: 'unknown' };
    preActionLog = [];
    postActionLog = [];
    injectionDetector = new PromptInjectionDetector();
    injectionConfig = {};
    /**
     * Initialize with OpenAI-specific configuration.
     */
    initialize(config) {
        super.initialize(config);
        const oaiConfig = config;
        this.blockedFunctions = oaiConfig.blockedFunctions ?? [];
        this.approvalRequiredFunctions = oaiConfig.approvalRequiredFunctions ?? [];
        this.agentIdentity = oaiConfig.agentIdentity ?? { name: oaiConfig.adapterName };
        this.injectionConfig = oaiConfig.promptInjection ?? {};
        this.injectionDetector = new PromptInjectionDetector(this.injectionConfig.detectorConfig);
        this.preActionLog = [];
        this.postActionLog = [];
    }
    /**
     * Scan an array of LLM messages for prompt injection BEFORE sending to OpenAI.
     *
     * Call this when you have the full messages array (e.g., before chat completion).
     * Returns an ActionDecision: DENY if injection detected at denyThreshold severity,
     * ALLOW otherwise.
     *
     * Audit event is written to stderr (privacy-safe redacted snippet, no full content).
     */
    scanMessagesForInjection(messages) {
        if (this.injectionConfig.enabled === false) {
            return { type: ActionDecisionType.ALLOW, reason: 'Prompt injection detection disabled.' };
        }
        const detection = this.injectionDetector.detectInjection(messages);
        if (detection.detected) {
            const denyThreshold = this.injectionConfig.denyThreshold ?? 'high';
            const shouldDeny = detection.severity === 'high' ||
                (denyThreshold === 'medium' && detection.severity === 'medium');
            const auditPayload = buildInjectionAuditEvent(messages, detection, this.agentIdentity.name, this.agentIdentity.orgId ?? 'unknown');
            process.stderr.write(`[OpenAIAdapter] prompt-injection:${detection.severity} detected. ` +
                `patterns=${detection.patterns.join(',')} confidence=${detection.confidence.toFixed(2)} ` +
                `contentHash=${auditPayload['contentHash']}\n`);
            if (shouldDeny) {
                const decision = {
                    type: ActionDecisionType.DENY,
                    reason: `Prompt injection detected (severity=${detection.severity}, ` +
                        `patterns=${detection.patterns.join(', ')}). ` +
                        'Request blocked by @connexum/ai-governance (Onyx #5 / OWASP LLM01).',
                    metadata: {
                        injectionPatterns: detection.patterns,
                        injectionSeverity: detection.severity,
                        injectionConfidence: detection.confidence,
                    },
                };
                this.preActionLog.push({
                    context: {
                        toolName: 'llm.messages.create',
                        input: { injectionDetected: true },
                        agent: { ...this.agentIdentity },
                        timestamp: new Date().toISOString(),
                    },
                    decision,
                });
                return decision;
            }
        }
        return { type: ActionDecisionType.ALLOW, reason: 'No injection pattern detected.' };
    }
    /**
     * Pre-action governance check.
     * Evaluates blocked and approval-required rules against the tool name.
     */
    preAction(context) {
        this.ensureInitialized();
        // Check blocked functions
        for (const rule of this.blockedFunctions) {
            if (this.matchesRule(context.toolName, rule)) {
                const decision = {
                    type: ActionDecisionType.DENY,
                    reason: rule.reason,
                    metadata: {
                        rule: 'blocked_function',
                        pattern: rule.pattern instanceof RegExp ? rule.pattern.source : rule.pattern,
                        toolName: context.toolName,
                    },
                };
                this.preActionLog.push({ context, decision });
                return decision;
            }
        }
        // Check approval-required functions
        for (const rule of this.approvalRequiredFunctions) {
            if (this.matchesRule(context.toolName, rule)) {
                const decision = {
                    type: ActionDecisionType.REQUIRE_APPROVAL,
                    reason: rule.reason,
                    metadata: {
                        rule: 'approval_required',
                        pattern: rule.pattern instanceof RegExp ? rule.pattern.source : rule.pattern,
                        toolName: context.toolName,
                    },
                };
                this.preActionLog.push({ context, decision });
                return decision;
            }
        }
        // Default: allow
        const decision = {
            type: ActionDecisionType.ALLOW,
            reason: 'No governance rule matched. Action permitted.',
        };
        this.preActionLog.push({ context, decision });
        return decision;
    }
    /**
     * Post-action audit logging.
     * OpenAI has no native post-action hook, so this relies on the
     * wrapper calling postAction() after execution.
     */
    postAction(context, result) {
        this.ensureInitialized();
        this.postActionLog.push({ context, result });
    }
    /**
     * Report adapter capabilities.
     * OpenAI lacks native hooks -- this adapter is a middleware wrapper.
     */
    getCapabilities() {
        return {
            preAction: false, // No native pre-action hook in OpenAI
            postAction: false, // No native post-action hook in OpenAI
            auditLogging: false, // No built-in audit in OpenAI API
            circuitBreaker: false, // Must be implemented externally
            approvalQueue: false, // Must be implemented externally
            tamperDetection: false, // No file-based governance in OpenAI
        };
    }
    // -----------------------------------------------------------------------
    // OpenAI format parsing
    // -----------------------------------------------------------------------
    /**
     * Parse an OpenAI function call (legacy format) into ActionContext.
     */
    parseFunctionCall(call) {
        this.ensureInitialized();
        let parsedArgs;
        try {
            parsedArgs = JSON.parse(call.arguments);
        }
        catch {
            parsedArgs = { _raw: call.arguments };
        }
        return {
            toolName: call.name,
            input: parsedArgs,
            agent: { ...this.agentIdentity },
            timestamp: new Date().toISOString(),
            sessionInfo: {
                format: 'function_calling',
                rawArguments: call.arguments,
            },
        };
    }
    /**
     * Parse an OpenAI tool call (tool_use format) into ActionContext.
     */
    parseToolCall(call) {
        this.ensureInitialized();
        let parsedArgs;
        try {
            parsedArgs = JSON.parse(call.function.arguments);
        }
        catch {
            parsedArgs = { _raw: call.function.arguments };
        }
        return {
            toolName: call.function.name,
            input: parsedArgs,
            agent: { ...this.agentIdentity },
            timestamp: new Date().toISOString(),
            sessionInfo: {
                format: 'tool_use',
                toolCallId: call.id,
                rawArguments: call.function.arguments,
            },
        };
    }
    /**
     * Parse either format into ActionContext (auto-detects).
     */
    parseCall(call) {
        if (this.isToolCall(call)) {
            return this.parseToolCall(call);
        }
        return this.parseFunctionCall(call);
    }
    // -----------------------------------------------------------------------
    // Governance-wrapped execution
    // -----------------------------------------------------------------------
    /**
     * Wrap a function call with governance enforcement.
     *
     * This is the primary integration point. Instead of executing an
     * OpenAI function call directly, callers pass it through this method
     * which applies preAction governance, optionally executes, and runs
     * postAction auditing.
     *
     * @param call - The OpenAI function/tool call object
     * @param executor - Optional function that executes the call.
     *                   Only invoked if governance allows.
     * @returns WrappedCallResult with decision and optional result
     */
    async wrapFunctionCall(call, executor) {
        const context = this.parseCall(call);
        const decision = this.preAction(context);
        if (decision.type !== ActionDecisionType.ALLOW) {
            return { decision, originalCall: call };
        }
        if (!executor) {
            return { decision, originalCall: call };
        }
        const startMs = Date.now();
        let actionResult;
        try {
            const output = await executor(context);
            actionResult = {
                success: true,
                outputSummary: typeof output === 'string' ? output : JSON.stringify(output ?? 'ok'),
                durationMs: Date.now() - startMs,
            };
        }
        catch (err) {
            actionResult = {
                success: false,
                outputSummary: '',
                error: err instanceof Error ? err.message : String(err),
                durationMs: Date.now() - startMs,
            };
        }
        this.postAction(context, actionResult);
        return {
            decision,
            result: actionResult,
            originalCall: call,
        };
    }
    /**
     * Synchronous version of wrapFunctionCall for non-async executors.
     */
    wrapFunctionCallSync(call, executor) {
        const context = this.parseCall(call);
        const decision = this.preAction(context);
        if (decision.type !== ActionDecisionType.ALLOW) {
            return { decision, originalCall: call };
        }
        if (!executor) {
            return { decision, originalCall: call };
        }
        const startMs = Date.now();
        let actionResult;
        try {
            const output = executor(context);
            actionResult = {
                success: true,
                outputSummary: typeof output === 'string' ? output : JSON.stringify(output ?? 'ok'),
                durationMs: Date.now() - startMs,
            };
        }
        catch (err) {
            actionResult = {
                success: false,
                outputSummary: '',
                error: err instanceof Error ? err.message : String(err),
                durationMs: Date.now() - startMs,
            };
        }
        this.postAction(context, actionResult);
        return {
            decision,
            result: actionResult,
            originalCall: call,
        };
    }
    // -----------------------------------------------------------------------
    // OpenAI response builders
    // -----------------------------------------------------------------------
    /**
     * Build an OpenAI-format tool response message from an ActionResult.
     */
    buildToolResponse(callId, result) {
        return {
            role: 'tool',
            tool_call_id: callId,
            content: result.success
                ? result.outputSummary
                : JSON.stringify({ error: result.error ?? 'Unknown error' }),
        };
    }
    /**
     * Build a governance-denied response in OpenAI tool response format.
     */
    buildDeniedResponse(callId, decision) {
        return {
            role: 'tool',
            tool_call_id: callId,
            content: JSON.stringify({
                error: 'governance_denied',
                type: decision.type,
                reason: decision.reason,
            }),
        };
    }
    // -----------------------------------------------------------------------
    // Audit accessors (for testing/monitoring)
    // -----------------------------------------------------------------------
    /** Get the pre-action decision log. */
    getPreActionLog() {
        return [...this.preActionLog];
    }
    /** Get the post-action result log. */
    getPostActionLog() {
        return [...this.postActionLog];
    }
    // -----------------------------------------------------------------------
    // Private helpers
    // -----------------------------------------------------------------------
    isToolCall(call) {
        return 'id' in call && 'type' in call && call.type === 'function';
    }
    matchesRule(toolName, rule) {
        if (rule.pattern instanceof RegExp) {
            return rule.pattern.test(toolName);
        }
        return toolName === rule.pattern;
    }
}
//# sourceMappingURL=openai.js.map