Task configuration:
{"task_id": "15", "task_name": "open_split_window", "description": "\u5728vscode\u4e2d\u4ee5\u62c6\u5206\u7a97\u53e3\u7684\u5f62\u5f0f\u6253\u5f00\u4e24\u4e2a\u6587\u4ef6", "application_info": {"name": "vscode", "executable_path": "/usr/share/code/code", "args": ["--no-sandbox", "--user-data-dir=/root/vscode_user_data_dir/"]}, "task_parameters": {"first_file": "bubble_sort.cpp", "second_file": "bead_sort.cpp", "expected_first_path": "/root/C-Plus-Plus/sorting/bubble_sort.cpp", "expected_second_path": "/root/C-Plus-Plus/sorting/bead_sort.cpp"}, "instruction_template": "\u5728vscode\u7a97\u53e3\u4e2d\u4ee5\u5de6\u53f3\u62c6\u5206\u7a97\u53e3\u7684\u5f62\u5f0f\u6253\u5f00 ${first_file} \u548c ${second_file} \u4e24\u4e2a\u6587\u4ef6", "evaluation_setup": {"scripts": [{"role": "hook", "path": "hooker.js"}, {"role": "handler", "path": "handler.py"}], "timeout": 120, "evaluator_type": "IpcInjector", "evaluate_on_completion": true}, "total_key_steps": 3, "preconditions": {"vscode_running": true}, "events": {"open_file1": {"description": "\u6210\u529f\u6253\u5f00\u76ee\u68071\u6587\u4ef6", "is_key_step": true, "key_step_index": 1, "key_step_name": "\u6253\u5f00\u6587\u4ef61"}, "open_file2": {"description": "\u6210\u529f\u6253\u5f00\u76ee\u68072\u6587\u4ef6", "is_key_step": true, "key_step_index": 2, "key_step_name": "\u6253\u5f00\u6587\u4ef62"}, "evaluate_on_completion": {"description": "\u5728\u4efb\u52a1\u7ed3\u675f\u65f6\u68c0\u67e5\u4efb\u52a1\u662f\u5426\u5b8c\u6210", "is_key_step": true, "key_step_index": 3, "key_step_name": "\u4efb\u52a1\u6210\u529f\u5b8c\u6210"}}, "mcp_servers": [{"command": "mcp-proxy", "args": ["http://127.0.0.1:6010/sse"]}], "context_data": [{"from": "tests/context_data/vscode/user_data", "to": "/root/vscode_user_data_dir"}, {"from": "tests/context_data/vscode/extensions", "to": "/root/.vscode/"}, {"from": "tests/context_data/vscode/C-Plus-Plus", "to": "/root/C-Plus-Plus"}]}

Hook script:
/**
 * 获取当前打开的文件和拆分窗口信息
 * @returns {Promise<Object>} 包含打开文件和拆分窗口信息的对象
 */
async function getOpenFilesAndSplits() {
    try {
        // 获取当前打开的文件
        const activeEditor = vscode.window.activeTextEditor;
        const activeFile = activeEditor ? activeEditor.document.uri.fsPath : 'No active file';

        // 获取所有打开的编辑器（包括非激活的）
        const openFiles = vscode.window.visibleTextEditors.map(editor => ({
            filePath: editor.document.uri.fsPath,
            isActive: editor === activeEditor
        }));

        // 获取拆分窗口信息
        const tabGroups = vscode.window.tabGroups.all;
        const splitInfo = tabGroups.map((group, index) => ({
            groupId: index + 1,
            isActive: group.activeTab ? true : false,
            tabs: group.tabs.map(tab => ({
                label: tab.label,
                filePath: tab.input instanceof vscode.TabInputText ? tab.input.uri.fsPath : 'Unknown',
                isActive: tab === group.activeTab
            }))
        }));

        return {
            activeFile,
            openFiles,
            splitInfo
        };
    } catch (error) {
        throw new Error(`Failed to get files and splits: ${error.message}`);
    }
}

// Listen for 'evaluate' event
socket.on('evaluate', async () => {
    const info = await getOpenFilesAndSplits();
    // vscode.window.showInformationMessage(message);
    socket.emit("send", {
        'event_type': "evaluate_on_completion",
        'message': "任务结束时获得打开文件的信息",
        'info': info
    });
});

// Register a listener for when a text document is opened
vscode.workspace.onDidOpenTextDocument(async (document) => {
    const filePath = document.uri.fsPath;
    socket.emit("send", {
        event_type: "open_file",
        message: "打开文件",
        path: filePath,
        scheme: document.uri.scheme
    });
});

Evaluation handler:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from typing import Dict, Any, Optional, List

def message_handler(message: Dict[str, Any], logger, task_parameter: Dict[str, Any]) -> Optional[List[Dict[str, Any]]]:    
    event_type = message.get('event_type')
    logger.info(message)
    expected_first = task_parameter.get("first_file", "bubble_sort.cpp")
    expected_second = task_parameter.get("second_file", "bead_sort.cpp")
    expected_first_path = task_parameter.get("expected_first_path", "/root/C-Plus-Plus/sorting/bubble_sort.cpp")
    expected_second_path = task_parameter.get("expected_second_path", "/root/C-Plus-Plus/sorting/bead_sort.cpp")
    if event_type == "evaluate_on_completion":
        split_info = message.get('info', {}).get('splitInfo', None)
        if not split_info or len(split_info) != 2:
            return None
        files = []
        for i in split_info:
            group = i.get('tabs', None)
            if not group:
                return None
            files.append([j.get('label', None) for j in group if j.get('isActive', False)])
        if (expected_first in files[0] and expected_second in files[1]) or (expected_first in files[1] and expected_second in files[0]):
            return [
                {"status": "key_step", "index": 3},
                {"status": "success", "reason": f"任务成功完成"}
            ]
        else:
            return [{"status": "error", "type": "evaluate_on_completion", "message": "任务没有完成"}]
    elif event_type == "open_file":
        file_path = message.get("path")
        if message.get("scheme") == "git":
            file_path = file_path[:-4]
        if file_path == expected_first_path:
            return [{"status": "key_step", "index": 1}]
        elif file_path == expected_second_path:
            return [{"status": "key_step", "index": 2}]
    return None