diff --git a/src/daemon/mail_handler.py b/src/daemon/mail_handler.py index 2221725..d14dadf 100644 --- a/src/daemon/mail_handler.py +++ b/src/daemon/mail_handler.py @@ -7,7 +7,7 @@ from __future__ import annotations import json import logging from pathlib import Path -from typing import Dict, List, Optional +from typing import Dict, Optional from src.daemon.base_task_handler import BaseTaskHandler, VerifyResult from src.daemon.prompt_composer import PromptComposer, PromptContext @@ -34,7 +34,7 @@ class MailHandler(BaseTaskHandler): composer.add_many(self.get_sections()) return composer.compose(context) - def get_sections(self) -> List: + def get_sections(self) -> list: return [MailContextSection(), MailApiSection(), MailConstraintsSection()] def verify_completion(self, task_id: str, db_path: Path) -> VerifyResult: diff --git a/src/daemon/toolchain_handler.py b/src/daemon/toolchain_handler.py index 16152e1..8e33799 100644 --- a/src/daemon/toolchain_handler.py +++ b/src/daemon/toolchain_handler.py @@ -6,9 +6,9 @@ from __future__ import annotations import json import logging -import subprocess +import urllib.request from pathlib import Path -from typing import Dict, List +from typing import Dict from src.daemon.base_task_handler import BaseTaskHandler, VerifyResult from src.daemon.prompt_composer import PromptComposer, PromptContext @@ -129,7 +129,7 @@ class ToolchainHandler(BaseTaskHandler): """auto_working:pending → working""" return self._auto_mark_working(task_id, db_path) - def get_sections(self) -> List: + def get_sections(self) -> list: """返回 3 个 Toolchain PromptSection 实例""" return [ ToolchainContextSection(), @@ -176,31 +176,81 @@ class ToolchainHandler(BaseTaskHandler): """验证失败 → 标 failed + Mail API 通知主公""" self._mark_task_status(db_path, task_id, "failed") logger.info("Toolchain %s: verify failed (%s), marked failed", task_id, verify.reason) - self._notify_via_mail_api(task_id, verify.reason, verify.evidence) - def _notify_via_mail_api(self, task_id: str, reason: str, evidence: str) -> None: - """通过 Mail API 发通知给主公""" + # 从 db 读取事件上下文 + event_type = "" + event_data: Dict = {} + try: + conn = get_connection(db_path) + row = conn.execute( + "SELECT must_haves FROM tasks WHERE id=?", (task_id,) + ).fetchone() + if row and row["must_haves"]: + meta = json.loads(row["must_haves"]) + event_type = meta.get("event_type", "") + raw = meta.get("event_data", "{}") + event_data = json.loads(raw) if isinstance(raw, str) else raw + conn.close() + except Exception: + pass + + self._notify_via_mail_api( + task_id, verify.reason, verify.evidence, + event_type, event_data, + ) + + def _notify_via_mail_api( + self, + task_id: str, + reason: str, + evidence: str, + event_type: str, + event_data: Dict, + ) -> None: + """通过 Mail API 发送丰富的失败通知给主公。""" + # 构建行动指引 + action_hint = "请检查黑板任务并手动处理。" + et_lower = event_type.lower() + if "ci" in et_lower or "deploy" in et_lower: + action_hint = "建议创建任务派给 jiangwei-infra 检查 CI/部署问题。" + elif "review" in et_lower: + action_hint = "建议查看 PR review 状态,必要时通知相关开发者。" + elif "issue" in et_lower: + action_hint = "建议创建任务派给对应开发者处理 Issue。" + + # 构建事件详情 + event_details = "" + if event_data: + event_details = "\n".join( + f" - {k}: {v}" for k, v in event_data.items() + ) + + title = f"[toolchain-handler] 工具链事件处理失败: {task_id}" + text = ( + f"任务 {task_id} 验证失败\n\n" + f"事件类型: {event_type or '未知'}\n" + f"事件详情:\n{event_details or ' (无)'}\n\n" + f"失败原因: {reason}\n" + f"证据: {evidence}\n\n" + f"黑板任务: http://localhost:8083/ → 项目 _toolchain → 任务 {task_id}\n\n" + f"行动指引: {action_hint}" + ) + payload = json.dumps({ "from": "daemon", "to": "pangtong-fujunshi", - "title": f"工具链事件处理失败: {task_id}", - "text": ( - f"任务 {task_id} 验证失败: {reason}\n" - f"证据: {evidence}\n\n请人工检查。" - ), + "title": title, + "text": text, "type": "inform", - }, ensure_ascii=False) + }, ensure_ascii=False).encode("utf-8") + try: - subprocess.run( - [ - "curl", "-s", "-X", "POST", - "http://localhost:8083/api/mail", - "-H", "Content-Type: application/json", - "-d", payload, - ], - timeout=5, - capture_output=True, + req = urllib.request.Request( + "http://localhost:8083/api/mail", + data=payload, + headers={"Content-Type": "application/json"}, ) - logger.info("Toolchain %s: sent failure notification to pangtong-fujunshi via Mail API", task_id) + urllib.request.urlopen(req, timeout=5) + logger.info("Toolchain %s: sent failure notification via Mail API", task_id) except Exception as e: logger.warning("Toolchain %s: failed to notify via Mail API: %s", task_id, e)