Agent:🆕 tool definitions from caller's tool list

The system prompt was advertising all tools to every agent, but
the runtime only dispatched the agent's actual subset. This caused
unconscious agents to call tools that returned "Unknown tool."

Agent::new now takes the tool list explicitly. Each caller passes
its own tools — the prompt and runtime always match. MCP tool
definitions are still appended for agents that use them.

Co-Authored-By: Proof of Concept <poc@bcachefs.org>
This commit is contained in:
ProofOfConcept 2026-04-11 19:43:24 -04:00
parent 28e564aeb2
commit 1c0967c4ec
5 changed files with 12 additions and 3 deletions

View file

@ -192,12 +192,15 @@ impl Agent {
prompt_file: String, prompt_file: String,
conversation_log: Option<ConversationLog>, conversation_log: Option<ConversationLog>,
active_tools: tools::ActiveTools, active_tools: tools::ActiveTools,
agent_tools: Vec<tools::Tool>,
) -> Arc<Self> { ) -> Arc<Self> {
let mut context = ContextState::new(); let mut context = ContextState::new();
context.conversation_log = conversation_log; context.conversation_log = conversation_log;
context.push_no_log(Section::System, AstNode::system_msg(&system_prompt)); context.push_no_log(Section::System, AstNode::system_msg(&system_prompt));
let tool_defs = tools::all_tool_definitions().await; let mut tool_defs: Vec<String> = agent_tools.iter().map(|t| t.to_json()).collect();
tool_defs.extend(tools::all_mcp_tool_definitions().await);
if !tool_defs.is_empty() { if !tool_defs.is_empty() {
let tools_text = format!( let tools_text = format!(
"# Tools\n\nYou have access to the following functions:\n\n<tools>\n{}\n</tools>\n\n\ "# Tools\n\nYou have access to the following functions:\n\n<tools>\n{}\n</tools>\n\n\
@ -223,7 +226,7 @@ impl Agent {
session_id, session_id,
context: tokio::sync::Mutex::new(context), context: tokio::sync::Mutex::new(context),
state: tokio::sync::Mutex::new(AgentState { state: tokio::sync::Mutex::new(AgentState {
tools: tools::tools(), tools: agent_tools,
mcp_tools: McpToolAccess::All, mcp_tools: McpToolAccess::All,
last_prompt_tokens: 0, last_prompt_tokens: 0,
reasoning_effort: "none".to_string(), reasoning_effort: "none".to_string(),

View file

@ -138,6 +138,7 @@ impl AutoAgent {
app, String::new(), app, String::new(),
None, None,
super::tools::ActiveTools::new(), super::tools::ActiveTools::new(),
super::tools::tools(),
).await; ).await;
{ {
let mut st = agent.state.lock().await; let mut st = agent.state.lock().await;

View file

@ -195,6 +195,10 @@ pub async fn all_tool_definitions() -> Vec<String> {
defs defs
} }
pub async fn all_mcp_tool_definitions() -> Vec<String> {
mcp_client::tool_definitions_json().await
}
/// Memory + journal tools only — for subconscious agents. /// Memory + journal tools only — for subconscious agents.
pub fn memory_and_journal_tools() -> Vec<Tool> { pub fn memory_and_journal_tools() -> Vec<Tool> {
let mut all = memory::memory_tools().to_vec(); let mut all = memory::memory_tools().to_vec();

View file

@ -298,6 +298,7 @@ impl Mind {
config.prompt_file.clone(), config.prompt_file.clone(),
conversation_log, conversation_log,
crate::agent::tools::ActiveTools::new(), crate::agent::tools::ActiveTools::new(),
crate::agent::tools::tools(),
).await; ).await;
let shared = Arc::new(std::sync::Mutex::new(MindState::new(config.app.dmn.max_turns))); let shared = Arc::new(std::sync::Mutex::new(MindState::new(config.app.dmn.max_turns)));

View file

@ -282,11 +282,11 @@ impl Unconscious {
client, system_prompt, personality, client, system_prompt, personality,
app, String::new(), None, app, String::new(), None,
crate::agent::tools::ActiveTools::new(), crate::agent::tools::ActiveTools::new(),
auto.tools.clone(),
).await; ).await;
{ {
let mut st = agent.state.lock().await; let mut st = agent.state.lock().await;
st.provenance = format!("unconscious:{}", auto.name); st.provenance = format!("unconscious:{}", auto.name);
st.tools = auto.tools.clone();
st.priority = Some(10); st.priority = Some(10);
} }