diff --git a/pkg/agent/instance.go b/pkg/agent/instance.go index 0ac3d8894..c0ede9a9c 100644 --- a/pkg/agent/instance.go +++ b/pkg/agent/instance.go @@ -9,7 +9,6 @@ import ( "strings" "github.com/sipeed/picoclaw/pkg/config" - "github.com/sipeed/picoclaw/pkg/git" "github.com/sipeed/picoclaw/pkg/providers" "github.com/sipeed/picoclaw/pkg/routing" "github.com/sipeed/picoclaw/pkg/session" @@ -141,23 +140,10 @@ func NewAgentInstance( agentID := routing.DefaultAgentID agentName := "" - var subagents *config.SubagentsConfig - var skillsFilter []string if agentCfg != nil { agentID = routing.NormalizeAgentID(agentCfg.ID) agentName = agentCfg.Name - subagents = agentCfg.Subagents - 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 @@ -243,19 +229,6 @@ func NewAgentInstance( candidates := providers.ResolveCandidatesWithLookup(modelCfg, defaults.Provider, resolveFromModelList) - // Resolve plan model (for interviewing/review phases) - planModel := resolvePlanModel(agentCfg, defaults) - planFallbacks := resolvePlanFallbacks(agentCfg, defaults) - - var planCandidates []providers.FallbackCandidate - if planModel != "" { - planModelCfg := providers.ModelConfig{ - Primary: planModel, - Fallbacks: planFallbacks, - } - planCandidates = providers.ResolveCandidates(planModelCfg, defaults.Provider) - } - // Model routing setup: pre-resolve light model candidates at creation time // to avoid repeated model_list lookups on every incoming message. var router *routing.Router @@ -275,17 +248,7 @@ func NewAgentInstance( } } - // Startup cleanup: prune orphaned worktrees - worktreesDir := filepath.Join(workspace, ".worktrees") - if repoRoot := git.FindRepoRoot(workspace); repoRoot != "" { - git.PruneOrphaned(repoRoot, worktreesDir) - } - - return &AgentInstance{ - instanceExt: instanceExt{ - Subagents: subagents, - SkillsFilter: skillsFilter, - }, + agent := &AgentInstance{ ID: agentID, Name: agentName, Model: model, @@ -304,12 +267,14 @@ func NewAgentInstance( ContextBuilder: contextBuilder, Tools: toolsRegistry, Candidates: candidates, - PlanModel: planModel, - PlanFallbacks: planFallbacks, - PlanCandidates: planCandidates, Router: router, LightCandidates: lightCandidates, } + + // Initialize fork-specific fields (subagents, plan model, worktree pruning). + agent.initInstanceExt(agentCfg, defaults, cfg) + + return agent } // resolveAgentWorkspace determines the workspace directory for an agent. diff --git a/pkg/agent/instance_ext.go b/pkg/agent/instance_ext.go index 10aa835e9..4a76a819e 100644 --- a/pkg/agent/instance_ext.go +++ b/pkg/agent/instance_ext.go @@ -2,10 +2,12 @@ package agent import ( "fmt" + "path/filepath" "sync" "github.com/sipeed/picoclaw/pkg/config" "github.com/sipeed/picoclaw/pkg/git" + "github.com/sipeed/picoclaw/pkg/providers" "github.com/sipeed/picoclaw/pkg/tools" ) @@ -28,6 +30,47 @@ type instanceExt struct { worktreeMu sync.RWMutex } +// initInstanceExt initializes fork-specific fields: subagents config, +// skills filter, plan model resolution, and worktree pruning. +func (ai *AgentInstance) initInstanceExt( + agentCfg *config.AgentConfig, + defaults *config.AgentDefaults, + cfg *config.Config, +) { + // Extract subagents and skills filter from agent config + if agentCfg != nil { + ai.Subagents = agentCfg.Subagents + ai.SkillsFilter = agentCfg.Skills + } + + // Apply defaults.Orchestration: if the flag is set, ensure orchestration is enabled. + if defaults.Orchestration { + if ai.Subagents == nil { + ai.Subagents = &config.SubagentsConfig{Enabled: true} + } else { + ai.Subagents.Enabled = true + } + } + + // Resolve plan model (for interviewing/review phases) + ai.PlanModel = resolvePlanModel(agentCfg, defaults) + ai.PlanFallbacks = resolvePlanFallbacks(agentCfg, defaults) + + if ai.PlanModel != "" { + planModelCfg := providers.ModelConfig{ + Primary: ai.PlanModel, + Fallbacks: ai.PlanFallbacks, + } + ai.PlanCandidates = providers.ResolveCandidates(planModelCfg, defaults.Provider) + } + + // Startup cleanup: prune orphaned worktrees + worktreesDir := filepath.Join(ai.Workspace, ".worktrees") + if repoRoot := git.FindRepoRoot(ai.Workspace); repoRoot != "" { + git.PruneOrphaned(repoRoot, worktreesDir) + } +} + // ActivateWorktree creates a worktree for a session. // projectDir is the git repository to create the worktree in. // If empty, falls back to ai.Workspace.