Task configuration:
{"task_id": "25", "task_name": "add_stinger_transition", "description": "Add Stinger transition and demonstrate switching", "application_info": {"name": "obs-studio", "executable_path": "/workspace/PC-Canary/apps/obs-studio/build_ubuntu/rundir/Debug/bin/obs", "args": []}, "task_parameters": {"stinger_file": "/home/agent/obs-studio-data/stinger_transition.mp4", "transition_point_ms": 300}, "instruction_template": "Current DISPLAY is defined in ~/.bashrc, Do not launch another obs-studio if there it is already launched. Add a Stinger transition effect in OBS Studio, using ${stinger_file} as the video file, set the transition point to ${transition_point_ms} milliseconds, then demonstrate switching between two scenes", "evaluation_setup": {"scripts": [{"role": "hook", "path": "hooker.js"}, {"role": "handler", "path": "handler.py"}], "timeout": 180}, "preconditions": {"obs_studio_running": true}, "total_key_steps": 3, "events": {"stinger_transition_created": {"description": "Stinger transition has been created", "is_key_step": true, "key_step_index": 1, "key_step_name": "Stinger transition has been created"}, "stinger_transition_configured": {"description": "Stinger transition has been configured", "is_key_step": true, "key_step_index": 2, "key_step_name": "Stinger transition has been configured"}, "stinger_transition_used": {"description": "Stinger transition has been used", "is_key_step": true, "key_step_index": 3, "key_step_name": "Stinger transition has been used"}}, "context_data": [{"from": "tests/context_data/obs-studio", "to": "/home/agent/obs-studio-data"}], "mcp_servers": [{"command": "node", "args": ["/home/agent/obs-mcp/build/index.js"]}]}

Hook script:
// OBS Studio Stinger transition monitoring script
// Used to monitor OBS's add Stinger transition operation and scene switch operation

(function () {
    // Script settings
    // Functions related to creating and configuring Stinger transitions
    const FUNCTION_AddTransition = "_ZN8OBSBasic13AddTransitionEPKc";
    const FUNCTION_SetTransition = "_ZN8OBSBasic13SetTransitionE10OBSSafeRefIP10obs_sourceXadL_Z18obs_source_get_refEEXadL_Z18obs_source_releaseEEE";
    const FUNCTION_TransitionToScene = "_ZN8OBSBasic17TransitionToSceneE10OBSSafeRefIP10obs_sourceXadL_Z18obs_source_get_refEEXadL_Z18obs_source_releaseEEEbbibb";
    
    // Constants and message definitions
    const OFFSET_info = 0x150;
    const MESSAGE_script_initialized = "Monitoring script has started";
    const MESSAGE_hook_installed = "Monitoring hook installed, waiting for operations...";
    const STINGER_TRANSITION_ID = "obs_stinger_transition";

    // Tracking status
    let stingerCreated = false;
    let stingerConfigured = false;
    let stingerUsed = false;

    // 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 the actual address of function ${functionName}`
        });
        return funcAddr;
    }

    // Monitor add transition function
    function initAddTransitionHook() {
        const funcAddr = getFunctionAddress(FUNCTION_AddTransition);
        if (!funcAddr) {
            return;
        }

        Interceptor.attach(funcAddr, {
            onEnter: function(args) {
                const transitionId = args[1].readCString();
                this.transitionId = transitionId;
                
                sendEvent("createStingerTransition_called", {
                    function: "OBSBasic::AddTransition",
                    transition_id: transitionId
                });
            },
            onLeave: function(retval) {
                sendEvent("createStingerTransition_returned", {
                    function: "OBSBasic::AddTransition",
                    transition_id: this.transitionId,
                });
                
                // Check if Stinger transition was created
                if (this.transitionId === STINGER_TRANSITION_ID) {
                    stingerCreated = true;
                    sendEvent("stinger_transition_created", {
                        transition_id: this.transitionId
                    });
                }
            }
        });
    }

    // Monitor set transition properties function - detect Stinger transition configuration
    function initStingerConfigHook() {
        // In OBS Studio, Stinger transition configuration is usually done by setting source properties
        // Here we monitor the key property setting function
        const onPropertyChangedSymbol = "_ZN8OBSBasic4SaveEPKc";
        const funcAddr = getFunctionAddress(onPropertyChangedSymbol);
        if (!funcAddr) {
            return;
        }

        Interceptor.attach(funcAddr, {
            onEnter: function(args) {
                sendEvent("configureStingerTransition_called", {
                    function: "OBSBasic::Save"
                });
                
                // Attempt to get property data
                this.file = args[1].readCString(-1);
            },
            onLeave: function(retval) {
                sendEvent("configureStingerTransition_returned", {
                    function: "OBSBasic::Save",
                    file: this.file
                });
                stingerConfigured = true;
            }
        });
    }
    
    // Monitor set transition function
    function initSetTransitionHook() {
        const funcAddr = getFunctionAddress(FUNCTION_SetTransition);
        if (!funcAddr) {
            return;
        }

        Interceptor.attach(funcAddr, {
            onEnter: function(args) {
                sendEvent("setTransition_called", {
                    function: "OBSBasic::SetTransition"
                });
                const transition = new NativePointer(args[1]);
                this.transition = transition;
            },
            onLeave: function(retval) {
                sendEvent("setTransition_returned", {
                    function: "OBSBasic::SetTransition"
                });
                
                try {
                    const val_pointer = this.transition.readPointer();
                    const info_pointer = val_pointer.add(OFFSET_info);
                    const id_pointer = info_pointer.readPointer();
                    const id = id_pointer.readCString(-1);
                    
                    sendEvent("transition_info", {
                        transition_id: id
                    });
                    
                    // Check if Stinger transition was used
                    if (id === STINGER_TRANSITION_ID && stingerCreated && stingerConfigured) {
                        stingerUsed = true;
                        sendEvent("stinger_transition_used", {
                            transition_id: id
                        });
                    }
                } catch (e) {
                    sendEvent("error", {
                        error_type: "read_transition_info",
                        message: `Failed to read transition info: ${e.toString()}`
                    });
                }
            }
        });
    }

    // Monitor scene switch function
    function initTransitionToSceneHook() {
        const funcAddr = getFunctionAddress(FUNCTION_TransitionToScene);
        if (!funcAddr) {
            return;
        }

        Interceptor.attach(funcAddr, {
            onEnter: function(args) {
                sendEvent("sceneSwitch_called", {
                    function: "OBSBasic::TransitionToScene"
                });
            },
            onLeave: function(retval) {
                sendEvent("sceneSwitch_returned", {
                    function: "OBSBasic::TransitionToScene"
                });
                
                // Check if Stinger transition was used
                if (stingerCreated && stingerConfigured && !stingerUsed) {
                    // In real scenarios, we need to check the type of transition used
                    // Here it is simplified to assume it was used if Stinger transition was created and configured
                    stingerUsed = true;
                    sendEvent("stinger_transition_used", {
                        message: "Detected scene switch using Stinger transition"
                    });
                }
            }
        });
    }

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

        // Initialize each hook
        initAddTransitionHook();
        initStingerConfigHook();
        initSetTransitionHook();
        initTransitionToSceneHook();
        
        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

