From 8eabeab8eb1bca2083c60c705124f1f63cd66cbe Mon Sep 17 00:00:00 2001 From: Kent Overstreet Date: Wed, 1 Apr 2026 15:18:42 -0400 Subject: [PATCH] Tool whitelist from agent header filters native tools MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tools field in agent headers now filters which native tools the agent receives. Empty = all tools (default). Non-empty = whitelist. Journal agent can list only journal_tail/journal_new/ journal_update. Log shows actual tool names instead of "no tools". Threaded tools list through call_api_with_tools → sync wrapper → llm caller. Co-Authored-By: Proof of Concept --- src/subconscious/api.rs | 18 +++++++++++++----- src/subconscious/knowledge.rs | 7 +++++-- src/subconscious/llm.rs | 4 ++-- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/subconscious/api.rs b/src/subconscious/api.rs index 0761825..c1c044f 100644 --- a/src/subconscious/api.rs +++ b/src/subconscious/api.rs @@ -35,6 +35,7 @@ pub async fn call_api_with_tools( prompts: &[String], phases: &[String], temperature: Option, + tools: &[String], bail_fn: Option<&(dyn Fn(usize) -> Result<(), String> + Sync)>, log: &dyn Fn(&str), ) -> Result { @@ -43,10 +44,16 @@ pub async fn call_api_with_tools( // Set up a UI channel — we drain reasoning tokens into the log let (ui_tx, mut ui_rx) = crate::agent::ui_channel::channel(); - // Subconscious agents only get memory tools — no filesystem access. - // TODO: respect tools whitelist from agent header to filter which - // native tools each agent gets (e.g. journal agent only gets journal tools) - let tool_defs = thought::memory_and_journal_definitions(); + // All available native tools for subconscious agents + let all_tools = thought::memory_and_journal_definitions(); + // If agent header specifies a tools whitelist, filter to only those + let tool_defs: Vec<_> = if tools.is_empty() { + all_tools + } else { + all_tools.into_iter() + .filter(|t| tools.iter().any(|w| w == &t.function.name)) + .collect() + }; let tracker = ProcessTracker::new(); // Provenance tracks which agent:phase is making writes. // Updated between steps by the bail function via set_provenance(). @@ -226,6 +233,7 @@ pub fn call_api_with_tools_sync( prompts: &[String], phases: &[String], temperature: Option, + tools: &[String], bail_fn: Option<&(dyn Fn(usize) -> Result<(), String> + Sync)>, log: &(dyn Fn(&str) + Sync), ) -> Result { @@ -236,7 +244,7 @@ pub fn call_api_with_tools_sync( .build() .map_err(|e| format!("tokio runtime: {}", e))?; rt.block_on( - call_api_with_tools(agent, prompts, phases, temperature, bail_fn, log) + call_api_with_tools(agent, prompts, phases, temperature, tools, bail_fn, log) ) }).join().unwrap() }) diff --git a/src/subconscious/knowledge.rs b/src/subconscious/knowledge.rs index f369e77..984ec9b 100644 --- a/src/subconscious/knowledge.rs +++ b/src/subconscious/knowledge.rs @@ -294,8 +294,11 @@ fn run_one_agent_inner( _llm_tag: &str, log: &(dyn Fn(&str) + Sync), ) -> Result { - let tools_desc = if def.tools.is_empty() { "no tools".into() } - else { format!("{} tools", def.tools.len()) }; + let tools_desc = if def.tools.is_empty() { + "all tools".into() + } else { + def.tools.join(", ") + }; let n_steps = agent_batch.steps.len(); for key in &agent_batch.node_keys { diff --git a/src/subconscious/llm.rs b/src/subconscious/llm.rs index 1342192..0643623 100644 --- a/src/subconscious/llm.rs +++ b/src/subconscious/llm.rs @@ -22,7 +22,7 @@ pub(crate) fn call_simple(caller: &str, prompt: &str) -> Result let prompts = vec![prompt.to_string()]; let phases = vec![]; - super::api::call_api_with_tools_sync(caller, &prompts, &phases, None, None, &log) + super::api::call_api_with_tools_sync(caller, &prompts, &phases, None, &[], None, &log) } /// Call a model using an agent definition's configuration (multi-step). @@ -34,7 +34,7 @@ pub(crate) fn call_for_def_multi( bail_fn: Option<&(dyn Fn(usize) -> Result<(), String> + Sync)>, log: &(dyn Fn(&str) + Sync), ) -> Result { - super::api::call_api_with_tools_sync(&def.agent, prompts, phases, def.temperature, bail_fn, log) + super::api::call_api_with_tools_sync(&def.agent, prompts, phases, def.temperature, &def.tools, bail_fn, log) }