/**
 * Claude Code Agent SDK Adapter
 *
 * 通过 @anthropic-ai/claude-agent-sdk 的 query() API
 * 实现与 Claude Code 的结构化交互。
 *
 * 核心特性：
 * - 支持 autoAccept 模式自动确认所有权限
 * - 支持 AskUserQuestion 拦截并转发到聊天面板
 * - 支持 turn_complete 事件精确判断 AI 就绪状态
 */
import { execSync } from 'child_process';
import { EventEmitter } from 'events';
import * as fs from 'fs';
import * as path from 'path';
import { v4 as uuidv4 } from 'uuid';
import { importBridgeSdkPackage } from '../utils/sdk-package-loader.js';
import { IdlePollLoop } from './idle-poll-loop.js';
import { getToolActionInfo } from './types.js';
// ============ AsyncIterableQueue ============
/**
 * 可排队的 AsyncIterable，用于向 SDK query() 输入流推送用户消息
 */
class AsyncIterableQueue {
    constructor() {
        this.queue = [];
        this.resolve = null;
        this.done = false;
    }
    enqueue(item) {
        if (this.done)
            return;
        if (this.resolve) {
            const r = this.resolve;
            this.resolve = null;
            r({ value: item, done: false });
        }
        else {
            this.queue.push(item);
        }
    }
    close() {
        this.done = true;
        if (this.resolve) {
            const r = this.resolve;
            this.resolve = null;
            r({ value: undefined, done: true });
        }
    }
    [Symbol.asyncIterator]() {
        return {
            next: () => {
                if (this.queue.length > 0) {
                    return Promise.resolve({ value: this.queue.shift(), done: false });
                }
                if (this.done) {
                    return Promise.resolve({ value: undefined, done: true });
                }
                return new Promise((resolve) => {
                    this.resolve = resolve;
                });
            },
        };
    }
}
// ============ ClaudeSdkAdapter ============
/**
 * Claude Code Agent SDK Adapter
 */
