diff --git a/pkg/agent/loop.go b/pkg/agent/loop.go index 9315ccf34..ca763b97c 100644 --- a/pkg/agent/loop.go +++ b/pkg/agent/loop.go @@ -203,11 +203,30 @@ func registerSharedTools(cfg *config.Config, msgBus *bus.MessageBus, registry *A agent.Tools.Register(multiagent.NewListAgentsTool(resolver)) } - // Update context builder with the complete tools registry + // Apply per-agent tool policy (static, startup-time filtering). + // This removes denied tools from the registry before the LLM ever sees them. + if agentCfg := findAgentConfig(cfg, agentID); agentCfg != nil && agentCfg.ToolPolicy != nil { + tools.ApplyPolicy(agent.Tools, tools.ToolPolicy{ + Allow: agentCfg.ToolPolicy.Allow, + Deny: agentCfg.ToolPolicy.Deny, + }) + } + + // Update context builder with the (possibly filtered) tools registry agent.ContextBuilder.SetToolsRegistry(agent.Tools) } } +// findAgentConfig returns the AgentConfig for a given agent ID, or nil if not found. +func findAgentConfig(cfg *config.Config, agentID string) *config.AgentConfig { + for i := range cfg.Agents.List { + if routing.NormalizeAgentID(cfg.Agents.List[i].ID) == agentID { + return &cfg.Agents.List[i] + } + } + return nil +} + func (al *AgentLoop) Run(ctx context.Context) error { al.running.Store(true) diff --git a/pkg/multiagent/handoff.go b/pkg/multiagent/handoff.go index fe8063f1f..47859f6c9 100644 --- a/pkg/multiagent/handoff.go +++ b/pkg/multiagent/handoff.go @@ -156,10 +156,19 @@ func ExecuteHandoff(ctx context.Context, resolver AgentResolver, board *Blackboa maxIter = 10 } + // Apply depth-based tool policy: clone target tools and remove depth-denied tools. + // At max depth, leaf agents lose spawn/handoff/list_agents to prevent further chaining. + targetTools := target.Tools + denyList := tools.DepthDenyList(req.Depth+1, maxDepth) + if len(denyList) > 0 && targetTools != nil { + targetTools = target.Tools.Clone() + tools.ApplyPolicy(targetTools, tools.ToolPolicy{Deny: denyList}) + } + loopResult, err := tools.RunToolLoop(ctx, tools.ToolLoopConfig{ Provider: target.Provider, Model: target.Model, - Tools: target.Tools, + Tools: targetTools, MaxIterations: maxIter, LLMOptions: map[string]any{ "max_tokens": 4096,