diff --git a/src/daemon/spawner.py b/src/daemon/spawner.py index 3b8e6cb..7876435 100644 --- a/src/daemon/spawner.py +++ b/src/daemon/spawner.py @@ -15,7 +15,7 @@ from datetime import datetime from pathlib import Path from typing import Any, Dict, List, Optional -from src.blackboard.db import get_connection +from src.blackboard.db import get_connection, init_db logger = logging.getLogger("moziplus-v2.spawner") @@ -163,7 +163,6 @@ class AgentBusyError(Exception): #07: reason 字段区分具体原因,便于 dispatcher 层区分处理。 """ - def __init__(self, agent_id: str, reason: str = "busy", detail: Optional[dict] = None): self.agent_id = agent_id self.reason = reason # counter_blocked / session_locked / session_running / session_compacting / session_stuck @@ -300,7 +299,7 @@ class AgentSpawner: project_id, agent_id) def _build_minimal_fallback(self, task_id, title, description, must_haves, - project_id, agent_id): + project_id, agent_id): """最小 fallback:只有任务上下文 + API 指令""" task_section = f"""## 任务 {title} @@ -312,7 +311,7 @@ class AgentSpawner: return task_section + "\n\n---\n\n" + api_section def _build_api_section(self, project_id: str, task_id: str, - agent_id: str) -> str: + agent_id: str) -> str: """构建 API 回写操作指令(BootstrapBuilder 模式下补充)""" # mail 任务直接 done,不走 review success_status = '"done"' if project_id == "_mail" else '"review"' @@ -338,8 +337,8 @@ curl -X POST http://{self.api_host}:{self.api_port}/api/projects/{project_id}/ta """ def _build_discussion_prompt(self, task_id: str, title: str, - description: str, must_haves: str, - project_id: str, agent_id: str) -> str: + description: str, must_haves: str, + project_id: str, agent_id: str) -> str: """构建讨论类 spawn prompt(§3.3 框架 + Boids)""" goal_snapshot = description or title constraints = must_haves or "(无特殊约束)" @@ -380,8 +379,9 @@ curl -X POST http://{self.api_host}:{self.api_port}/api/projects/{project_id}/ta return router.agent_profiles.get(agent_id) return None + def _build_mail_prompt(self, task_id: str, title: str, description: str, - must_haves: str, agent_id: str) -> str: + must_haves: str, agent_id: str) -> str: """构建 Mail 专用精简模板""" # 解析 must_haves 获取 from 和 performative from_agent = agent_id @@ -575,7 +575,7 @@ curl -X POST http://{self.api_host}:{self.api_port}/api/projects/{project_id}/ta stderr=asyncio.subprocess.PIPE, ) self._register_session(session_id, agent_id, task_id, proc.pid, - broadcast_task_ids=broadcast_task_ids) + broadcast_task_ids=broadcast_task_ids) logger.info("Spawned agent %s (session=%s, pid=%d)", agent_id, session_id, proc.pid) @@ -881,6 +881,9 @@ curl -X POST http://{api_host}:{api_port}/api/projects/{project_id}/tasks/{task_ except Exception: pass + stderr_text = b"".join(stderr_chunks).decode("utf-8", errors="replace") + + # 检查 session 状态 state = self._check_session_state(agent_id) # B1: 假死 - 先复活,连续假死 ≥2 次再 failed @@ -1216,7 +1219,7 @@ curl -X POST http://{api_host}:{api_port}/api/projects/{project_id}/tasks/{task_ 实测 50KB 在长对话中不够(compact 记录被推出窗口导致漏检)。 正常扫描量不变:从尾部往前扫,遇到超过 15min 的 timestamp 即 break。 """ - if not session_file or not Path(session_file).exists(): + if not session_file or not pathlib.Path(session_file).exists(): return False try: from datetime import datetime, timezone @@ -1425,7 +1428,7 @@ curl -X POST http://{api_host}:{api_port}/api/projects/{project_id}/tasks/{task_ return defaults def _update_retry_counts(self, db_path: Optional[Path], - task_id: Optional[str], counts: dict): + task_id: Optional[str], counts: dict): """将 retry counts 写回最新 task_attempt 的 metadata""" if not db_path or not task_id: return @@ -1485,8 +1488,8 @@ curl -X POST http://{api_host}:{api_port}/api/projects/{project_id}/tasks/{task_ from src.blackboard.operations import Blackboard bb = Blackboard(db_path) cid = bb.add_comment(task_id, "daemon", - f"@pangtong-fujunshi 任务执行失败: {reason},请评估是否需要介入", - comment_type="system") + f"@pangtong-fujunshi 任务执行失败: {reason},请评估是否需要介入", + comment_type="system") bb.record_mentions(cid, task_id, ["pangtong-fujunshi"]) logger.info("Task %s: failure notified pangtong via comment+mention (reason=%s)", task_id, reason) except Exception as e: