Task configuration:
{"task_id": "25", "task_name": "sort_import", "description": "\u5728vscode\u4e2d\u6253\u5f00\u6587\u4ef6, \u5e76\u4e14\u6309\u7167\u5b57\u6bcd\u8868\u987a\u5e8f\u5bf9\u6240\u6709\u7684import\u5e93\u8fdb\u884c\u6392\u5e8f", "application_info": {"name": "vscode", "executable_path": "/usr/share/code/code", "args": ["--no-sandbox", "--user-data-dir=/root/vscode_user_data_dir/"]}, "task_parameters": {"filename": "sort_import.py", "expected_file_content": "importargparseimportcollectionsimportdatetimeimportfunctoolsimportjsonimportloggingimportmathimportnumpyimportosimportpandasimportpathlibimportrandomimportreimportrequestsimportsqlite3importsysimporttimelogging.basicConfig(level=logging.INFO,format='%(asctime)s-%(levelname)s-%(message)s')@functools.lru_cache(maxsize=128)deffetch_data(url):try:logging.info(f'Fetchingdatafrom{url}')response=requests.get(url)response.raise_for_status()returnresponse.json()exceptrequests.RequestExceptionase:logging.error(f'Requestfailed:{e}')sys.exit(1)defclean_and_transform(data):df=pandas.DataFrame(data)df=df.dropna()df['timestamp']=pandas.to_datetime(df['created_at'],errors='coerce')df['word_count']=df['text'].apply(lambdax:len(str(x).split()))returndfdefanalyze(df):logging.info('Analyzingdata...')avg_words=df['word_count'].mean()top_words=collections.Counter(''.join(df['text']).split()).most_common(5)returnavg_words,top_wordsdefsave_to_sqlite(df,db_path='data.db'):conn=sqlite3.connect(db_path)df.to_sql('api_data',conn,if_exists='replace',index=False)conn.close()logging.info('DatasavedtoSQLite')defsave_to_file(df,out_dir='output'):os.makedirs(out_dir,exist_ok=True)out_path=os.path.join(out_dir,f'data_{datetime.date.today()}.json')df.to_json(out_path,orient='records',indent=2)logging.info(f'Datasavedto{out_path}')defmain():parser=argparse.ArgumentParser(description='ProcessAPIdata')parser.add_argument('--url',type=str,required=False,default='https://jsonplaceholder.typicode.com/posts',help='APIURLtofetchJSONdatafrom')args=parser.parse_args()json_data=fetch_data(args.url)ifisinstance(json_data,dict):json_data=[json_data]df=clean_and_transform(json_data)avg,top=analyze(df)logging.info(f'Averagewordsperentry:{avg}')logging.info(f'Top5words:{top}')save_to_sqlite(df)save_to_file(df)logging.info('Scriptfinished.')if__name__=='__main__':main()", "expected_file_path": "/root/C-Plus-Plus/python_test/sort_import.py"}, "instruction_template": "\u5728vscode\u5f53\u524d\u6253\u5f00\u7684\u5de5\u4f5c\u533a\u4e2d\u6253\u5f00\u540d\u4e3a ${filename} \u7684\u6587\u4ef6, \u5e76\u4e14\u6309\u7167\u5b57\u6bcd\u8868\u987a\u5e8f\u5bf9\u6240\u6709\u7684import\u5e93\u8fdb\u884c\u6392\u5e8f", "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": {"vscode_running": true}, "events": {"evaluate_on_completion": {"description": "\u5728\u4efb\u52a1\u7ed3\u675f\u65f6\u68c0\u67e5\u4efb\u52a1\u662f\u5426\u5b8c\u6210", "is_key_step": true, "key_step_index": 1, "key_step_name": "\u4efb\u52a1\u6210\u529f\u5b8c\u6210"}}, "mcp_servers": [{"command": "mcp-proxy", "args": ["http://127.0.0.1:6010/sse"]}], "context_data": [{"from": "tests/context_data/vscode/user_data", "to": "/root/vscode_user_data_dir"}, {"from": "tests/context_data/vscode/extensions", "to": "/root/.vscode/"}, {"from": "tests/context_data/vscode/C-Plus-Plus", "to": "/root/C-Plus-Plus"}]}

Hook script:
/**
 * 检查当前打开的文件是否含有错误提示，并返回文件名和文件内容（包括未保存的修改）
 * @returns {Promise<{hasErrors: boolean, fileName: string | null, content: string | null}>} 返回对象包含错误状态、文件名和文件内容
 */
