Task configuration:
{"task_id": "08", "task_name": "rename_scene", "description": "obs-studio rename scene", "application_info": {"name": "obs-studio", "executable_path": "/workspace/PC-Canary/apps/obs-studio/build_ubuntu/rundir/Debug/bin/obs", "args": []}, "task_parameters": {"old_scene_name": "tmp_scene", "new_scene_name": "new_scene"}, "instruction_template": "Please rename ${old_scene_name} to ${new_scene_name} in obs-studio", "evaluation_setup": {"scripts": [{"role": "hook", "path": "hooker.js"}, {"role": "handler", "path": "handler.py"}], "timeout": 180}, "preconditions": {"obs_studio_running": true}, "total_key_steps": 1, "events": {"scene_renamed": {"description": "Scene renamed successfully", "is_key_step": true, "key_step_index": 1, "key_step_name": "Current scene renamed successfully"}}, "mcp_servers": [{"command": "node", "args": ["/home/agent/obs-mcp/build/index.js"]}]}

Hook script:
(function () {
    // Script settings
    const FUNCTION_NAME = "OBSBasic::Save";
    const FUNCTION_SYMBOL = "_ZN8OBSBasic4SaveEPKc";
    const FUNCTION_RENAME_SOURCES_NAME = "OBSBasic::RenameSources";
    const FUNCTION_RENAME_SOURCES_SYMBOL = "_ZN8OBSBasic13RenameSourcesE10OBSSafeRefIP10obs_sourceXadL_Z18obs_source_get_refEEXadL_Z18obs_source_releaseEEE7QStringS4_";

    const EVENT_ON_ENTER = "function called";
    const EVENT_ON_LEAVE = "function returned";
    const EVENT_ON_SUCCESS = "scene_json_path";
    const EVENT_RENAME_SUCCESS = "rename_success";

    const MESSAGE_called = "Intercepted function call";
    const MESSAGE_returned = "Function returned";
    const MESSAGE_ON_SUCCESS = "Configuration file write operation completed";
    const MESSAGE_RENAME_ON_SUCCESS = "Scene rename operation completed";
    const MESSAGE_script_initialized = "Monitoring script has started";
    const MESSAGE_hook_installed = "Monitoring hook installed, waiting for operation...";

    // Send events to the evaluation system
    function sendEvent(eventType, data = {}) {
        const payload = {
            event: eventType,
            ...data,
            timestamp: new Date().getTime()
        };
        send(payload);
    }

    // Get function address
    function getFunctionAddress(functionName) {
        const funcAddr = DebugSymbol.getFunctionByName(functionName);
        if (!funcAddr) {
            sendEvent("error", {
                error_type: "function_not_found",
                message: `Cannot find function ${functionName}`
            });
            return null;
        }

        sendEvent("function_found", {
            address: funcAddr.toString(),
            message: `Found the actual address of function ${functionName}`
        });
        return funcAddr;
    }

    // Initialize recording update hooks
    function initHook_save() {
        const funcAddr = getFunctionAddress(FUNCTION_SYMBOL);
        if (!funcAddr) {
            return;
        }

        Interceptor.attach(funcAddr, {
            onEnter(args) {
                sendEvent(EVENT_ON_ENTER, {
                    message: MESSAGE_called,
                    function: FUNCTION_NAME,
                    symbol: FUNCTION_SYMBOL
                });
                this.file_path = args[1];
            },

            onLeave(retval) {
                sendEvent(EVENT_ON_LEAVE, {
                    message: MESSAGE_returned,
                    function: FUNCTION_NAME,
                    symbol: FUNCTION_SYMBOL
                });
                const val = this.file_path.readCString(-1);
                sendEvent(EVENT_ON_SUCCESS, {
                    message: MESSAGE_ON_SUCCESS,
                    path: val,
                });
            }
        });
    }

    function initHook_rename() {
        const funcAddr = getFunctionAddress(FUNCTION_RENAME_SOURCES_SYMBOL);
        if (!funcAddr) {
            return;
        }

        Interceptor.attach(funcAddr, {
            onEnter(args) {
                sendEvent(EVENT_ON_ENTER, {
                    message: MESSAGE_called,
                    function: FUNCTION_RENAME_SOURCES_NAME,
                    symbol: FUNCTION_RENAME_SOURCES_SYMBOL
                });
                this.prev_name = args[2];
                this.new_name = args[1];
            },

            onLeave(retval) {
                sendEvent(EVENT_ON_LEAVE, {
                    message: MESSAGE_returned,
                    function: FUNCTION_RENAME_SOURCES_NAME,
                    symbol: FUNCTION_RENAME_SOURCES_SYMBOL
                });
                sendEvent(EVENT_RENAME_SUCCESS, {
                    message: MESSAGE_RENAME_ON_SUCCESS,
                    pre_name: this.pre_name,
                    new_name: this.new_name,
                });
            }
        });
    }

    // Initialize hooks
    function initHook() {
        sendEvent("script_initialized", {
            message: MESSAGE_script_initialized
        });

        // Initialize individual hooks
        initHook_save();
        initHook_rename();
        sendEvent("hook_installed", {
            message: MESSAGE_hook_installed
        });
    }

    // Start script
    initHook();
})();

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

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

_EVENT_SUCCESS = "scene_json_path"
_PAYLOAD_SUCCESS = "path"


def message_handler(
    message: Dict[str, Any], logger, task_parameter: Dict[str, Any]
) -> Optional[List[Dict[str, Any]]]:
    payload = message["payload"]
    event_type = payload["event"]
    logger.debug(f"receive: {event_type}")
    if event_type == _EVENT_SUCCESS:
        logger.info(payload.get("message", ""))
        old = task_parameter.get("old_scene_name", "")
        new = task_parameter.get("new_scene_name", "")
        print(payload)
        current = payload.get(_PAYLOAD_SUCCESS, "")
        # 在json文件里面找是否old不存在并且new存在
        with open(current, "r") as f:
            data = json.load(f)
            flag_old = False
            flag_new = False
            if "scene_order" in data and isinstance(data["scene_order"], list):
                for scene in data["scene_order"]:
                    if (
                        isinstance(scene, dict)
                        and "name" in scene
                        and scene["name"] == old
                    ):
                        flag_old = True
                    if (
                        isinstance(scene, dict)
                        and "name" in scene
                        and scene["name"] == new
                    ):
                        flag_new = True
            if not flag_old and flag_new:
                return [
                    {"status": "key_step", "index": 1},
                    {"status": "success", "reason": "rename scene success"},
                ]
    return None