feat(cross-project): découverte et contact inter-projets — tools MCP idea_project_{list,read_context,list_agents,ask_agent,cancel_agent}, permission contactable par agent + default_contactable persistée dans les templates, routage orchestrator par projectId+agentId — #260 (tests ciblés verts, QA en attente)

This commit is contained in:
2026-09-07 16:31:14 +02:00
parent d7b24ad183
commit 98954986a7
18 changed files with 721 additions and 15 deletions

View File

@ -78,6 +78,8 @@ impl TemplateToolProvider for AppTemplateToolProvider {
"defaultMcpToolPermissions",
"defaultMcpToolPolicy",
)?,
default_contactable: optional_bool(&arguments, "defaultContactable")?
.unwrap_or(false),
})
.await
.map_err(template_error)?;
@ -160,6 +162,17 @@ fn optional_string<'a>(
}
}
fn optional_bool(value: &Value, key: &str) -> Result<Option<bool>, TemplateToolError> {
match value.get(key) {
None | Some(Value::Null) => Ok(None),
Some(Value::Bool(value)) => Ok(Some(*value)),
Some(_) => Err(TemplateToolError::new(
"invalid",
format!("field `{key}` must be a boolean"),
)),
}
}
fn optional_json_value<T>(arguments: &Value, key: &str) -> Result<Option<T>, TemplateToolError>
where
T: serde::de::DeserializeOwned,
@ -317,6 +330,7 @@ mod tests {
"defaultAgentName": "Base Agent",
"defaultSystemPermissions": { "network": "allow" },
"defaultMcpToolPermissions": { "allowedTools": ["idea_template_read"] },
"defaultContactable": true,
}),
)
.await
@ -332,6 +346,7 @@ mod tests {
created["defaultMcpToolPermissions"],
json!({ "allowedTools": ["idea_template_read"] })
);
assert_eq!(created["defaultContactable"], true);
let read = provider
.handle_template_tool(
@ -353,6 +368,7 @@ mod tests {
read["defaultMcpToolPermissions"],
json!({ "allowedTools": ["idea_template_read"] })
);
assert_eq!(read["defaultContactable"], true);
let listed = provider
.handle_template_tool(&project(), "agent", "idea_template_list", json!({}))
@ -370,6 +386,7 @@ mod tests {
"content": "v2",
"defaultSystemPermissions": { "network": "deny" },
"defaultMcpToolPermissions": { "allowedTools": ["idea_template_update"] },
"defaultContactable": false,
}),
)
.await
@ -385,6 +402,7 @@ mod tests {
updated["defaultMcpToolPermissions"],
json!({ "allowedTools": ["idea_template_update"] })
);
assert_eq!(updated["defaultContactable"], false);
assert_eq!(
store
.get(TemplateId::from_uuid(