async function hasFileErrors() {
    // 获取当前活动编辑器
    const editor = vscode.window.activeTextEditor;
    if (!editor) {
        vscode.window.showInformationMessage('No active editor found.');
        return { hasErrors: false, fileName: null, content: null };
    }

    // 获取当前文件的 URI、文件名和内容
    const fileUri = editor.document.uri;
    const fileName = editor.document.fileName;
    const content = editor.document.getText(); // 获取当前文档内容，包括未保存的修改

    // 获取文件的诊断信息
    const diagnostics = vscode.languages.getDiagnostics(fileUri);

    // 检查是否有错误
    const hasErrors = diagnostics.some(diagnostic => 
        diagnostic.severity === vscode.DiagnosticSeverity.Error
    );

    return { hasErrors, fileName, content };
}

socket.on('evaluate', async () => {
    const hasErrors = await hasFileErrors();
    // vscode.window.showInformationMessage(message);
    socket.emit("send", {
        'event_type': "evaluate_on_completion",
        'message': "任务结束时检查打开的文件是否含有报错",
        'hasErrors': hasErrors
    });
});

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

from typing import Dict, Any, Optional, List

def message_handler(message: Dict[str, Any], logger, task_parameter: Dict[str, Any]) -> Optional[List[Dict[str, Any]]]:    
    event_type = message.get('event_type')
    info = message.get('hasErrors', {})
    fileName = info.get('fileName', None)
    hasErrors = info.get('hasErrors', None)
    content = info.get('content', None)
    expected_file_content = task_parameter.get("expected_file_content", "importargparseimportcollectionsimportdatetimeimportfunctoolsimportjsonimportloggingimportmathimportnumpyimportosimportpandasimportpathlibimportrandomimportreimportrequestsimportsqlite3importsysimporttimelogging.basicConfig(level=logging.INFO,format='%(asctime)s-%(levelname)s-%(message)s')@functools.lru_cache(maxsize=128)deffetch_data(url):try:logging.info(f'Fetchingdatafrom{url}')response=requests.get(url)response.raise_for_status()returnresponse.json()exceptrequests.RequestExceptionase:logging.error(f'Requestfailed:{e}')sys.exit(1)defclean_and_transform(data):df=pandas.DataFrame(data)df=df.dropna()df['timestamp']=pandas.to_datetime(df['created_at'],errors='coerce')df['word_count']=df['text'].apply(lambdax:len(str(x).split()))returndfdefanalyze(df):logging.info('Analyzingdata...')avg_words=df['word_count'].mean()top_words=collections.Counter(''.join(df['text']).split()).most_common(5)returnavg_words,top_wordsdefsave_to_sqlite(df,db_path='data.db'):conn=sqlite3.connect(db_path)df.to_sql('api_data',conn,if_exists='replace',index=False)conn.close()logging.info('DatasavedtoSQLite')defsave_to_file(df,out_dir='output'):os.makedirs(out_dir,exist_ok=True)out_path=os.path.join(out_dir,f'data_{datetime.date.today()}.json')df.to_json(out_path,orient='records',indent=2)logging.info(f'Datasavedto{out_path}')defmain():parser=argparse.ArgumentParser(description='ProcessAPIdata')parser.add_argument('--url',type=str,required=False,default='https://jsonplaceholder.typicode.com/posts',help='APIURLtofetchJSONdatafrom')args=parser.parse_args()json_data=fetch_data(args.url)ifisinstance(json_data,dict):json_data=[json_data]df=clean_and_transform(json_data)avg,top=analyze(df)logging.info(f'Averagewordsperentry:{avg}')logging.info(f'Top5words:{top}')save_to_sqlite(df)save_to_file(df)logging.info('Scriptfinished.')if__name__=='__main__':main()")
    expected_file_path = task_parameter.get("expected_file_path", "/root/C-Plus-Plus/python_test/sort_import.py")
    logger.info(message.get('message'))
    if event_type == 'evaluate_on_completion' and fileName == expected_file_path and hasErrors == False:
        if ''.join(content.split()) == expected_file_content:
            return [
                {"status": "key_step", "index": 1},
                {"status": "success", "reason": f"任务成功完成"}
            ]
        else:
            return [{"status": "error", "type": "evaluate_on_completion", "message": "任务没有完成"}]
    return None