- domain: CapabilityPolicy 新增 allowed_subagents/denied_subagents 字段及 check_subagent_allowed 方法 - experts: 专家 frontmatter 解析/渲染支持子代理策略字段 - task/runtime: spawn/resume 双路径校验父代理子代理策略;新增 update_subagent 写回 SUBAGENT.md(与 update_expert 对称);新增 SubagentPromptProvider 按专家策略过滤子代理索引 - task/runtime: 子代理自身 capability 作为孙代理的 parent_capability 透传(ToolContext),保持解耦 - traits: ToolContext 新增 parent_capability 字段 - agent_factory: 主 agent 注入 expert_capability 到 ToolContext 安全:策略不通过即拒绝(与 def 不可用即拒绝范式一致),防止 LLM 通过选择被禁子代理绕过限制;max_nesting_depth 兜底防递归不可被 def 覆盖。
83 lines
3.6 KiB
Rust
83 lines
3.6 KiB
Rust
pub mod messages;
|
||
pub mod tools;
|
||
|
||
use serde::{Deserialize, Serialize};
|
||
|
||
/// 角色能力策略:工具、技能、子代理的白/黑名单。全为空表示沿用默认(不过滤)。
|
||
///
|
||
/// 生效顺序:先白名单取交集,再黑名单扣除。专家与子代理共用此结构,
|
||
/// 确保语义一致。MCP 工具注册在 `ToolRegistry` 中(名 `mcp_*`),与内置
|
||
/// 工具同源,因此 `allowed_tools`/`denied_tools` 覆盖内置 + MCP 工具;
|
||
/// `allowed_skills`/`denied_skills` 仅覆盖 SKILL.md 技能;
|
||
/// `allowed_subagents`/`denied_subagents` 覆盖子代理加载(通过 ToolContext
|
||
/// 传递给 TaskTool,在 spawn/resume 时强制校验)。
|
||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||
pub struct CapabilityPolicy {
|
||
/// 技能白名单:`None` = 不限;`Some(vec)` = 仅这些 SKILL.md 技能可见。
|
||
/// `Some(vec![])` 表示全禁(与 `ToolRegistry::only` 的空交集语义对齐)。
|
||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||
pub allowed_skills: Option<Vec<String>>,
|
||
/// 技能黑名单:禁用这些 SKILL.md 技能。空表示不禁。
|
||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||
pub denied_skills: Vec<String>,
|
||
/// 工具白名单(含 `mcp_*` 工具):`None` = 不限。
|
||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||
pub allowed_tools: Option<Vec<String>>,
|
||
/// 工具黑名单(含 `mcp_*` 工具)。
|
||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||
pub denied_tools: Vec<String>,
|
||
/// 子代理白名单:`None` = 不限;`Some(vec)` = 仅这些子代理可被加载。
|
||
/// 通过 ToolContext 传递给 TaskTool,在 spawn/resume 时强制校验。
|
||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||
pub allowed_subagents: Option<Vec<String>>,
|
||
/// 子代理黑名单:禁止加载这些子代理。空表示不禁。
|
||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||
pub denied_subagents: Vec<String>,
|
||
}
|
||
|
||
impl CapabilityPolicy {
|
||
/// 所有策略字段均为空 → 沿用主智能体默认配置(不过滤)。
|
||
pub fn is_empty(&self) -> bool {
|
||
self.allowed_skills.is_none()
|
||
&& self.denied_skills.is_empty()
|
||
&& self.allowed_tools.is_none()
|
||
&& self.denied_tools.is_empty()
|
||
&& self.allowed_subagents.is_none()
|
||
&& self.denied_subagents.is_empty()
|
||
}
|
||
|
||
/// 是否声明了任何技能策略。
|
||
pub fn has_skill_policy(&self) -> bool {
|
||
self.allowed_skills.is_some() || !self.denied_skills.is_empty()
|
||
}
|
||
|
||
/// 是否声明了任何工具策略。
|
||
pub fn has_tool_policy(&self) -> bool {
|
||
self.allowed_tools.is_some() || !self.denied_tools.is_empty()
|
||
}
|
||
|
||
/// 是否声明了任何子代理策略。
|
||
pub fn has_subagent_policy(&self) -> bool {
|
||
self.allowed_subagents.is_some() || !self.denied_subagents.is_empty()
|
||
}
|
||
|
||
/// 校验指定子代理是否被允许。返回 Err 时附带拒绝原因。
|
||
pub fn check_subagent_allowed(&self, name: &str) -> Result<(), String> {
|
||
if let Some(list) = &self.allowed_subagents {
|
||
if !list.iter().any(|s| s == name) {
|
||
return Err(format!(
|
||
"subagent '{}' is not in the allowed_subagents whitelist",
|
||
name
|
||
));
|
||
}
|
||
}
|
||
if self.denied_subagents.iter().any(|s| s == name) {
|
||
return Err(format!(
|
||
"subagent '{}' is in the denied_subagents blacklist",
|
||
name
|
||
));
|
||
}
|
||
Ok(())
|
||
}
|
||
}
|