Task configuration:
{"task_id": "23", "task_name": "batch_manage_text_source_filters", "description": "Batch add, enable, disable and remove filters for text source", "application_info": {"name": "obs-studio", "executable_path": "/workspace/PC-Canary/apps/obs-studio/build_ubuntu/rundir/Debug/bin/obs", "args": []}, "task_parameters": {"text_source_name": "text_src", "filters": [{"name": "f1", "type": "color_key_filter"}, {"name": "f2", "type": "scroll_filter"}]}, "instruction_template": "Please add ${filters} filters to the text source named '${text_source_name}' in obs-studio, then disable, enable and remove them", "evaluation_setup": {"scripts": [{"role": "hook", "path": "hooker.js"}, {"role": "handler", "path": "handler.py"}], "timeout": 180}, "preconditions": {"obs_studio_running": true}, "success_conditions": ["filters_added", "filters_enabled_disabled", "filters_removed"], "total_key_steps": 3, "key_steps": [{"name": "Add filters", "description": "Add filters to text source", "is_key_step": true, "key_step_index": 1}, {"name": "Enable disable filters", "description": "Enable and disable filters", "is_key_step": true, "key_step_index": 2}, {"name": "Remove filters", "description": "Remove filters", "is_key_step": true, "key_step_index": 3}], "mcp_servers": [{"command": "node", "args": ["/home/agent/obs-mcp/build/index.js"]}]}

