/**
 * WindsurfAdapter -- Windsurf (Codeium) IDE adapter.
 *
 * Windsurf is an agentic IDE built by Codeium. Its core feature is Cascade,
 * a multi-step agentic system that plans and executes multi-tool workflows
 * autonomously. Windsurf competes directly with Cursor and has a fast adoption
 * curve among professional developers. Its agentic depth makes it the
 * highest-governance-exposure IDE in the Connexum catalogue.
 *
 * Deployment modes:
 *   windsurf-cloud      -- Codeium-managed SaaS. Default for individuals and teams.
 *   windsurf-enterprise -- Customer-operated VPC or air-gapped deployment.
 *                          Enterprise mode supports air-gapped operation, making it
 *                          suitable for classified-network and sovereign-data deployments.
 *   windsurf-individual -- Individual Windsurf subscription. No enterprise controls.
 *
 * Hook surfaces (seven distinct governable surfaces):
 *
 *   cascadeFlowRequested(flowDescription, tools, steps)
 *     Multi-step agentic flow. The highest-leverage governance gate. Cascade
 *     can plan and execute dozens of tool calls in a single flow. REQUIRES a
 *     non-empty flowDescription for auditability -- empty/missing description
 *     is DENIED. Reason code: windsurf-cascade-flow-description-required.
 *
 *   chatRequested(prompt)
 *     Standard chat gate. Scanned for secrets before dispatch.
 *
 *   inlineCompletionRequested(file, cursor, context)
 *     Autocomplete gate. Most frequent surface. Surrounding context is scanned
 *     for secrets and PHI indicators.
 *
 *   commandRequested(command, targetFile)
 *     Slash command execution (file edits, refactors). Target file is checked
 *     for sensitive-path patterns.
 *
 *   terminalCommandRequested(command)
 *     Terminal execution that Windsurf can run autonomously. CRITICAL governance
 *     surface. Strict-mode enforcement:
 *       - Default: DENIED unless allowTerminalExecution: true in config.
 *       - windsurf-individual: only allowed if explicitly opted in.
 *       - airGapped: true sub-mode: terminal commands MUST pass an
 *         allowed-command-prefix list (allowedTerminalPrefixes[]). If the
 *         command does not start with an allowed prefix, DENIED even when
 *         allowTerminalExecution is true.
 *       This gate prevents autonomous terminal execution in classified/air-gapped
 *       environments where arbitrary shell access is unacceptable.
 *
 *   webSearchRequested(query, url)
 *     Cascade can browse the web. Sensitive surface for data-exfiltration risk.
 *     In airGapped: true mode: ALWAYS DENIED (unconditionally). Air-gapped
 *     deployments cannot permit outbound web requests.
 *     In non-air-gapped: audited and allowed.
 *
 *   multiFileEditRequested(files[])
 *     Apply edits across files atomically. Blast-radius cap enforced:
 *     count of affected files > multiFileEditCap (default 10) = DENIED.
 *     This limits blast radius of runaway Cascade flows.
 *
 * Air-gapped enforcement (the Windsurf-distinct privacy differentiator):
 *   windsurf-enterprise mode supports air-gapped operation. Set airGapped: true
 *   to activate classified-network policy:
 *     - Web search is UNCONDITIONALLY DENIED.
 *     - Terminal command requires BOTH allowTerminalExecution: true AND the
 *       command must start with one of the strings in allowedTerminalPrefixes[].
 *     - This enables governance of fully-offline enterprise Windsurf deployments.
 *
 * Pen-tests:
 *   PT-ADAPT-WINDSURF-1: empty packId throws at construction; denied on each wrap call
 *   PT-ADAPT-WINDSURF-2: Windsurf auth token never appears in audit entries
 *   PT-ADAPT-WINDSURF-3: malformed config throws at construction (invalid deploymentMode,
 *                        negative multiFileEditCap, airGapped on non-enterprise)
 *   PT-ADAPT-WINDSURF-4: terminal command denials produce audit entries with reason codes
 *   PT-ADAPT-WINDSURF-5: cascade flow without description produces audit with
 *                        windsurf-cascade-flow-description-required
 *
 * Spec: P3-SUB-IDE-08
 *
 * @connexum/ai-governance
 */
