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 <noreply@anthropic.com>
This commit is contained in:
dj-oyu 2026-02-25 01:54:49 +09:00
parent 55d3a08fcd
commit bfabdea8b7
4 changed files with 29 additions and 9 deletions

View file

@ -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)

View file

@ -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

View file

@ -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)

View file

@ -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 {