Hook script:
(function () {
    // Script settings
    const MESSAGE_script_initialized = "Monitoring script has started";
    const MESSAGE_hook_installed = "Monitoring hook installed, waiting for operation...";
    const MESSAGE_filter_created = "Captured filter creation event";
    const MESSAGE_filter_enabled = "Captured filter enabled event";
    const MESSAGE_filter_disabled = "Captured filter disabled event";
    const MESSAGE_filter_removed = "Captured filter removal event";

    // 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;
    }

    // Create hook function
    function hookFilterCreate() {
        // Function: obs_source_filter_add, used to add filters
        let symbol_name = "obs_source_filter_add";

        Interceptor.attach(getFunctionAddress(symbol_name), {
            onEnter(args) {
                this.source = args[0];
                this.filter = args[1];
                
                // Get source and filter names
                if (this.source && this.filter) {
                    try {
                        // Call obs_source_get_name to get source name
                        const obs_source_get_name = DebugSymbol.getFunctionByName("obs_source_get_name");
                        const sourceName = new NativeFunction(obs_source_get_name, "pointer", ["pointer"])(this.source).readUtf8String();
                        const filterName = new NativeFunction(obs_source_get_name, "pointer", ["pointer"])(this.filter).readUtf8String();
                        
                        // Call obs_source_get_unversioned_id to get filter type
                        const obs_source_get_id = DebugSymbol.getFunctionByName("obs_source_get_unversioned_id");
                        const filterId = new NativeFunction(obs_source_get_id, "pointer", ["pointer"])(this.filter).readUtf8String();

                        sendEvent("filter_created", {
                            message: MESSAGE_filter_created,
                            sourceName: sourceName,
                            filterName: filterName,
                            filterKind: filterId
                        });
                    } catch (e) {
                        sendEvent("error", {
                            error_type: "get_source_info_error",
                            message: `Error occurred while getting source information: ${e.toString()}`
                        });
                    }
                }
            }
        });
    }

    // Hook: Enable/Disable filter
    function hookFilterEnable() {
        // Function: obs_source_set_enabled, used to enable or disable filters
        let symbol_name = "obs_source_set_enabled";

        Interceptor.attach(getFunctionAddress(symbol_name), {
            onEnter(args) {
                this.source = args[0];
                this.enabled = args[1].toInt32(); // Boolean parameter, 1 for enable, 0 for disable
                
                // Check if it is a filter
                if (this.source) {
                    try {
                        // Check if it is a filter
                        const obs_source_get_type = DebugSymbol.getFunctionByName("obs_source_get_type");
                        const sourceType = new NativeFunction(obs_source_get_type, "int", ["pointer"])(this.source);
                        
                        // OBS_SOURCE_TYPE_FILTER = 1
                        if (sourceType === 1) {
                            // Get filter name
                            const obs_source_get_name = DebugSymbol.getFunctionByName("obs_source_get_name");
                            const filterName = new NativeFunction(obs_source_get_name, "pointer", ["pointer"])(this.source).readUtf8String();
                            
                            // Get parent source
                            const obs_filter_get_parent = DebugSymbol.getFunctionByName("obs_filter_get_parent");
                            const parent = new NativeFunction(obs_filter_get_parent, "pointer", ["pointer"])(this.source);
                            
                            let sourceName = "unknown";
                            if (parent) {
                                sourceName = new NativeFunction(obs_source_get_name, "pointer", ["pointer"])(parent).readUtf8String();
                            }
                            
                            if (this.enabled === 1) {
                                sendEvent("filter_enabled", {
                                    message: MESSAGE_filter_enabled,
                                    sourceName: sourceName,
                                    filterName: filterName
                                });
                            } else {
                                sendEvent("filter_disabled", {
                                    message: MESSAGE_filter_disabled,
                                    sourceName: sourceName,
                                    filterName: filterName
                                });
                            }
                        }
                    } catch (e) {
                        sendEvent("error", {
                            error_type: "get_filter_enable_info_error",
                            message: `Error occurred while getting filter enable information: ${e.toString()}`
                        });
                    }
                }
            }
        });
    }

    // Hook: Remove filter
    function hookFilterRemove() {
        // Function: obs_source_filter_remove, used to remove filters
        let symbol_name = "obs_source_filter_remove";

        Interceptor.attach(getFunctionAddress(symbol_name), {
            onEnter(args) {
                this.source = args[0];
                this.filter = args[1];
                
                // Get source and filter names
                if (this.source && this.filter) {
                    try {
                        // Call obs_source_get_name to get source name
                        const obs_source_get_name = DebugSymbol.getFunctionByName("obs_source_get_name");
                        const sourceName = new NativeFunction(obs_source_get_name, "pointer", ["pointer"])(this.source).readUtf8String();
                        const filterName = new NativeFunction(obs_source_get_name, "pointer", ["pointer"])(this.filter).readUtf8String();

                        sendEvent("filter_removed", {
                            message: MESSAGE_filter_removed,
                            sourceName: sourceName,
                            filterName: filterName
                        });
                    } catch (e) {
                        sendEvent("error", {
                            error_type: "get_source_info_error",
                            message: `Error occurred while getting source information: ${e.toString()}`
                        });
                    }
                }
            }
        });
    }

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

        // Initialize hooks
        hookFilterCreate();
        hookFilterEnable(); 
        hookFilterRemove();

        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

# Track filter-related states
_FILTERS_ADDED = set()
_FILTERS_ENABLED = set()
_FILTERS_DISABLED = set()
_FILTERS_REMOVED = set()

filters_added = False
filters_enabled_disabled = False
filters_removed = False


def message_handler(
    message: Dict[str, Any], logger, task_parameter: Dict[str, Any]
) -> Optional[List[Dict[str, Any]]]:
    global filters_added, filters_enabled_disabled, filters_removed
    payload = message["payload"]
    print(payload)
    event_type = payload["event"]
    logger.debug(f"Received event: {event_type}")
    """
    Handle messages received from the hook script
    
    Args:
        message: Injector message object
        data: Additional data
        
    Returns:
        str: Returns "success" if the task is successfully completed, otherwise returns None
    """
    global _FILTERS_ADDED, _FILTERS_ENABLED, _FILTERS_DISABLED, _FILTERS_REMOVED

    # Get expected filter information
    expected_filters = []
    filter_types_map = {}  # Used to store the mapping of filter names and types

    expected_filters = [
        filter_info["name"] for filter_info in task_parameter["filters"]
    ]

    # Create a mapping of filter names to filter types
    for filter_info in task_parameter["filters"]:
        filter_types_map[filter_info["name"]] = filter_info["type"]

    # Handle events sent from hooker.js
    payload = message.get("payload", {})
    event_type = payload.get("event")

    logger.info(f"Received event: {event_type}, payload: {payload}")

    if event_type == "filter_created":
        filter_name = payload.get("filterName")
        source_name = payload.get("sourceName")
        filter_kind = payload.get("filterKind")

        if filter_name in expected_filters:
            # Check if the filter type matches
            expected_type = filter_types_map.get(filter_name)

            if expected_type and filter_kind:
                if expected_type in filter_kind or filter_kind in expected_type:
                    logger.info(
                        f"Filter '{filter_name}' type matched successfully: Expected '{expected_type}', Actual '{filter_kind}'"
                    )
                    _FILTERS_ADDED.add(filter_name)
                    logger.info(
                        f"Filter '{filter_name}' has been added to source '{source_name}'"
                    )
                else:
                    logger.warning(
                        f"Filter '{filter_name}' type does not match: Expected '{expected_type}', Actual '{filter_kind}'"
                    )
            else:
                # If there is no type information, check based on name only
                _FILTERS_ADDED.add(filter_name)
                logger.info(
                    f"Filter '{filter_name}' has been added to source '{source_name}', but type check was not performed"
                )

            # Check if all filters have been added
            if all(filter_name in _FILTERS_ADDED for filter_name in expected_filters):
                logger.info("All filters have been successfully added")
                filters_added = True
                return [{"status": "key_step", "index": 1}]

    elif event_type == "filter_enabled":
        filter_name = payload.get("filterName")
        if filter_name in expected_filters:
            _FILTERS_ENABLED.add(filter_name)
            logger.info(f"Filter '{filter_name}' has been enabled")

            # Check enable and disable conditions
            return check_enable_disable_status(logger, task_parameter)

    elif event_type == "filter_disabled":
        filter_name = payload.get("filterName")
        if filter_name in expected_filters:
            _FILTERS_DISABLED.add(filter_name)
            logger.info(f"Filter '{filter_name}' has been disabled")

            # Check enable and disable conditions
            return check_enable_disable_status(logger, task_parameter)

    elif event_type == "filter_removed":
        filter_name = payload.get("filterName")

        if filter_name in expected_filters:
            _FILTERS_REMOVED.add(filter_name)
            logger.info(f"Filter '{filter_name}' has been removed")

            # Check if all filters have been removed
            if all(filter_name in _FILTERS_REMOVED for filter_name in expected_filters):
                logger.info("All filters have been successfully removed")
                filters_removed = True
                # Check if the task is completed
                if check_task_completed():
                    return [
                        {"status": "key_step", "index": 3},
                        {
                            "status": "success",
                            "reason": "Add, enable/disable, and remove filter operations completed",
                        },
                    ]
    return None


def check_enable_disable_status(
    logger, task_parameter
) -> Optional[List[Dict[str, Any]]]:
    """Check the enable and disable status of filters"""
    global _FILTERS_ENABLED, _FILTERS_DISABLED, filters_enabled_disabled

    expected_filters = []
    expected_filters = [
        filter_info["name"] for filter_info in task_parameter["filters"]
    ]

    # Check if each filter has been enabled and disabled
    if all(filter_name in _FILTERS_ENABLED for filter_name in expected_filters) and all(
        filter_name in _FILTERS_DISABLED for filter_name in expected_filters
    ):
        logger.info("All filters have been successfully enabled and disabled")
        filters_enabled_disabled = True
        return [{"status": "key_step", "index": 2}]
    return None


def check_task_completed():
    """Check if the task is completed"""
    global filters_added, filters_enabled_disabled, filters_removed
    # Check all success conditions
    is_completed = filters_added and filters_enabled_disabled and filters_removed

    return is_completed