"use strict";
/**
 * NotebookAIAdapter -- unified Notebook AI governance adapter.
 *
 * Governs AI-assisted notebook surfaces across four platforms via a
 * `platform` discriminator on the config:
 *
 *   jupyter-ai   -- JupyterLab built-in AI (self-hosted or on-prem);
 *                   multi-tenant capable; requires instanceUrl.
 *   hex-ai       -- Hex AI (cloud SaaS, team-based); high data-egress
 *                   sensitivity (Snowflake/BigQuery sample rows).
 *   deepnote-ai  -- Deepnote AI (cloud SaaS, team workspaces); integrates
 *                   cloud DBs and cloud storage.
 *   colab-ai     -- Google Colab AI (cloud SaaS by Google); all notebook
 *                   content is sent to Google by design.
 *
 * Why unified vs four separate adapters:
 *   All four platforms expose the same governable surface: cells, kernel,
 *   datasets, and exports. The platform-specific differences are deployment
 *   shape and data-residency concerns, not method shape. A unified adapter
 *   with a `platform` discriminator keeps the contract simple and lets
 *   healthcare-research customers standardize across multiple platforms.
 *
 * Hook surfaces (9):
 *
 *   wrapCellGeneration(prompt, language?)
 *     AI generates a new cell (code or markdown) from a prompt.
 *     Secret scan applied to generated content before placement.
 *
 *   wrapCellEdit(cellId, originalContent, instruction)
 *     AI rewrites / refactors an existing cell.
 *     Secret scan applied to the edited content.
 *
 *   wrapCellExplain(cellId, cellContent)
 *     AI explains an existing cell (read-only). Lower risk surface.
 *
 *   wrapKernelExecution(cellId, cellContent, language?)
 *     AI proposes / triggers cell execution in the kernel. HIGH RISK.
 *     Denied unless allowKernelExecution: true.
 *     In air-gapped mode: additionally denied if cell contains network-egress
 *     patterns (requests.get, urllib, socket, etc.).
 *     Minor / student safeguard: ALWAYS denied regardless of opt-in.
 *
 *   wrapDatasetIntrospection(source, sampleRows?)
 *     AI reads dataset schema or sample rows. PRIVACY-CRITICAL.
 *     PHI scan applied to sample rows (SSN, MRN, ICD-10 patterns).
 *     Cloud modes: source must be in allowedDatasetSources if set.
 *     Air-gapped mode: non-local sources (not file://) denied.
 *
 *   wrapChartGeneration(source, chartType?)
 *     AI generates a chart from a dataset.
 *     Same dataset-source allowlist gate as introspection.
 *
 *   wrapNotebookExport(format, exportTarget?)
 *     AI helps export the notebook (HTML, PDF, slides).
 *     Air-gapped: only local file targets allowed; upload denied.
 *     Education mode: default-deny unless allowExport: true is explicit.
 *
 *   wrapChat(prompt)
 *     Assistant panel chat. Secret scan applied.
 *
 *   wrapSlashCommand(command, args?)
 *     Slash commands: /explain, /fix, /optimize, etc.
 *
 * Platform-specific config validation:
 *   jupyter-ai:           instanceUrl required; rejected for cloud platforms.
 *   hex-ai/deepnote-ai/colab-ai: instanceUrl rejected (managed by vendor).
 *
 * Deployment modes:
 *   notebook-ai-multi-tenant-cloud -- Hex, Deepnote, Colab default.
 *   notebook-ai-self-hosted        -- Jupyter on-prem.
 *   notebook-ai-air-gapped         -- Jupyter offline / sovereign deployment.
 *   notebook-ai-education          -- Any platform with minor/student users.
 *
 * Strict-mode enforcement (the differentiators):
 *   - Air-gapped + kernel execution: denied if cell contains egress patterns.
 *   - Air-gapped + dataset introspection: non-local source denied.
 *   - Air-gapped + notebook export: upload / non-local target denied.
 *   - Cloud + dataset allowlist: source NOT in list denied.
 *   - PHI scan: SSN, MRN, ICD-10 patterns in sample rows trigger redaction.
 *   - Minor / student + kernel execution: ALWAYS denied (non-negotiable).
 *   - Cell generation / edit: secret scan on generated content.
 *   - Multi-cell edit cap: batch edits beyond cap denied.
 *   - Education mode + export: default-deny unless allowExport: true.
 *
 * Pen-tests:
 *   PT-ADAPT-NOTEBOOK-1: empty packBinding[] denied at every wrap call
 *   PT-ADAPT-NOTEBOOK-2: API keys / dataset connection strings never in audits
 *   PT-ADAPT-NOTEBOOK-3: malformed config throws at construction
 *   PT-ADAPT-NOTEBOOK-4: minor + kernel execution ALWAYS denied
 *   PT-ADAPT-NOTEBOOK-5: air-gapped + network-egress code ALWAYS denied
 *   PT-ADAPT-NOTEBOOK-6: dataset introspection PHI pattern triggers redaction
 *   PT-ADAPT-NOTEBOOK-7: cell generation with secret content denied
 *
 * Spec: P3-SUB-IDE-10
 *
 * @connexum/ai-governance
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.NotebookAIAdapter = exports.NOTEBOOK_AI_VALID_USER_TIERS = exports.NOTEBOOK_AI_VALID_DEPLOYMENT_MODES = exports.NOTEBOOK_AI_CLOUD_PLATFORMS = exports.NOTEBOOK_AI_VALID_PLATFORMS = void 0;
const base_1 = require("./base");
const index_1 = require("./index");
const shared_1 = require("./shared");
exports.NOTEBOOK_AI_VALID_PLATFORMS = [
    'jupyter-ai',
    'hex-ai',
    'deepnote-ai',
    'colab-ai',
];
/** Cloud-managed platforms (instanceUrl is rejected for these). */
exports.NOTEBOOK_AI_CLOUD_PLATFORMS = [
    'hex-ai',
    'deepnote-ai',
    'colab-ai',
];
exports.NOTEBOOK_AI_VALID_DEPLOYMENT_MODES = [
    'notebook-ai-multi-tenant-cloud',
    'notebook-ai-self-hosted',
    'notebook-ai-air-gapped',
    'notebook-ai-education',
];
exports.NOTEBOOK_AI_VALID_USER_TIERS = [
    'professional',
    'student',
    'researcher',
    'admin',
];
// ---------------------------------------------------------------------------
// Network egress patterns (air-gapped kernel execution guard)
// ---------------------------------------------------------------------------
const KERNEL_EGRESS_PATTERNS = [
    /\brequests\.get\s*\(/,
    /\brequests\.post\s*\(/,
    /\brequests\.put\s*\(/,
    /\burllib\.request\b/,
    /\burllib\.urlopen\s*\(/,
    /\burllib3\b/,
    /\bsocket\.connect\s*\(/,
    /\bsocket\.create_connection\s*\(/,
    /\bhttplib2\b/,
    /\bhttpx\b/,
    /\baiohttp\b/,
    /\bfetch\s*\(/,
    /\baxios\s*\./,
    /\bhttp\.get\s*\(/,
    /\bhttp\.request\s*\(/,
    /\bhttps\.get\s*\(/,
    /\bhttps\.request\s*\(/,
];
function detectKernelEgress(code) {
    if (!code)
        return null;
    for (const pattern of KERNEL_EGRESS_PATTERNS) {
        if (pattern.test(code))
            return pattern;
    }
    return null;
}
// ---------------------------------------------------------------------------
// Secret scan patterns (cell content guard)
// ---------------------------------------------------------------------------
const NOTEBOOK_SECRET_PATTERNS = [
    { name: 'AWS access key', re: /AKIA[0-9A-Z]{16}/ },
    { name: 'AWS secret key', re: /aws_secret_access_key\s*=\s*\S{20,}/i },
    { 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|DSA|ED25519) PRIVATE KEY-----/ },
    { name: 'Generic secret env var', re: /(SECRET|PASSWORD|PASSWD|API_KEY|PRIVATE_KEY|ACCESS_TOKEN)\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|snowflake|bigquery):\/\/[^:]+:[^@]+@/i },
    { name: 'Snowflake account key', re: /snowflake[_\-]?(account|key|password|token)\s*[:=]\s*["']?\S{8,}["']?/i },
    { name: 'Connection string', re: /Server\s*=\s*[^;]{4,};.*Password\s*=\s*[^;]{4,}/i },
];
function scanCellForSecrets(content) {
    if (!content)
        return null;
    for (const pattern of NOTEBOOK_SECRET_PATTERNS) {
        if (pattern.re.test(content))
            return pattern;
    }
    return null;
}
// ---------------------------------------------------------------------------
// PHI scan patterns (dataset introspection guard)
// ---------------------------------------------------------------------------
const PHI_PATTERNS = [
    { name: 'SSN pattern', re: /\b\d{3}-\d{2}-\d{4}\b/ },
    { name: 'MRN-like pattern', re: /\b(mrn|medical[_\s]?record[_\s]?(number|no|#?))\s*[:=]?\s*[A-Z0-9\-]{4,}/i },
    { name: 'ICD-10 code', re: /\b[A-Z]\d{2}(\.\d{1,4})?\b/ },
    { name: 'Patient name field', re: /\b(patient|pt)[_\s]?(name|first|last)\b/i },
    { name: 'Date of birth', re: /\b(dob|date[_\s]?of[_\s]?birth)\b/i },
    { name: 'Diagnosis field', re: /\bdiagnos(is|es|tic)\b/i },
    { name: 'PHI marker', re: /\bphi\b/i },
    { name: 'HIPAA indicator', re: /\bhipaa\b/i },
];
function scanForPHI(text) {
    if (!text)
        return null;
    for (const pattern of PHI_PATTERNS) {
        if (pattern.re.test(text))
            return pattern;
    }
    return null;
}
// ---------------------------------------------------------------------------
// Dataset source allowlist check
// ---------------------------------------------------------------------------
function isDatasetSourceAllowed(source, allowedSources, deploymentMode) {
    // Air-gapped: only local sources (file:// or local prefix)
    if (deploymentMode === 'notebook-ai-air-gapped') {
        const isLocal = source.startsWith('file://') || source.startsWith('/') || source.startsWith('./');
        if (!isLocal) {
            return {
                allowed: false,
                reason: `notebook-ai-dataset-source-not-local-in-air-gapped: source '${source}' is not a local path. Air-gapped mode only permits file:// or local-path sources.`,
            };
        }
        return { allowed: true };
    }
    // Cloud modes with an explicit allowlist: source must match
    if (allowedSources.length > 0) {
        const matched = allowedSources.some((allowed) => {
            return source === allowed || source.includes(allowed) || allowed.includes(source);
        });
        if (!matched) {
            return {
                allowed: false,
                reason: `notebook-ai-dataset-source-not-in-allowlist: source '${source}' is not in allowedDatasetSources. Permitted sources: ${allowedSources.join(', ')}.`,
            };
        }
    }
    return { allowed: true };
}
// ---------------------------------------------------------------------------
// NotebookAIAdapter
// ---------------------------------------------------------------------------
class NotebookAIAdapter extends base_1.BaseIdeAdapter {
    adapterId = 'notebook-ai';
    ideId = 'notebook-ai';
    platform = 'jupyter-ai';
    deploymentMode = 'notebook-ai-multi-tenant-cloud';
    userTier = 'professional';
    userIsMinor = false;
    instanceUrl = null;
    allowedDatasetSources = [];
    allowKernelExecution = false;
    allowedKernelLanguages = null;
    allowExport = true;
    cellExecutionTimeMs = 30000;
    multiCellEditCap = 25;
    instructorOverride = false;
    // ---------------------------------------------------------------------------
    // Lifecycle
    // ---------------------------------------------------------------------------
    initialize(config) {
        // Base performs packId guard
        super.initialize(config);
        // PT-ADAPT-NOTEBOOK-3: platform is required
        const platform = config.platform;
        if (!platform) {
            throw new index_1.IdeSessionError({
                message: 'NotebookAIAdapter: platform is required. Must be one of: ' +
                    exports.NOTEBOOK_AI_VALID_PLATFORMS.join(', ') + '.',
                ideId: this.ideId,
                adapterId: this.adapterId,
            });
        }
        if (!exports.NOTEBOOK_AI_VALID_PLATFORMS.includes(platform)) {
            throw new index_1.IdeSessionError({
                message: `NotebookAIAdapter: invalid platform '${platform}'. ` +
                    `Valid platforms: ${exports.NOTEBOOK_AI_VALID_PLATFORMS.join(', ')}.`,
                ideId: this.ideId,
                adapterId: this.adapterId,
            });
        }
        // Derive default deployment mode from platform
        const defaultMode = platform === 'jupyter-ai' ? 'notebook-ai-self-hosted' : 'notebook-ai-multi-tenant-cloud';
        const mode = config.deploymentMode ?? defaultMode;
        if (!exports.NOTEBOOK_AI_VALID_DEPLOYMENT_MODES.includes(mode)) {
            throw new index_1.IdeSessionError({
                message: `NotebookAIAdapter: invalid deploymentMode '${mode}'. ` +
                    `Valid modes: ${exports.NOTEBOOK_AI_VALID_DEPLOYMENT_MODES.join(', ')}.`,
                ideId: this.ideId,
                adapterId: this.adapterId,
            });
        }
        // PT-ADAPT-NOTEBOOK-3: jupyter-ai requires instanceUrl; cloud platforms reject it
        const isCloudPlatform = exports.NOTEBOOK_AI_CLOUD_PLATFORMS.includes(platform);
        if (platform === 'jupyter-ai' && !config.instanceUrl) {
            throw new index_1.IdeSessionError({
                message: 'NotebookAIAdapter: instanceUrl is required for jupyter-ai. ' +
                    'Jupyter AI is a self-hosted platform -- provide the base URL of ' +
                    'your JupyterLab instance (e.g. "https://jupyter.acme.com").',
                ideId: this.ideId,
                adapterId: this.adapterId,
            });
        }
        if (isCloudPlatform && config.instanceUrl) {
            throw new index_1.IdeSessionError({
                message: `NotebookAIAdapter: instanceUrl is not permitted for platform '${platform}'. ` +
                    'Cloud-managed platforms (hex-ai, deepnote-ai, colab-ai) are operated by the ' +
                    'vendor -- there is no customer-controlled instance URL. Remove instanceUrl ' +
                    'from your config for this platform.',
                ideId: this.ideId,
                adapterId: this.adapterId,
            });
        }
        // Validate user tier
        const userTier = config.userTier ?? 'professional';
        if (!exports.NOTEBOOK_AI_VALID_USER_TIERS.includes(userTier)) {
            throw new index_1.IdeSessionError({
                message: `NotebookAIAdapter: invalid userTier '${userTier}'. ` +
                    `Valid tiers: ${exports.NOTEBOOK_AI_VALID_USER_TIERS.join(', ')}.`,
                ideId: this.ideId,
                adapterId: this.adapterId,
            });
        }
        // Validate caps
        const multiCellEditCap = config.multiCellEditCap ?? 25;
        if (typeof multiCellEditCap !== 'number' || multiCellEditCap < 1) {
            throw new index_1.IdeSessionError({
                message: `NotebookAIAdapter: multiCellEditCap must be a positive number. Got: ${String(multiCellEditCap)}.`,
                ideId: this.ideId,
                adapterId: this.adapterId,
            });
        }
        const cellExecutionTimeMs = config.cellExecutionTimeMs ?? 30000;
        if (typeof cellExecutionTimeMs !== 'number' || cellExecutionTimeMs < 0) {
            throw new index_1.IdeSessionError({
                message: `NotebookAIAdapter: cellExecutionTimeMs must be a non-negative number. Got: ${String(cellExecutionTimeMs)}.`,
                ideId: this.ideId,
                adapterId: this.adapterId,
            });
        }
        // Resolve allowExport default: false in education mode or student tier
        const isEducation = mode === 'notebook-ai-education' ||
            userTier === 'student';
        const allowExport = config.allowExport ?? !isEducation;
        this.platform = platform;
        this.deploymentMode = mode;
        this.userTier = userTier;
        this.userIsMinor = config.userIsMinor ?? false;
        this.instanceUrl = config.instanceUrl ?? null;
        this.allowedDatasetSources = config.allowedDatasetSources ?? [];
        this.allowKernelExecution = config.allowKernelExecution ?? false;
        this.allowedKernelLanguages =
            config.allowedKernelLanguages && config.allowedKernelLanguages.length > 0
                ? config.allowedKernelLanguages
                : null;
        this.allowExport = allowExport;
        this.cellExecutionTimeMs = cellExecutionTimeMs;
        this.multiCellEditCap = multiCellEditCap;
        this.instructorOverride = config.instructorOverride ?? false;
    }
    // ---------------------------------------------------------------------------
    // IdeAdapter: resolveUserIdentity
    // ---------------------------------------------------------------------------
    async resolveUserIdentity(session) {
        this.ensureInitialized();
        const envUser = process.env['NOTEBOOK_SESSION_USER']?.trim() ||
            process.env['CLAUDE_SESSION_USER']?.trim();
        if (envUser) {
            const identity = { userId: envUser, attributionMethod: 'session-env' };
            this.emitIdentityResolved(envUser, session.clientSessionId);
            return identity;
        }
        const ctxUser = session.identity?.userId?.trim() ||
            session.metadata?.['notebookUserId']?.trim() ||
            session.clientSessionId?.trim();
        if (ctxUser) {
            const identity = { userId: ctxUser, attributionMethod: 'session-env' };
            if (session.identity?.email)
                identity.email = session.identity.email;
            this.emitIdentityResolved(ctxUser, session.clientSessionId);
            return identity;
        }
        if (this.config?.allowAnonymousIdeSessions) {
            const identity = { userId: 'anonymous', attributionMethod: 'session-env' };
            this.emitIdentityResolved('anonymous', session.clientSessionId);
            return identity;
        }
        this.emitIdentityUnresolved(session.clientSessionId);
        throw new index_1.IdeAttributionError({
            message: 'NotebookAIAdapter: cannot resolve user identity. Set the ' +
                'NOTEBOOK_SESSION_USER or CLAUDE_SESSION_USER environment variable, or ' +
                'provide a userId in the session context. To permit anonymous sessions, ' +
                'set allowAnonymousIdeSessions: true in adapter config.',
            ideId: this.ideId,
            adapterId: this.adapterId,
        });
    }
    // ---------------------------------------------------------------------------
    // IdeAdapter: getIdeCapabilities
    // ---------------------------------------------------------------------------
    getIdeCapabilities() {
        return {
            runtimeHooks: true,
            prBoundary: false,
            userIdentity: true,
            reviewerRouting: true,
            integrationMode: `${this.platform}/${this.deploymentMode}`,
        };
    }
    // ---------------------------------------------------------------------------
    // IdeAdapter: interceptToolInvocation -- dispatcher
    // ---------------------------------------------------------------------------
    async interceptToolInvocation(ctx) {
        this.ensureInitialized();
        const packBinding = this.config?.packId;
        if (!packBinding || packBinding.trim() === '') {
            return this.routeToolInvocation(ctx, {
                decision: 'DENY',
                reason: 'NotebookAIAdapter: tool invocation denied -- packBinding is empty. ' +
                    'Pack-binding primacy: every invocation MUST carry a pack binding.',
            });
        }
        const { tool, input } = ctx;
        switch (tool) {
            case 'cellGenerationRequested':
                return this.wrapCellGeneration(ctx, String(input['prompt'] ?? ''), input['language']);
            case 'cellEditRequested':
                return this.wrapCellEdit(ctx, String(input['cellId'] ?? ''), String(input['originalContent'] ?? ''), String(input['instruction'] ?? ''), input['cellCount']);
            case 'cellExplainRequested':
                return this.wrapCellExplain(ctx, String(input['cellId'] ?? ''), String(input['cellContent'] ?? ''));
            case 'kernelExecutionRequested':
                return this.wrapKernelExecution(ctx, String(input['cellId'] ?? ''), String(input['cellContent'] ?? ''), input['language']);
            case 'datasetIntrospectionRequested':
                return this.wrapDatasetIntrospection(ctx, String(input['source'] ?? ''), input['sampleRows']);
            case 'chartGenerationRequested':
                return this.wrapChartGeneration(ctx, String(input['source'] ?? ''), input['chartType']);
            case 'notebookExportRequested':
                return this.wrapNotebookExport(ctx, String(input['format'] ?? ''), input['exportTarget']);
            case 'chatRequested':
                return this.wrapChat(ctx, String(input['prompt'] ?? ''));
            case 'slashCommandRequested':
                return this.wrapSlashCommand(ctx, String(input['command'] ?? ''), input['args']);
            default:
                return this.routeToolInvocation(ctx, {
                    decision: 'ALLOW',
                    reason: `NotebookAIAdapter: unknown surface '${tool}' -- allowed with audit.`,
                });
        }
    }
    // ---------------------------------------------------------------------------
    // Pack-binding guard helper
    // ---------------------------------------------------------------------------
    checkPackBinding(ctx) {
        const packBinding = this.config?.packId;
        if (!packBinding || packBinding.trim() === '') {
            return {
                decision: 'DENY',
                reason: 'NotebookAIAdapter: tool invocation denied -- packBinding is empty. ' +
                    'Pack-binding primacy: every invocation MUST carry a pack binding.',
            };
        }
        return null;
    }
    // ---------------------------------------------------------------------------
    // Minor / student kernel execution guard helper
    // ---------------------------------------------------------------------------
    checkMinorKernelExecution(ctx) {
        const isMinorOrStudent = this.userIsMinor || this.userTier === 'student';
        if (!isMinorOrStudent)
            return null;
        // instructorOverride=true AND userTier='admin' can override student gate
        // but NOT the minor (COPPA) gate -- minor is always denied.
        if (this.instructorOverride && this.userTier === 'admin' && !this.userIsMinor) {
            return null;
        }
        const reasonCode = this.userIsMinor
            ? 'notebook-ai-kernel-execution-not-permitted-minor'
            : 'notebook-ai-kernel-execution-not-permitted-student';
        return {
            decision: 'DENY',
            reason: `NotebookAIAdapter: kernel execution denied for ${this.userIsMinor ? 'minor' : 'student'} user. ` +
                'Kernel execution (running code in a live kernel) is not permitted for minor or student ' +
                'users regardless of allowKernelExecution configuration. This safeguard is non-negotiable. ' +
                `Reason code: ${reasonCode}.`,
            metadata: { reasonCode },
        };
    }
    // ---------------------------------------------------------------------------
    // Surface: wrapCellGeneration
    // ---------------------------------------------------------------------------
    async wrapCellGeneration(ctx, prompt, language) {
        this.ensureInitialized();
        const deny = this.checkPackBinding(ctx);
        if (deny)
            return this.routeToolInvocation(ctx, deny);
        // PT-ADAPT-NOTEBOOK-7: secret scan on generated content (carried in prompt output)
        // We scan the prompt as a proxy; real generated content is scanned post-generation.
        // The scan here catches LLMs that echo provided keys back in their cell output.
        const secretMatch = scanCellForSecrets(prompt);
        if (secretMatch) {
            return this.routeToolInvocation(ctx, {
                decision: 'DENY',
                reason: `NotebookAIAdapter: cell generation denied -- secret pattern detected in generated content ` +
                    `('${secretMatch.name}'). AI models sometimes echo API keys or connection strings from context ` +
                    'into generated cells. The content has been blocked to prevent credential exposure in notebooks. ' +
                    'Reason code: notebook-ai-cell-generation-secret-detected.',
                metadata: { reasonCode: 'notebook-ai-cell-generation-secret-detected', secretType: secretMatch.name },
            });
        }
        return this.routeToolInvocation(ctx, {
            decision: 'ALLOW',
            reason: `NotebookAIAdapter: cell generation allowed (platform: ${this.platform}, mode: ${this.deploymentMode}).`,
            metadata: {
                platform: this.platform,
                deploymentMode: this.deploymentMode,
                language: language ?? 'unspecified',
                promptLength: prompt.length,
            },
        });
    }
    // ---------------------------------------------------------------------------
    // Surface: wrapCellEdit
    // ---------------------------------------------------------------------------
    async wrapCellEdit(ctx, cellId, originalContent, instruction, cellCount) {
        this.ensureInitialized();
        const deny = this.checkPackBinding(ctx);
        if (deny)
            return this.routeToolInvocation(ctx, deny);
        // Multi-cell edit cap
        if (cellCount !== undefined && cellCount > this.multiCellEditCap) {
            return this.routeToolInvocation(ctx, {
                decision: 'DENY',
                reason: `NotebookAIAdapter: batch cell edit denied -- ${cellCount} cells exceeds the ` +
                    `multi-cell edit cap of ${this.multiCellEditCap}. Reduce the batch size or ` +
                    'increase multiCellEditCap in config. ' +
                    'Reason code: notebook-ai-multi-cell-edit-cap-exceeded.',
                metadata: {
                    reasonCode: 'notebook-ai-multi-cell-edit-cap-exceeded',
                    cellCount,
                    cap: this.multiCellEditCap,
                },
            });
        }
        // Secret scan on content being edited (may contain connection strings etc.)
        const secretMatch = scanCellForSecrets(originalContent);
        if (secretMatch) {
            return this.routeToolInvocation(ctx, {
                decision: 'DENY',
                reason: `NotebookAIAdapter: cell edit denied -- secret pattern detected in cell content ` +
                    `('${secretMatch.name}'). Sending cell content containing secrets to the AI service ` +
                    'would risk credential exposure. Remove the secret before editing. ' +
                    'Reason code: notebook-ai-cell-edit-secret-detected.',
                metadata: { reasonCode: 'notebook-ai-cell-edit-secret-detected', secretType: secretMatch.name },
            });
        }
        return this.routeToolInvocation(ctx, {
            decision: 'ALLOW',
            reason: `NotebookAIAdapter: cell edit allowed (platform: ${this.platform}, cellId: ${cellId}).`,
            metadata: {
                platform: this.platform,
                deploymentMode: this.deploymentMode,
                cellId,
                cellCount: cellCount ?? 1,
                instructionLength: instruction.length,
            },
        });
    }
    // ---------------------------------------------------------------------------
    // Surface: wrapCellExplain
    // ---------------------------------------------------------------------------
    async wrapCellExplain(ctx, cellId, cellContent) {
        this.ensureInitialized();
        const deny = this.checkPackBinding(ctx);
        if (deny)
            return this.routeToolInvocation(ctx, deny);
        return this.routeToolInvocation(ctx, {
            decision: 'ALLOW',
            reason: `NotebookAIAdapter: cell explain allowed (platform: ${this.platform}, cellId: ${cellId}).`,
            metadata: {
                platform: this.platform,
                deploymentMode: this.deploymentMode,
                cellId,
                contentLength: cellContent.length,
            },
        });
    }
    // ---------------------------------------------------------------------------
    // Surface: wrapKernelExecution
    // ---------------------------------------------------------------------------
    async wrapKernelExecution(ctx, cellId, cellContent, language) {
        this.ensureInitialized();
        const deny = this.checkPackBinding(ctx);
        if (deny)
            return this.routeToolInvocation(ctx, deny);
        // PT-ADAPT-NOTEBOOK-4: minor / student safeguard is NON-NEGOTIABLE
        const minorDeny = this.checkMinorKernelExecution(ctx);
        if (minorDeny)
            return this.routeToolInvocation(ctx, minorDeny);
        // allowKernelExecution gate
        if (!this.allowKernelExecution) {
            return this.routeToolInvocation(ctx, {
                decision: 'DENY',
                reason: 'NotebookAIAdapter: kernel execution denied -- allowKernelExecution is false. ' +
                    'AI-triggered kernel execution is high-risk (runs arbitrary code in the notebook ' +
                    'kernel). Set allowKernelExecution: true in adapter config to enable. ' +
                    'Reason code: notebook-ai-kernel-execution-not-permitted.',
                metadata: { reasonCode: 'notebook-ai-kernel-execution-not-permitted' },
            });
        }
        // Language allowlist check
        if (this.allowedKernelLanguages && language) {
            const lang = language.toLowerCase();
            if (!this.allowedKernelLanguages.includes(lang)) {
                return this.routeToolInvocation(ctx, {
                    decision: 'DENY',
                    reason: `NotebookAIAdapter: kernel execution denied -- language '${language}' is not in ` +
                        `allowedKernelLanguages (${this.allowedKernelLanguages.join(', ')}). ` +
                        'Reason code: notebook-ai-kernel-language-not-permitted.',
                    metadata: {
                        reasonCode: 'notebook-ai-kernel-language-not-permitted',
                        language,
                        allowedKernelLanguages: this.allowedKernelLanguages,
                    },
                });
            }
        }
        // PT-ADAPT-NOTEBOOK-5: air-gapped mode -- deny network egress code
        if (this.deploymentMode === 'notebook-ai-air-gapped') {
            const egressPattern = detectKernelEgress(cellContent);
            if (egressPattern) {
                return this.routeToolInvocation(ctx, {
                    decision: 'DENY',
                    reason: 'NotebookAIAdapter: kernel execution denied in air-gapped mode -- network egress ' +
                        `pattern detected in cell content (pattern: ${egressPattern.source}). ` +
                        'Air-gapped deployments must not make outbound network requests from notebook kernels. ' +
                        'Reason code: notebook-ai-kernel-egress-denied-air-gapped.',
                    metadata: {
                        reasonCode: 'notebook-ai-kernel-egress-denied-air-gapped',
                        egressPattern: egressPattern.source,
                    },
                });
            }
        }
        return this.routeToolInvocation(ctx, {
            decision: 'ALLOW',
            reason: `NotebookAIAdapter: kernel execution allowed (platform: ${this.platform}, cellId: ${cellId}).`,
            metadata: {
                platform: this.platform,
                deploymentMode: this.deploymentMode,
                cellId,
                language: language ?? 'unspecified',
                advisoryCap: this.cellExecutionTimeMs,
            },
        });
    }
    // ---------------------------------------------------------------------------
    // Surface: wrapDatasetIntrospection
    // ---------------------------------------------------------------------------
    async wrapDatasetIntrospection(ctx, source, sampleRows) {
        this.ensureInitialized();
        const deny = this.checkPackBinding(ctx);
        if (deny)
            return this.routeToolInvocation(ctx, deny);
        // Dataset source allowlist / air-gapped check
        const sourceCheck = isDatasetSourceAllowed(source, this.allowedDatasetSources, this.deploymentMode);
        if (!sourceCheck.allowed) {
            return this.routeToolInvocation(ctx, {
                decision: 'DENY',
                reason: `NotebookAIAdapter: dataset introspection denied. ${sourceCheck.reason ?? ''}`,
                metadata: {
                    reasonCode: this.deploymentMode === 'notebook-ai-air-gapped'
                        ? 'notebook-ai-dataset-source-not-local-in-air-gapped'
                        : 'notebook-ai-dataset-source-not-in-allowlist',
                    source,
                },
            });
        }
        // PT-ADAPT-NOTEBOOK-6: PHI scan on sample rows
        let phiDetected = false;
        let phiType = '';
        if (sampleRows) {
            const phiMatch = scanForPHI(sampleRows);
            if (phiMatch) {
                phiDetected = true;
                phiType = phiMatch.name;
                // Audit with PHI redacted -- never log sample row content
                this.emit((0, shared_1.buildIdeAuditEvent)('ide:tool-invocation-require-approval', {
                    ideId: this.ideId,
                    adapterId: this.adapterId,
                    sessionId: ctx.sessionHandle?.sessionId,
                    initiatingUserId: ctx.initiatingUser?.userId,
                    tool: 'datasetIntrospectionRequested',
                    detail: `NotebookAIAdapter: PHI pattern detected in dataset sample rows (type: ${phiType}). ` +
                        'Sample row content redacted from audit. Dataset source: [REDACTED]. ' +
                        'Review required before introspection proceeds.',
                    data: {
                        phiDetected: true,
                        phiType,
                        source: '[REDACTED]',
                        sampleRowsRedacted: true,
                    },
                }));
            }
        }
        return this.routeToolInvocation(ctx, {
            decision: 'ALLOW',
            reason: `NotebookAIAdapter: dataset introspection allowed (platform: ${this.platform}).`,
            metadata: {
                platform: this.platform,
                deploymentMode: this.deploymentMode,
                // PT-ADAPT-NOTEBOOK-2: source omitted from audit metadata to prevent credential leakage
                sourceProvided: source.length > 0,
                phiDetected,
                phiType: phiDetected ? phiType : undefined,
                sampleRowsProvided: sampleRows !== undefined,
            },
        });
    }
    // ---------------------------------------------------------------------------
    // Surface: wrapChartGeneration
    // ---------------------------------------------------------------------------
    async wrapChartGeneration(ctx, source, chartType) {
        this.ensureInitialized();
        const deny = this.checkPackBinding(ctx);
        if (deny)
            return this.routeToolInvocation(ctx, deny);
        // Same dataset-source allowlist as introspection
        const sourceCheck = isDatasetSourceAllowed(source, this.allowedDatasetSources, this.deploymentMode);
        if (!sourceCheck.allowed) {
            return this.routeToolInvocation(ctx, {
                decision: 'DENY',
                reason: `NotebookAIAdapter: chart generation denied. ${sourceCheck.reason ?? ''}`,
                metadata: {
                    reasonCode: this.deploymentMode === 'notebook-ai-air-gapped'
                        ? 'notebook-ai-dataset-source-not-local-in-air-gapped'
                        : 'notebook-ai-dataset-source-not-in-allowlist',
                    source,
                },
            });
        }
        return this.routeToolInvocation(ctx, {
            decision: 'ALLOW',
            reason: `NotebookAIAdapter: chart generation allowed (platform: ${this.platform}, type: ${chartType ?? 'unspecified'}).`,
            metadata: {
                platform: this.platform,
                deploymentMode: this.deploymentMode,
                chartType: chartType ?? 'unspecified',
                sourceProvided: source.length > 0,
            },
        });
    }
    // ---------------------------------------------------------------------------
    // Surface: wrapNotebookExport
    // ---------------------------------------------------------------------------
    async wrapNotebookExport(ctx, format, exportTarget) {
        this.ensureInitialized();
        const deny = this.checkPackBinding(ctx);
        if (deny)
            return this.routeToolInvocation(ctx, deny);
        // Education mode: default-deny unless allowExport is explicitly true
        if ((this.deploymentMode === 'notebook-ai-education' || this.userTier === 'student') &&
            !this.allowExport) {
            return this.routeToolInvocation(ctx, {
                decision: 'DENY',
                reason: 'NotebookAIAdapter: notebook export denied in education mode. ' +
                    'Export is default-denied for student/education sessions. ' +
                    'Set allowExport: true in adapter config to enable. ' +
                    'Reason code: notebook-ai-export-denied-education-mode.',
                metadata: { reasonCode: 'notebook-ai-export-denied-education-mode' },
            });
        }
        if (!this.allowExport) {
            return this.routeToolInvocation(ctx, {
                decision: 'DENY',
                reason: 'NotebookAIAdapter: notebook export denied -- allowExport is false. ' +
                    'Set allowExport: true in adapter config to enable. ' +
                    'Reason code: notebook-ai-export-not-permitted.',
                metadata: { reasonCode: 'notebook-ai-export-not-permitted' },
            });
        }
        // Air-gapped mode: only local file targets allowed
        if (this.deploymentMode === 'notebook-ai-air-gapped' && exportTarget) {
            const isLocalTarget = exportTarget.startsWith('file://') ||
                exportTarget.startsWith('/') ||
                exportTarget.startsWith('./') ||
                exportTarget.startsWith('../');
            if (!isLocalTarget) {
                return this.routeToolInvocation(ctx, {
                    decision: 'DENY',
                    reason: `NotebookAIAdapter: notebook export denied in air-gapped mode -- export target ` +
                        `'${exportTarget}' is not a local path. Air-gapped deployments may only export ` +
                        'to local file paths (file://, /, ./, ../). Upload targets are not permitted. ' +
                        'Reason code: notebook-ai-export-target-not-local-air-gapped.',
                    metadata: {
                        reasonCode: 'notebook-ai-export-target-not-local-air-gapped',
                        exportTarget,
                    },
                });
            }
        }
        return this.routeToolInvocation(ctx, {
            decision: 'ALLOW',
            reason: `NotebookAIAdapter: notebook export allowed (platform: ${this.platform}, format: ${format}).`,
            metadata: {
                platform: this.platform,
                deploymentMode: this.deploymentMode,
                format,
                exportTargetProvided: exportTarget !== undefined,
            },
        });
    }
    // ---------------------------------------------------------------------------
    // Surface: wrapChat
    // ---------------------------------------------------------------------------
    async wrapChat(ctx, prompt) {
        this.ensureInitialized();
        const deny = this.checkPackBinding(ctx);
        if (deny)
            return this.routeToolInvocation(ctx, deny);
        // Secret scan on prompt
        const secretMatch = scanCellForSecrets(prompt);
        if (secretMatch) {
            return this.routeToolInvocation(ctx, {
                decision: 'DENY',
                reason: `NotebookAIAdapter: chat request denied -- secret pattern detected in prompt ` +
                    `('${secretMatch.name}'). The chat prompt appears to contain a credential or ` +
                    'connection string. Remove the secret before sending. ' +
                    'Reason code: notebook-ai-chat-secret-detected.',
                metadata: { reasonCode: 'notebook-ai-chat-secret-detected', secretType: secretMatch.name },
            });
        }
        return this.routeToolInvocation(ctx, {
            decision: 'ALLOW',
            reason: `NotebookAIAdapter: chat request allowed (platform: ${this.platform}).`,
            metadata: {
                platform: this.platform,
                deploymentMode: this.deploymentMode,
                promptLength: prompt.length,
            },
        });
    }
    // ---------------------------------------------------------------------------
    // Surface: wrapSlashCommand
    // ---------------------------------------------------------------------------
    async wrapSlashCommand(ctx, command, args) {
        this.ensureInitialized();
        const deny = this.checkPackBinding(ctx);
        if (deny)
            return this.routeToolInvocation(ctx, deny);
        return this.routeToolInvocation(ctx, {
            decision: 'ALLOW',
            reason: `NotebookAIAdapter: slash command '${command}' allowed (platform: ${this.platform}).`,
            metadata: {
                platform: this.platform,
                deploymentMode: this.deploymentMode,
                command,
                argsLength: args?.length ?? 0,
            },
        });
    }
}
exports.NotebookAIAdapter = NotebookAIAdapter;
//# sourceMappingURL=notebook-ai.js.map