Task configuration:
{"task_id": "20", "task_name": "create_scene_collection_with_scenes", "description": "Create a new scene collection and create scenes inside it", "application_info": {"name": "obs-studio", "executable_path": "/workspace/PC-Canary/apps/obs-studio/build_ubuntu/rundir/Debug/bin/obs", "args": []}, "task_parameters": {"collection_name": "test_collection", "scene_names": ["a", "b"]}, "instruction_template": "Please create a new scene collection in obs-studio named ${collection_name}, and create scenes ${scene_names} inside it", "evaluation_setup": {"scripts": [{"role": "hook", "path": "hooker.js"}, {"role": "handler", "path": "handler.py"}], "timeout": 180}, "preconditions": {"obs_studio_running": true}, "total_key_steps": 2, "events": {"scene_collections_name_matches": {"description": "Scene collection name matched successfully", "is_key_step": true, "key_step_index": 1, "key_step_name": "Scene collection name matched successfully"}, "scenes_list_matches": {"description": "Scene list matched successfully", "is_key_step": true, "key_step_index": 2, "key_step_name": "Scene list matched successfully"}}, "mcp_servers": [{"command": "node", "args": ["/home/agent/obs-mcp/build/index.js"]}]}

Hook script:
(function () {
    // Script settings
    const FUNCTION_NAME_SAVE = "OBSBasic::Save";
    const FUNCTION_SYMBOL_SAVE = "_ZN8OBSBasic4SaveEPKc";

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

    const MESSAGE_CALLED = "Intercepted function call";
    const MESSAGE_RETURNED = "Function returned";
    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: `Unable to find function ${functionName}`
            });
            return null;
        }

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

    // Initialize hook - Monitor OBSBasic::Save to get scene collection configuration file path
    function hookSaveSceneCollection() {
        const funcAddr = getFunctionAddress(FUNCTION_SYMBOL_SAVE);
        if (!funcAddr) {
            return;
        }

        // Monitor OBSBasic::Save function
        Interceptor.attach(funcAddr, {
            onEnter(args) {
                sendEvent(EVENT_ON_ENTER, {
                    message: MESSAGE_CALLED,
                    function: FUNCTION_NAME_SAVE,
                    symbol: FUNCTION_SYMBOL_SAVE
                });
                this.path = args[1].readCString();
                console.log("this.path is: ", this.path);
            },
            
            onLeave(retval) {
                sendEvent(EVENT_ON_LEAVE, {
                    message: MESSAGE_RETURNED,
                    function: FUNCTION_NAME_SAVE,
                    symbol: FUNCTION_SYMBOL_SAVE,
                    path: this.path,
                });
            }
        });
    }

    // Initialize hook
    function initHook() {
        sendEvent("script_initialized", {
            message: MESSAGE_SCRIPT_INITIALIZED
        });

        // Initialize hooks
        hookSaveSceneCollection();

        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_FUNCTION_RETURN = "function returned"
_PAYLOAD_SUCCESS = "path"


def message_handler(
    message: Dict[str, Any], logger, task_parameter: Dict[str, Any]
) -> Optional[List[Dict[str, Any]]]:
    print(message)
    payload = message["payload"]
    print(payload)
    event_type = payload["event"]
    logger.debug(f"Received event: {event_type}")
    key_step = []
    if event_type == _EVENT_FUNCTION_RETURN:
        logger.info("Function returned: " + payload.get("function", ""))
        current_file_path = payload.get(_PAYLOAD_SUCCESS, "")
        expected_collection = task_parameter.get("collection_name", "")
        expected_scenes = task_parameter.get("scene_names", [])
        # Check scene collection configuration file
        try:
            with open(current_file_path, "r") as f:
                data = json.load(f)

                # Check scene collection name
                collection_name_found = False
                if "name" in data and data["name"] == expected_collection:
                    collection_name_found = True
                    key_step.append({"status": "key_step", "index": 1})

                # Check if scenes exist
                scenes_found = []
                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:
                            for expected_scene in expected_scenes:
                                if (
                                    scene["name"] == expected_scene
                                    and expected_scene not in scenes_found
                                ):
                                    scenes_found.append(expected_scene)

                # Determine if the task is completed
                if collection_name_found and len(scenes_found) == len(expected_scenes):
                    key_step.append({"status": "key_step", "index": 2})
                    key_step.append(
                        {
                            "status": "success",
                            "reason": "Scene collection name and scene list both matched successfully",
                        }
                    )
                    return key_step
                # else:
                #     # Record incomplete cases
                #     if not collection_name_found:
                #         return [{"status": "error", "message": f"Specified scene collection not found: {expected_collection}"}]
                #     if len(scenes_found) == 0:
                #         return [{"status": "error", "message": f"None of the specified scenes found: {expected_scenes}"}]
                #     elif len(scenes_found) < len(expected_scenes):
                #         return [{"status": "error", "message": f"Some scenes not found, found: {scenes_found}, expected: {expected_scenes}"}]
        except Exception as e:
            return [
                {
                    "status": "error",
                    "message": f"Error occurred while checking configuration file: {str(e)}",
                }
            ]

    return None