fix(agents): route spawn to target agent

This commit is contained in:
Anton Bogdanovich 2026-05-06 15:14:55 -07:00
parent 6e6293e596
commit 46c0e936fa
2 changed files with 25 additions and 18 deletions

View file

@ -103,6 +103,7 @@ type subTurnRuntimeConfig struct {
// // Result also available in parent's pendingResults channel // // Result also available in parent's pendingResults channel
// // Parent turn will poll and process it in a later iteration // // Parent turn will poll and process it in a later iteration
type SubTurnConfig struct { type SubTurnConfig struct {
AgentID string
Model string Model string
Tools []tools.Tool Tools []tools.Tool
SystemPrompt string SystemPrompt string
@ -224,6 +225,7 @@ func (s *AgentLoopSpawner) SpawnSubTurn(
// Convert tools.SubTurnConfig to agent.SubTurnConfig // Convert tools.SubTurnConfig to agent.SubTurnConfig
agentCfg := SubTurnConfig{ agentCfg := SubTurnConfig{
AgentID: cfg.AgentID,
Model: cfg.Model, Model: cfg.Model,
Tools: cfg.Tools, Tools: cfg.Tools,
SystemPrompt: cfg.SystemPrompt, SystemPrompt: cfg.SystemPrompt,
@ -318,9 +320,14 @@ func spawnSubTurn(
return nil, ErrDepthLimitExceeded return nil, ErrDepthLimitExceeded
} }
// 2. Config validation: Model is required unless TargetAgentID is set // 2. Config validation: Model is required unless a target agent is set
// (the target agent provides its own model). // (the target agent provides its own model). Accept both TargetAgentID
if cfg.Model == "" && cfg.TargetAgentID == "" { // and AgentID for backward compatibility with older spawn callers.
targetAgentID := cfg.TargetAgentID
if targetAgentID == "" {
targetAgentID = cfg.AgentID
}
if cfg.Model == "" && targetAgentID == "" {
return nil, ErrInvalidSubTurnConfig return nil, ErrInvalidSubTurnConfig
} }
@ -338,23 +345,22 @@ func spawnSubTurn(
childID := al.generateSubTurnID() childID := al.generateSubTurnID()
// Resolve the agent instance for the child turn. // Get the requested agent instance, falling back to the parent agent and
// When TargetAgentID is set, look up that agent from the registry so the // then to the default agent. The requested agent path lets spawn(agent_id)
// child runs with the target's workspace, model, tools, and system prompt. // use that agent's model, tools, workspace, skills, and injected files.
// Otherwise fall back to the parent's agent (existing behavior). // Wrap it in a shallow copy that uses an ephemeral (in-memory only) session store
var baseAgent *AgentInstance // so that child turns never pollute or persist to the parent's session history.
if cfg.TargetAgentID != "" { baseAgent := parentTS.agent
var ok bool if targetAgentID != "" {
baseAgent, ok = al.registry.GetAgent(cfg.TargetAgentID) if targetAgent, ok := al.registry.GetAgent(targetAgentID); ok {
if !ok { baseAgent = targetAgent
return nil, fmt.Errorf("target agent %q not found in registry", cfg.TargetAgentID)
}
} else { } else {
baseAgent = parentTS.agent return nil, fmt.Errorf("target agent %q not found", targetAgentID)
}
}
if baseAgent == nil { if baseAgent == nil {
baseAgent = al.registry.GetDefaultAgent() baseAgent = al.registry.GetDefaultAgent()
} }
}
if baseAgent == nil { if baseAgent == nil {
return nil, errors.New("parent turnState has no agent instance") return nil, errors.New("parent turnState has no agent instance")
} }

View file

@ -18,6 +18,7 @@ type SubTurnSpawner interface {
// SubTurnConfig holds configuration for spawning a sub-turn. // SubTurnConfig holds configuration for spawning a sub-turn.
type SubTurnConfig struct { type SubTurnConfig struct {
AgentID string
Model string Model string
Tools []Tool Tools []Tool
SystemPrompt string SystemPrompt string