"use strict";
/**
 * ZedAIAdapter -- Zed AI assistant governance adapter.
 *
 * Zed (https://zed.dev) is a high-performance, Rust-native code editor built
 * for collaboration. Its built-in AI assistant ("Zed AI") ships three
 * deployment modes:
 *
 *   zed-cloud      -- Anthropic-backed default; requests route through
 *                     Zed's cloud infrastructure (zed.dev servers).
 *   zed-byok       -- Bring your own key; the user configures a provider
 *                     (Anthropic, OpenAI, etc.) via Zed settings; requests
 *                     route directly from the local machine to that provider.
 *   zed-local-only -- Local Ollama / LM Studio integration; the model runs
 *                     on-device; no data leaves the machine. This adapter
 *                     enforces strict local-only isolation: any surface that
 *                     flags network egress is DENIED unconditionally when
 *                     localOnlyEnforcement is true (the default in this mode).
 *
 * Hook surfaces (9):
 *
 *   wrapInlineCompletion(prompt)
 *     Zed Predict / Zeta model auto-complete suggestion.
 *     In zed-local-only + localOnlyEnforcement: denied unless context
 *     provider flag indicates local model.
 *
 *   wrapChat(prompt)
 *     Assistant panel chat prompt.
 *     In zed-local-only + localOnlyEnforcement: denied on network-egress flag.
 *
 *   wrapAssistantThread(prompt, threadId)
 *     Threaded conversation context (multi-turn assistant panel).
 *     In zed-local-only + localOnlyEnforcement: denied on network-egress flag.
 *
 *   wrapSlashCommand(command, args)
 *     Slash commands: /explain, /refactor, /test, /diagnose, /migrate, etc.
 *     Denied if command not in slashCommandAllowList (when that list is set).
 *
 *   wrapInlineTransform(selection, instruction)
 *     cmd-Enter inline transform of selected code.
 *
 *   wrapTerminalAssist(command)
 *     Zed integrated terminal AI (assistant: terminal config).
 *     Requires allowTerminalAssist: true.
 *     When allowedTerminalPrefixes is set, the command must match at least
 *     one prefix.
 *
 *   wrapFileContext(filePath, content)
 *     Zed adds a file to the assistant context.
 *     File content is scanned for secrets before inclusion.
 *     Denied with zed-file-context-secret-detected on finding.
 *
 *   wrapMultiBufferEdit(bufferPaths, instruction)
 *     Zed multi-buffer editor -- simultaneous edits across files.
 *     Buffer count > multiBufferEditCap denied.
 *
 *   wrapVoiceInput(transcribedText)
 *     Voice mode input. Denied unless allowVoiceInput: true.
 *
 * Deployment mode enforcement (the differentiator):
 *
 *   zed-local-only with localOnlyEnforcement: true (default):
 *     All surfaces that can trigger network egress are denied unless the
 *     request carries localProvider: true in its ideMetadata. The
 *     enforcement logic is multi-layered:
 *       1. The mode guard runs first on every wrap call.
 *       2. Opt-in flags (allowTerminalAssist, allowVoiceInput) cannot
 *          override the local-only mode guard -- those are checked after
 *          the network-egress gate fails. PT-ADAPT-ZED-4 validates this.
 *       3. Setting localOnlyEnforcement: false in zed-local-only mode is
 *          rejected at construction (ambiguous privacy contract).
 *
 * Strict-mode enforcement:
 *   wrapTerminalAssist: allowTerminalAssist must be true + prefix match.
 *   wrapVoiceInput: allowVoiceInput must be true.
 *   wrapMultiBufferEdit: buffers.length > multiBufferEditCap denied.
 *   wrapSlashCommand: command must be in slashCommandAllowList if set.
 *   wrapFileContext: secret scan always applied; denied on finding.
 *   wrapInlineCompletion, wrapChat, wrapAssistantThread:
 *     in zed-local-only mode, denied if network egress flagged.
 *
 * Secret scanning:
 *   File content scanned for: AWS access keys, GitHub PATs, Anthropic API
 *   keys, OpenAI API keys, private key headers, generic SECRET/PASSWORD/
 *   API_KEY env vars, database URLs with embedded credentials, Bearer tokens.
 *   Secrets are NEVER echoed in audit entries (sanitized before emission).
 *
 * Pen-tests:
 *   PT-ADAPT-ZED-1: empty packBinding[] denied at every wrap call
 *   PT-ADAPT-ZED-2: API keys / Zed auth tokens never appear in audit entries
 *   PT-ADAPT-ZED-3: malformed config throws at construction (invalid mode,
 *                   negative cap, localOnlyEnforcement:false with zed-local-only)
 *   PT-ADAPT-ZED-4: local-only mode + network-egress feature ALWAYS denied
 *                   (even with explicit opt-in flags trying to override)
 *   PT-ADAPT-ZED-5: file context with .env content denied with
 *                   zed-file-context-secret-detected
 *
 * Spec: P3-SUB-IDE-11
 *
 * @connexum/ai-governance
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.ZedAIAdapter = exports.ZED_VALID_DEPLOYMENT_MODES = void 0;
const base_1 = require("./base");
const index_1 = require("./index");
exports.ZED_VALID_DEPLOYMENT_MODES = [
    'zed-cloud',
    'zed-byok',
    'zed-local-only',
];
// ---------------------------------------------------------------------------
// Secret scan patterns (mirrors Aider for consistency, Zed-adapted)
// ---------------------------------------------------------------------------
const ZED_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: 'Zed auth token', re: /zed_[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):\/\/[^:]+:[^@]+@/ },
    { name: 'env file marker', re: /^(export\s+)?(SECRET|PASSWORD|PASSWD|API_KEY|TOKEN|KEY)\w*=\S/im },
];
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
/** Scan text content for known secret patterns. Returns first match or null. */
function scanForSecrets(text) {
    for (const pattern of ZED_SECRET_PATTERNS) {
        if (pattern.re.test(text))
            return pattern;
    }
    return null;
}
/** Sanitize text so that any matched secret pattern value is redacted. */
function sanitizeSecretFromAudit(text) {
    let out = text;
    for (const pattern of ZED_SECRET_PATTERNS) {
        out = out.replace(pattern.re, '[REDACTED]');
    }
    return out;
}
/**
 * Check whether a tool invocation context signals that the request will
 * use a local provider (on-device model, no network egress).
 *
 * Callers set ideMetadata.localProvider: true when the Zed session is
 * configured to use a local Ollama/LM Studio endpoint.
 */