import { BaseIdeAdapter } from './base.js';
import { IdeAttributionError, IdeSessionError, } from './index.js';
import { buildIdeAuditEvent } from './shared.js';
export const WINDSURF_VALID_DEPLOYMENT_MODES = [
    'windsurf-cloud',
    'windsurf-enterprise',
    'windsurf-individual',
];
/** Modes eligible for HIPAA BAA coverage. */
export const WINDSURF_BAA_ELIGIBLE_MODES = [
    'windsurf-enterprise',
];
/** Modes that support air-gapped operation (classified-network deployments). */
export const WINDSURF_AIR_GAPPED_CAPABLE_MODES = [
    'windsurf-enterprise',
];
// ---------------------------------------------------------------------------
// Secret patterns (Windsurf-specific additions over the shared set)
// ---------------------------------------------------------------------------
const WINDSURF_SECRET_PATTERNS = [
    { name: 'AWS access key', re: /AKIA[0-9A-Z]{16}/ },
    { name: 'GitHub PAT', re: /ghp_[0-9A-Za-z]{36}|github_pat_[0-9A-Za-z_]{82}/ },
    { name: 'Anthropic API key', re: /sk-ant-[0-9A-Za-z\-_]{20,}/ },
    { name: 'OpenAI API key', re: /sk-[0-9A-Za-z]{20,}/ },
    { name: 'Private key header', re: /-----BEGIN (RSA|EC|OPENSSH) PRIVATE KEY-----/ },
    { name: 'Generic secret env var', re: /(SECRET|PASSWORD|PASSWD|API_KEY|PRIVATE_KEY)\s*=\s*["']?\S{8,}["']?/i },
    { name: 'Bearer token', re: /Authorization:\s*Bearer\s+[0-9A-Za-z\-._~+/]{20,}/i },
    { name: 'Database URL with creds', re: /(postgres|mysql|mongodb|redis):\/\/[^:]+:[^@]+@/ },
    { name: 'Windsurf auth token', re: /windsurf[_\-]?(auth|token|key|secret)\s*[:=]\s*["']?\S{8,}["']?/i },
    { name: 'Codeium auth token', re: /codeium[_\-]?(auth|token|key|secret)\s*[:=]\s*["']?\S{8,}["']?/i },
];
// ---------------------------------------------------------------------------
// PHI indicators
// ---------------------------------------------------------------------------
const WINDSURF_PHI_INDICATORS = [
    /\bpatient[_\s]?(id|name|dob|ssn|mrn|record)\b/i,
    /\bdiagnos(is|es)\b/i,
    /\bprescription\b/i,
    /\bmedical[_\s]?record\b/i,
    /\bhealth[_\s]?plan\b/i,
    /\bssn\b/,
    /\bdate[_\s]?of[_\s]?birth\b/i,
    /\btreatment[_\s]?plan\b/i,
    /\bhipaa\b/i,
    /\bphi\b/,
];
// ---------------------------------------------------------------------------
// Sensitive path patterns
// ---------------------------------------------------------------------------
const WINDSURF_SENSITIVE_PATH_PATTERNS = [
    /^\/etc\//,
    /^\/root\//,
    /(^|\/)\.env$/,
    /\.env\./,
    /^\/proc\//,
    /^\/sys\//,
    /secrets?\//i,
    /private_?key/i,
    /\.pem$/i,
    /\.key$/i,
];
// ---------------------------------------------------------------------------
// WindsurfAdapter
// ---------------------------------------------------------------------------
export class WindsurfAdapter extends BaseIdeAdapter {
    adapterId = 'windsurf';
    ideId = 'windsurf';
    windsurfConfig = null;
    // ---------------------------------------------------------------------------
    // Lifecycle
    // ---------------------------------------------------------------------------
    initialize(config) {
        const wsConfig = config;
        // PT-ADAPT-WINDSURF-3: validate deploymentMode at construction time.
        const validModes = [...WINDSURF_VALID_DEPLOYMENT_MODES];
        if (!wsConfig.deploymentMode || !validModes.includes(wsConfig.deploymentMode)) {
            throw new IdeSessionError({
                message: `WindsurfAdapter: invalid or missing deploymentMode '${wsConfig.deploymentMode}'. ` +
                    `Must be one of: ${validModes.join(', ')}. ` +
                    `Deployment mode is required to determine terminal-execution, web-search, ` +
                    `and air-gapped policies.`,
                ideId: this.ideId,
                adapterId: this.adapterId,
            });
        }
        // PT-ADAPT-WINDSURF-3: airGapped only valid in windsurf-enterprise mode.
        if (wsConfig.airGapped === true && !WINDSURF_AIR_GAPPED_CAPABLE_MODES.includes(wsConfig.deploymentMode)) {
            throw new IdeSessionError({
                message: `WindsurfAdapter: airGapped: true is only valid in 'windsurf-enterprise' mode. ` +
                    `Current deploymentMode: '${wsConfig.deploymentMode}'. ` +
                    `Air-gapped operation requires enterprise deployment.`,
                ideId: this.ideId,
                adapterId: this.adapterId,
            });
        }
        // PT-ADAPT-WINDSURF-3: multiFileEditCap must be a positive integer if provided.
        if (wsConfig.multiFileEditCap !== undefined) {
            if (!Number.isInteger(wsConfig.multiFileEditCap) || wsConfig.multiFileEditCap < 1) {
                throw new IdeSessionError({
                    message: `WindsurfAdapter: multiFileEditCap must be a positive integer. ` +
                        `Received: ${wsConfig.multiFileEditCap}.`,
                    ideId: this.ideId,
                    adapterId: this.adapterId,
                });
            }
        }
        super.initialize(config);
        this.windsurfConfig = wsConfig;
        const cap = wsConfig.multiFileEditCap ?? 10;
        const isAirGapped = wsConfig.airGapped === true;
        this.emit(buildIdeAuditEvent('ide:session-open', {
            ideId: this.ideId,
            adapterId: this.adapterId,
            detail: `WindsurfAdapter initialized. ` +
                `deploymentMode: ${wsConfig.deploymentMode}. ` +
                `airGapped: ${isAirGapped}. ` +
                `allowTerminalExecution: ${wsConfig.allowTerminalExecution === true}. ` +
                `multiFileEditCap: ${cap}.`,
            data: {
                deploymentMode: wsConfig.deploymentMode,
                airGapped: isAirGapped,
                allowTerminalExecution: wsConfig.allowTerminalExecution === true,
                multiFileEditCap: cap,
                baaEligible: WINDSURF_BAA_ELIGIBLE_MODES.includes(wsConfig.deploymentMode),
            },
        }));
    }
    // ---------------------------------------------------------------------------
    // IdeAdapter: resolveUserIdentity
    //
    // Windsurf identity is provided by the Codeium authentication layer
    // (Codeium account, enterprise SSO, or individual account).
    // ---------------------------------------------------------------------------
    async resolveUserIdentity(session) {
        this.ensureInitialized();
        const fromSession = session.identity?.userId?.trim();
        if (fromSession) {
            const identity = {
                userId: fromSession,
                email: session.identity?.email,
                orgId: session.identity?.orgId,
                attributionMethod: 'cursor-composer',
            };
            this.emitIdentityResolved(identity.userId, session.clientSessionId);
            return identity;
        }
        const allowAnon = this.config?.allowAnonymousIdeSessions === true;
        if (allowAnon) {
            const anon = { userId: 'anonymous', attributionMethod: 'cursor-composer' };
            this.emitIdentityResolved(anon.userId, session.clientSessionId);
            return anon;
        }
        this.emitIdentityUnresolved(session.clientSessionId, 'WindsurfAdapter: no user identity in session context.');
        throw new IdeAttributionError({
            message: 'WindsurfAdapter: cannot resolve user identity. Provide a userId in ' +
                'session.identity.userId (the Codeium account ID or enterprise SSO subject), ' +
                'or set allowAnonymousIdeSessions: true in adapter config.',
            ideId: this.ideId,
            adapterId: this.adapterId,
        });
    }
    // ---------------------------------------------------------------------------
    // IdeAdapter: interceptToolInvocation
    //
    // Normalised tool surface names:
    //   'windsurf:cascade-flow-requested'        -> cascadeFlowRequested gate
    //   'windsurf:chat-requested'                -> chatRequested gate
    //   'windsurf:inline-completion-requested'   -> inlineCompletionRequested gate
    //   'windsurf:command-requested'             -> commandRequested gate
    //   'windsurf:terminal-command-requested'    -> terminalCommandRequested gate (strict)
    //   'windsurf:web-search-requested'          -> webSearchRequested gate
    //   'windsurf:multi-file-edit-requested'     -> multiFileEditRequested gate
    // ---------------------------------------------------------------------------
    async interceptToolInvocation(ctx) {
        this.ensureInitialized();
        switch (ctx.tool) {
            case 'windsurf:cascade-flow-requested':
                return this._gateCascadeFlowRequest(ctx);
            case 'windsurf:chat-requested':
                return this._gateChatRequest(ctx);
            case 'windsurf:inline-completion-requested':
                return this._gateInlineCompletionRequest(ctx);
            case 'windsurf:command-requested':
                return this._gateCommandRequest(ctx);
            case 'windsurf:terminal-command-requested':
                return this._gateTerminalCommandRequest(ctx);
            case 'windsurf:web-search-requested':
                return this._gateWebSearchRequest(ctx);
            case 'windsurf:multi-file-edit-requested':
                return this._gateMultiFileEditRequest(ctx);
            default:
                this.emit(buildIdeAuditEvent('ide:tool-invocation-approved', {
                    ideId: this.ideId,
                    adapterId: this.adapterId,
                    sessionId: ctx.sessionHandle.sessionId,
                    initiatingUserId: ctx.initiatingUser.userId,
                    tool: ctx.tool,
                    detail: `WindsurfAdapter: unknown surface '${ctx.tool}' -- allowed with audit.`,
                }));
                return this.routeToolInvocation(ctx, {
                    decision: 'ALLOW',
                    reason: `Unknown Windsurf surface '${ctx.tool}' permitted with audit.`,
                });
        }
    }
    // ---------------------------------------------------------------------------
    // Hook: cascadeFlowRequested (PT-ADAPT-WINDSURF-5)
    //
    // The highest-leverage governance gate. Cascade can execute dozens of tool
    // calls autonomously. REQUIRES a non-empty flowDescription -- governance
    // cannot approve an unlabeled flow.
    // ---------------------------------------------------------------------------
    async _gateCascadeFlowRequest(ctx) {
        const flowDescription = String(ctx.input['flowDescription'] ?? ctx.input['description'] ?? '').trim();
        const tools = ctx.input['tools'];
        const estimatedSteps = ctx.input['estimatedSteps'];
        // PT-ADAPT-WINDSURF-5: flowDescription is REQUIRED.
        if (!flowDescription) {
            const decision = {
                decision: 'DENY',
                reason: 'Windsurf Cascade flow denied: flowDescription is required for governance audit. ' +
                    'Every Cascade flow must carry a non-empty flowDescription so the compliance ' +
                    'system can evaluate and record what the flow is intended to do. ' +
                    'Reason code: windsurf-cascade-flow-description-required.',
                metadata: {
                    reasonCode: 'windsurf-cascade-flow-description-required',
                    tools,
                    estimatedSteps,
                },
            };
            this.emit(buildIdeAuditEvent('ide:tool-invocation-denied', {
                ideId: this.ideId,
                adapterId: this.adapterId,
                sessionId: ctx.sessionHandle.sessionId,
                initiatingUserId: ctx.initiatingUser.userId,
                tool: ctx.tool,
                detail: decision.reason,
                data: {
                    reasonCode: 'windsurf-cascade-flow-description-required',
                    tools,
                    estimatedSteps,
                },
            }));
            return decision;
        }
        return this.routeToolInvocation(ctx, {
            decision: 'ALLOW',
            reason: `Cascade flow permitted. Description: "${flowDescription.slice(0, 120)}". ` +
                `deploymentMode: ${this.windsurfConfig?.deploymentMode}.`,
            metadata: {
                flowDescription: flowDescription.slice(0, 200),
                tools,
                estimatedSteps,
                deploymentMode: this.windsurfConfig?.deploymentMode,
            },
        });
    }
    // ---------------------------------------------------------------------------
    // Hook: chatRequested
    // ---------------------------------------------------------------------------
    async _gateChatRequest(ctx) {
        const promptText = String(ctx.input['promptText'] ?? ctx.input['prompt'] ?? '');
        const secretMatch = this._scanForSecrets(promptText);
        if (secretMatch) {
            return this.routeToolInvocation(ctx, {
                decision: 'DENY',
                reason: `Chat request blocked: secret pattern '${secretMatch}' in prompt.`,
                metadata: { secretPattern: secretMatch },
            });
        }
        if (this._isHipaaBinding()) {
            const phiMatch = this._scanForPhiIndicators(promptText);
            if (phiMatch) {
                const tier = this.windsurfConfig?.hipaaReviewerTier ?? 'T2_LEAD';
                return this.routeToolInvocation(ctx, {
                    decision: 'REQUIRE_APPROVAL',
                    reason: `Chat request routed for HIPAA review: PHI indicator '${phiMatch}' in prompt. ` +
                        `Reviewer tier: ${tier}.`,
                    metadata: { phiIndicator: phiMatch, tier },
                });
            }
        }
        return this.routeToolInvocation(ctx, {
            decision: 'ALLOW',
            reason: 'Chat request permitted by WindsurfAdapter.',
            metadata: { deploymentMode: this.windsurfConfig?.deploymentMode },
        });
    }
    // ---------------------------------------------------------------------------
    // Hook: inlineCompletionRequested
    // ---------------------------------------------------------------------------
    async _gateInlineCompletionRequest(ctx) {
        const surroundingContext = String(ctx.input['surroundingContext'] ?? ctx.input['context'] ?? '');
        const filePath = String(ctx.input['filePath'] ?? ctx.input['file_path'] ?? '');
        const secretMatch = this._scanForSecrets(surroundingContext);
        if (secretMatch) {
            return this.routeToolInvocation(ctx, {
                decision: 'DENY',
                reason: `Inline completion blocked: secret pattern '${secretMatch}' in surrounding ` +
                    `context (${filePath || 'unknown file'}).`,
                metadata: { filePath, secretPattern: secretMatch },
            });
        }
        if (this._isHipaaBinding()) {
            const phiMatch = this._scanForPhiIndicators(surroundingContext);
            if (phiMatch) {
                const tier = this.windsurfConfig?.hipaaReviewerTier ?? 'T2_LEAD';
                return this.routeToolInvocation(ctx, {
                    decision: 'REQUIRE_APPROVAL',
                    reason: `Inline completion routed for HIPAA review: PHI indicator '${phiMatch}' ` +
                        `in context. Reviewer tier: ${tier}.`,
                    metadata: { filePath, phiIndicator: phiMatch, tier },
                });
            }
        }
        return this.routeToolInvocation(ctx, {
            decision: 'ALLOW',
            reason: 'Inline completion permitted by WindsurfAdapter.',
            metadata: { filePath, deploymentMode: this.windsurfConfig?.deploymentMode },
        });
    }
    // ---------------------------------------------------------------------------
    // Hook: commandRequested
    // ---------------------------------------------------------------------------
    async _gateCommandRequest(ctx) {
        const command = String(ctx.input['command'] ?? '');
        const targetFile = String(ctx.input['targetFile'] ?? ctx.input['target_file'] ?? '');
        // Sensitive path check on the target file
        if (targetFile) {
            const pathMatch = this._isSensitivePath(targetFile);
            if (pathMatch) {
                return this.routeToolInvocation(ctx, {
                    decision: 'DENY',
                    reason: `Windsurf command blocked: target file '${targetFile}' matches sensitive ` +
                        `path pattern. Command: ${command.slice(0, 80)}.`,
                    metadata: { command: command.slice(0, 80), targetFile, sensitivePathMatch: pathMatch },
                });
            }
        }
        return this.routeToolInvocation(ctx, {
            decision: 'ALLOW',
            reason: `Command permitted by WindsurfAdapter. Command: ${command.slice(0, 60)}.`,
            metadata: { command: command.slice(0, 80), targetFile, deploymentMode: this.windsurfConfig?.deploymentMode },
        });
    }
    // ---------------------------------------------------------------------------
    // Hook: terminalCommandRequested (PT-ADAPT-WINDSURF-4)
    //
    // CRITICAL governance surface. Cascade can run terminal commands autonomously.
    //
    // Strict-mode enforcement (default-deny):
    //   1. allowTerminalExecution: false (default) -> DENIED unconditionally.
    //   2. allowTerminalExecution: true in airGapped: false -> ALLOWED with audit.
    //   3. airGapped: true with allowTerminalExecution: false -> DENIED.
    //   4. airGapped: true with allowTerminalExecution: true AND command starts
    //      with an entry in allowedTerminalPrefixes -> ALLOWED.
    //   5. airGapped: true with allowTerminalExecution: true AND command does NOT
    //      start with any allowedTerminalPrefixes entry -> DENIED.
    //
    // windsurf-individual is treated the same as other modes for terminal execution:
    // the allowTerminalExecution flag must be explicitly set to true.
    // ---------------------------------------------------------------------------
    async _gateTerminalCommandRequest(ctx) {
        const command = String(ctx.input['command'] ?? '').trim();
        const mode = this.windsurfConfig?.deploymentMode ?? 'windsurf-cloud';
        const isAirGapped = this.windsurfConfig?.airGapped === true;
        const allowTerminal = this.windsurfConfig?.allowTerminalExecution === true;
        const allowedPrefixes = this.windsurfConfig?.allowedTerminalPrefixes ?? [];
        // Default deny: terminal execution not opted in.
        if (!allowTerminal) {
            const reasonCode = isAirGapped
                ? 'windsurf-terminal-denied-air-gapped-no-opt-in'
                : 'windsurf-terminal-execution-not-permitted';
            const decision = {
                decision: 'DENY',
                reason: `Windsurf terminal command blocked: allowTerminalExecution is not enabled. ` +
                    `Set allowTerminalExecution: true in WindsurfAdapterConfig to permit terminal ` +
                    `execution. deploymentMode: ${mode}. airGapped: ${isAirGapped}. ` +
                    `Reason code: ${reasonCode}.`,
                metadata: {
                    reasonCode,
                    command: command.slice(0, 80),
                    deploymentMode: mode,
                    airGapped: isAirGapped,
                },
            };
            this.emit(buildIdeAuditEvent('ide:tool-invocation-denied', {
                ideId: this.ideId,
                adapterId: this.adapterId,
                sessionId: ctx.sessionHandle.sessionId,
                initiatingUserId: ctx.initiatingUser.userId,
                tool: ctx.tool,
                detail: decision.reason,
                data: {
                    reasonCode,
                    command: command.slice(0, 80),
                    deploymentMode: mode,
                    airGapped: isAirGapped,
                },
            }));
            return decision;
        }
        // Terminal opt-in is confirmed. In air-gapped mode, also require prefix match.
        if (isAirGapped) {
            if (allowedPrefixes.length === 0) {
                const reasonCode = 'windsurf-terminal-denied-air-gapped-no-prefix-list';
                const decision = {
                    decision: 'DENY',
                    reason: `Windsurf terminal command blocked: air-gapped mode requires ` +
                        `allowedTerminalPrefixes[] to be configured with at least one entry. ` +
                        `Empty prefix list in air-gapped mode means ALL terminal commands are denied ` +
                        `(safe default). Configure allowedTerminalPrefixes in WindsurfAdapterConfig. ` +
                        `Reason code: ${reasonCode}.`,
                    metadata: {
                        reasonCode,
                        command: command.slice(0, 80),
                        deploymentMode: mode,
                        airGapped: true,
                    },
                };
                this.emit(buildIdeAuditEvent('ide:tool-invocation-denied', {
                    ideId: this.ideId,
                    adapterId: this.adapterId,
                    sessionId: ctx.sessionHandle.sessionId,
                    initiatingUserId: ctx.initiatingUser.userId,
                    tool: ctx.tool,
                    detail: decision.reason,
                    data: { reasonCode, command: command.slice(0, 80), deploymentMode: mode, airGapped: true },
                }));
                return decision;
            }
            const prefixMatch = allowedPrefixes.find((prefix) => command.startsWith(prefix));
            if (!prefixMatch) {
                const reasonCode = 'windsurf-terminal-denied-air-gapped-prefix-mismatch';
                const decision = {
                    decision: 'DENY',
                    reason: `Windsurf terminal command blocked: air-gapped mode is active and the command ` +
                        `'${command.slice(0, 60)}' does not start with any entry in ` +
                        `allowedTerminalPrefixes [${allowedPrefixes.slice(0, 5).join(', ')}]. ` +
                        `Air-gapped deployments restrict terminal access to a pre-approved command set. ` +
                        `Reason code: ${reasonCode}.`,
                    metadata: {
                        reasonCode,
                        command: command.slice(0, 80),
                        allowedPrefixes,
                        deploymentMode: mode,
                        airGapped: true,
                    },
                };
                this.emit(buildIdeAuditEvent('ide:tool-invocation-denied', {
                    ideId: this.ideId,
                    adapterId: this.adapterId,
                    sessionId: ctx.sessionHandle.sessionId,
                    initiatingUserId: ctx.initiatingUser.userId,
                    tool: ctx.tool,
                    detail: decision.reason,
                    data: {
                        reasonCode,
                        command: command.slice(0, 80),
                        allowedPrefixes,
                        deploymentMode: mode,
                        airGapped: true,
                    },
                }));
                return decision;
            }
            // Air-gapped, opt-in confirmed, prefix matched.
            return this.routeToolInvocation(ctx, {
                decision: 'ALLOW',
                reason: `Terminal command permitted in air-gapped mode. ` +
                    `Matched prefix: '${prefixMatch}'. Command: ${command.slice(0, 60)}.`,
                metadata: {
                    command: command.slice(0, 80),
                    matchedPrefix: prefixMatch,
                    deploymentMode: mode,
                    airGapped: true,
                },
            });
        }
        // Non-air-gapped, opt-in confirmed.
        return this.routeToolInvocation(ctx, {
            decision: 'ALLOW',
            reason: `Terminal command permitted. allowTerminalExecution: true. ` +
                `Command: ${command.slice(0, 60)}. deploymentMode: ${mode}.`,
            metadata: { command: command.slice(0, 80), deploymentMode: mode, airGapped: false },
        });
    }
    // ---------------------------------------------------------------------------
    // Hook: webSearchRequested
    //
    // Cascade can browse the web. In air-gapped mode this is UNCONDITIONALLY DENIED.
    // ---------------------------------------------------------------------------
    async _gateWebSearchRequest(ctx) {
        const query = String(ctx.input['query'] ?? '');
        const url = String(ctx.input['url'] ?? '');
        const isAirGapped = this.windsurfConfig?.airGapped === true;
        const mode = this.windsurfConfig?.deploymentMode ?? 'windsurf-cloud';
        if (isAirGapped) {
            const reasonCode = 'windsurf-web-search-denied-air-gapped';
            const decision = {
                decision: 'DENY',
                reason: `Windsurf web search blocked: air-gapped mode is active. ` +
                    `Web search is unconditionally denied in air-gapped deployments to prevent ` +
                    `data exfiltration and ensure network isolation. ` +
                    `deploymentMode: ${mode}. Reason code: ${reasonCode}.`,
                metadata: {
                    reasonCode,
                    query: query.slice(0, 80),
                    url: url.slice(0, 120),
                    deploymentMode: mode,
                    airGapped: true,
                },
            };
            this.emit(buildIdeAuditEvent('ide:tool-invocation-denied', {
                ideId: this.ideId,
                adapterId: this.adapterId,
                sessionId: ctx.sessionHandle.sessionId,
                initiatingUserId: ctx.initiatingUser.userId,
                tool: ctx.tool,
                detail: decision.reason,
                data: {
                    reasonCode,
                    deploymentMode: mode,
                    airGapped: true,
                },
            }));
            return decision;
        }
        // Non-air-gapped: allowed with audit.
        return this.routeToolInvocation(ctx, {
            decision: 'ALLOW',
            reason: `Web search permitted with audit. query: '${query.slice(0, 80)}'. ` +
                `deploymentMode: ${mode}.`,
            metadata: {
                query: query.slice(0, 80),
                deploymentMode: mode,
                airGapped: false,
            },
        });
    }
    // ---------------------------------------------------------------------------
    // Hook: multiFileEditRequested
    //
    // Blast-radius cap: file count > multiFileEditCap => DENIED.
    // ---------------------------------------------------------------------------
    async _gateMultiFileEditRequest(ctx) {
        const files = ctx.input['files'] ?? [];
        const cap = this.windsurfConfig?.multiFileEditCap ?? 10;
        if (files.length > cap) {
            const reasonCode = 'windsurf-multi-file-edit-blast-radius-exceeded';
            return this.routeToolInvocation(ctx, {
                decision: 'DENY',
                reason: `Windsurf multi-file edit blocked: ${files.length} files exceeds the ` +
                    `blast-radius cap of ${cap}. Reduce the number of files in a single edit ` +
                    `operation or increase multiFileEditCap in WindsurfAdapterConfig. ` +
                    `Reason code: ${reasonCode}.`,
                metadata: {
                    reasonCode,
                    fileCount: files.length,
                    cap,
                    deploymentMode: this.windsurfConfig?.deploymentMode,
                },
            });
        }
        return this.routeToolInvocation(ctx, {
            decision: 'ALLOW',
            reason: `Multi-file edit permitted. File count: ${files.length} (cap: ${cap}). ` +
                `deploymentMode: ${this.windsurfConfig?.deploymentMode}.`,
            metadata: {
                fileCount: files.length,
                cap,
                deploymentMode: this.windsurfConfig?.deploymentMode,
            },
        });
    }
    // ---------------------------------------------------------------------------
    // Public wrap methods (typed direct-call API, mirroring TabnineAdapter pattern)
    // ---------------------------------------------------------------------------
    async wrapCascadeFlow(ctx) {
        this.ensureInitialized();
        return this._gateCascadeFlowRequest({
            tool: 'windsurf:cascade-flow-requested',
            input: {
                flowDescription: ctx.flowDescription,
                tools: ctx.tools,
                estimatedSteps: ctx.estimatedSteps,
            },
            sessionHandle: { sessionId: ctx.sessionId ?? 'windsurf-session', openedAt: new Date().toISOString() },
            initiatingUser: { userId: 'windsurf-user', attributionMethod: 'cursor-composer' },
            timestamp: new Date().toISOString(),
        });
    }
    async wrapChat(ctx) {
        this.ensureInitialized();
        return this._gateChatRequest({
            tool: 'windsurf:chat-requested',
            input: { promptText: ctx.promptText },
            sessionHandle: { sessionId: ctx.sessionId ?? 'windsurf-session', openedAt: new Date().toISOString() },
            initiatingUser: { userId: 'windsurf-user', attributionMethod: 'cursor-composer' },
            timestamp: new Date().toISOString(),
        });
    }
    async wrapInlineCompletion(ctx) {
        this.ensureInitialized();
        return this._gateInlineCompletionRequest({
            tool: 'windsurf:inline-completion-requested',
            input: {
                filePath: ctx.filePath,
                surroundingContext: ctx.surroundingContext ?? '',
                cursorPosition: ctx.cursorPosition,
            },
            sessionHandle: { sessionId: ctx.sessionId ?? 'windsurf-session', openedAt: new Date().toISOString() },
            initiatingUser: { userId: 'windsurf-user', attributionMethod: 'cursor-composer' },
            timestamp: new Date().toISOString(),
        });
    }
    async wrapCommand(ctx) {
        this.ensureInitialized();
        return this._gateCommandRequest({
            tool: 'windsurf:command-requested',
            input: { command: ctx.command, targetFile: ctx.targetFile ?? '' },
            sessionHandle: { sessionId: ctx.sessionId ?? 'windsurf-session', openedAt: new Date().toISOString() },
            initiatingUser: { userId: 'windsurf-user', attributionMethod: 'cursor-composer' },
            timestamp: new Date().toISOString(),
        });
    }
    async wrapTerminalCommand(ctx) {
        this.ensureInitialized();
        return this._gateTerminalCommandRequest({
            tool: 'windsurf:terminal-command-requested',
            input: { command: ctx.command, workingDirectory: ctx.workingDirectory },
            sessionHandle: { sessionId: ctx.sessionId ?? 'windsurf-session', openedAt: new Date().toISOString() },
            initiatingUser: { userId: 'windsurf-user', attributionMethod: 'cursor-composer' },
            timestamp: new Date().toISOString(),
        });
    }
    async wrapWebSearch(ctx) {
        this.ensureInitialized();
        return this._gateWebSearchRequest({
            tool: 'windsurf:web-search-requested',
            input: { query: ctx.query, url: ctx.url ?? '' },
            sessionHandle: { sessionId: ctx.sessionId ?? 'windsurf-session', openedAt: new Date().toISOString() },
            initiatingUser: { userId: 'windsurf-user', attributionMethod: 'cursor-composer' },
            timestamp: new Date().toISOString(),
        });
    }
    async wrapMultiFileEdit(ctx) {
        this.ensureInitialized();
        return this._gateMultiFileEditRequest({
            tool: 'windsurf:multi-file-edit-requested',
            input: { files: ctx.files, description: ctx.description },
            sessionHandle: { sessionId: ctx.sessionId ?? 'windsurf-session', openedAt: new Date().toISOString() },
            initiatingUser: { userId: 'windsurf-user', attributionMethod: 'cursor-composer' },
            timestamp: new Date().toISOString(),
        });
    }
    // ---------------------------------------------------------------------------
    // Capabilities
    // ---------------------------------------------------------------------------
    getIdeCapabilities() {
        const mode = this.windsurfConfig?.deploymentMode ?? 'windsurf-cloud';
        const isAirGapped = this.windsurfConfig?.airGapped === true;
        return {
            runtimeHooks: true,
            prBoundary: false,
            userIdentity: true,
            reviewerRouting: true,
            integrationMode: isAirGapped
                ? `windsurf-enterprise-air-gapped`
                : `windsurf-${mode}`,
        };
    }
    // ---------------------------------------------------------------------------
    // Error translation
    // ---------------------------------------------------------------------------
    translateError(raw) {
        if (raw instanceof IdeAttributionError)
            return raw;
        return super.translateError(raw);
    }
    // ---------------------------------------------------------------------------
    // Public accessors for diagnostics / tests
    // ---------------------------------------------------------------------------
    getDeploymentMode() {
        return this.windsurfConfig?.deploymentMode ?? 'windsurf-cloud';
    }
    isAirGapped() {
        return this.windsurfConfig?.airGapped === true;
    }
    isTerminalExecutionAllowed() {
        return this.windsurfConfig?.allowTerminalExecution === true;
    }
    getMultiFileEditCap() {
        return this.windsurfConfig?.multiFileEditCap ?? 10;
    }
    // ---------------------------------------------------------------------------
    // Private helpers
    // ---------------------------------------------------------------------------
    _scanForSecrets(text) {
        if (!text)
            return null;
        for (const { name, re } of WINDSURF_SECRET_PATTERNS) {
            if (re.test(text))
                return name;
        }
        return null;
    }
    _scanForPhiIndicators(text) {
        if (!text)
            return null;
        for (const re of WINDSURF_PHI_INDICATORS) {
            if (re.test(text))
                return re.source;
        }
        return null;
    }
    _isHipaaBinding() {
        const packId = this.config?.packId ?? '';
        return packId === 'hipaa' || packId.startsWith('hipaa');
    }
    _isSensitivePath(filePath) {
        for (const re of WINDSURF_SENSITIVE_PATH_PATTERNS) {
            if (re.test(filePath))
                return re.source;
        }
        return null;
    }
}
//# sourceMappingURL=windsurf.js.map