export class ClaudeSdkAdapter extends EventEmitter {
    constructor() {
        super(...arguments);
        this.providerId = 'claude-code';
        this.displayName = 'Claude Code';
        this.sessions = new Map();
        this.sdkQueries = new Map();
        this.inputStreams = new Map();
        this.abortControllers = new Map();
        this.sdkModule = null;
        // 挂起的权限请求
        this.pendingPermissions = new Map();
        // 挂起的 AskUserQuestion
        this.pendingQuestions = new Map();
        // 正在处理的工具调用 ID（用于去重）
        this.activeToolUseIds = new Map(); // sessionId -> toolUseId
        // 最近处理的工具名（用于 stream_event 和 tool_use 的去重）
        this.recentToolNames = new Map(); // sessionId -> { name, time }
        // 收集工具参数的缓冲区（用于 stream_event 模式）
        this.toolInputBuffers = new Map(); // "${sessionId}:${toolUseId}" -> JSON string
        // ============ 空闲命令管理 ============
        this.idleCommands = new Map();
        this.idleTimers = new Map();
        this.idlePollLoops = new Map();
        this.lastActivityAt = new Map();
    }
    // ============ 生命周期方法 ============
    async startSession(sessionId, config) {
        // ★ 特征日志：确认使用的是 Claude Code Adapter
        console.log(`[ClaudeSdkAdapter] ★★★ 启动 Claude Code SDK 会话 ★★★ sessionId="${sessionId}", command="${config.command}", workingDirectory="${config.workingDirectory}"`);
        const session = {
            sessionId,
            status: 'starting',
            messages: [],
            createdAt: new Date().toISOString(),
            totalUsage: { inputTokens: 0, outputTokens: 0 },
        };
        this.sessions.set(sessionId, session);
        try {
            const sdk = await this.loadSdk();
            const abortController = new AbortController();
            this.abortControllers.set(sessionId, abortController);
            // 创建输入流
            const inputStream = new AsyncIterableQueue();
            this.inputStreams.set(sessionId, inputStream);
            // 构建选项
            const options = this.buildQueryOptions(sessionId, config, abortController);
            // 启动 SDK query
            const sdkQuery = sdk.query({
                prompt: inputStream,
                options,
            });
            this.sdkQueries.set(sessionId, sdkQuery);
            // 启动流消费（异步，不阻塞）
            this.consumeStream(sessionId, sdkQuery).catch(err => {
                console.error(`[ClaudeSdkAdapter] Stream error for ${sessionId}:`, err);
            });
            // ★ 等待一小段时间让 SDK 进程启动
            await new Promise(resolve => setTimeout(resolve, 100));
            // 发送初始 prompt
            if (config.initialPrompt) {
                // 有初始 prompt，启动后会进入思考状态
                session.status = 'starting';
                this.emit('status-change', sessionId, 'starting');
                const userMsg = {
                    id: uuidv4(),
                    sessionId,
                    role: 'user',
                    content: config.initialPrompt,
                    timestamp: new Date().toISOString(),
                };
                session.messages.push(userMsg);
                this.emit('conversation-message', sessionId, userMsg);
                // ★ 解析初始 prompt 中的图片标记
                const initialContent = this.parseMessageContent(config.initialPrompt);
                inputStream.enqueue({
                    type: 'user',
                    message: {
                        role: 'user',
                        content: initialContent,
                    },
                });
            }
            else {
                // 没有初始 prompt，等待用户输入
                session.status = 'waiting_input';
                this.emit('status-change', sessionId, 'waiting_input', { usage: session.totalUsage });
                // 如果有空闲命令配置，立即启动空闲检测
                if (this.idleCommands.has(sessionId)) {
                    this.startIdleDetection(sessionId);
                }
            }
        }
        catch (error) {
            const errorMessage = error instanceof Error ? error.message : String(error);
            session.status = 'error';
            this.emit('status-change', sessionId, 'error');
            this.emitEvent(sessionId, {
                type: 'error',
                sessionId,
                timestamp: new Date().toISOString(),
                data: { text: `Failed to start session: ${errorMessage}` },
            });
            throw error;
        }
    }
    async sendMessage(sessionId, message) {
        const session = this.sessions.get(sessionId);
        const inputStream = this.inputStreams.get(sessionId);
        if (!session || !inputStream) {
            throw new Error(`Session ${sessionId} not found`);
        }
        if (session.status === 'error') {
            throw new Error(`Session ${sessionId} is in error state`);
        }
        // 记录用户消息
        const userMsg = {
            id: uuidv4(),
            sessionId,
            role: 'user',
            content: message,
            timestamp: new Date().toISOString(),
        };
        session.messages.push(userMsg);
        this.emit('conversation-message', sessionId, userMsg);
        // 更新状态 - 用户发送消息后，AI 将开始处理
        session.status = 'thinking';
        this.emit('status-change', sessionId, 'thinking');
        // ★ 解析消息中的图片标记，构造多模态内容
        const content = this.parseMessageContent(message);
        // 推送到输入流
        inputStream.enqueue({
            type: 'user',
            message: {
                role: 'user',
                content,
            },
        });
    }
    /**
     * 解析消息内容，提取图片和文本
     * 支持 [图片: url] 标记格式
     */
    parseMessageContent(message) {
        const content = [];
        // 匹配 [图片: url] 格式
        const imageTagRegex = /\[图片\s*:\s*([^\]\n]+?)\s*\]/gi;
        let lastIndex = 0;
        let match = imageTagRegex.exec(message);
        while (match !== null) {
            // 添加图片之前的文本
            if (match.index > lastIndex) {
                const text = message.slice(lastIndex, match.index).trim();
                if (text) {
                    content.push({ type: 'text', text });
                }
            }
            // 添加图片
            const imageUrl = match[1].trim();
            if (imageUrl) {
                console.log(`[ClaudeSdkAdapter] 添加图片 URL: ${imageUrl}`);
                content.push({
                    type: 'image',
                    source: {
                        type: 'url',
                        url: imageUrl,
                    },
                });
            }
            lastIndex = match.index + match[0].length;
            match = imageTagRegex.exec(message);
        }
        // 添加最后的文本
        if (lastIndex < message.length) {
            const text = message.slice(lastIndex).trim();
            if (text) {
                content.push({ type: 'text', text });
            }
        }
        // 如果没有找到任何内容，返回原始消息作为文本
        if (content.length === 0) {
            content.push({ type: 'text', text: message });
        }
        return content;
    }
    async sendConfirmation(sessionId, accept) {
        const pending = this.pendingPermissions.get(sessionId);
        if (pending) {
            if (accept) {
                pending.resolve({ behavior: 'allow', updatedInput: pending.toolInput });
            }
            else {
                pending.resolve({ behavior: 'deny', message: 'User denied' });
            }
            this.pendingPermissions.delete(sessionId);
        }
    }
    async sendQuestionAnswer(sessionId, answers) {
        const pending = this.pendingQuestions.get(sessionId);
        if (!pending)
            return;
        const questions = pending.toolInput.questions;
        let answersText = '用户已回答了您的问题：\n';
        if (Array.isArray(questions)) {
            questions.forEach((q, i) => {
                const key = String(i);
                const answer = answers[key] || answers[q.header || ''] || answers[q.question] || '（未填写）';
                answersText += `• ${q.header || q.question}：${answer}\n`;
            });
        }
        else {
            answersText += JSON.stringify(answers);
        }
        pending.resolve({ behavior: 'deny', message: answersText });
        this.pendingQuestions.delete(sessionId);
    }
    async abortCurrentTurn(sessionId) {
        const abortController = this.abortControllers.get(sessionId);
        if (abortController) {
            abortController.abort();
        }
    }
    async terminateSession(sessionId) {
        const session = this.sessions.get(sessionId);
        const sdkQuery = this.sdkQueries.get(sessionId);
        const inputStream = this.inputStreams.get(sessionId);
        if (inputStream) {
            inputStream.close();
        }
        if (sdkQuery) {
            try {
                sdkQuery.close();
            }
            catch {
                // ignore
            }
        }
        if (session) {
            session.status = 'terminated';
            this.emit('status-change', sessionId, 'terminated');
            this.emitEvent(sessionId, {
                type: 'session_complete',
                sessionId,
                timestamp: new Date().toISOString(),
                data: { exitCode: 0 },
            });
        }
        this.cleanupSession(sessionId);
    }
    getConversation(sessionId) {
        return this.sessions.get(sessionId)?.messages ?? [];
    }
    hasSession(sessionId) {
        return this.sessions.has(sessionId);
    }
    getProviderSessionId(sessionId) {
        return this.sessions.get(sessionId)?.providerSessionId;
    }
    getSessionStatus(sessionId) {
        return this.sessions.get(sessionId)?.status;
    }
    getSessionPid(sessionId) {
        return this.sessions.get(sessionId)?.pid;
    }
    cleanup() {
        for (const sessionId of this.sessions.keys()) {
            try {
                this.terminateSession(sessionId);
            }
            catch {
                // ignore
            }
        }
        this.sessions.clear();
        this.sdkQueries.clear();
        this.inputStreams.clear();
        this.abortControllers.clear();
        this.pendingPermissions.clear();
        this.pendingQuestions.clear();
        this.idleCommands.clear();
        this.idleTimers.clear();
    }
    /**
     * 设置空闲命令配置
     */
    setIdleCommands(sessionId, commands) {
        this.idleCommands.set(sessionId, commands);
        // 如果当前状态是 waiting_input，立即启动空闲检测
        const session = this.sessions.get(sessionId);
        if (session && session.status === 'waiting_input' && (commands.idleInputCommand || commands.nonInputCommand)) {
            this.startIdleDetection(sessionId);
        }
    }
    /**
     * 启动空闲检测定时器
     */
    startIdleDetection(sessionId) {
        // 清除现有定时器
        this.stopIdleDetection(sessionId);
        const commands = this.idleCommands.get(sessionId);
        if (!commands?.idleInputCommand) {
            return;
        }
        if (this.isMessagePollingIdleCommand(commands.idleInputCommand)) {
            const idleLoop = this.getIdlePollLoop(sessionId);
            idleLoop.start();
            return;
        }
        this.lastActivityAt.set(sessionId, Date.now());
        // 每500ms检测一次空闲状态
        const timer = setInterval(() => {
            this.checkIdleState(sessionId);
        }, 500);
        this.idleTimers.set(sessionId, timer);
    }
    /**
     * 停止空闲检测定时器
     */
    stopIdleDetection(sessionId) {
        this.idlePollLoops.get(sessionId)?.stop();
        const timer = this.idleTimers.get(sessionId);
        if (timer) {
            clearInterval(timer);
            this.idleTimers.delete(sessionId);
        }
    }
    /**
     * 检测空闲状态并执行命令
     */
    checkIdleState(sessionId) {
        const session = this.sessions.get(sessionId);
        if (!session || session.status !== 'waiting_input') {
            return;
        }
        const commands = this.idleCommands.get(sessionId);
        if (!commands || (!commands.idleInputCommand && !commands.nonInputCommand)) {
            return;
        }
        const lastActivity = this.lastActivityAt.get(sessionId) || 0;
        const idleTime = Date.now() - lastActivity;
        // 空闲1.5秒后执行空闲命令
        const IDLE_THRESHOLD = 1500;
        if (idleTime < IDLE_THRESHOLD) {
            return;
        }
        // 标记活动时间，避免重复执行
        this.lastActivityAt.set(sessionId, Date.now());
        // SDK 模式：空闲时发送一个简单的提示消息让 AI 调用 poll_message
        // 实际的消息获取由 AI 通过 MCP 工具 poll_message 完成
        if (commands.idleInputCommand) {
            // 解析空闲命令，提取实际要执行的指令
            const command = commands.idleInputCommand;
            // 如果空闲命令包含 poll_message/get_message，引导 AI 进入阻塞式消息轮询。
            if (this.isMessagePollingIdleCommand(command)) {
                this.sendIdlePrompt(sessionId, this.buildMessagePollingIdlePrompt());
            }
            else {
                // 其他命令，直接作为消息发送给 AI
                this.sendIdlePrompt(sessionId, command);
            }
        }
    }
    /**
     * 发送空闲提示到 SDK
     */
    sendIdlePrompt(sessionId, prompt, options) {
        const inputStream = this.inputStreams.get(sessionId);
        if (!inputStream)
            return;
        // 更新状态。poll_message 是阻塞监听动作，保持 waiting_input，避免 UI 误判为“思考中卡死”。
        const session = this.sessions.get(sessionId);
        if (session && !options?.keepWaitingStatus) {
            session.status = 'thinking';
            this.emit('status-change', sessionId, 'thinking');
        }
        // 发送空闲提示
        inputStream.enqueue({
            type: 'user',
            message: {
                role: 'user',
                content: [{ type: 'text', text: prompt }],
            },
        });
    }
    isMessagePollingIdleCommand(command) {
        const normalized = command.trim();
        if (!normalized) {
            return false;
        }
        const lower = normalized.toLowerCase();
        return lower.includes('poll_message') || lower.includes('get_message') || normalized.includes('取消息');
    }
    buildMessagePollingIdlePrompt() {
        return '系统提醒：当前工作已完成或运行时已进入空闲状态。请立即调用 poll_message 工具阻塞等待新消息和流程线任务提醒；收到流程任务提醒后调用 my_task 查看已由后台静默抢占的任务详情并直接处理；完成任务后调用 submit_task_result 提交结果，然后继续 poll_message。';
    }
    /**
     * 获取当前会话的空闲轮询驱动器。
     * 主流程：惰性创建 -> 绑定会话状态与命令读取 -> 统一派发 poll/普通 idle prompt。
     */
    getIdlePollLoop(sessionId) {
        const existing = this.idlePollLoops.get(sessionId);
        if (existing)
            return existing;
        const loop = new IdlePollLoop({
            getStatus: () => this.sessions.get(sessionId)?.status,
            getCommand: () => this.idleCommands.get(sessionId)?.idleInputCommand,
            isPollingCommand: (command) => this.isMessagePollingIdleCommand(command),
            dispatchPollingPrompt: () => this.sendIdlePrompt(sessionId, this.buildMessagePollingIdlePrompt(), { keepWaitingStatus: true }),
            dispatchRegularIdleCommand: (command) => this.sendIdlePrompt(sessionId, command),
        });
        this.idlePollLoops.set(sessionId, loop);
        return loop;
    }
    /**
     * 发送手动快捷键 token 序列，和空闲自动命令共用解析逻辑。
     */
    async sendShortcutInput(sessionId, command) {
        await this.executeIdleCommand(sessionId, command);
    }
    /**
     * 执行空闲命令（支持 [enter], [wait:n] 等 token）
     */
    async executeIdleCommand(sessionId, command) {
        const inputStream = this.inputStreams.get(sessionId);
        if (!inputStream)
            return;
        // 解析命令 token
        const tokenPattern = /\[(.+?)\]/g;
        let cursor = 0;
        const tokens = [];
        let match = tokenPattern.exec(command);
        while (match !== null) {
            if (match.index > cursor) {
                tokens.push({ type: 'text', value: command.slice(cursor, match.index) });
            }
            const token = match[1];
            if (token.startsWith('wait:')) {
                const seconds = parseFloat(token.slice(5));
                if (!isNaN(seconds)) {
                    tokens.push({ type: 'wait', value: seconds * 1000 });
                }
            }
            else {
                // 映射按键 token
                const keyMap = {
                    'enter': '\r',
                    'up': '\x1b[A',
                    'down': '\x1b[B',
                    'left': '\x1b[D',
                    'right': '\x1b[C',
                    'tab': '\t',
                    'esc': '\x1b',
                    'escape': '\x1b',
                    'space': ' ',
                    'ctrl+c': '\x03',
                };
                tokens.push({ type: 'key', value: keyMap[token.toLowerCase()] || token });
            }
            cursor = match.index + match[0].length;
            match = tokenPattern.exec(command);
        }
        if (cursor < command.length) {
            tokens.push({ type: 'text', value: command.slice(cursor) });
        }
        // 执行 tokens
        for (const token of tokens) {
            if (token.type === 'wait') {
                await new Promise(resolve => setTimeout(resolve, token.value));
            }
            else {
                const text = String(token.value);
                inputStream.enqueue({
                    type: 'user',
                    message: {
                        role: 'user',
                        content: [{ type: 'text', text }],
                    },
                });
            }
        }
    }
    /**
     * 更新活动时间（在收到输出或状态变化时调用）
     */
    updateActivity(sessionId) {
        this.lastActivityAt.set(sessionId, Date.now());
    }
    /**
     * 恢复之前的会话
     * 注意：SDK 的 resume 功能需要 providerSessionId
     */
    async resumeSession(sessionId, providerSessionId, config) {
        // 目前简化实现：重新启动一个新会话
        // TODO: 使用 SDK 的 resume API 恢复会话上下文
        const session = {
            sessionId,
            providerSessionId,
            status: 'starting',
            messages: [],
            createdAt: new Date().toISOString(),
            totalUsage: { inputTokens: 0, outputTokens: 0 },
        };
        this.sessions.set(sessionId, session);
        try {
            await this.startSession(sessionId, config);
            session.providerSessionId = providerSessionId;
        }
        catch (error) {
            session.status = 'error';
            this.emit('status-change', sessionId, 'error');
            throw error;
        }
    }
    // ============ 私有方法 ============
    async loadSdk() {
        if (!this.sdkModule) {
            try {
                this.sdkModule = await importBridgeSdkPackage('@anthropic-ai/claude-agent-sdk');
            }
            catch (err) {
                throw new Error(`Failed to load @anthropic-ai/claude-agent-sdk. Please install: npm install @anthropic-ai/claude-agent-sdk. Original error: ${err}`);
            }
        }
        return this.sdkModule;
    }
    /**
     * 查找 Claude Code 可执行文件路径
     * 支持 Windows / macOS / Linux
     */
    findClaudeExecutable(command) {
        const normalizedCommand = this.extractExecutableCommand(command);
        // 1. 如果是绝对路径，直接返回
        if (path.isAbsolute(normalizedCommand) && fs.existsSync(normalizedCommand)) {
            const resolvedWrapper = this.resolveWindowsWrapper(normalizedCommand);
            if (resolvedWrapper) {
                return resolvedWrapper;
            }
            if (this.isWindowsWrapperPath(normalizedCommand)) {
                console.warn(`[ClaudeSdkAdapter] Skipping wrapper script for Claude Code executable: ${normalizedCommand}`);
                return undefined;
            }
            return normalizedCommand;
        }
        const pathExecutable = this.resolveExecutableFromPath(normalizedCommand);
        if (pathExecutable) {
            return pathExecutable;
        }
        // 2. Windows 特定路径（作为 claude 默认命令的兜底）
        if (process.platform === 'win32' && this.shouldUseDefaultClaudeFallbacks(normalizedCommand)) {
            const userProfile = process.env.USERPROFILE || '';
            const localAppData = process.env.LOCALAPPDATA || '';
            const appData = process.env.APPDATA || '';
            // Volta 安装路径（优先检查）
            const voltaCandidates = [
                path.join(localAppData, 'Volta', 'tools', 'shared', '@anthropic-ai', 'claude-code', 'cli.js'),
                path.join(localAppData, 'Volta', 'tools', 'image', 'packages', '@anthropic-ai', 'claude-code', 'cli.js'),
            ];
            for (const p of voltaCandidates) {
                if (fs.existsSync(p)) {
                    console.log(`[ClaudeSdkAdapter] Found Claude Code CLI via Volta: ${p}`);
                    return p;
                }
            }
            const candidates = [
                path.join(appData, 'npm', 'node_modules', '@anthropic-ai', 'claude-code', 'cli.js'),
                path.join(localAppData, 'Programs', 'claude-code', 'claude.exe'),
                path.join(userProfile, '.local', 'bin', 'claude.exe'),
            ];
            for (const p of candidates) {
                if (p && fs.existsSync(p)) {
                    console.log(`[ClaudeSdkAdapter] Found Claude Code CLI: ${p}`);
                    return p;
                }
            }
        }
        // 3. macOS/Linux 特定路径（作为 claude 默认命令的兜底）
        const home = process.env.HOME || '';
        const candidates = [
            path.join(home, '.local', 'bin', 'claude'),
            path.join(home, '.npm-global', 'bin', 'claude'),
            path.join(home, '.local', 'share', 'mise', 'shims', 'claude'),
            path.join(home, '.asdf', 'shims', 'claude'),
            `/opt/homebrew/bin/claude`,
            `/usr/local/bin/claude`,
            `/usr/bin/claude`,
            `/usr/local/lib/node_modules/@anthropic-ai/claude-code/cli.js`,
            `/usr/lib/node_modules/@anthropic-ai/claude-code/cli.js`,
            path.join(home, '.local', 'share', 'volta', 'tools', 'shared', '@anthropic-ai', 'claude-code', 'cli.js'),
            path.join(home, '.volta', 'tools', 'shared', '@anthropic-ai', 'claude-code', 'cli.js'),
        ];
        if (this.shouldUseDefaultClaudeFallbacks(normalizedCommand)) {
            for (const p of candidates) {
                if (p && fs.existsSync(p)) {
                    console.log(`[ClaudeSdkAdapter] Found Claude Code CLI: ${p}`);
                    return p;
                }
            }
        }
        // 4. 尝试 npm root -g
        if (this.shouldUseDefaultClaudeFallbacks(normalizedCommand)) {
            try {
                const npmRoot = execSync('npm root -g', { encoding: 'utf8', timeout: 5000 }).trim();
                const cliJs = path.join(npmRoot, '@anthropic-ai', 'claude-code', 'cli.js');
                if (fs.existsSync(cliJs)) {
                    console.log(`[ClaudeSdkAdapter] Found Claude Code CLI via npm root: ${cliJs}`);
                    return cliJs;
                }
            }
            catch {
                // 忽略
            }
        }
        console.warn(`[ClaudeSdkAdapter] Claude Code executable not found for command: ${normalizedCommand}`);
        return undefined;
    }
    /**
     * 从面板配置的命令行中提取实际可执行文件，避免把参数一起传给 PATH 查找。
     */
    extractExecutableCommand(command) {
        const input = (command || 'claude').trim() || 'claude';
        const firstToken = input.match(/^\s*(?:"([^"]+)"|'([^']+)'|(\S+))/);
        return firstToken?.[1] || firstToken?.[2] || firstToken?.[3] || 'claude';
    }
    /**
     * 按当前 Bridge 进程的 PATH/PATHEXT 直接查找可执行文件，避免依赖 which/where 和 shell 解析。
     */
    resolveExecutableFromPath(command) {
        const pathValue = process.env.PATH || '';
        if (!pathValue) {
            return undefined;
        }
        const commandHasExtension = path.extname(command).length > 0;
        const extensions = process.platform === 'win32'
            ? this.getWindowsPathExtensions(commandHasExtension)
            : [''];
        for (const directory of pathValue.split(path.delimiter)) {
            if (!directory) {
                continue;
            }
            for (const extension of extensions) {
                const candidate = path.join(directory, `${command}${extension}`);
                if (!fs.existsSync(candidate)) {
                    continue;
                }
                if (this.isWindowsWrapperPath(candidate)) {
                    const resolvedWrapper = this.resolveWindowsWrapper(candidate);
                    if (resolvedWrapper) {
                        return resolvedWrapper;
                    }
                    continue;
                }
                console.log(`[ClaudeSdkAdapter] Found Claude Code CLI via PATH: ${candidate}`);
                return candidate;
            }
        }
        return undefined;
    }
    getWindowsPathExtensions(commandHasExtension) {
        if (commandHasExtension) {
            return [''];
        }
        const pathExt = process.env.PATHEXT || '.EXE;.CMD;.BAT;.COM;.PS1';
        return pathExt
            .split(';')
            .map((extension) => extension.trim())
            .filter((extension) => extension.length > 0);
    }
    shouldUseDefaultClaudeFallbacks(command) {
        return ['claude', 'claude-code', 'claudecode'].includes(command.toLowerCase());
    }
    isWindowsWrapperPath(filePath) {
        const extension = path.extname(filePath).toLowerCase();
        return ['.cmd', '.bat', '.ps1'].includes(extension);
    }
    resolveWindowsWrapper(wrapperPath) {
        if (!this.isWindowsWrapperPath(wrapperPath)) {
            return undefined;
        }
        return this.resolveCliJsFromWrapper(wrapperPath);
    }
    /**
     * 从 wrapper 脚本解析 cli.js 路径
     */
    resolveCliJsFromWrapper(wrapperPath) {
        try {
            const content = fs.readFileSync(wrapperPath, 'utf8');
            // Volta wrapper: volta run %~n0 %*
            if (content.includes('volta run')) {
                const voltaShared = path.join(process.env.LOCALAPPDATA || '', 'Volta', 'tools', 'shared', '@anthropic-ai', 'claude-code', 'cli.js');
                if (fs.existsSync(voltaShared)) {
                    return voltaShared;
                }
            }
            const wrapperDirectory = path.dirname(wrapperPath);
            const npmCliJs = path.join(wrapperDirectory, 'node_modules', '@anthropic-ai', 'claude-code', 'cli.js');
            if (fs.existsSync(npmCliJs)) {
                return npmCliJs;
            }
        }
        catch {
            // 忽略
        }
        return undefined;
    }
    buildQueryOptions(sessionId, config, abortController) {
        const options = {
            cwd: config.workingDirectory,
            abortController,
            settingSources: ['user', 'project', 'local'],
            // 启用部分消息以获取增量输出
            includePartialMessages: true,
        };
        // ★ 关键：指定 Claude Code 可执行文件路径
        // SDK 需要这个来启动 Claude Code CLI
        const execPath = this.findClaudeExecutable(config.command);
        if (execPath) {
            options.pathToClaudeCodeExecutable = execPath;
            // ★ 特征日志：明确显示使用的是 Claude Code 可执行文件
            console.log(`[ClaudeSdkAdapter] ★★★ 使用 Claude Code 可执行文件 ★★★ path="${execPath}"`);
        }
        else {
            console.warn(`[ClaudeSdkAdapter] ★★★ 警告：未找到 Claude Code 可执行文件 ★★★ command="${config.command}"`);
        }
        // 模型配置
        if (config.model) {
            options.model = config.model;
        }
        // 最大轮次
        if (config.maxTurns && config.maxTurns > 0) {
            options.maxTurns = config.maxTurns;
        }
        // MCP 服务器
        if (config.mcpConfigPath) {
            try {
                const raw = fs.readFileSync(config.mcpConfigPath, 'utf-8');
                const parsed = JSON.parse(raw);
                options.mcpServers = { ...config.extraMcpServers, ...parsed.mcpServers };
            }
            catch {
                options.mcpServers = config.extraMcpServers || {};
            }
        }
        else {
            options.mcpServers = config.extraMcpServers || {};
        }
        // 允许的工具
        if (config.allowedTools && config.allowedTools.length > 0) {
            options.allowedTools = config.allowedTools;
        }
        else {
            options.allowedTools = [
                'Read',
                'Write',
                'Edit',
                'Bash',
                'Glob',
                'Grep',
                'WebSearch',
                'WebFetch',
                'Task',
                'NotebookEdit',
                'LSP',
                'Skill',
                'mcp__*',
            ];
        }
        // 环境变量
        options.env = { ...process.env, ...config.envOverrides };
        // 权限模式 - 使用 canUseTool 回调统一处理权限，不使用 bypassPermissions
        // 原因：bypassPermissions 模式下 SDK 可能跳过 canUseTool 回调，
        // 导致 AskUserQuestion 等需要用户交互的工具无法被拦截，前端收不到提问事件。
        // 改为通过 canUseTool 回调实现 autoAccept 逻辑：
        //   - AskUserQuestion → 始终拦截，转发到前端等待用户回答
        //   - autoAccept 普通工具 → 直接允许
        //   - 非 autoAccept 普通工具 → 询问用户
        options.canUseTool = this.createPermissionHandler(sessionId, config.autoAccept);
        return options;
    }
    createPermissionHandler(sessionId, autoAccept) {
        return async (toolName, toolInput, _options) => {
            // AskUserQuestion 特殊处理：始终拦截，转发到前端等待用户回答
            if (toolName === 'AskUserQuestion' || toolName === 'ask_user_question') {
                return new Promise((resolve) => {
                    this.pendingQuestions.set(sessionId, { resolve, toolInput });
                    this.emit('ask-user-question', {
                        sessionId,
                        questions: toolInput.questions,
                    });
                });
            }
            // autoAccept 模式直接允许普通工具
            // 返回 { behavior: 'allow', updatedInput } 格式
            if (autoAccept) {
                return { behavior: 'allow', updatedInput: toolInput };
            }
            // 非 autoAccept 模式，询问用户
            return new Promise((resolve) => {
                this.pendingPermissions.set(sessionId, { resolve, toolInput });
                this.emit('permission-request', {
                    sessionId,
                    toolName,
                    toolInput,
                    permissionPrompt: `Allow ${toolName}?`,
                });
            });
        };
    }
    async consumeStream(sessionId, sdkQuery) {
        const session = this.sessions.get(sessionId);
        if (!session)
            return;
        try {
            for await (const msg of sdkQuery) {
                const sdkMsg = msg;
                this.handleSdkMessage(sessionId, session, sdkMsg);
            }
        }
        catch (error) {
            if (this.abortControllers.get(sessionId)?.signal.aborted) {
                // 主动中止，正常结束
                return;
            }
            const errorMessage = error instanceof Error ? error.message : String(error);
            this.emitEvent(sessionId, {
                type: 'error',
                sessionId,
                timestamp: new Date().toISOString(),
                data: { text: `Stream error: ${errorMessage}` },
            });
        }
    }
    /**
     * Apply Claude SDK usage snapshots to the adapter session.
     * Main flow: accept assistant-step or final-result usage, then keep the largest observed totals.
     */
    applyUsage(session, usage) {
        if (!usage)
            return;
        if (Number.isFinite(usage.input_tokens)) {
            session.totalUsage.inputTokens = Math.max(session.totalUsage.inputTokens, usage.input_tokens ?? 0);
        }
        if (Number.isFinite(usage.output_tokens)) {
            session.totalUsage.outputTokens = Math.max(session.totalUsage.outputTokens, usage.output_tokens ?? 0);
        }
    }
    handleSdkMessage(sessionId, session, msg) {
        const timestamp = new Date().toISOString();
        // ★ 调试日志：记录所有收到的消息类型
        console.log(`[ClaudeSdkAdapter] Received message type: "${msg.type}", subtype: "${msg.subtype || ''}"`);
        // ★ 如果有工具名，也记录下来
        if (msg.tool_name) {
            console.log(`[ClaudeSdkAdapter] Tool name: "${msg.tool_name}"`);
        }
        switch (msg.type) {
            case 'text':
                // AI 正在输出文本
                if (msg.delta) {
                    // 更新状态为思考中（如果有文本输出）
                    if (session.status !== 'thinking') {
                        session.status = 'thinking';
                        this.emit('status-change', sessionId, 'thinking');
                        this.stopIdleDetection(sessionId);
                    }
                    this.emitEvent(sessionId, {
                        type: 'text_delta',
                        sessionId,
                        timestamp,
                        data: { text: msg.delta },
                    });
                }
                break;
            case 'thinking':
                // AI 正在思考/推理
                if (session.status !== 'thinking') {
                    session.status = 'thinking';
                    this.emit('status-change', sessionId, 'thinking');
                    this.stopIdleDetection(sessionId);
                }
                if (msg.content || msg.delta) {
                    this.emitEvent(sessionId, {
                        type: 'thinking',
                        sessionId,
                        timestamp,
                        data: { text: msg.delta || msg.content },
                    });
                }
                break;
            case 'tool_use':
            case 'tool_use_start':
                // AI 正在调用工具
                {
                    const actionInfo = { ...getToolActionInfo(msg.tool_name, msg.tool_input), actionId: msg.tool_use_id };
                    const nextStatus = actionInfo.actionType === 'idle' ? 'waiting_input' : 'tool_using';
                    session.status = nextStatus;
                    this.emit('status-change', sessionId, nextStatus, {
                        actionType: actionInfo.actionType,
                        actionLabel: actionInfo.actionLabel,
                        actionDetail: actionInfo.actionDetail,
                        actionId: actionInfo.actionId,
                        usage: session.totalUsage,
                    });
                    if (actionInfo.actionType !== 'idle') {
                        this.stopIdleDetection(sessionId);
                    }
                    this.handleToolUse(sessionId, session, msg, timestamp);
                }
                break;
            case 'tool_result':
            case 'tool_use_end':
                this.emitEvent(sessionId, {
                    type: 'tool_use_end',
                    sessionId,
                    timestamp,
                    data: {
                        toolName: msg.tool_name,
                        toolResult: msg.tool_result,
                        isError: msg.is_error,
                        toolUseId: msg.tool_use_id,
                    },
                });
                break;
            case 'result':
                // ★ SDK 的 turn_complete 事件是 'result' 类型
                {
                    const isSuccess = msg.subtype === 'success';
                    this.applyUsage(session, msg.usage);
                    // 发送 turn_complete 事件
                    this.emitEvent(sessionId, {
                        type: 'turn_complete',
                        sessionId,
                        timestamp,
                        data: {
                            usage: session.totalUsage,
                            text: isSuccess ? msg.result : undefined,
                        },
                    });
                    // ★ 更新状态为等待输入
                    session.status = 'waiting_input';
                    this.emit('status-change', sessionId, 'waiting_input', { usage: session.totalUsage });
                    // 如果有空闲命令配置，启动空闲检测
                    if (this.idleCommands.has(sessionId)) {
                        this.startIdleDetection(sessionId);
                    }
                }
                break;
            case 'system':
                // 系统消息，可能包含初始化数据
                if (msg.subtype === 'init') {
                    console.log(`[ClaudeSdkAdapter] Session ${sessionId} initialized: model=${msg.model}, tools=${msg.tools?.length || 0}`);
                }
                break;
            case 'assistant':
                // AI 响应完成，可能包含完整消息
                this.applyUsage(session, msg.message?.usage);
                if (msg.message?.content) {
                    const textContent = msg.message.content
                        .filter((c) => c.type === 'text' && c.text)
                        .map((c) => c.text || '')
                        .join('');
                    if (textContent) {
                        session.messages.push({
                            id: uuidv4(),
                            sessionId,
                            role: 'assistant',
                            content: textContent,
                            timestamp,
                        });
                    }
                }
                break;
            case 'user':
                // 用户消息（工具结果）
                if (msg.tool_use_result !== undefined) {
                    // 工具执行结果
                }
                break;
            case 'error':
                session.status = 'error';
                this.emit('status-change', sessionId, 'error');
                this.emitEvent(sessionId, {
                    type: 'error',
                    sessionId,
                    timestamp,
                    data: { text: msg.content || 'Unknown error' },
                });
                break;
            case 'stream_event':
                // SDK 流式事件 - 处理工具调用的增量更新
                this.handleStreamEvent(sessionId, session, msg, timestamp);
                break;
            default:
                // 未知消息类型，记录日志
                console.log(`[ClaudeSdkAdapter] Unknown message type: ${msg.type}`, msg);
        }
    }
    handleToolUse(sessionId, session, msg, timestamp) {
        // 检查是否与 stream_event 处理的工具调用匹配（去重）
        const recentInfo = this.recentToolNames.get(sessionId);
        const toolNameSuffix = this.extractToolNameSuffix(msg.tool_name || '');
        if (recentInfo) {
            const timeDiff = Date.now() - recentInfo.time;
            // 如果在 30s 内，且工具名后缀相同，认为是同一个调用（content_block_stop 已发出事件）
            if (timeDiff < 30000 && recentInfo.name === toolNameSuffix) {
                console.log(`[ClaudeSdkAdapter] Tool use ${msg.tool_name} matches recent stream_event (${recentInfo.name}), skipping duplicate event`);
                // 清理记录
                this.recentToolNames.delete(sessionId);
                this.activeToolUseIds.delete(sessionId);
                // 只记录到消息历史，不重复发送事件（content_block_stop 已经发过）
                session.messages.push({
                    id: uuidv4(),
                    sessionId,
                    role: 'tool_use',
                    content: `${msg.tool_name}: ${JSON.stringify(msg.tool_input).slice(0, 100)}`,
                    timestamp,
                    toolName: msg.tool_name,
                    toolInput: msg.tool_input,
                    toolUseId: msg.tool_use_id,
                });
                return;
            }
        }
        // 清理过期记录
        this.recentToolNames.delete(sessionId);
        this.activeToolUseIds.delete(sessionId);
        // 获取工具动作分类信息
        const actionInfo = getToolActionInfo(msg.tool_name, msg.tool_input);
        console.log(`[ClaudeSdkAdapter] Tool use: ${msg.tool_name}, actionType: ${actionInfo.actionType}, actionDetail: ${actionInfo.actionDetail}`);
        this.emitEvent(sessionId, {
            type: 'tool_use_start',
            sessionId,
            timestamp,
            data: {
                toolName: msg.tool_name,
                toolInput: msg.tool_input,
                toolUseId: msg.tool_use_id,
                actionType: actionInfo.actionType,
                actionLabel: actionInfo.actionLabel,
                actionDetail: actionInfo.actionDetail,
            },
        });
        // 记录到消息历史
        session.messages.push({
            id: uuidv4(),
            sessionId,
            role: 'tool_use',
            content: `${msg.tool_name}: ${JSON.stringify(msg.tool_input).slice(0, 100)}`,
            timestamp,
            toolName: msg.tool_name,
            toolInput: msg.tool_input,
            toolUseId: msg.tool_use_id,
        });
    }
    /**
     * 处理 SDK 流式事件
     * 用于检测工具调用开始、增量更新和结束
     *
     * 注意：此方法只更新状态，不发送时间线事件
     * 时间线事件由 handleToolUse 发送，这样能获取完整的工具参数
     */
    handleStreamEvent(sessionId, session, msg, timestamp) {
        const event = msg.event;
        if (!event)
            return;
        // 处理 content_block_start 事件 - 工具调用开始
        if (event.type === 'content_block_start') {
            const contentBlock = event.content_block;
            if (contentBlock?.type === 'tool_use' && contentBlock.name) {
                const toolName = contentBlock.name;
                const toolId = contentBlock.id || `stream_${Date.now()}`;
                console.log(`[ClaudeSdkAdapter] Tool use started via stream_event: ${toolName} (id: ${toolId})`);
                // 记录当前活跃的工具调用 ID（用于去重）
                this.activeToolUseIds.set(sessionId, toolId);
                // 记录工具名（用于与后续 tool_use 消息匹配去重）
                // 提取工具名后缀：aws-mcp__get_profile -> get_profile
                const toolNameSuffix = this.extractToolNameSuffix(toolName);
                this.recentToolNames.set(sessionId, { name: toolNameSuffix, time: Date.now() });
                // 初始化参数缓冲区
                const bufferKey = `${sessionId}:${toolId}`;
                this.toolInputBuffers.set(bufferKey, '');
                // 先不更新状态，等待收集参数后更新
            }
        }
        // 处理 content_block_delta 事件 - 收集工具参数增量
        if (event.type === 'content_block_delta') {
            if (event.delta?.type === 'input_json_delta' && event.delta.partial_json) {
                const toolId = this.activeToolUseIds.get(sessionId);
                if (toolId) {
                    const bufferKey = `${sessionId}:${toolId}`;
                    const currentBuffer = this.toolInputBuffers.get(bufferKey) || '';
                    this.toolInputBuffers.set(bufferKey, currentBuffer + event.delta.partial_json);
                    console.log(`[ClaudeSdkAdapter] Tool input delta at index ${event.index}, buffer length: ${currentBuffer.length + event.delta.partial_json.length}`);
                }
            }
        }
        // 处理 content_block_stop 事件 - 工具调用参数收集完成
        if (event.type === 'content_block_stop') {
            const toolId = this.activeToolUseIds.get(sessionId);
            const recentInfo = this.recentToolNames.get(sessionId);
            if (toolId && recentInfo) {
                const bufferKey = `${sessionId}:${toolId}`;
                const jsonStr = this.toolInputBuffers.get(bufferKey) || '';
                console.log(`[ClaudeSdkAdapter] Content block stopped at index ${event.index}, collected JSON length: ${jsonStr.length}`);
                // 尝试解析完整的工具参数
                let toolInput;
                if (jsonStr) {
                    try {
                        toolInput = JSON.parse(jsonStr);
                        console.log(`[ClaudeSdkAdapter] Parsed tool input:`, JSON.stringify(toolInput).slice(0, 200));
                    }
                    catch {
                        console.warn(`[ClaudeSdkAdapter] Failed to parse tool input JSON:`, jsonStr.slice(0, 100));
                    }
                }
                // 获取工具动作分类信息（使用完整参数）
                const actionInfo = { ...getToolActionInfo(recentInfo.name, toolInput), actionId: toolId };
                // 更新状态
                session.status = 'tool_using';
                this.emit('status-change', sessionId, 'tool_using', {
                    actionType: actionInfo.actionType,
                    actionLabel: actionInfo.actionLabel,
                    actionDetail: actionInfo.actionDetail,
                    actionId: actionInfo.actionId,
                    usage: session.totalUsage,
                });
                this.stopIdleDetection(sessionId);
                // 发送工具调用开始事件（此时有完整参数）
                this.emitEvent(sessionId, {
                    type: 'tool_use_start',
                    sessionId,
                    timestamp,
                    data: {
                        toolName: recentInfo.name,
                        toolInput,
                        toolUseId: toolId,
                        actionType: actionInfo.actionType,
                        actionLabel: actionInfo.actionLabel,
                        actionDetail: actionInfo.actionDetail,
                        actionId: actionInfo.actionId,
                    },
                });
                // 记录到消息历史
                session.messages.push({
                    id: uuidv4(),
                    sessionId,
                    role: 'tool_use',
                    content: `${recentInfo.name}: ${jsonStr.slice(0, 100)}`,
                    timestamp,
                    toolName: recentInfo.name,
                    toolInput,
                    toolUseId: toolId,
                });
                // 清理缓冲区（保留 recentToolNames 用于后续 tool_use 消息去重）
                this.toolInputBuffers.delete(bufferKey);
                this.activeToolUseIds.delete(sessionId);
                // 注意：不在此处删除 recentToolNames，让后续 tool_use 消息能匹配去重
                // recentToolNames 会在 handleToolUse 中匹配成功后删除，或过期后自动清理
            }
        }
    }
    /**
     * 提取工具名后缀
     * 用于匹配不同格式的工具名
     * 例如：aws-mcp__get_profile -> get_profile
     */
    extractToolNameSuffix(toolName) {
        // 如果包含 __，取最后一部分
        if (toolName.includes('__')) {
            return toolName.split('__').pop() || toolName;
        }
        // 如果包含 -mcp__，取后面的部分
        if (toolName.includes('-mcp__')) {
            const idx = toolName.indexOf('-mcp__');
            return toolName.substring(idx + 6);
        }
        return toolName;
    }
    emitEvent(sessionId, event) {
        this.emit('event', event);
    }
    cleanupSession(sessionId) {
        this.stopIdleDetection(sessionId);
        this.sessions.delete(sessionId);
        this.sdkQueries.delete(sessionId);
        this.inputStreams.delete(sessionId);
        this.abortControllers.delete(sessionId);
        this.pendingPermissions.delete(sessionId);
        this.pendingQuestions.delete(sessionId);
        this.idleCommands.delete(sessionId);
        this.idlePollLoops.delete(sessionId);
        this.lastActivityAt.delete(sessionId);
    }
}
