Task configuration:
{"task_id": "13", "task_name": "add_todo_due_date", "description": "\u5728Joplin\u4e2d\u4e3a\u6307\u5b9a\u5f85\u529e\u4e8b\u9879\u6dfb\u52a0\u622a\u6b62\u65e5\u671f", "application_info": {"name": "joplin", "executable_path": "/workspace/PC-Canary/apps/joplin/Joplin-3.3.12.AppImage", "args": ["--env", "dev", "--profile", "/workspace/PC-Canary/apps/joplin/joplindev-desktop"]}, "task_parameters": {"todo_name": "test_todo", "due_date": "2025-05-20"}, "instruction_template": "\u5728Joplin\u4e2d\u4e3a\u540d\u4e3a ${todo_name} \u7684\u5f85\u529e\u4e8b\u9879\u8bbe\u7f6e\u63d0\u9192\u65e5\u671f\u4e3a ${due_date}", "evaluation_setup": {"scripts": [{"role": "hook", "path": "hooker.js"}, {"role": "handler", "path": "handler.py"}], "timeout": 120, "evaluator_type": "IpcInjector", "evaluate_on_completion": true}, "total_key_steps": 1, "preconditions": {"joplin_running": true}, "events": {"evaluate_on_completion": {"description": "\u5728\u4efb\u52a1\u7ed3\u675f\u65f6\u5224\u65ad\u6307\u5b9a\u5f85\u529e\u4e8b\u9879\u662f\u5426\u5df2\u8bbe\u7f6e\u622a\u6b62\u65e5\u671f", "is_key_step": true, "key_step_index": 1, "key_step_name": "\u5f85\u529e\u4e8b\u9879\u622a\u6b62\u65e5\u671f\u8bbe\u7f6e\u5b8c\u6210"}}, "mcp_servers": [{"command": "/home/user/.local/bin/uv", "args": ["--directory", "/home/user/Desktop/joplin-mcp-server", "run", "src/mcp/joplin_mcp.py"]}]}

Hook script:
// 使用 Joplin 插件 API 来处理通信
joplin.plugins.register({
    onStart: async function() {
        console.log('Joplin plugin started');
        
        // 监听 evaluate 事件
        socket.on("evaluate", async () => {
            console.log('Task13_addTodoDueDate---------------------------');
            try {
                // 获取所有待办事项
                const todos = await joplin.data.get(['notes'], {
                    fields: ['id', 'title', 'is_todo', 'todo_due'],
                    page: 1,
                });
                
                // 过滤出待办事项并获取详细信息
                const todoList = todos.items
                    .filter(note => note.is_todo)
                    .map(todo => ({
                        title: todo.title,
                        todo_due: todo.todo_due || ''
                    }));
                
                console.log('Current todos:', todoList);
                
                // 使用 postMessage 发送评估消息
                window.postMessage({
                    "event_type": "evaluate_on_completion",
                    "message": "当前待办事项列表: " + todoList.map(t => `${t.title}(${t.todo_due || '无截止日期'})`).join(', '),
                    "data": {
                        "todos": todoList
                    }
                }, '*');
                console.log('Evaluation result sent via postMessage');
            } catch (error) {
                console.error('Error in evaluate handler:', error);
            }
        });
    }
}); 

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

"""
joplin添加待办事项截止日期的事件处理器
负责处理钩子脚本产生的事件并更新评估指标
"""

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

def load_config_defaults() -> Dict[str, Any]:
    """从config.json加载默认参数值"""
    config_path = os.path.join(os.path.dirname(__file__), 'config.json')
    with open(config_path, 'r', encoding='utf-8') as f:
        config = json.load(f)
    return config.get('task_parameters', {})

def timestamp_to_date(timestamp: int) -> str:
    """将时间戳转换为YYYY-MM-DD格式的日期字符串"""
    try:
        # 将毫秒时间戳转换为秒
        seconds = timestamp / 1000
        # 转换为datetime对象
        dt = datetime.fromtimestamp(seconds)
        # 格式化为YYYY-MM-DD
        return dt.strftime('%Y-%m-%d')
    except (ValueError, TypeError):
        return ''

def message_handler(message: Dict[str, Any], logger, task_parameter: Dict[str, Any]) -> Optional[List[Dict[str, Any]]]:    
    event_type = message.get('event_type')
    logger.info(message.get('message'))
    
    if event_type == "evaluate_on_completion":
        data = message.get("data", {})
        todos = data.get("todos", [])
        
        # 从config.json加载默认值
        config_defaults = load_config_defaults()
        expected_todo = task_parameter.get('todo_name', config_defaults.get('todo_name'))
        expected_date = task_parameter.get('due_date', config_defaults.get('due_date'))
        
        # 查找指定名称的待办事项
        target_todo = next((todo for todo in todos if todo.get("title") == expected_todo), None)
        
        if not target_todo:
            return [{"status": "error", "type": "evaluate_on_completion", "message": f"名为 {expected_todo} 的待办事项不存在"}]
        
        # 检查待办事项是否设置了截止日期
        todo_due_timestamp = target_todo.get("todo_due", "")
        todo_due_date = timestamp_to_date(todo_due_timestamp) if todo_due_timestamp else ""
        
        logger.info(f"待办事项 {expected_todo} 的截止日期时间戳: {todo_due_timestamp}")
        logger.info(f"转换后的日期: {todo_due_date}")
        logger.info(f"期望的日期: {expected_date}")
        
        if todo_due_date == expected_date:
            return [
                {"status": "key_step", "index": 1},
                {"status": "success", "reason": f"成功为待办事项 {expected_todo} 设置截止日期：{expected_date}"}
            ]
        else:
            return [{"status": "error", "type": "evaluate_on_completion", "message": f"待办事项 {expected_todo} 的截止日期不匹配，期望：{expected_date}，实际：{todo_due_date}"}]
    return None