Task configuration:
{"task_id": "12", "task_name": "add_new_scene_and_color_source", "description": "obs-studio add new color background source and set its dimensions", "application_info": {"name": "obs-studio", "executable_path": "/workspace/PC-Canary/apps/obs-studio/build_ubuntu/rundir/Debug/bin/obs", "args": []}, "task_parameters": {"new_scene_name": "test_scene", "source_name": "test", "source_type": "color_source_v3", "width": 1280, "height": 720}, "instruction_template": "Please create a new scene in obs-studio named ${new_scene_name}, and add a new source to this scene named ${source_name}, with type ${source_type}. Set color source width to ${width} and height to ${height}", "evaluation_setup": {"scripts": [{"role": "hook", "path": "hooker.js"}, {"role": "handler", "path": "handler.py"}], "timeout": 180}, "preconditions": {"obs_studio_running": true}, "total_key_steps": 4, "events": {"scene_name_matched": {"description": "Scene name matched successfully", "is_key_step": true, "key_step_index": 1, "key_step_name": "Scene name matched successfully"}, "source_name_matched": {"description": "Source name matched successfully", "is_key_step": true, "key_step_index": 2, "key_step_name": "Source name matched successfully"}, "source_type_matched": {"description": "Source type matched successfully", "is_key_step": true, "key_step_index": 3, "key_step_name": "Source type matched successfully"}, "color_source_config_matched": {"description": "Color source properties update completed", "is_key_step": true, "key_step_index": 4, "key_step_name": "Set as color source"}}, "mcp_servers": [{"command": "node", "args": ["/home/agent/obs-mcp/build/index.js"]}]}

Hook script:
(function () {
    // Script settings
    const FUNCTION_NAME = "obs_source_create";
    const FUNCTION_SYMBOL = "obs_source_create";

    const EVENT_ON_ENTER = "function called";
    const EVENT_ON_LEAVE = "function returned";
    const EVENT_SUCCESS = "create_success";

    const MESSAGE_called = "Intercepted function call";
    const MESSAGE_returned = "Function returned";
    const MESSAGE_ON_SUCCESS = "Create new source 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 hook() {
        const funcAddr = getFunctionAddress(FUNCTION_NAME);
        if (!funcAddr) {
            return;
        }

        Interceptor.attach(funcAddr, {
            onEnter(args) {
                sendEvent(EVENT_ON_ENTER, {
                    message: MESSAGE_called,
                    function: FUNCTION_NAME,
                    symbol: FUNCTION_SYMBOL
                });
                this.name = args[1].readCString(-1);
                this.type = args[0].readCString(-1);
                console.log(this.name);
                console.log(this.type);
            },

            onLeave(retval) {
                sendEvent(EVENT_ON_LEAVE, {
                    message: MESSAGE_returned,
                    function: FUNCTION_NAME,
                    symbol: FUNCTION_SYMBOL
                });
                sendEvent(EVENT_SUCCESS, {
                    message: MESSAGE_ON_SUCCESS,
                    name: this.name,
                    type: this.type
                });
            }
        });

        Interceptor.attach(getFunctionAddress("_ZN8OBSBasic4SaveEPKc"), {
            onEnter(args) {
                sendEvent(EVENT_ON_ENTER, {
                    message: MESSAGE_called,
                    function: "OBSBasic::Save",
                    symbol: "_ZN8OBSBasic4SaveEPKc"
                });
                this.path = args[1].readCString(-1);
                console.log(this.path);
            },

            onLeave(retval) {
                sendEvent(EVENT_ON_LEAVE, {
                    message: MESSAGE_returned,
                    function: "OBSBasic::Save",
                    symbol: "_ZN8OBSBasic4SaveEPKc"
                });
                sendEvent("config_save", {
                    message: "Configuration file saved",
                    path: this.path
                });
            }
        });
    }

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

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

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

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

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

_EVENT_SUCCESS = "config_save"


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"Received event: {event_type}")

    if event_type == "create_success":
        expected_scene = task_parameter.get("new_scene_name", "")
        expected_name = task_parameter.get("source_name", "")
        expected_type = task_parameter.get("source_type", "")
        source_name = payload.get("name", "")
        source_type = payload.get("type", "")
        if source_name == expected_scene:
            return [{"status": "key_step", "index": 1}]
        if source_name == expected_name and source_type == expected_type:
            return [
                {"status": "key_step", "index": 2},
                {"status": "key_step", "index": 3},
            ]

    elif event_type == _EVENT_SUCCESS:
        exp_width = task_parameter.get("width", 0)
        exp_height = task_parameter.get("height", 0)
        exp_name = task_parameter.get("source_name", "")
        logger.info(payload.get("message", ""))
        file_path = payload.get("path", "")
        with open(file_path, "r") as f:
            file_content = json.load(f)
            sources = file_content.get("sources", [])
            for source in sources:
                if source.get("name", "") == exp_name:
                    settings = source.get("settings", {})
                    width = settings.get("width", 0)
                    height = settings.get("height", 0)
                    if width == exp_width and height == exp_height:
                        return [
                            {"status": "key_step", "index": 4},
                            {
                                "status": "success",
                                "reason": "Successfully added color background source",
                            },
                        ]
                    else:
                        logger.info(
                            f"Resolution mismatch: width={width}, height={height}"
                        )
    return None