diff --git a/src/daemon/dispatcher.py b/src/daemon/dispatcher.py index df2d702..2f8e675 100644 --- a/src/daemon/dispatcher.py +++ b/src/daemon/dispatcher.py @@ -653,3 +653,71 @@ class Dispatcher: logger.error("Mail %s: reply check error: %s", original_task_id, e) # 查询失败时保守处理:假设有回复(避免误标 failed) return True + + # ══════════════════════════════════════════════ + # #02: Task 路径幻觉门控 + # ══════════════════════════════════════════════ + + def _task_auto_complete(self, task_id: str, db_path) -> None: + """Task on_complete 后自动标 review/failed(三层幻觉门控第一层)""" + from pathlib import Path + if not db_path: + logger.warning("Task %s: no db_path, skip auto_complete", task_id) + return + db_path = Path(db_path) if not isinstance(db_path, Path) else db_path + try: + passed = self._task_verify_completion(task_id, db_path) + if passed: + logger.info("Task %s: verify passed, marking review", task_id) + self._mark_task_status(db_path, task_id, "review") + else: + logger.info("Task %s: verify not passed (no signal), leaving working", task_id) + except Exception as e: + logger.error("Task %s: auto-complete error: %s", task_id, e) + + def _task_verify_completion(self, task_id: str, db_path: Path): + """普通 Task 完成验证(三信号检查) + Returns: True=passed, None=no signal + """ + TERMINAL_STATES = {"review", "done", "failed", "cancelled"} + try: + conn = get_connection(db_path) + try: + row = conn.execute( + "SELECT status FROM tasks WHERE id=?", (task_id,) + ).fetchone() + if not row or row["status"] in TERMINAL_STATES: + return True + output_count = conn.execute( + "SELECT COUNT(*) FROM outputs WHERE task_id=?", (task_id,) + ).fetchone()[0] + if output_count > 0: + return True + comment_count = conn.execute( + "SELECT COUNT(*) FROM comments WHERE task_id=? AND author != 'system' AND LENGTH(content) >= 50", + (task_id,) + ).fetchone()[0] + if comment_count > 0: + return True + return None + finally: + conn.close() + except Exception as e: + logger.error("Task %s: verify error: %s", task_id, e) + return True + + def _mark_task_status(self, db_path: Path, task_id: str, status: str) -> None: + """更新任务状态""" + try: + conn = get_connection(db_path) + try: + conn.execute("BEGIN IMMEDIATE") + conn.execute( + "UPDATE tasks SET status=?, updated_at=datetime('now') WHERE id=?", + (status, task_id), + ) + conn.commit() + finally: + conn.close() + except Exception as e: + logger.error("Task %s: mark status error: %s", task_id, e)