use std::sync::Arc; use std::time::Duration; use async_trait::async_trait; use tokio::sync::{mpsc, watch}; use crate::domain::CapabilityPolicy; use crate::tools::task::SubagentResult; #[derive(Debug, Clone)] pub struct ToolResult { pub success: bool, pub output: String, pub error: Option, } /// wait_for_subagents 工具等待期间的事件。 #[derive(Debug, Clone)] pub enum WaitEvent { /// 一个子代理完成,携带其结果 SubagentResult(SubagentResult), /// wait 期间有新用户消息到达(已注入 history),携带新用户消息的内容列表 UserMessage(Vec), /// 等待超时 Timeout, /// 收到取消信号(/stop),等待已优雅终止,状态已清理完毕 Cancelled, } /// wait_for_subagents 工具的协调器接口。 /// /// 封装了释放/重获取 serial_lock + select! 等待 + 管理等待状态的逻辑, /// 使 wait 工具不直接依赖 gateway 模块(避免循环依赖)。 /// /// 具体实现 `SessionWaitCoordinator` 在 gateway 模块中,持有 Session 引用。 #[async_trait] pub trait WaitCoordinator: Send + Sync + 'static { /// 查询当前 topic 下仍处于 running 状态的子代理 task_id 列表。 fn query_pending_task_ids(&self) -> Vec; /// 尝试排空 sub_done_q 中已缓冲的子代理结果(非阻塞 drain,但方法本身是 async /// 因为需要获取 Session 锁)。 /// /// 用于两种场景: /// 1. wait_for_subagents 入口处:即使 DB 中无 running 子代理, /// 队列可能仍缓冲了已完成子代理的结果(子代理完成 → send 到队列 → /// DB 更新为 completed,但 LLM 上轮未消费队列)。 /// 2. wait() 进入 select! 前:多个子代理同时完成时,批量消费避免 /// 每个结果各触发一次 LLM 调用。 async fn try_drain_queued_results(&self) -> Vec; /// 进入等待状态:释放 serial_lock → select! → 重获取 serial_lock。 /// /// 调用前提:serial_lock 已被执行路径获取,guard 存于 coordinator 内部。 /// 返回后:serial_lock 已被重新获取,waiting 标志已清除。 /// /// `cancel_rx`:可选的取消信号接收端。当收到信号时(/stop 命令), /// select! 立即返回 `WaitEvent::Cancelled`,并完成完整的状态清理 ///(重获取锁、回填 guard、清除 is_waiting、归还 receiver)。 /// 为 None 时退化为不检查取消(向后兼容,子代理场景)。 async fn wait(&self, timeout: Duration, cancel_rx: Option>) -> WaitEvent; } #[derive(Clone, Default)] pub struct ToolContext { pub channel_name: Option, pub sender_id: Option, pub chat_id: Option, pub session_id: Option, pub topic_id: Option, pub message_id: Option, pub message_seq: Option, /// 子代理标识,用于标注消息来源 pub subagent_description: Option, /// 当前嵌套深度(0 = 主 agent,1 = 子 agent,2 = 孙 agent...) pub nesting_depth: u32, /// 当前智能体自身的任务 ID(主 agent 为 None,子/孙 agent 为 Some(uuid)) pub task_id: Option, /// 父任务 ID(仅子/孙智能体有值,用于构建任务层级) pub parent_task_id: Option, /// 当前工具调用的 ID(由 agent_loop 在执行前注入,用于精确关联 TaskStarted 事件) pub tool_call_id: Option, /// 父智能体(主 agent 所选专家或上级子代理)的 capability 策略快照。 /// TaskTool 据此强制校验子代理加载(白/黑名单),与 spawn/resume 安全范式一致。 /// 以数据形式传递,避免 task 模块反向依赖 experts 模块。 pub parent_capability: Option, /// 端到端追踪 ID(从 InboundMessage 继承,用于 tool 执行路径的日志关联)。 /// None 表示无追踪上下文(如子代理独立执行或测试环境)。 pub trace_id: Option, /// 异步子代理完成队列的 sender(按 topic 隔离)。 /// 仅主 agent(nesting_depth=0)有值:agent_factory 构建时从 SessionHistory 注入。 /// TaskTool spawn 异步子代理后,子代理完成时通过此 sender 发送 SubagentResult, /// 由 wait_for_subagents 工具的 receiver 端消费。 /// 子代理自身(nesting_depth>0)为 None:嵌套层不支持异步,走同步路径。 pub sub_done_sender: Option>, /// wait_for_subagents 工具的协调器(仅主 agent 有值)。 /// 封装了释放/重获取 serial_lock + select! 等待逻辑。 /// wait 工具通过此接口实现真等待(释放锁让 process_one 注入用户消息)。 pub wait_coordinator: Option>, /// 取消信号接收端(仅主 agent 有值,由 agent_factory 从 cancel_token clone 注入)。 /// wait_for_subagents 工具将其传给 coordinator.wait() 的 select!, /// 使 /stop 命令能立即中断等待并完成状态清理。 /// watch::Receiver 可安全 clone,多个 receiver 共享同一 sender, /// 互不影响各自的 has_changed() / changed() 状态。 pub cancel_rx: Option>, } impl std::fmt::Debug for ToolContext { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("ToolContext") .field("channel_name", &self.channel_name) .field("sender_id", &self.sender_id) .field("chat_id", &self.chat_id) .field("session_id", &self.session_id) .field("topic_id", &self.topic_id) .field("message_id", &self.message_id) .field("message_seq", &self.message_seq) .field("subagent_description", &self.subagent_description) .field("nesting_depth", &self.nesting_depth) .field("task_id", &self.task_id) .field("parent_task_id", &self.parent_task_id) .field("tool_call_id", &self.tool_call_id) .field("parent_capability", &self.parent_capability) .field("trace_id", &self.trace_id) .field("sub_done_sender", &self.sub_done_sender) .field("wait_coordinator", &self.wait_coordinator.is_some()) .field("cancel_rx", &self.cancel_rx.is_some()) .finish() } } #[async_trait] pub trait Tool: Send + Sync + 'static { fn name(&self) -> &str; fn description(&self) -> &str; fn parameters_schema(&self) -> serde_json::Value; async fn execute(&self, args: serde_json::Value) -> anyhow::Result; async fn execute_with_context( &self, _context: &ToolContext, args: serde_json::Value, ) -> anyhow::Result { self.execute(args).await } /// Whether this tool is side-effect free and safe to parallelize. fn read_only(&self) -> bool { false } /// Whether this tool can run alongside other concurrency-safe tools. fn concurrency_safe(&self) -> bool { self.read_only() && !self.exclusive() } /// Whether this tool should run alone even if concurrency is enabled. fn exclusive(&self) -> bool { false } }