Task configuration:
{"task_id": "17", "task_name": "rename_files", "description": "\u5728vscode\u5f53\u524d\u5de5\u4f5c\u533a\u4e2d\u6279\u91cf\u91cd\u547d\u540d\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": {"relative_path": "greedy_algorithms", "origin_suffix": ".h", "expected_suffix": ".hpp"}, "instruction_template": "\u5728vscode\u5f53\u524d\u5de5\u4f5c\u533a\u4e2d\u76f8\u5bf9\u8def\u5f84\u4e3a ${relative_path} \u7684\u76ee\u5f55\u4e0b, \u5c06\u6240\u6709\u4ee5 ${origin_suffix} \u4e3a\u540e\u7f00\u7684\u6587\u4ef6\u7684\u540e\u7f00\u4fee\u6539\u4e3a ${expected_suffix}, \u8bf7\u6ce8\u610f, \u4f60\u9700\u8981\u5728\u5f53\u524dvscode\u5df2\u7ecf\u6253\u5f00\u7a97\u53e3\u7684\u5de5\u4f5c\u533a\u4e2d\u5b8c\u6210\u8be5\u4efb\u52a1", "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": 1, "preconditions": {"vscode_running": true}, "events": {"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": 1, "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:
/**
 * Gets the root directory of the current workspace.
 * @returns {string | undefined} The workspace root directory path or undefined if no workspace is open.
 */
async function getWorkspaceRoot() {
    const workspaceFolders = vscode.workspace.workspaceFolders;
    if (workspaceFolders && workspaceFolders.length > 0) {
        // Return the first workspace folder's path
        return workspaceFolders[0].uri.fsPath;
    }
    return undefined;
}

getWorkspaceRoot().then((root) => {
    socket.emit("send", {
        'event_type': "get_root_when_start",
        'message': "任务开始时获取工作区的目录",
        'root': root
    });
});

socket.on('evaluate', async () => {
    const root = await getWorkspaceRoot();
    // vscode.window.showInformationMessage(message);
    socket.emit("send", {
        'event_type': "evaluate_on_completion",
        'message': "任务结束时检查文件是否存在",
        'root': root
    });
});

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

import os
from typing import Dict, Any, Optional, List

ORIGIN_FILE_LIST = None

def message_handler(message: Dict[str, Any], logger, task_parameter: Dict[str, Any]) -> Optional[List[Dict[str, Any]]]:    
    global ORIGIN_FILE_LIST
    event_type = message.get('event_type')
    logger.info(message)
    expected_relative_path = task_parameter.get("relative_path", "greedy_algorithms")
    expected_origin_suffix = task_parameter.get("origin_suffix", ".h")
    expected_suffix = task_parameter.get("expected_suffix", ".hpp")
    if event_type == "get_root_when_start":
        root = message.get('root', '')
        dir_path = os.path.join(root, expected_relative_path)
        ORIGIN_FILE_LIST = os.listdir(dir_path)
    elif event_type == "evaluate_on_completion" and ORIGIN_FILE_LIST is not None:
        root = message.get('root', '')
        dir_path = os.path.join(root, expected_relative_path)
        file_list = os.listdir(dir_path)
        expected_file_list = [i if not i.endswith(expected_origin_suffix) else i.replace(expected_origin_suffix, expected_suffix) for i in ORIGIN_FILE_LIST]
        file_list.sort()
        expected_file_list.sort()
        logger.info(expected_file_list)
        logger.info(file_list)
        if file_list == expected_file_list:
            return [
                {"status": "key_step", "index": 1},
                {"status": "success", "reason": f"任务成功完成"}
            ]
        else:
            return [{"status": "error", "type": "evaluate_on_completion", "message": "任务没有完成"}]
    return None