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