"use strict";
/**
 * ReplitAgentAdapter -- Replit Agent IDE adapter.
 *
 * Replit is a browser-based IDE with full sandbox execution. The Replit Agent
 * is an AI system that operates INSIDE the Repl environment: it can plan and
 * execute tasks, run code, manipulate files, install packages, deploy to
 * Replit Deployments, access Replit Secrets, and query databases -- all
 * autonomously.
 *
 * Replit's user base is the most diverse in the Connexum catalogue: students,
 * hobbyists, indie developers, small teams, and early-stage startups. This
 * diversity creates governance challenges unique to Replit:
 *   - Minor / student users require extra protection (COPPA, FERPA concerns).
 *   - High-volume signup means credentials are frequently mishandled.
 *   - Supply-chain risk is elevated because students copy-paste package installs.
 *   - Replit Deployments are production-facing changes triggered from a browser
 *     -- no separate CI/CD layer means the governance gate IS the last line.
 *
 * Deployment modes:
 *   replit-cloud        -- Replit-managed SaaS (default). Individual Repls.
 *   replit-teams        -- Replit Teams for Education or business orgs.
 *                          Governed multi-user workspace.
 *   replit-deployments  -- Replit Deployments (production-facing). The only
 *                          mode where wrapDeployment is permitted by default.
 *
 * Hook surfaces (eight distinct governable surfaces):
 *
 *   agentTaskRequested(taskDescription)
 *     High-level natural language task gate ("build a flask app that...").
 *     REQUIRES a non-empty taskDescription for auditability.
 *     Reason code: replit-agent-task-description-required.
 *
 *   sandboxCodeExecutionRequested(code, language)
 *     Execute code in the Repl sandbox. Scans for network-egress patterns.
 *     For minor users: egress patterns cause DENIAL.
 *     For non-minor: egress patterns produce an audit warning (allowed).
 *
 *   fileSystemMutationRequested(files[])
 *     Create / modify / delete files in the Repl. Blast-radius cap enforced:
 *     file count > multiFileEditCap (default 25) = DENIED.
 *     Replit's default is 25 (higher than other IDEs because file mutations
 *     are Replit's core agentic surface).
 *
 *   packageInstallRequested(packageName, manager)
 *     Install npm/pip/etc. dependencies. Carries network-egress + supply-chain
 *     risk. Guards:
 *       - minor + student: DENIED unconditionally.
 *       - packageInstallAllowList[] configured: only listed packages allowed.
 *       - typosquat heuristics: package names > 40 chars or containing
 *         unusual punctuation are flagged.
 *
 *   deploymentRequested(target, branch)
 *     Replit Deployments push (production-facing change). CRITICAL surface.
 *     Guards:
 *       - userIsMinor: true -> ALWAYS DENIED.
 *       - userTier: 'student' -> ALWAYS DENIED.
 *       - deploymentMode !== 'replit-deployments' AND allowDeployment !== true
 *         -> DENIED (only the deployments mode or explicit opt-in allows deploys).
 *
 *   secretAccessRequested(secretName)
 *     Replit Secrets manager read. HIGH SENSITIVITY.
 *     Guards:
 *       - userIsMinor: true -> ALWAYS DENIED.
 *       - allowSecretAccess !== true -> DENIED.
 *
 *   databaseQueryRequested(query, dbType)
 *     Replit DB / hosted DB query. Scanned for destructive SQL patterns.
 *
 *   chatRequested(prompt)
 *     Replit Agent chat. Scanned for secrets and PHI.
 *
 * Minor / student safeguards (the Replit-distinct differentiators):
 *   - userIsMinor: true OR userTier: 'student' triggers strict defaults:
 *       - deployment: ALWAYS DENIED
 *       - secret access: ALWAYS DENIED (minor) / requires opt-in (student)
 *       - package install: DENIED (minor+student combo)
 *   - userAge: 'minor' is an alias for userIsMinor: true (legacy compat)
 *
 * Pen-tests:
 *   PT-ADAPT-REPLIT-1: empty packBinding[] denied at every wrap call
 *   PT-ADAPT-REPLIT-2: Replit auth token never appears in audit entries
 *   PT-ADAPT-REPLIT-3: malformed config throws at construction (invalid mode,
 *                      negative cap, userIsMinor + userTier='admin')
 *   PT-ADAPT-REPLIT-4: minor + deployment attempt always denied with
 *                      replit-deployment-not-permitted-minor
 *   PT-ADAPT-REPLIT-5: minor + secret access always denied with
 *                      replit-secret-access-not-permitted
 *   PT-ADAPT-REPLIT-6: package install allowList enforcement audit entry
 *                      contains the offending package name (sanitized)
 *
 * Spec: P3-SUB-IDE-13
 *
 * @connexum/ai-governance
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.ReplitAgentAdapter = exports.REPLIT_VALID_USER_TIERS = exports.REPLIT_BAA_ELIGIBLE_MODES = exports.REPLIT_VALID_DEPLOYMENT_MODES = void 0;
const base_1 = require("./base");
const index_1 = require("./index");
const shared_1 = require("./shared");
exports.REPLIT_VALID_DEPLOYMENT_MODES = [
    'replit-cloud',
    'replit-teams',
    'replit-deployments',
];
/** BAA-eligible modes (Replit Teams has an enterprise BAA path). */
exports.REPLIT_BAA_ELIGIBLE_MODES = [
    'replit-teams',
    'replit-deployments',
];
exports.REPLIT_VALID_USER_TIERS = [
    'student',
    'individual',
    'team-member',
    'admin',
];
// ---------------------------------------------------------------------------
// Typosquat heuristics (supply-chain guard)
// ---------------------------------------------------------------------------
/**
 * Heuristics for package name typosquatting.
 * Kept intentionally simple: length and punctuation signals only.
 * A real scanner would do edit-distance comparisons against a popular-package
 * corpus; this adapter applies fast static checks before dispatch.
 */