# Task success condition tracking status
_STINGER_CREATED = False
_STINGER_CONFIGURED = False
_STINGER_USED = False

key_steps = []


def message_handler(
    message: Dict[str, Any], logger, task_parameter: Dict[str, Any]
) -> Optional[List[Dict[str, Any]]]:
    global key_steps
    payload = message["payload"]
    event_type = payload["event"]
    logger.debug(f"Received event: {event_type}")
    global _EVALUATOR, _CONFIG, _START_TIME, _STINGER_CREATED, _STINGER_CONFIGURED, _STINGER_USED

    if event_type == "stinger_transition_created":
        logger.info("Stinger transition has been created")
        _STINGER_CREATED = True
        if not dict_have_index(key_steps, 1):
            key_steps.append({"status": "key_step", "index": 1})

    # Events related to Stinger transition configuration
    elif event_type == "configureStingerTransition_called":
        logger.info("Intercepted call to configure Stinger transition function")

    elif event_type == "configureStingerTransition_returned":
        logger.info("Configure Stinger transition function returned")
        expected_file = task_parameter.get("stinger_file", "")
        expected_transition_point = task_parameter.get("transition_point_ms", 0)
        file = payload.get("file")
        try:
            with open(file, "r") as f:
                data = json.load(f)
                transitions = data.get("transitions", [])
                for transition in transitions:
                    if transition.get("id") == "obs_stinger_transition":
                        transition_point = transition.get("settings", {}).get(
                            "transition_point", 0
                        )
                        transition_file = transition.get("settings", {}).get("path", "")

                        if (
                            transition_point == expected_transition_point
                            and transition_file == expected_file
                        ):
                            logger.info(
                                "Stinger transition configuration file validation succeeded: transition_point is 300"
                            )
                            _STINGER_CONFIGURED = True
                            if not dict_have_index(key_steps, 2):
                                key_steps.append({"status": "key_step", "index": 2})
                        else:
                            logger.warning(
                                f"Stinger transition configuration file validation failed: transition_point is {transition_point}, expected value is 300"
                            )
        except Exception as e:
            logger.error(f"Failed to read or parse configuration file: {str(e)}")

    elif event_type == "stinger_transition_used":
        logger.info("Stinger transition has been used")
        _STINGER_USED = True
        if not dict_have_index(key_steps, 3):
            key_steps.append({"status": "key_step", "index": 3})

    # Check if the task is completed
    if _STINGER_CREATED and _STINGER_CONFIGURED and _STINGER_USED:
        key_steps.append(
            {
                "status": "success",
                "reason": "All settings have been successfully completed",
            }
        )
        return key_steps

    return None


def dict_have_index(key_steps: List[Dict[str, Any]], index: int) -> bool:
    for key_step in key_steps:
        if "index" in key_step and key_step["index"] == index:
            return True

    return False