function isLocalProvider(ctx) {
    const meta = ctx.ideMetadata;
    if (!meta)
        return false;
    return meta['localProvider'] === true;
}
// ---------------------------------------------------------------------------
// ZedAIAdapter
// ---------------------------------------------------------------------------
class ZedAIAdapter extends base_1.BaseIdeAdapter {
    adapterId = 'zed-ai';
    ideId = 'zed-ai';
    deploymentMode = 'zed-cloud';
    localOnlyEnforcement = true;
    allowTerminalAssist = false;
    allowedTerminalPrefixes = [];
    allowVoiceInput = false;
    multiBufferEditCap = 15;
    slashCommandAllowList = null;
    // ---------------------------------------------------------------------------
    // Lifecycle
    // ---------------------------------------------------------------------------
    initialize(config) {
        // Base performs packId guard
        super.initialize(config);
        const mode = config.deploymentMode ?? 'zed-cloud';
        // Validate mode
        if (!exports.ZED_VALID_DEPLOYMENT_MODES.includes(mode)) {
            throw new index_1.IdeSessionError({
                message: `ZedAIAdapter: invalid deploymentMode '${mode}'. ` +
                    `Valid modes: ${exports.ZED_VALID_DEPLOYMENT_MODES.join(', ')}.`,
                ideId: this.ideId,
                adapterId: this.adapterId,
            });
        }
        // PT-ADAPT-ZED-3: localOnlyEnforcement:false in zed-local-only mode is rejected.
        // The combination creates an ambiguous privacy contract: the mode name implies
        // local-only but enforcement is disabled, which can mislead users about
        // whether their data leaves the machine.
        if (mode === 'zed-local-only' && config.localOnlyEnforcement === false) {
            throw new index_1.IdeSessionError({
                message: 'ZedAIAdapter: localOnlyEnforcement: false is not permitted in ' +
                    'zed-local-only mode. Disabling enforcement while using the local-only ' +
                    'mode creates an ambiguous privacy contract: users expect all data to ' +
                    'stay on-device but the adapter would not enforce it. Either use ' +
                    'zed-cloud or zed-byok mode (where network egress is expected), or ' +
                    'keep localOnlyEnforcement: true (the default) in zed-local-only mode.',
                ideId: this.ideId,
                adapterId: this.adapterId,
            });
        }
        // Validate numeric cap
        const multiBufferEditCap = config.multiBufferEditCap ?? 15;
        if (typeof multiBufferEditCap !== 'number' || multiBufferEditCap < 1) {
            throw new index_1.IdeSessionError({
                message: `ZedAIAdapter: multiBufferEditCap must be a positive number, got: ${String(multiBufferEditCap)}.`,
                ideId: this.ideId,
                adapterId: this.adapterId,
            });
        }
        this.deploymentMode = mode;
        // localOnlyEnforcement defaults to true in zed-local-only, ignored elsewhere
        this.localOnlyEnforcement = config.localOnlyEnforcement ?? true;
        this.allowTerminalAssist = config.allowTerminalAssist ?? false;
        this.allowedTerminalPrefixes = config.allowedTerminalPrefixes ?? [];
        this.allowVoiceInput = config.allowVoiceInput ?? false;
        this.multiBufferEditCap = multiBufferEditCap;
        this.slashCommandAllowList =
            config.slashCommandAllowList && config.slashCommandAllowList.length > 0
                ? config.slashCommandAllowList
                : null;
    }
    // ---------------------------------------------------------------------------
    // IdeAdapter: resolveUserIdentity
    //
    // Zed AI is an in-editor tool. Identity comes from ZED_SESSION_USER env var,
    // the session context userId, or the CLAUDE_SESSION_USER env var (shared
    // convention for cross-adapter compatibility).
    // ---------------------------------------------------------------------------
    async resolveUserIdentity(session) {
        this.ensureInitialized();
        const envUser = process.env['ZED_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?.['zedUserId']?.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: 'ZedAIAdapter: cannot resolve user identity. Set the ZED_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.deploymentMode,
        };
    }
    // ---------------------------------------------------------------------------
    // IdeAdapter: interceptToolInvocation -- dispatcher
    //
    // Routes by ctx.tool to the typed wrap methods below.
    // ---------------------------------------------------------------------------
    async interceptToolInvocation(ctx) {
        this.ensureInitialized();
        const packBinding = this.config?.packId;
        if (!packBinding || packBinding.trim() === '') {
            const decision = {
                decision: 'DENY',
                reason: 'ZedAIAdapter: tool invocation denied -- packBinding is empty. ' +
                    'Pack-binding primacy: every invocation MUST carry a pack binding.',
            };
            return this.routeToolInvocation(ctx, decision);
        }
        const { tool, input } = ctx;
        switch (tool) {
            case 'inlineCompletionRequested':
                return this.wrapInlineCompletion(ctx, String(input['prompt'] ?? ''));
            case 'chatRequested':
                return this.wrapChat(ctx, String(input['prompt'] ?? ''));
            case 'assistantThreadRequested':
                return this.wrapAssistantThread(ctx, String(input['prompt'] ?? ''), String(input['threadId'] ?? ''));
            case 'slashCommandRequested':
                return this.wrapSlashCommand(ctx, String(input['command'] ?? ''), String(input['args'] ?? ''));
            case 'inlineTransformRequested':
                return this.wrapInlineTransform(ctx, String(input['selection'] ?? ''), String(input['instruction'] ?? ''));
            case 'terminalAssistRequested':
                return this.wrapTerminalAssist(ctx, String(input['command'] ?? ''));
            case 'fileContextRequested':
                return this.wrapFileContext(ctx, String(input['filePath'] ?? ''), String(input['content'] ?? ''));
            case 'multiBufferEditRequested':
                return this.wrapMultiBufferEdit(ctx, input['bufferPaths'] ?? [], String(input['instruction'] ?? ''));
            case 'voiceInputRequested':
                return this.wrapVoiceInput(ctx, String(input['transcribedText'] ?? ''));
            default:
                return this.routeToolInvocation(ctx, {
                    decision: 'ALLOW',
                    reason: `ZedAIAdapter: unknown surface '${tool}' -- allowed with audit.`,
                });
        }
    }
    // ---------------------------------------------------------------------------
    // Surface: wrapInlineCompletion
    // ---------------------------------------------------------------------------
    async wrapInlineCompletion(ctx, prompt) {
        this.ensureInitialized();
        const deny = this.checkPackBinding(ctx);
        if (deny)
            return this.routeToolInvocation(ctx, deny);
        // PT-ADAPT-ZED-4: local-only enforcement
        const localOnlyDeny = this.checkLocalOnlyEnforcement(ctx, 'inline-completion');
        if (localOnlyDeny)
            return this.routeToolInvocation(ctx, localOnlyDeny);
        return this.routeToolInvocation(ctx, {
            decision: 'ALLOW',
            reason: `ZedAIAdapter: inline completion allowed (mode: ${this.deploymentMode}).`,
            metadata: { mode: this.deploymentMode, promptLength: prompt.length },
        });
    }
    // ---------------------------------------------------------------------------
    // Surface: wrapChat
    // ---------------------------------------------------------------------------
    async wrapChat(ctx, prompt) {
        this.ensureInitialized();
        const deny = this.checkPackBinding(ctx);
        if (deny)
            return this.routeToolInvocation(ctx, deny);
        const localOnlyDeny = this.checkLocalOnlyEnforcement(ctx, 'chat');
        if (localOnlyDeny)
            return this.routeToolInvocation(ctx, localOnlyDeny);
        return this.routeToolInvocation(ctx, {
            decision: 'ALLOW',
            reason: `ZedAIAdapter: chat request allowed (mode: ${this.deploymentMode}).`,
            metadata: { mode: this.deploymentMode, promptLength: prompt.length },
        });
    }
    // ---------------------------------------------------------------------------
    // Surface: wrapAssistantThread
    // ---------------------------------------------------------------------------
    async wrapAssistantThread(ctx, prompt, threadId) {
        this.ensureInitialized();
        const deny = this.checkPackBinding(ctx);
        if (deny)
            return this.routeToolInvocation(ctx, deny);
        const localOnlyDeny = this.checkLocalOnlyEnforcement(ctx, 'assistant-thread');
        if (localOnlyDeny)
            return this.routeToolInvocation(ctx, localOnlyDeny);
        return this.routeToolInvocation(ctx, {
            decision: 'ALLOW',
            reason: `ZedAIAdapter: assistant thread allowed (mode: ${this.deploymentMode}).`,
            metadata: { mode: this.deploymentMode, threadId, promptLength: prompt.length },
        });
    }
    // ---------------------------------------------------------------------------
    // Surface: wrapSlashCommand
    // ---------------------------------------------------------------------------
    async wrapSlashCommand(ctx, command, args) {
        this.ensureInitialized();
        const deny = this.checkPackBinding(ctx);
        if (deny)
            return this.routeToolInvocation(ctx, deny);
        // Slash command allow-list enforcement
        if (this.slashCommandAllowList !== null) {
            const normalizedCommand = command.replace(/^\//, '').trim().toLowerCase();
            const isAllowed = this.slashCommandAllowList.some((allowed) => allowed.replace(/^\//, '').toLowerCase() === normalizedCommand);
            if (!isAllowed) {
                return this.routeToolInvocation(ctx, {
                    decision: 'DENY',
                    reason: `ZedAIAdapter: slash command '/${normalizedCommand}' denied -- not in ` +
                        'slashCommandAllowList. ' +
                        'Reason code: zed-slash-command-not-allowed. ' +
                        `Allowed commands: [${this.slashCommandAllowList.map((c) => `'${c}'`).join(', ')}].`,
                    metadata: { command: normalizedCommand, allowList: this.slashCommandAllowList },
                });
            }
        }
        return this.routeToolInvocation(ctx, {
            decision: 'ALLOW',
            reason: `ZedAIAdapter: slash command '/${command}' allowed.`,
            metadata: { command, args },
        });
    }
    // ---------------------------------------------------------------------------
    // Surface: wrapInlineTransform
    // ---------------------------------------------------------------------------
    async wrapInlineTransform(ctx, selection, instruction) {
        this.ensureInitialized();
        const deny = this.checkPackBinding(ctx);
        if (deny)
            return this.routeToolInvocation(ctx, deny);
        const localOnlyDeny = this.checkLocalOnlyEnforcement(ctx, 'inline-transform');
        if (localOnlyDeny)
            return this.routeToolInvocation(ctx, localOnlyDeny);
        return this.routeToolInvocation(ctx, {
            decision: 'ALLOW',
            reason: `ZedAIAdapter: inline transform allowed (mode: ${this.deploymentMode}).`,
            metadata: { selectionLength: selection.length, instructionLength: instruction.length },
        });
    }
    // ---------------------------------------------------------------------------
    // Surface: wrapTerminalAssist
    //
    // Zed integrated terminal AI. Two gates applied in order:
    //   1. local-only enforcement (if in zed-local-only mode)
    //   2. opt-in required (allowTerminalAssist must be true)
    //   3. prefix allowlist (if allowedTerminalPrefixes is set)
    // ---------------------------------------------------------------------------
    async wrapTerminalAssist(ctx, command) {
        this.ensureInitialized();
        const deny = this.checkPackBinding(ctx);
        if (deny)
            return this.routeToolInvocation(ctx, deny);
        // PT-ADAPT-ZED-4: local-only enforcement runs before opt-in check.
        // Even if allowTerminalAssist is true, local-only mode must gate first.
        const localOnlyDeny = this.checkLocalOnlyEnforcement(ctx, 'terminal-assist');
        if (localOnlyDeny)
            return this.routeToolInvocation(ctx, localOnlyDeny);
        // Opt-in required
        if (!this.allowTerminalAssist) {
            return this.routeToolInvocation(ctx, {
                decision: 'DENY',
                reason: 'ZedAIAdapter: terminal assist denied -- allowTerminalAssist is false. ' +
                    'Reason code: zed-terminal-assist-not-permitted. ' +
                    'Zed terminal AI can execute arbitrary commands. Set allowTerminalAssist: true ' +
                    'in ZedAIAdapterConfig to enable this surface.',
                metadata: { mode: this.deploymentMode },
            });
        }
        // Prefix allowlist enforcement
        if (this.allowedTerminalPrefixes.length > 0) {
            const trimmedCmd = command.trim();
            const prefixMatch = this.allowedTerminalPrefixes.some((prefix) => trimmedCmd.startsWith(prefix));
            if (!prefixMatch) {
                return this.routeToolInvocation(ctx, {
                    decision: 'DENY',
                    reason: `ZedAIAdapter: terminal assist denied -- no allowed prefix matched ` +
                        `for '${trimmedCmd.slice(0, 80)}'. ` +
                        'Reason code: zed-terminal-assist-prefix-not-allowed. ' +
                        `Allowed prefixes: [${this.allowedTerminalPrefixes.map((p) => `'${p}'`).join(', ')}].`,
                    metadata: { allowedPrefixes: this.allowedTerminalPrefixes },
                });
            }
        }
        return this.routeToolInvocation(ctx, {
            decision: 'ALLOW',
            reason: `ZedAIAdapter: terminal assist allowed.`,
            metadata: { commandLength: command.length },
        });
    }
    // ---------------------------------------------------------------------------
    // Surface: wrapFileContext
    //
    // Zed adds a file to the assistant context (sends to LLM provider).
    // Secret scan is always applied. Denied with zed-file-context-secret-detected
    // when a secret is found.
    // PT-ADAPT-ZED-5: .env content denied regardless of caller intent.
    // ---------------------------------------------------------------------------
    async wrapFileContext(ctx, filePath, content) {
        this.ensureInitialized();
        const deny = this.checkPackBinding(ctx);
        if (deny)
            return this.routeToolInvocation(ctx, deny);
        // Secret scan always applied
        const finding = scanForSecrets(content);
        if (finding) {
            // PT-ADAPT-ZED-2: never echo the secret value in the audit entry
            const sanitizedPath = sanitizeSecretFromAudit(filePath);
            return this.routeToolInvocation(ctx, {
                decision: 'DENY',
                reason: `ZedAIAdapter: file context blocked -- secret pattern '${finding.name}' detected ` +
                    `in content of '${sanitizedPath}'. ` +
                    'Reason code: zed-file-context-secret-detected. ' +
                    'Adding this file to Zed AI context would transmit secrets to the LLM provider. ' +
                    'Remove the secret from the file before adding it to the assistant context.',
                metadata: { filePath: sanitizedPath, secretPattern: finding.name },
            });
        }
        return this.routeToolInvocation(ctx, {
            decision: 'ALLOW',
            reason: `ZedAIAdapter: file context allowed for '${filePath}'.`,
            metadata: { filePath, contentLength: content.length },
        });
    }
    // ---------------------------------------------------------------------------
    // Surface: wrapMultiBufferEdit
    // ---------------------------------------------------------------------------
    async wrapMultiBufferEdit(ctx, bufferPaths, instruction) {
        this.ensureInitialized();
        const deny = this.checkPackBinding(ctx);
        if (deny)
            return this.routeToolInvocation(ctx, deny);
        const bufferCount = bufferPaths.length;
        if (bufferCount > this.multiBufferEditCap) {
            return this.routeToolInvocation(ctx, {
                decision: 'DENY',
                reason: `ZedAIAdapter: multi-buffer edit denied -- buffer count (${bufferCount}) ` +
                    `exceeds multiBufferEditCap (${this.multiBufferEditCap}). ` +
                    'Reason code: zed-multi-buffer-edit-cap-exceeded. ' +
                    'Reduce the number of buffers in a single edit or raise multiBufferEditCap.',
                metadata: { bufferCount, multiBufferEditCap: this.multiBufferEditCap },
            });
        }
        return this.routeToolInvocation(ctx, {
            decision: 'ALLOW',
            reason: `ZedAIAdapter: multi-buffer edit allowed (${bufferCount} buffers).`,
            metadata: { bufferCount, instructionLength: instruction.length },
        });
    }
    // ---------------------------------------------------------------------------
    // Surface: wrapVoiceInput
    // ---------------------------------------------------------------------------
    async wrapVoiceInput(ctx, transcribedText) {
        this.ensureInitialized();
        const deny = this.checkPackBinding(ctx);
        if (deny)
            return this.routeToolInvocation(ctx, deny);
        // PT-ADAPT-ZED-4: local-only enforcement runs before allow flag.
        // Voice transcription typically routes through a network API even when
        // the text model is local.
        const localOnlyDeny = this.checkLocalOnlyEnforcement(ctx, 'voice-input');
        if (localOnlyDeny)
            return this.routeToolInvocation(ctx, localOnlyDeny);
        if (!this.allowVoiceInput) {
            return this.routeToolInvocation(ctx, {
                decision: 'DENY',
                reason: 'ZedAIAdapter: voice input denied -- allowVoiceInput is false. ' +
                    'Reason code: zed-voice-input-not-permitted. ' +
                    'Voice transcription may contain PII and can route through external APIs. ' +
                    'Set allowVoiceInput: true in ZedAIAdapterConfig to enable.',
                metadata: { mode: this.deploymentMode },
            });
        }
        return this.routeToolInvocation(ctx, {
            decision: 'ALLOW',
            reason: 'ZedAIAdapter: voice input allowed.',
            metadata: { transcribedLength: transcribedText.length },
        });
    }
    // ---------------------------------------------------------------------------
    // Private helpers
    // ---------------------------------------------------------------------------
    checkPackBinding(ctx) {
        const packId = this.config?.packId;
        if (!packId || packId.trim() === '') {
            return {
                decision: 'DENY',
                reason: 'ZedAIAdapter: tool invocation denied -- packBinding is empty. ' +
                    'Pack-binding primacy: every invocation MUST carry a pack binding. ' +
                    `Tool: ${ctx.tool}.`,
            };
        }
        return null;
    }
    /**
     * Local-only enforcement gate.
     *
     * In zed-local-only mode with localOnlyEnforcement: true (default), all
     * surfaces that can route requests to a network provider are denied unless
     * the request carries ideMetadata.localProvider: true.
     *
     * This is the primary differentiator of the Zed local-only mode: it is
     * a default-deny posture. The caller must explicitly signal "I am using
     * a local provider" to pass. Any feature that does not carry this signal
     * is treated as potentially routing to a network provider and denied.
     *
     * PT-ADAPT-ZED-4: opt-in flags (allowTerminalAssist, allowVoiceInput)
     * cannot override this gate. The local-only enforcement check happens
     * BEFORE the opt-in check in every wrap method.
     *
     * Returns a DENY decision if enforcement applies, or null to continue.
     */
    checkLocalOnlyEnforcement(ctx, surface) {
        if (this.deploymentMode !== 'zed-local-only')
            return null;
        if (!this.localOnlyEnforcement)
            return null; // only possible if not zed-local-only (validated at init)
        if (!isLocalProvider(ctx)) {
            return {
                decision: 'DENY',
                reason: `ZedAIAdapter: ${surface} denied in zed-local-only mode -- ` +
                    'request does not carry localProvider: true in ideMetadata. ' +
                    'Reason code: zed-local-only-network-not-permitted. ' +
                    'In zed-local-only mode with localOnlyEnforcement enabled, all AI ' +
                    'surfaces require the request context to signal local provider use ' +
                    '(ideMetadata.localProvider: true). This prevents data from being ' +
                    'inadvertently routed to a network-based LLM provider.',
                metadata: { mode: this.deploymentMode, surface, localOnlyEnforcement: this.localOnlyEnforcement },
            };
        }
        return null;
    }
}
exports.ZedAIAdapter = ZedAIAdapter;
//# sourceMappingURL=zed-ai.js.map