const TYPOSQUAT_MAX_NAME_LENGTH = 40;
const TYPOSQUAT_SUSPICIOUS_PUNCTUATION = /[_\-]{3,}|[0-9]{5,}|\.\./;
function isTyposquatSuspicious(packageName) {
    if (!packageName)
        return false;
    if (packageName.length > TYPOSQUAT_MAX_NAME_LENGTH)
        return true;
    if (TYPOSQUAT_SUSPICIOUS_PUNCTUATION.test(packageName))
        return true;
    return false;
}
// ---------------------------------------------------------------------------
// Network egress patterns (sandbox guard)
// ---------------------------------------------------------------------------
const SANDBOX_EGRESS_PATTERNS = [
    /\bfetch\s*\(/,
    /\burllib\.request\b/,
    /\bhttp\.get\s*\(/,
    /\bhttp\.request\s*\(/,
    /\bhttps\.get\s*\(/,
    /\bhttps\.request\s*\(/,
    /\bnew\s+WebSocket\s*\(/,
    /\bsocket\.connect\s*\(/,
    /\brequests\.get\s*\(/,
    /\brequests\.post\s*\(/,
    /\baiohttp\b/,
    /\bhttpx\b/,
    /\baxios\s*\./,
];
function detectSandboxEgress(code) {
    if (!code)
        return null;
    for (const pattern of SANDBOX_EGRESS_PATTERNS) {
        if (pattern.test(code))
            return pattern;
    }
    return null;
}
// ---------------------------------------------------------------------------
// Secret patterns (Replit-specific additions)
// ---------------------------------------------------------------------------
const REPLIT_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):\/\/[^:]+:[^@]+@/ },
    // PT-ADAPT-REPLIT-2: Replit auth tokens must never appear in audit entries.
    { name: 'Replit auth token', re: /replit[_\-]?(auth|token|key|secret)\s*[:=]\s*["']?\S{8,}["']?/i },
    { name: 'Replit session cookie', re: /connect\.sid\s*[:=]\s*["']?\S{8,}["']?/i },
];
// ---------------------------------------------------------------------------
// PHI indicators
// ---------------------------------------------------------------------------
const REPLIT_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/,
];
// ---------------------------------------------------------------------------
// Destructive SQL patterns
// ---------------------------------------------------------------------------
const DESTRUCTIVE_SQL_PATTERNS = [
    /\bDROP\s+(TABLE|DATABASE|SCHEMA)\b/i,
    /\bTRUNCATE\s+TABLE\b/i,
    /\bDELETE\s+FROM\b(?!\s+WHERE\b)/i,
    /\bDROP\s+ALL\s+TABLES\b/i,
];
function detectDestructiveSql(query) {
    if (!query)
        return null;
    for (const pattern of DESTRUCTIVE_SQL_PATTERNS) {
        if (pattern.test(query))
            return pattern;
    }
    return null;
}
// ---------------------------------------------------------------------------
// ReplitAgentAdapter
// ---------------------------------------------------------------------------
class ReplitAgentAdapter extends base_1.BaseIdeAdapter {
    adapterId = 'replit-agent';
    ideId = 'replit-agent';
    replitConfig = null;
    // ---------------------------------------------------------------------------
    // Lifecycle
    // ---------------------------------------------------------------------------
    initialize(config) {
        const rc = config;
        // Validate deploymentMode.
        if (!rc.deploymentMode || !exports.REPLIT_VALID_DEPLOYMENT_MODES.includes(rc.deploymentMode)) {
            throw new index_1.IdeSessionError({
                message: `ReplitAgentAdapter: invalid or missing deploymentMode '${rc.deploymentMode}'. ` +
                    `Must be one of: ${exports.REPLIT_VALID_DEPLOYMENT_MODES.join(', ')}. ` +
                    `Deployment mode is required to determine deployment permission and ` +
                    `educational-safeguard defaults.`,
                ideId: this.ideId,
                adapterId: this.adapterId,
            });
        }
        // Validate userTier if provided.
        if (rc.userTier !== undefined && !exports.REPLIT_VALID_USER_TIERS.includes(rc.userTier)) {
            throw new index_1.IdeSessionError({
                message: `ReplitAgentAdapter: invalid userTier '${rc.userTier}'. ` +
                    `Must be one of: ${exports.REPLIT_VALID_USER_TIERS.join(', ')}.`,
                ideId: this.ideId,
                adapterId: this.adapterId,
            });
        }
        // PT-ADAPT-REPLIT-3: userIsMinor + userTier='admin' is a logical inconsistency.
        if (rc.userIsMinor === true && rc.userTier === 'admin') {
            throw new index_1.IdeSessionError({
                message: `ReplitAgentAdapter: conflicting configuration -- userIsMinor: true is ` +
                    `incompatible with userTier: 'admin'. A minor user cannot hold admin ` +
                    `privileges. Review configuration before proceeding.`,
                ideId: this.ideId,
                adapterId: this.adapterId,
            });
        }
        // Validate multiFileEditCap must be positive integer.
        if (rc.multiFileEditCap !== undefined) {
            if (!Number.isInteger(rc.multiFileEditCap) || rc.multiFileEditCap < 1) {
                throw new index_1.IdeSessionError({
                    message: `ReplitAgentAdapter: multiFileEditCap must be a positive integer. ` +
                        `Received: ${rc.multiFileEditCap}.`,
                    ideId: this.ideId,
                    adapterId: this.adapterId,
                });
            }
        }
        // Validate sandboxResourceCap fields.
        if (rc.sandboxResourceCap !== undefined) {
            const cap = rc.sandboxResourceCap;
            if (cap.maxCpuSeconds !== undefined && cap.maxCpuSeconds < 0) {
                throw new index_1.IdeSessionError({
                    message: `ReplitAgentAdapter: sandboxResourceCap.maxCpuSeconds must be >= 0. ` +
                        `Received: ${cap.maxCpuSeconds}.`,
                    ideId: this.ideId,
                    adapterId: this.adapterId,
                });
            }
            if (cap.maxMemoryMb !== undefined && cap.maxMemoryMb < 0) {
                throw new index_1.IdeSessionError({
                    message: `ReplitAgentAdapter: sandboxResourceCap.maxMemoryMb must be >= 0. ` +
                        `Received: ${cap.maxMemoryMb}.`,
                    ideId: this.ideId,
                    adapterId: this.adapterId,
                });
            }
        }
        super.initialize(config);
        this.replitConfig = rc;
        const isMinor = rc.userIsMinor === true;
        const tier = rc.userTier ?? 'individual';
        const cap = rc.multiFileEditCap ?? 25;
        this.emit((0, shared_1.buildIdeAuditEvent)('ide:session-open', {
            ideId: this.ideId,
            adapterId: this.adapterId,
            detail: `ReplitAgentAdapter initialized. ` +
                `deploymentMode: ${rc.deploymentMode}. ` +
                `userTier: ${tier}. ` +
                `userIsMinor: ${isMinor}. ` +
                `allowDeployment: ${this._isDeploymentAllowed()}. ` +
                `allowSecretAccess: ${rc.allowSecretAccess === true}. ` +
                `multiFileEditCap: ${cap}.`,
            data: {
                deploymentMode: rc.deploymentMode,
                userTier: tier,
                userIsMinor: isMinor,
                allowDeployment: this._isDeploymentAllowed(),
                allowSecretAccess: rc.allowSecretAccess === true,
                allowPackageInstall: rc.allowPackageInstall !== false,
                multiFileEditCap: cap,
                baaEligible: exports.REPLIT_BAA_ELIGIBLE_MODES.includes(rc.deploymentMode),
            },
        }));
    }
    // ---------------------------------------------------------------------------
    // IdeAdapter: resolveUserIdentity
    // ---------------------------------------------------------------------------
    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, 'ReplitAgentAdapter: no user identity in session context.');
        throw new index_1.IdeAttributionError({
            message: 'ReplitAgentAdapter: cannot resolve user identity. Provide a userId in ' +
                'session.identity.userId (the Replit account ID or SSO subject), or set ' +
                'allowAnonymousIdeSessions: true in adapter config.',
            ideId: this.ideId,
            adapterId: this.adapterId,
        });
    }
    // ---------------------------------------------------------------------------
    // IdeAdapter: interceptToolInvocation
    // ---------------------------------------------------------------------------
    async interceptToolInvocation(ctx) {
        this.ensureInitialized();
        switch (ctx.tool) {
            case 'replit:agent-task-requested':
                return this._gateAgentTaskRequest(ctx);
            case 'replit:sandbox-code-execution-requested':
                return this._gateSandboxCodeExecutionRequest(ctx);
            case 'replit:file-system-mutation-requested':
                return this._gateFileSystemMutationRequest(ctx);
            case 'replit:package-install-requested':
                return this._gatePackageInstallRequest(ctx);
            case 'replit:deployment-requested':
                return this._gateDeploymentRequest(ctx);
            case 'replit:secret-access-requested':
                return this._gateSecretAccessRequest(ctx);
            case 'replit:database-query-requested':
                return this._gateDatabaseQueryRequest(ctx);
            case 'replit:chat-requested':
                return this._gateChatRequest(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: `ReplitAgentAdapter: unknown surface '${ctx.tool}' -- allowed with audit.`,
                }));
                return this.routeToolInvocation(ctx, {
                    decision: 'ALLOW',
                    reason: `Unknown Replit surface '${ctx.tool}' permitted with audit.`,
                });
        }
    }
    // ---------------------------------------------------------------------------
    // Hook: agentTaskRequested
    //
    // REQUIRES a non-empty taskDescription. Governance cannot approve an
    // unlabeled high-level task -- we must be able to record intent.
    // ---------------------------------------------------------------------------
    async _gateAgentTaskRequest(ctx) {
        const taskDescription = String(ctx.input['taskDescription'] ?? ctx.input['description'] ?? '').trim();
        if (!taskDescription) {
            const reasonCode = 'replit-agent-task-description-required';
            const decision = {
                decision: 'DENY',
                reason: 'Replit Agent task denied: taskDescription is required for governance audit. ' +
                    'Every agent task must carry a non-empty taskDescription so the compliance ' +
                    'system can evaluate and record the intended operation. ' +
                    `Reason code: ${reasonCode}.`,
                metadata: { reasonCode },
            };
            this.emit((0, shared_1.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 },
            }));
            return decision;
        }
        return this.routeToolInvocation(ctx, {
            decision: 'ALLOW',
            reason: `Replit Agent task permitted. Task: "${taskDescription.slice(0, 120)}". ` +
                `deploymentMode: ${this.replitConfig?.deploymentMode}.`,
            metadata: {
                taskDescription: taskDescription.slice(0, 200),
                deploymentMode: this.replitConfig?.deploymentMode,
                userTier: this.replitConfig?.userTier,
                userIsMinor: this.replitConfig?.userIsMinor === true,
            },
        });
    }
    // ---------------------------------------------------------------------------
    // Hook: sandboxCodeExecutionRequested
    //
    // Scans for network-egress patterns. For minor users, egress = DENIED.
    // For non-minor, egress = audit warning (allowed with note).
    // ---------------------------------------------------------------------------
    async _gateSandboxCodeExecutionRequest(ctx) {
        const code = String(ctx.input['code'] ?? '');
        const language = String(ctx.input['language'] ?? 'unknown');
        const isMinor = this.replitConfig?.userIsMinor === true;
        const egressPattern = detectSandboxEgress(code);
        if (egressPattern) {
            if (isMinor) {
                const reasonCode = 'replit-sandbox-network-egress-blocked-for-minor';
                const decision = {
                    decision: 'DENY',
                    reason: `Replit sandbox code execution blocked: network egress pattern detected ` +
                        `('${egressPattern.source}') in code and userIsMinor is true. ` +
                        'Minor users may not execute code that makes outbound network requests. ' +
                        `Reason code: ${reasonCode}.`,
                    metadata: {
                        reasonCode,
                        language,
                        egressPatternSource: egressPattern.source,
                        userIsMinor: true,
                    },
                };
                this.emit((0, shared_1.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, language, egressPatternSource: egressPattern.source, userIsMinor: true },
                }));
                return decision;
            }
            // Non-minor: allowed with audit warning.
            return this.routeToolInvocation(ctx, {
                decision: 'ALLOW',
                reason: `Replit sandbox code execution permitted with egress audit. ` +
                    `Network egress pattern ('${egressPattern.source}') detected. ` +
                    `language: ${language}. deploymentMode: ${this.replitConfig?.deploymentMode}.`,
                metadata: {
                    language,
                    egressPatternDetected: true,
                    egressPatternSource: egressPattern.source,
                    deploymentMode: this.replitConfig?.deploymentMode,
                },
            });
        }
        return this.routeToolInvocation(ctx, {
            decision: 'ALLOW',
            reason: `Replit sandbox code execution permitted. ` +
                `language: ${language}. deploymentMode: ${this.replitConfig?.deploymentMode}.`,
            metadata: {
                language,
                egressPatternDetected: false,
                deploymentMode: this.replitConfig?.deploymentMode,
            },
        });
    }
    // ---------------------------------------------------------------------------
    // Hook: fileSystemMutationRequested
    //
    // Blast-radius cap: file count > multiFileEditCap (default 25) = DENIED.
    // ---------------------------------------------------------------------------
    async _gateFileSystemMutationRequest(ctx) {
        const files = ctx.input['files'] ?? [];
        const description = String(ctx.input['description'] ?? '');
        const cap = this.replitConfig?.multiFileEditCap ?? 25;
        if (files.length > cap) {
            const reasonCode = 'replit-file-system-mutation-blast-radius-exceeded';
            return this.routeToolInvocation(ctx, {
                decision: 'DENY',
                reason: `Replit file-system mutation blocked: ${files.length} files exceeds the ` +
                    `blast-radius cap of ${cap}. Reduce the number of files in a single mutation ` +
                    `operation or increase multiFileEditCap in ReplitAgentAdapterConfig. ` +
                    `Reason code: ${reasonCode}.`,
                metadata: {
                    reasonCode,
                    fileCount: files.length,
                    cap,
                    deploymentMode: this.replitConfig?.deploymentMode,
                },
            });
        }
        return this.routeToolInvocation(ctx, {
            decision: 'ALLOW',
            reason: `File-system mutation permitted. File count: ${files.length} (cap: ${cap}). ` +
                `deploymentMode: ${this.replitConfig?.deploymentMode}.`,
            metadata: {
                fileCount: files.length,
                cap,
                description: description.slice(0, 120),
                deploymentMode: this.replitConfig?.deploymentMode,
            },
        });
    }
    // ---------------------------------------------------------------------------
    // Hook: packageInstallRequested
    //
    // Supply-chain risk gate. Multiple enforcement layers:
    //   1. minor + student -> DENIED unconditionally.
    //   2. packageInstallAllowList configured -> only listed packages allowed.
    //   3. Typosquat heuristics -> flag suspiciously named packages.
    // ---------------------------------------------------------------------------
    async _gatePackageInstallRequest(ctx) {
        const packageName = String(ctx.input['packageName'] ?? ctx.input['package'] ?? '').trim();
        const manager = String(ctx.input['manager'] ?? 'unknown');
        const isMinor = this.replitConfig?.userIsMinor === true;
        const userTier = this.replitConfig?.userTier ?? 'individual';
        const allowPackageInstall = this.replitConfig?.allowPackageInstall !== false;
        const allowList = this.replitConfig?.packageInstallAllowList;
        // Gate: minor AND student tier -> DENIED.
        if (isMinor && userTier === 'student') {
            const reasonCode = 'replit-package-install-not-permitted-minor-student';
            const decision = {
                decision: 'DENY',
                reason: `Replit package install blocked: userIsMinor is true and userTier is 'student'. ` +
                    `Minor students may not install packages without explicit authorization. ` +
                    `Reason code: ${reasonCode}.`,
                metadata: {
                    reasonCode,
                    packageName,
                    manager,
                    userIsMinor: true,
                    userTier,
                },
            };
            this.emit((0, shared_1.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, packageName, manager, userIsMinor: true, userTier },
            }));
            return decision;
        }
        // Gate: allowPackageInstall explicitly false.
        if (!allowPackageInstall) {
            const reasonCode = 'replit-package-install-not-permitted';
            return this.routeToolInvocation(ctx, {
                decision: 'DENY',
                reason: `Replit package install blocked: allowPackageInstall is false in config. ` +
                    `Set allowPackageInstall: true in ReplitAgentAdapterConfig to enable. ` +
                    `Reason code: ${reasonCode}.`,
                metadata: { reasonCode, packageName, manager },
            });
        }
        // Gate: allowList enforcement. PT-ADAPT-REPLIT-6: audit entry contains package name.
        if (allowList && allowList.length > 0) {
            if (!allowList.includes(packageName)) {
                const reasonCode = 'replit-package-install-not-in-allowlist';
                const decision = {
                    decision: 'DENY',
                    reason: `Replit package install blocked: package '${packageName}' is not in the ` +
                        `packageInstallAllowList. Only pre-approved packages may be installed in ` +
                        `this environment. Reason code: ${reasonCode}.`,
                    metadata: {
                        reasonCode,
                        // PT-ADAPT-REPLIT-6: package name is not a secret; include it for audit traceability.
                        offendingPackage: packageName,
                        manager,
                        allowListSize: allowList.length,
                    },
                };
                this.emit((0, shared_1.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,
                        offendingPackage: packageName,
                        manager,
                        allowListSize: allowList.length,
                    },
                }));
                return decision;
            }
        }
        // Gate: typosquat heuristic check.
        if (isTyposquatSuspicious(packageName)) {
            const reasonCode = 'replit-package-install-typosquat-suspected';
            return this.routeToolInvocation(ctx, {
                decision: 'REQUIRE_APPROVAL',
                reason: `Replit package install flagged: package name '${packageName}' matches ` +
                    `typosquatting heuristics (name length > ${TYPOSQUAT_MAX_NAME_LENGTH} chars ` +
                    `or suspicious punctuation). Supply-chain review required before install. ` +
                    `Reason code: ${reasonCode}.`,
                metadata: {
                    reasonCode,
                    packageName,
                    manager,
                    typosquatHeuristicMatch: true,
                },
            });
        }
        return this.routeToolInvocation(ctx, {
            decision: 'ALLOW',
            reason: `Package install permitted. package: '${packageName}'. manager: ${manager}. ` +
                `deploymentMode: ${this.replitConfig?.deploymentMode}.`,
            metadata: {
                packageName,
                manager,
                deploymentMode: this.replitConfig?.deploymentMode,
            },
        });
    }
    // ---------------------------------------------------------------------------
    // Hook: deploymentRequested (PT-ADAPT-REPLIT-4)
    //
    // CRITICAL governance gate. Replit Deployments push directly to production
    // without a separate CI/CD layer. Strict-mode enforcement:
    //   1. userIsMinor: true -> ALWAYS DENIED (PT-ADAPT-REPLIT-4).
    //   2. userTier: 'student' -> ALWAYS DENIED.
    //   3. deploymentMode !== 'replit-deployments' AND allowDeployment !== true -> DENIED.
    // ---------------------------------------------------------------------------
    async _gateDeploymentRequest(ctx) {
        const target = String(ctx.input['target'] ?? 'production');
        const branch = String(ctx.input['branch'] ?? '');
        const isMinor = this.replitConfig?.userIsMinor === true;
        const userTier = this.replitConfig?.userTier ?? 'individual';
        const mode = this.replitConfig?.deploymentMode ?? 'replit-cloud';
        // PT-ADAPT-REPLIT-4: minor user ALWAYS DENIED.
        if (isMinor) {
            const reasonCode = 'replit-deployment-not-permitted-minor';
            const decision = {
                decision: 'DENY',
                reason: `Replit deployment blocked: userIsMinor is true. Minor users may never ` +
                    `trigger production deployments. This is an unconditional governance rule. ` +
                    `Reason code: ${reasonCode}.`,
                metadata: {
                    reasonCode,
                    target,
                    branch,
                    userIsMinor: true,
                    deploymentMode: mode,
                },
            };
            this.emit((0, shared_1.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, target, userIsMinor: true, deploymentMode: mode },
            }));
            return decision;
        }
        // Student tier -> ALWAYS DENIED.
        if (userTier === 'student') {
            const reasonCode = 'replit-deployment-not-permitted-student';
            const decision = {
                decision: 'DENY',
                reason: `Replit deployment blocked: userTier is 'student'. Student-tier users may not ` +
                    `trigger production deployments. Reason code: ${reasonCode}.`,
                metadata: {
                    reasonCode,
                    target,
                    branch,
                    userTier,
                    deploymentMode: mode,
                },
            };
            this.emit((0, shared_1.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, target, userTier, deploymentMode: mode },
            }));
            return decision;
        }
        // Mode check: only replit-deployments mode allows deployment by default.
        const isDeployMode = mode === 'replit-deployments';
        const hasExplicitOptIn = this.replitConfig?.allowDeployment === true;
        if (!isDeployMode && !hasExplicitOptIn) {
            const reasonCode = 'replit-deployment-not-permitted-mode';
            return this.routeToolInvocation(ctx, {
                decision: 'DENY',
                reason: `Replit deployment blocked: deploymentMode is '${mode}' (not 'replit-deployments') ` +
                    `and allowDeployment is not explicitly set to true. Only the 'replit-deployments' ` +
                    `mode or explicit allowDeployment: true in config permits deployment operations. ` +
                    `Reason code: ${reasonCode}.`,
                metadata: {
                    reasonCode,
                    target,
                    branch,
                    deploymentMode: mode,
                    allowDeployment: false,
                },
            });
        }
        return this.routeToolInvocation(ctx, {
            decision: 'ALLOW',
            reason: `Replit deployment permitted. target: '${target}'. ` +
                `deploymentMode: ${mode}. allowDeployment: ${hasExplicitOptIn || isDeployMode}.`,
            metadata: {
                target,
                branch,
                deploymentMode: mode,
                userTier,
            },
        });
    }
    // ---------------------------------------------------------------------------
    // Hook: secretAccessRequested (PT-ADAPT-REPLIT-5)
    //
    // High-sensitivity gate. Replit Secrets can contain production API keys,
    // database passwords, and service credentials.
    //   1. userIsMinor: true -> ALWAYS DENIED (PT-ADAPT-REPLIT-5).
    //   2. allowSecretAccess !== true -> DENIED (governance default-deny).
    // ---------------------------------------------------------------------------
    async _gateSecretAccessRequest(ctx) {
        const secretName = String(ctx.input['secretName'] ?? ctx.input['key'] ?? '');
        const isMinor = this.replitConfig?.userIsMinor === true;
        const allowSecretAccess = this.replitConfig?.allowSecretAccess === true;
        // PT-ADAPT-REPLIT-5: minor user ALWAYS DENIED.
        if (isMinor) {
            const reasonCode = 'replit-secret-access-not-permitted';
            const decision = {
                decision: 'DENY',
                reason: `Replit secret access blocked: userIsMinor is true. Minor users may never ` +
                    `access Replit Secrets. This is an unconditional governance rule. ` +
                    `Reason code: ${reasonCode}.`,
                metadata: {
                    reasonCode,
                    secretName,
                    userIsMinor: true,
                },
            };
            this.emit((0, shared_1.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,
                // NOTE: secretName recorded in audit for traceability; the value is never included.
                data: { reasonCode, secretName, userIsMinor: true },
            }));
            return decision;
        }
        // Default-deny: allowSecretAccess must be explicitly true.
        if (!allowSecretAccess) {
            const reasonCode = 'replit-secret-access-not-permitted';
            return this.routeToolInvocation(ctx, {
                decision: 'DENY',
                reason: `Replit secret access blocked: allowSecretAccess is not enabled. ` +
                    `Set allowSecretAccess: true in ReplitAgentAdapterConfig to permit ` +
                    `Replit Secrets reads. Reason code: ${reasonCode}.`,
                metadata: {
                    reasonCode,
                    secretName,
                    allowSecretAccess: false,
                },
            });
        }
        return this.routeToolInvocation(ctx, {
            decision: 'ALLOW',
            reason: `Replit secret access permitted. secretName: '${secretName}'. ` +
                `allowSecretAccess: true. deploymentMode: ${this.replitConfig?.deploymentMode}.`,
            metadata: {
                secretName,
                deploymentMode: this.replitConfig?.deploymentMode,
            },
        });
    }
    // ---------------------------------------------------------------------------
    // Hook: databaseQueryRequested
    //
    // Scans for destructive SQL patterns. REQUIRE_APPROVAL for destructive ops.
    // ---------------------------------------------------------------------------
    async _gateDatabaseQueryRequest(ctx) {
        const query = String(ctx.input['query'] ?? '');
        const dbType = String(ctx.input['dbType'] ?? ctx.input['db_type'] ?? 'unknown');
        const destructiveMatch = detectDestructiveSql(query);
        if (destructiveMatch) {
            const reasonCode = 'replit-database-query-destructive-op-requires-approval';
            return this.routeToolInvocation(ctx, {
                decision: 'REQUIRE_APPROVAL',
                reason: `Replit database query flagged: destructive SQL pattern detected ` +
                    `('${destructiveMatch.source}'). Destructive operations (DROP, TRUNCATE, ` +
                    `unguarded DELETE) require human approval before execution. ` +
                    `dbType: ${dbType}. Reason code: ${reasonCode}.`,
                metadata: {
                    reasonCode,
                    dbType,
                    destructivePatternSource: destructiveMatch.source,
                    queryPreview: query.slice(0, 80),
                },
            });
        }
        return this.routeToolInvocation(ctx, {
            decision: 'ALLOW',
            reason: `Database query permitted. dbType: ${dbType}. ` +
                `deploymentMode: ${this.replitConfig?.deploymentMode}.`,
            metadata: {
                dbType,
                queryPreview: query.slice(0, 80),
                deploymentMode: this.replitConfig?.deploymentMode,
            },
        });
    }
    // ---------------------------------------------------------------------------
    // Hook: chatRequested
    //
    // Scanned for secrets and PHI.
    // ---------------------------------------------------------------------------
    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: `Replit Agent chat blocked: secret pattern '${secretMatch}' in prompt.`,
                metadata: { secretPattern: secretMatch },
            });
        }
        if (this._isHipaaBinding()) {
            const phiMatch = this._scanForPhi(promptText);
            if (phiMatch) {
                const tier = this.replitConfig?.hipaaReviewerTier ?? 'T2_LEAD';
                return this.routeToolInvocation(ctx, {
                    decision: 'REQUIRE_APPROVAL',
                    reason: `Replit Agent chat routed for HIPAA review: PHI indicator '${phiMatch}' ` +
                        `in prompt. Reviewer tier: ${tier}.`,
                    metadata: { phiIndicator: phiMatch, tier },
                });
            }
        }
        return this.routeToolInvocation(ctx, {
            decision: 'ALLOW',
            reason: 'Replit Agent chat permitted.',
            metadata: { deploymentMode: this.replitConfig?.deploymentMode },
        });
    }
    // ---------------------------------------------------------------------------
    // Public wrap methods (typed direct-call API)
    // ---------------------------------------------------------------------------
    async wrapAgentTask(ctx) {
        this.ensureInitialized();
        return this._gateAgentTaskRequest({
            tool: 'replit:agent-task-requested',
            input: { taskDescription: ctx.taskDescription },
            sessionHandle: { sessionId: ctx.sessionId ?? 'replit-session', openedAt: new Date().toISOString() },
            initiatingUser: { userId: 'replit-user', attributionMethod: 'cursor-composer' },
            timestamp: new Date().toISOString(),
        });
    }
    async wrapSandboxCodeExecution(ctx) {
        this.ensureInitialized();
        return this._gateSandboxCodeExecutionRequest({
            tool: 'replit:sandbox-code-execution-requested',
            input: { code: ctx.code, language: ctx.language ?? 'unknown' },
            sessionHandle: { sessionId: ctx.sessionId ?? 'replit-session', openedAt: new Date().toISOString() },
            initiatingUser: { userId: 'replit-user', attributionMethod: 'cursor-composer' },
            timestamp: new Date().toISOString(),
        });
    }
    async wrapFileSystemMutation(ctx) {
        this.ensureInitialized();
        return this._gateFileSystemMutationRequest({
            tool: 'replit:file-system-mutation-requested',
            input: { files: ctx.files, description: ctx.description ?? '' },
            sessionHandle: { sessionId: ctx.sessionId ?? 'replit-session', openedAt: new Date().toISOString() },
            initiatingUser: { userId: 'replit-user', attributionMethod: 'cursor-composer' },
            timestamp: new Date().toISOString(),
        });
    }
    async wrapPackageInstall(ctx) {
        this.ensureInitialized();
        return this._gatePackageInstallRequest({
            tool: 'replit:package-install-requested',
            input: { packageName: ctx.packageName, manager: ctx.manager ?? 'unknown' },
            sessionHandle: { sessionId: ctx.sessionId ?? 'replit-session', openedAt: new Date().toISOString() },
            initiatingUser: { userId: 'replit-user', attributionMethod: 'cursor-composer' },
            timestamp: new Date().toISOString(),
        });
    }
    async wrapDeployment(ctx) {
        this.ensureInitialized();
        return this._gateDeploymentRequest({
            tool: 'replit:deployment-requested',
            input: { target: ctx.target ?? 'production', branch: ctx.branch ?? '' },
            sessionHandle: { sessionId: ctx.sessionId ?? 'replit-session', openedAt: new Date().toISOString() },
            initiatingUser: { userId: 'replit-user', attributionMethod: 'cursor-composer' },
            timestamp: new Date().toISOString(),
        });
    }
    async wrapSecretAccess(ctx) {
        this.ensureInitialized();
        return this._gateSecretAccessRequest({
            tool: 'replit:secret-access-requested',
            input: { secretName: ctx.secretName },
            sessionHandle: { sessionId: ctx.sessionId ?? 'replit-session', openedAt: new Date().toISOString() },
            initiatingUser: { userId: 'replit-user', attributionMethod: 'cursor-composer' },
            timestamp: new Date().toISOString(),
        });
    }
    async wrapDatabaseQuery(ctx) {
        this.ensureInitialized();
        return this._gateDatabaseQueryRequest({
            tool: 'replit:database-query-requested',
            input: { query: ctx.query, dbType: ctx.dbType ?? 'unknown' },
            sessionHandle: { sessionId: ctx.sessionId ?? 'replit-session', openedAt: new Date().toISOString() },
            initiatingUser: { userId: 'replit-user', attributionMethod: 'cursor-composer' },
            timestamp: new Date().toISOString(),
        });
    }
    async wrapChat(ctx) {
        this.ensureInitialized();
        return this._gateChatRequest({
            tool: 'replit:chat-requested',
            input: { promptText: ctx.promptText },
            sessionHandle: { sessionId: ctx.sessionId ?? 'replit-session', openedAt: new Date().toISOString() },
            initiatingUser: { userId: 'replit-user', attributionMethod: 'cursor-composer' },
            timestamp: new Date().toISOString(),
        });
    }
    // ---------------------------------------------------------------------------
    // Capabilities
    // ---------------------------------------------------------------------------
    getIdeCapabilities() {
        const mode = this.replitConfig?.deploymentMode ?? 'replit-cloud';
        return {
            runtimeHooks: true,
            prBoundary: false,
            userIdentity: true,
            reviewerRouting: true,
            integrationMode: `replit-agent-${mode}`,
        };
    }
    // ---------------------------------------------------------------------------
    // Error translation
    // ---------------------------------------------------------------------------
    translateError(raw) {
        if (raw instanceof index_1.IdeAttributionError)
            return raw;
        return super.translateError(raw);
    }
    // ---------------------------------------------------------------------------
    // Public accessors for diagnostics / tests
    // ---------------------------------------------------------------------------
    getDeploymentMode() {
        return this.replitConfig?.deploymentMode ?? 'replit-cloud';
    }
    getUserTier() {
        return this.replitConfig?.userTier ?? 'individual';
    }
    isMinorUser() {
        return this.replitConfig?.userIsMinor === true;
    }
    getMultiFileEditCap() {
        return this.replitConfig?.multiFileEditCap ?? 25;
    }
    isSecretAccessAllowed() {
        return this.replitConfig?.allowSecretAccess === true && this.replitConfig?.userIsMinor !== true;
    }
    // ---------------------------------------------------------------------------
    // Private helpers
    // ---------------------------------------------------------------------------
    _isDeploymentAllowed() {
        const mode = this.replitConfig?.deploymentMode ?? 'replit-cloud';
        return mode === 'replit-deployments' || this.replitConfig?.allowDeployment === true;
    }
    _scanForSecrets(text) {
        if (!text)
            return null;
        for (const { name, re } of REPLIT_SECRET_PATTERNS) {
            if (re.test(text))
                return name;
        }
        return null;
    }
    _scanForPhi(text) {
        if (!text)
            return null;
        for (const re of REPLIT_PHI_INDICATORS) {
            if (re.test(text))
                return re.source;
        }
        return null;
    }
    _isHipaaBinding() {
        const packId = this.config?.packId ?? '';
        return packId === 'hipaa' || packId.startsWith('hipaa');
    }
}
exports.ReplitAgentAdapter = ReplitAgentAdapter;
//# sourceMappingURL=replit-agent.js.map