"use strict";
/**
 * TabnineAdapter -- Tabnine adapter.
 *
 * Tabnine is an enterprise code-completion AI that is editor-agnostic (VS Code,
 * JetBrains, Vim, Emacs, Eclipse, Sublime Text, Visual Studio). Its original
 * positioning is enterprise-compliance-first: every enterprise deal involves
 * a security questionnaire. That customer base is already sensitized to
 * governance -- making this adapter a high-value integration.
 *
 * Deployment modes:
 *   tabnine-cloud            -- Tabnine-managed SaaS (EU and US clusters)
 *   enterprise-self-hosted   -- Customer-operated instance on VPC or air-gapped
 *                               hardware. Tabnine Enterprise can train models on
 *                               customer code locally without sending data outside
 *                               the perimeter.
 *   pro                      -- Tabnine Pro individual subscription. No enterprise
 *                               controls; no on-premises model training.
 *
 * Hook surfaces (seven distinct governable surfaces):
 *   inlineCompletionRequested(file, cursor, context)
 *     Inline completion gate. Most frequent surface; scanned for secrets in
 *     surrounding context before the completion call is dispatched.
 *   chatRequested(prompt)
 *     Tabnine Chat gate. Scanned for secrets and PHI before dispatch.
 *   codeReviewRequested(diff, filePath)
 *     Automated code review on a diff fragment. Diff is scanned for secrets
 *     before review generation.
 *   testGenerationRequested(functionContext, targetFile)
 *     Test scaffold generation gate. Function context scanned for PHI indicators
 *     under HIPAA binding before test generation proceeds.
 *   documentationRequested(symbolContext, targetFile)
 *     Auto-documentation generation gate. Symbol context scanned for secrets
 *     and PHI before dispatch.
 *   commitMessageRequested(diff, branchName)
 *     Commit message generation gate (mirrors JetBrains/Cody pattern). Diff
 *     is scanned for secrets on added lines before message generation.
 *   learnFromCodebaseRequested(repos, dataScope)
 *     On-premises codebase learning gate -- THE TABNINE-DISTINCT SURFACE.
 *     Tabnine Enterprise can fine-tune a local model on customer code. This
 *     is a HEAVILY governable surface: the training corpus may contain PII,
 *     IP, secrets, or regulated health data. Strict-mode enforcement:
 *       - 'pro' mode: ALWAYS DENIED. Pro has no on-prem learning capability.
 *       - 'tabnine-cloud': ALWAYS DENIED. Model training uses Tabnine servers
 *         (data leaves the customer perimeter).
 *       - 'enterprise-self-hosted': ALLOWED only when `learnFromCodebase: true`
 *         is explicitly set in the adapter config (default: false).
 *     The governance default is deny; explicit opt-in required for enterprise.
 *
 * Tabnine-specific pen-tests:
 *   PT-ADAPT-TABNINE-1: empty packBinding[] throws UnboundDataError pre-call
 *   PT-ADAPT-TABNINE-2: Tabnine API key / auth token never appears in audit entries
 *   PT-ADAPT-TABNINE-3: malformed config (invalid deploymentMode) throws at construction
 *   PT-ADAPT-TABNINE-4: learnFromCodebase privacy gate -- rejection produces audit entry
 *                        with reason code "tabnine-learn-from-codebase-not-permitted-in-mode"
 *
 * Spec: P3-SUB-IDE-07
 *
 * @connexum/ai-governance
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.TabnineAdapter = exports.TABNINE_LEARN_CAPABLE_MODES = exports.TABNINE_BAA_ELIGIBLE_MODES = exports.TABNINE_VALID_DEPLOYMENT_MODES = void 0;
const base_1 = require("./base");
const index_1 = require("./index");
const shared_1 = require("./shared");
exports.TABNINE_VALID_DEPLOYMENT_MODES = [
    'tabnine-cloud',
    'enterprise-self-hosted',
    'pro',
];
exports.TABNINE_BAA_ELIGIBLE_MODES = [
    'enterprise-self-hosted', // customer controls data residency
    'tabnine-cloud', // eligible with active Tabnine BAA
];
/**
 * Modes where learnFromCodebase is architecturally possible
 * (i.e. training runs on-premises). Even within this set, the config
 * opt-in flag is still required.
 */
