From bfabdea8b759e5acaaaa5a72679e14fe9332fa1f Mon Sep 17 00:00:00 2001 From: dj-oyu <68707227+dj-oyu@users.noreply.github.com> Date: Wed, 25 Feb 2026 01:54:49 +0900 Subject: [PATCH] feat: add --orchestration startup flag to gate spawn tool Orchestration (subagent spawning) is now opt-in rather than always-on. The spawn tool is only registered when orchestration is explicitly enabled, preventing the LLM from having a tool it has no guidance to use. Changes: - config: SubagentsConfig.Enabled bool (opt-in per agent via JSON config) - config: AgentDefaults.Orchestration bool (env PICOCLAW_AGENTS_DEFAULTS_ORCHESTRATION) - agent/instance: apply defaults.Orchestration to agent.Subagents at construction - agent/loop: gate spawn tool registration on agent.Subagents.Enabled - cmd/agent: --orchestration flag sets cfg.Agents.Defaults.Orchestration = true Usage: picoclaw agent --orchestration Co-Authored-By: Claude Sonnet 4.6 --- cmd/picoclaw/cmd_agent.go | 7 +++++++ pkg/agent/instance.go | 9 +++++++++ pkg/agent/loop.go | 20 +++++++++++--------- pkg/config/config.go | 2 ++ 4 files changed, 29 insertions(+), 9 deletions(-) diff --git a/cmd/picoclaw/cmd_agent.go b/cmd/picoclaw/cmd_agent.go index 8658c9d32..5ce0c3b99 100644 --- a/cmd/picoclaw/cmd_agent.go +++ b/cmd/picoclaw/cmd_agent.go @@ -24,6 +24,7 @@ func agentCmd() { message := "" sessionKey := "cli:default" modelOverride := "" + orchestrationEnabled := false args := os.Args[2:] for i := 0; i < len(args); i++ { @@ -46,6 +47,8 @@ func agentCmd() { modelOverride = args[i+1] i++ } + case "--orchestration": + orchestrationEnabled = true } } @@ -59,6 +62,10 @@ func agentCmd() { cfg.Agents.Defaults.Model = modelOverride } + if orchestrationEnabled { + cfg.Agents.Defaults.Orchestration = true + } + provider, modelID, err := providers.CreateProvider(cfg) if err != nil { fmt.Printf("Error creating provider: %v\n", err) diff --git a/pkg/agent/instance.go b/pkg/agent/instance.go index a767bcb04..c84a70660 100644 --- a/pkg/agent/instance.go +++ b/pkg/agent/instance.go @@ -91,6 +91,15 @@ func NewAgentInstance( skillsFilter = agentCfg.Skills } + // Apply defaults.Orchestration: if the flag is set, ensure orchestration is enabled. + if defaults.Orchestration { + if subagents == nil { + subagents = &config.SubagentsConfig{Enabled: true} + } else { + subagents.Enabled = true + } + } + maxIter := defaults.MaxToolIterations if maxIter == 0 { maxIter = 20 diff --git a/pkg/agent/loop.go b/pkg/agent/loop.go index 75284b6dc..cdcd1f608 100644 --- a/pkg/agent/loop.go +++ b/pkg/agent/loop.go @@ -216,15 +216,17 @@ func registerSharedTools( agent.Tools.Register(tools.NewFindSkillsTool(registryMgr, searchCache)) agent.Tools.Register(tools.NewInstallSkillTool(registryMgr, agent.Workspace)) - // Spawn tool with allowlist checker - subagentManager := tools.NewSubagentManager(provider, agent.Model, agent.Workspace, msgBus) - subagentManager.SetLLMOptions(agent.MaxTokens, agent.Temperature) - spawnTool := tools.NewSpawnTool(subagentManager) - currentAgentID := agentID - spawnTool.SetAllowlistChecker(func(targetAgentID string) bool { - return registry.CanSpawnSubagent(currentAgentID, targetAgentID) - }) - agent.Tools.Register(spawnTool) + // Spawn tool — only registered when orchestration is explicitly enabled. + if agent.Subagents != nil && agent.Subagents.Enabled { + subagentManager := tools.NewSubagentManager(provider, agent.Model, agent.Workspace, msgBus) + subagentManager.SetLLMOptions(agent.MaxTokens, agent.Temperature) + spawnTool := tools.NewSpawnTool(subagentManager) + currentAgentID := agentID + spawnTool.SetAllowlistChecker(func(targetAgentID string) bool { + return registry.CanSpawnSubagent(currentAgentID, targetAgentID) + }) + agent.Tools.Register(spawnTool) + } // Update context builder with the complete tools registry agent.ContextBuilder.SetToolsRegistry(agent.Tools) diff --git a/pkg/config/config.go b/pkg/config/config.go index 7ac337856..299f7334a 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -141,6 +141,7 @@ type AgentConfig struct { } type SubagentsConfig struct { + Enabled bool `json:"enabled,omitempty"` AllowAgents []string `json:"allow_agents,omitempty"` Model *AgentModelConfig `json:"model,omitempty"` } @@ -182,6 +183,7 @@ type AgentDefaults struct { Temperature *float64 `json:"temperature,omitempty" env:"PICOCLAW_AGENTS_DEFAULTS_TEMPERATURE"` MaxToolIterations int `json:"max_tool_iterations" env:"PICOCLAW_AGENTS_DEFAULTS_MAX_TOOL_ITERATIONS"` TaskReminderInterval int `json:"task_reminder_interval" env:"PICOCLAW_AGENTS_DEFAULTS_TASK_REMINDER_INTERVAL"` + Orchestration bool `json:"orchestration,omitempty" env:"PICOCLAW_AGENTS_DEFAULTS_ORCHESTRATION"` } type ChannelsConfig struct {