exports.TABNINE_LEARN_CAPABLE_MODES = [
    'enterprise-self-hosted',
];
// ---------------------------------------------------------------------------
// Secret patterns
// ---------------------------------------------------------------------------
const TABNINE_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: 'Tabnine auth token', re: /tabnine[_\-]?(auth|token|key|secret)\s*[:=]\s*["']?\S{8,}["']?/i },
];
// ---------------------------------------------------------------------------
// PHI indicators
// ---------------------------------------------------------------------------
const TABNINE_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/,
];
// ---------------------------------------------------------------------------
// TabnineAdapter
// ---------------------------------------------------------------------------
class TabnineAdapter extends base_1.BaseIdeAdapter {
    adapterId = 'tabnine';
    ideId = 'tabnine';
    tabnineConfig = null;
    // ---------------------------------------------------------------------------
    // Lifecycle
    // ---------------------------------------------------------------------------
    initialize(config) {
        const tabnineConfig = config;
        // PT-ADAPT-TABNINE-3: validate deploymentMode at construction time.
        const validModes = [...exports.TABNINE_VALID_DEPLOYMENT_MODES];
        if (!tabnineConfig.deploymentMode || !validModes.includes(tabnineConfig.deploymentMode)) {
            throw new index_1.IdeSessionError({
                message: `TabnineAdapter: invalid or missing deploymentMode '${tabnineConfig.deploymentMode}'. ` +
                    `Must be one of: ${validModes.join(', ')}. ` +
                    `Deployment mode is required to enforce data-residency and training-eligibility bounds.`,
                ideId: this.ideId,
                adapterId: this.adapterId,
            });
        }
        super.initialize(config);
        this.tabnineConfig = tabnineConfig;
        const learnEnabled = tabnineConfig.deploymentMode === 'enterprise-self-hosted' &&
            tabnineConfig.learnFromCodebase === true;
        this.emit((0, shared_1.buildIdeAuditEvent)('ide:session-open', {
            ideId: this.ideId,
            adapterId: this.adapterId,
            detail: `TabnineAdapter initialized. ` +
                `deploymentMode: ${tabnineConfig.deploymentMode}. ` +
                `BAA eligible: ${exports.TABNINE_BAA_ELIGIBLE_MODES.includes(tabnineConfig.deploymentMode)}. ` +
                `learnFromCodebase: ${learnEnabled ? 'opt-in enabled' : 'disabled/not-applicable'}.`,
            data: {
                deploymentMode: tabnineConfig.deploymentMode,
                baaEligible: exports.TABNINE_BAA_ELIGIBLE_MODES.includes(tabnineConfig.deploymentMode),
                learnFromCodebaseEnabled: learnEnabled,
            },
        }));
    }
    // ---------------------------------------------------------------------------
    // IdeAdapter: resolveUserIdentity
    //
    // Tabnine identity is provided by the Tabnine authentication layer
    // (Tabnine account, SSO in enterprise-self-hosted, or Pro 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, 'TabnineAdapter: no user identity in session context.');
        throw new index_1.IdeAttributionError({
            message: 'TabnineAdapter: cannot resolve user identity. Provide a userId in ' +
                'session.identity.userId (the Tabnine account ID or SSO subject), or set ' +
                'allowAnonymousIdeSessions: true in adapter config.',
            ideId: this.ideId,
            adapterId: this.adapterId,
        });
    }
    // ---------------------------------------------------------------------------
    // IdeAdapter: interceptToolInvocation
    //
    // Normalised tool surface names:
    //   'tabnine:inline-completion-requested'   -> inlineCompletionRequested gate
    //   'tabnine:chat-requested'                -> chatRequested gate
    //   'tabnine:code-review-requested'         -> codeReviewRequested gate
    //   'tabnine:test-generation-requested'     -> testGenerationRequested gate
    //   'tabnine:documentation-requested'       -> documentationRequested gate
    //   'tabnine:commit-message-requested'      -> commitMessageRequested gate
    //   'tabnine:learn-from-codebase-requested' -> learnFromCodebaseRequested gate (strict)
    // ---------------------------------------------------------------------------
    async interceptToolInvocation(ctx) {
        this.ensureInitialized();
        switch (ctx.tool) {
            case 'tabnine:inline-completion-requested':
                return this._gateInlineCompletionRequest(ctx);
            case 'tabnine:chat-requested':
                return this._gateChatRequest(ctx);
            case 'tabnine:code-review-requested':
                return this._gateCodeReviewRequest(ctx);
            case 'tabnine:test-generation-requested':
                return this._gateTestGenerationRequest(ctx);
            case 'tabnine:documentation-requested':
                return this._gateDocumentationRequest(ctx);
            case 'tabnine:commit-message-requested':
                return this._gateCommitMessageRequest(ctx);
            case 'tabnine:learn-from-codebase-requested':
                return this._gateLearnFromCodebaseRequest(ctx);
            default:
                this.emit((0, shared_1.buildIdeAuditEvent)('ide:tool-invocation-approved', {
                    ideId: this.ideId,
                    adapterId: this.adapterId,
                    sessionId: ctx.sessionHandle.sessionId,
                    initiatingUserId: ctx.initiatingUser.userId,
                    tool: ctx.tool,
                    detail: `TabnineAdapter: unknown surface '${ctx.tool}' -- allowed with audit.`,
                }));
                return this.routeToolInvocation(ctx, {
                    decision: 'ALLOW',
                    reason: `Unknown Tabnine surface '${ctx.tool}' permitted with audit.`,
                });
        }
    }
    // ---------------------------------------------------------------------------
    // 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}).`,
                metadata: { filePath, secretPattern: secretMatch },
            });
        }
        if (this._isHipaaBinding()) {
            const phiMatch = this._scanForPhiIndicators(surroundingContext);
            if (phiMatch) {
                const tier = this.tabnineConfig?.hipaaReviewerTier ?? 'T2_LEAD';
                return this.routeToolInvocation(ctx, {
                    decision: 'REQUIRE_APPROVAL',
                    reason: `Inline completion routed for HIPAA review: PHI indicator '${phiMatch}' in context. Tier: ${tier}.`,
                    metadata: { filePath, phiIndicator: phiMatch, tier },
                });
            }
        }
        const deployDecision = await this._checkDeploymentModeCompliance(ctx);
        if (deployDecision)
            return deployDecision;
        return this.routeToolInvocation(ctx, {
            decision: 'ALLOW',
            reason: `Inline completion request permitted by TabnineAdapter.`,
            metadata: { filePath, deploymentMode: this.tabnineConfig?.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.tabnineConfig?.hipaaReviewerTier ?? 'T2_LEAD';
                return this.routeToolInvocation(ctx, {
                    decision: 'REQUIRE_APPROVAL',
                    reason: `Chat request routed for HIPAA review: PHI indicator '${phiMatch}'. Tier: ${tier}.`,
                    metadata: { phiIndicator: phiMatch, tier },
                });
            }
        }
        const deployDecision = await this._checkDeploymentModeCompliance(ctx);
        if (deployDecision)
            return deployDecision;
        return this.routeToolInvocation(ctx, {
            decision: 'ALLOW',
            reason: 'Chat request permitted by TabnineAdapter.',
            metadata: { deploymentMode: this.tabnineConfig?.deploymentMode },
        });
    }
    // ---------------------------------------------------------------------------
    // Hook: codeReviewRequested
    // ---------------------------------------------------------------------------
    async _gateCodeReviewRequest(ctx) {
        const diff = String(ctx.input['diff'] ?? '');
        const filePath = String(ctx.input['filePath'] ?? ctx.input['file_path'] ?? '');
        const secretMatch = this._scanDiffForSecrets(diff);
        if (secretMatch) {
            return this.routeToolInvocation(ctx, {
                decision: 'DENY',
                reason: `Code review blocked: secret pattern '${secretMatch.pattern}' detected ` +
                    `in diff at line ${secretMatch.line}. Remove the secret before requesting ` +
                    `a code review. File: ${filePath || 'unknown'}.`,
                metadata: { filePath, secretPattern: secretMatch.pattern, diffLine: secretMatch.line },
            });
        }
        return this.routeToolInvocation(ctx, {
            decision: 'ALLOW',
            reason: `Code review request permitted by TabnineAdapter. Diff passed secret scan.`,
            metadata: { filePath, deploymentMode: this.tabnineConfig?.deploymentMode },
        });
    }
    // ---------------------------------------------------------------------------
    // Hook: testGenerationRequested
    // ---------------------------------------------------------------------------
    async _gateTestGenerationRequest(ctx) {
        const functionContext = String(ctx.input['functionContext'] ?? ctx.input['function'] ?? '');
        const targetFile = String(ctx.input['targetFile'] ?? '');
        const secretMatch = this._scanForSecrets(functionContext);
        if (secretMatch) {
            return this.routeToolInvocation(ctx, {
                decision: 'DENY',
                reason: `Test generation blocked: secret pattern '${secretMatch}' in function context.`,
                metadata: { targetFile, secretPattern: secretMatch },
            });
        }
        if (this._isHipaaBinding()) {
            const phiMatch = this._scanForPhiIndicators(functionContext);
            if (phiMatch) {
                const tier = this.tabnineConfig?.testGenReviewerTier ?? 'T2_LEAD';
                return this.routeToolInvocation(ctx, {
                    decision: 'REQUIRE_APPROVAL',
                    reason: `Test generation routed for HIPAA review: PHI indicator '${phiMatch}' in ` +
                        `function context. Ensure generated tests do not contain identifiable health ` +
                        `information. Tier: ${tier}.`,
                    metadata: { targetFile, phiIndicator: phiMatch, tier },
                });
            }
        }
        return this.routeToolInvocation(ctx, {
            decision: 'ALLOW',
            reason: `Test generation request permitted by TabnineAdapter.`,
            metadata: { targetFile, deploymentMode: this.tabnineConfig?.deploymentMode },
        });
    }
    // ---------------------------------------------------------------------------
    // Hook: documentationRequested
    // ---------------------------------------------------------------------------
    async _gateDocumentationRequest(ctx) {
        const symbolContext = String(ctx.input['symbolContext'] ?? ctx.input['symbol'] ?? '');
        const targetFile = String(ctx.input['targetFile'] ?? '');
        const secretMatch = this._scanForSecrets(symbolContext);
        if (secretMatch) {
            return this.routeToolInvocation(ctx, {
                decision: 'DENY',
                reason: `Documentation generation blocked: secret pattern '${secretMatch}' in symbol context.`,
                metadata: { targetFile, secretPattern: secretMatch },
            });
        }
        if (this._isHipaaBinding()) {
            const phiMatch = this._scanForPhiIndicators(symbolContext);
            if (phiMatch) {
                const tier = this.tabnineConfig?.hipaaReviewerTier ?? 'T2_LEAD';
                return this.routeToolInvocation(ctx, {
                    decision: 'REQUIRE_APPROVAL',
                    reason: `Documentation generation routed for HIPAA review: PHI indicator '${phiMatch}'. Tier: ${tier}.`,
                    metadata: { targetFile, phiIndicator: phiMatch, tier },
                });
            }
        }
        return this.routeToolInvocation(ctx, {
            decision: 'ALLOW',
            reason: `Documentation generation request permitted by TabnineAdapter.`,
            metadata: { targetFile, deploymentMode: this.tabnineConfig?.deploymentMode },
        });
    }
    // ---------------------------------------------------------------------------
    // Hook: commitMessageRequested
    // ---------------------------------------------------------------------------
    async _gateCommitMessageRequest(ctx) {
        const diff = String(ctx.input['diff'] ?? '');
        const branchName = String(ctx.input['branchName'] ?? ctx.input['branch'] ?? 'unknown');
        const secretMatch = this._scanDiffForSecrets(diff);
        if (secretMatch) {
            return this.routeToolInvocation(ctx, {
                decision: 'DENY',
                reason: `Commit message generation blocked: secret pattern '${secretMatch.pattern}' ` +
                    `detected in diff at line ${secretMatch.line}. ` +
                    `Remove the secret from staged changes before generating a commit message. ` +
                    `Branch: ${branchName}.`,
                metadata: {
                    branchName,
                    secretPattern: secretMatch.pattern,
                    diffLine: secretMatch.line,
                },
            });
        }
        return this.routeToolInvocation(ctx, {
            decision: 'ALLOW',
            reason: `Commit message generation permitted. Diff passed secret scan.`,
            metadata: { branchName },
        });
    }
    // ---------------------------------------------------------------------------
    // Hook: learnFromCodebaseRequested (PT-ADAPT-TABNINE-4)
    //
    // This is the Tabnine-distinct privacy-critical surface. Tabnine Enterprise
    // can train a local model on customer code. This surface is HEAVILY governable:
    // the training corpus may contain PII, IP, secrets, or regulated data.
    //
    // Strict-mode enforcement (default-deny, explicit-opt-in):
    //   'pro' mode:
    //     ALWAYS DENIED. Pro has no on-premises training capability. The denial
    //     reason carries the code "tabnine-learn-from-codebase-not-permitted-in-mode".
    //
    //   'tabnine-cloud' mode:
    //     ALWAYS DENIED. Training on Tabnine servers means data leaves the customer
    //     network perimeter. The denial reason carries the same code.
    //
    //   'enterprise-self-hosted' WITHOUT learnFromCodebase: true in config:
    //     DENIED. Operator must make an explicit governance decision to enable
    //     on-prem learning. The denial reason carries the same code.
    //
    //   'enterprise-self-hosted' WITH learnFromCodebase: true in config:
    //     ALLOWED -- the operator has opted in. The training corpus is still
    //     scanned for embedded secrets as a best-effort guard.
    // ---------------------------------------------------------------------------
    async wrapLearnFromCodebase(ctx) {
        this.ensureInitialized();
        const mode = this.tabnineConfig?.deploymentMode ?? 'pro';
        // PT-ADAPT-TABNINE-4: strict mode check.
        if (!exports.TABNINE_LEARN_CAPABLE_MODES.includes(mode)) {
            const decision = {
                decision: 'DENY',
                reason: `Tabnine learnFromCodebase blocked: deployment mode '${mode}' does not support ` +
                    `on-premises model training. ` +
                    `On-prem learning requires 'enterprise-self-hosted' mode. ` +
                    `In '${mode}' mode, training data would leave the customer network perimeter ` +
                    `or is not supported. ` +
                    `Reason code: tabnine-learn-from-codebase-not-permitted-in-mode.`,
                metadata: {
                    deploymentMode: mode,
                    reasonCode: 'tabnine-learn-from-codebase-not-permitted-in-mode',
                    repos: ctx.repos,
                },
            };
            this.emit((0, shared_1.buildIdeAuditEvent)('ide:tool-invocation-denied', {
                ideId: this.ideId,
                adapterId: this.adapterId,
                tool: 'tabnine:learn-from-codebase-requested',
                sessionId: ctx.sessionId,
                detail: decision.reason,
                data: {
                    deploymentMode: mode,
                    reasonCode: 'tabnine-learn-from-codebase-not-permitted-in-mode',
                    repoCount: ctx.repos.length,
                },
            }));
            return decision;
        }
        // 'enterprise-self-hosted': require explicit opt-in flag.
        if (this.tabnineConfig?.learnFromCodebase !== true) {
            const decision = {
                decision: 'DENY',
                reason: `Tabnine learnFromCodebase blocked: 'enterprise-self-hosted' mode detected but ` +
                    `the adapter config does not carry an explicit opt-in (learnFromCodebase: true). ` +
                    `This is a deliberate governance default-deny. To enable on-premises codebase ` +
                    `learning, set learnFromCodebase: true in TabnineAdapterConfig and acknowledge ` +
                    `that the training corpus will be scanned for secrets and PHI before the ` +
                    `learn operation proceeds. ` +
                    `Reason code: tabnine-learn-from-codebase-not-permitted-in-mode.`,
                metadata: {
                    deploymentMode: mode,
                    reasonCode: 'tabnine-learn-from-codebase-not-permitted-in-mode',
                    repos: ctx.repos,
                },
            };
            this.emit((0, shared_1.buildIdeAuditEvent)('ide:tool-invocation-denied', {
                ideId: this.ideId,
                adapterId: this.adapterId,
                tool: 'tabnine:learn-from-codebase-requested',
                sessionId: ctx.sessionId,
                detail: decision.reason,
                data: {
                    deploymentMode: mode,
                    reasonCode: 'tabnine-learn-from-codebase-not-permitted-in-mode',
                    repoCount: ctx.repos.length,
                },
            }));
            return decision;
        }
        // Opt-in confirmed. Scan dataScope for embedded secrets as a best-effort guard.
        if (ctx.dataScope) {
            const secretMatch = this._scanForSecrets(ctx.dataScope);
            if (secretMatch) {
                const decision = {
                    decision: 'DENY',
                    reason: `Tabnine learnFromCodebase blocked: secret pattern '${secretMatch}' found ` +
                        `in dataScope. Remove credentials from the data scope before initiating ` +
                        `codebase learning. Repos: ${ctx.repos.slice(0, 3).join(', ')}.`,
                    metadata: {
                        deploymentMode: mode,
                        secretPattern: secretMatch,
                        repos: ctx.repos,
                    },
                };
                this.emit((0, shared_1.buildIdeAuditEvent)('ide:tool-invocation-denied', {
                    ideId: this.ideId,
                    adapterId: this.adapterId,
                    tool: 'tabnine:learn-from-codebase-requested',
                    sessionId: ctx.sessionId,
                    detail: decision.reason,
                    data: { deploymentMode: mode, secretPattern: secretMatch },
                }));
                return decision;
            }
        }
        this.emit((0, shared_1.buildIdeAuditEvent)('ide:tool-invocation-approved', {
            ideId: this.ideId,
            adapterId: this.adapterId,
            tool: 'tabnine:learn-from-codebase-requested',
            sessionId: ctx.sessionId,
            detail: `Tabnine learnFromCodebase permitted. ` +
                `Operator opt-in confirmed. Repos: ${ctx.repos.length}. ` +
                `Mode: enterprise-self-hosted.`,
            data: { repos: ctx.repos, deploymentMode: mode, dataScope: ctx.dataScope ?? 'unspecified' },
        }));
        return {
            decision: 'ALLOW',
            reason: `learnFromCodebase permitted for ${ctx.repos.length} repo(s). Operator opt-in confirmed.`,
            metadata: { repos: ctx.repos, deploymentMode: mode },
        };
    }
    // ---------------------------------------------------------------------------
    // Sync convenience wrappers (matching adapter pattern from other IDE adapters)
    // ---------------------------------------------------------------------------
    async wrapInlineCompletion(ctx) {
        this.ensureInitialized();
        return this._gateInlineCompletionRequest({
            tool: 'tabnine:inline-completion-requested',
            input: {
                surroundingContext: ctx.surroundingContext ?? '',
                filePath: ctx.filePath,
                cursorPosition: ctx.cursorPosition,
            },
            sessionHandle: { sessionId: ctx.sessionId ?? 'tabnine-session', openedAt: new Date().toISOString() },
            initiatingUser: { userId: 'tabnine-user', attributionMethod: 'cursor-composer' },
            timestamp: new Date().toISOString(),
        });
    }
    async wrapChat(ctx) {
        this.ensureInitialized();
        return this._gateChatRequest({
            tool: 'tabnine:chat-requested',
            input: { promptText: ctx.promptText },
            sessionHandle: { sessionId: ctx.sessionId ?? 'tabnine-session', openedAt: new Date().toISOString() },
            initiatingUser: { userId: 'tabnine-user', attributionMethod: 'cursor-composer' },
            timestamp: new Date().toISOString(),
        });
    }
    async wrapCodeReview(ctx) {
        this.ensureInitialized();
        return this._gateCodeReviewRequest({
            tool: 'tabnine:code-review-requested',
            input: { diff: ctx.diff, filePath: ctx.filePath ?? '' },
            sessionHandle: { sessionId: ctx.sessionId ?? 'tabnine-session', openedAt: new Date().toISOString() },
            initiatingUser: { userId: 'tabnine-user', attributionMethod: 'cursor-composer' },
            timestamp: new Date().toISOString(),
        });
    }
    async wrapTestGeneration(ctx) {
        this.ensureInitialized();
        return this._gateTestGenerationRequest({
            tool: 'tabnine:test-generation-requested',
            input: { functionContext: ctx.functionContext, targetFile: ctx.targetFile ?? '' },
            sessionHandle: { sessionId: ctx.sessionId ?? 'tabnine-session', openedAt: new Date().toISOString() },
            initiatingUser: { userId: 'tabnine-user', attributionMethod: 'cursor-composer' },
            timestamp: new Date().toISOString(),
        });
    }
    async wrapDocumentation(ctx) {
        this.ensureInitialized();
        return this._gateDocumentationRequest({
            tool: 'tabnine:documentation-requested',
            input: { symbolContext: ctx.symbolContext, targetFile: ctx.targetFile ?? '' },
            sessionHandle: { sessionId: ctx.sessionId ?? 'tabnine-session', openedAt: new Date().toISOString() },
            initiatingUser: { userId: 'tabnine-user', attributionMethod: 'cursor-composer' },
            timestamp: new Date().toISOString(),
        });
    }
    async wrapCommitMessage(ctx) {
        this.ensureInitialized();
        return this._gateCommitMessageRequest({
            tool: 'tabnine:commit-message-requested',
            input: { diff: ctx.diff, branchName: ctx.branchName ?? 'unknown' },
            sessionHandle: { sessionId: ctx.sessionId ?? 'tabnine-session', openedAt: new Date().toISOString() },
            initiatingUser: { userId: 'tabnine-user', attributionMethod: 'cursor-composer' },
            timestamp: new Date().toISOString(),
        });
    }
    // ---------------------------------------------------------------------------
    // interceptToolInvocation dispatch helpers for Tabnine-specific surfaces
    // ---------------------------------------------------------------------------
    async _gateLearnFromCodebaseRequest(ctx) {
        return this.wrapLearnFromCodebase({
            repos: ctx.input['repos'] ?? [],
            dataScope: ctx.input['dataScope'],
            sessionId: ctx.sessionHandle.sessionId,
        });
    }
    // ---------------------------------------------------------------------------
    // Capabilities
    // ---------------------------------------------------------------------------
    getIdeCapabilities() {
        return {
            runtimeHooks: true,
            prBoundary: false,
            userIdentity: true,
            reviewerRouting: true,
            integrationMode: 'tabnine-normalised-event-shim',
        };
    }
    // ---------------------------------------------------------------------------
    // Error translation
    // ---------------------------------------------------------------------------
    translateError(raw) {
        if (raw instanceof index_1.IdeAttributionError)
            return raw;
        if (raw instanceof index_1.IdeUnsupportedError)
            return raw;
        return super.translateError(raw);
    }
    // ---------------------------------------------------------------------------
    // Public accessors for diagnostics / tests
    // ---------------------------------------------------------------------------
    getDeploymentMode() {
        return this.tabnineConfig?.deploymentMode ?? 'pro';
    }
    isLearnFromCodebaseEnabled() {
        return (this.tabnineConfig?.deploymentMode === 'enterprise-self-hosted' &&
            this.tabnineConfig?.learnFromCodebase === true);
    }
    // ---------------------------------------------------------------------------
    // Helpers
    // ---------------------------------------------------------------------------
    _scanForSecrets(text) {
        if (!text)
            return null;
        for (const { name, re } of TABNINE_SECRET_PATTERNS) {
            if (re.test(text))
                return name;
        }
        return null;
    }
    _scanForPhiIndicators(text) {
        if (!text)
            return null;
        for (const re of TABNINE_PHI_INDICATORS) {
            if (re.test(text))
                return re.source;
        }
        return null;
    }
    _isHipaaBinding() {
        const packId = this.config?.packId ?? '';
        return packId === 'hipaa' || packId.startsWith('hipaa');
    }
    /**
     * Scan a unified diff for secrets. Returns the first finding or null.
     * Only scans added lines (lines starting with '+').
     */
    _scanDiffForSecrets(diff) {
        const lines = diff.split('\n');
        for (let i = 0; i < lines.length; i++) {
            const line = lines[i];
            if (!line || !line.startsWith('+') || line.startsWith('+++'))
                continue;
            const content = line.slice(1);
            for (const { name, re } of TABNINE_SECRET_PATTERNS) {
                if (re.test(content)) {
                    return { line: i + 1, pattern: name };
                }
            }
        }
        return null;
    }
    /**
     * Check deployment-mode-level compliance for the active pack binding.
     * Returns a DENY decision if 'pro' mode is used under a regulated pack binding
     * (pro has no BAA eligibility), or null if no issue.
     */
    async _checkDeploymentModeCompliance(ctx) {
        if (!this._isHipaaBinding())
            return null;
        const mode = this.tabnineConfig?.deploymentMode ?? 'pro';
        if (!exports.TABNINE_BAA_ELIGIBLE_MODES.includes(mode)) {
            return this.routeToolInvocation(ctx, {
                decision: 'DENY',
                reason: `TabnineAdapter: deployment mode '${mode}' is not BAA-eligible for HIPAA pack ` +
                    `binding '${this.config?.packId}'. ` +
                    `Switch to 'enterprise-self-hosted' or 'tabnine-cloud' (with an active ` +
                    `Tabnine BAA) before using Tabnine on HIPAA-regulated code.`,
                metadata: {
                    deploymentMode: mode,
                    hipaaPackId: this.config?.packId,
                    eligibleModes: [...exports.TABNINE_BAA_ELIGIBLE_MODES],
                },
            });
        }
        return null;
    }
}
exports.TabnineAdapter = TabnineAdapter;
//# sourceMappingURL=tabnine.js.map