From 9387263dfd0afc9b0817140242940c2264ed75e0 Mon Sep 17 00:00:00 2001 From: dj-oyu <68707227+dj-oyu@users.noreply.github.com> Date: Sun, 15 Mar 2026 18:37:38 +0900 Subject: [PATCH] refactor: consolidate NewAgentLoop fork initialization into initLoopExt Move stats tracker creation, orchestration broadcaster detection, session tracker init, and gcLoop startup into initLoopExt() in loop_ext.go. NewAgentLoop now calls initLoopExt() with a single line instead of ~30 lines of inline fork code. Co-Authored-By: Claude Opus 4.6 (1M context) --- pkg/agent/loop.go | 44 +++---------------------------------------- pkg/agent/loop_ext.go | 33 ++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 41 deletions(-) diff --git a/pkg/agent/loop.go b/pkg/agent/loop.go index 1a6e42e46..c9377e1ac 100644 --- a/pkg/agent/loop.go +++ b/pkg/agent/loop.go @@ -24,12 +24,10 @@ import ( "github.com/sipeed/picoclaw/pkg/constants" "github.com/sipeed/picoclaw/pkg/logger" "github.com/sipeed/picoclaw/pkg/media" - "github.com/sipeed/picoclaw/pkg/orch" "github.com/sipeed/picoclaw/pkg/providers" "github.com/sipeed/picoclaw/pkg/routing" "github.com/sipeed/picoclaw/pkg/skills" "github.com/sipeed/picoclaw/pkg/state" - "github.com/sipeed/picoclaw/pkg/stats" "github.com/sipeed/picoclaw/pkg/tools" "github.com/sipeed/picoclaw/pkg/utils" "github.com/sipeed/picoclaw/pkg/voice" @@ -136,43 +134,7 @@ func NewAgentLoop( providerCache := make(map[string]providers.LLMProvider) - // Create stats tracker if enabled - - var statsTracker *stats.Tracker - - if len(enableStats) > 0 && enableStats[0] && defaultAgent != nil { - statsTracker = stats.NewTracker(defaultAgent.Workspace) - } - - // Determine if orchestration broadcaster is needed (any agent has subagents enabled). - - // Note: instance.go maps defaults.Orchestration → Subagents.Enabled, so --orchestration - - // is automatically reflected here. - - var orchBroadcaster *orch.Broadcaster - - var orchReporter orch.AgentReporter = orch.Noop - - for _, id := range registry.ListAgentIDs() { - if a, ok := registry.GetAgent(id); ok && a.Subagents != nil && a.Subagents.Enabled { - orchBroadcaster = orch.NewBroadcaster() - - orchReporter = orchBroadcaster - - break - } - } - al := &AgentLoop{ - loopExt: loopExt{ - stats: statsTracker, - sessions: NewSessionTracker(), - orchBroadcaster: orchBroadcaster, - orchReporter: orchReporter, - done: make(chan struct{}), - }, - bus: msgBus, cfg: cfg, @@ -190,12 +152,12 @@ func NewAgentLoop( cmdRegistry: commands.NewRegistry(commands.BuiltinDefinitions()), } + // Initialize fork-specific fields (stats, sessions, orchestration, gcLoop). + al.initLoopExt(cfg, registry, len(enableStats) > 0 && enableStats[0]) + // Register shared tools to all agents (needs al for reporter injection). - registerSharedTools(cfg, msgBus, registry, provider, al) - go al.gcLoop() - return al } diff --git a/pkg/agent/loop_ext.go b/pkg/agent/loop_ext.go index 0be7b7def..1dbf3122c 100644 --- a/pkg/agent/loop_ext.go +++ b/pkg/agent/loop_ext.go @@ -39,6 +39,39 @@ type loopExt struct { onHeartbeatThreadUpdate func(int) } +// initLoopExt initializes all fork-specific fields: stats tracker, +// session tracker, orchestration broadcaster, and background goroutines. +// Called from NewAgentLoop after the struct is constructed. +func (al *AgentLoop) initLoopExt(cfg *config.Config, registry *AgentRegistry, enableStats bool) { + defaultAgent := registry.GetDefaultAgent() + + // Stats tracker + if enableStats && defaultAgent != nil { + al.stats = stats.NewTracker(defaultAgent.Workspace) + } + + // Session tracker + al.sessions = NewSessionTracker() + + // Orchestration broadcaster — needed if any agent has subagents enabled. + // Note: instance.go maps defaults.Orchestration → Subagents.Enabled, + // so --orchestration is automatically reflected here. + al.orchReporter = orch.Noop + for _, id := range registry.ListAgentIDs() { + if a, ok := registry.GetAgent(id); ok && a.Subagents != nil && a.Subagents.Enabled { + al.orchBroadcaster = orch.NewBroadcaster() + al.orchReporter = al.orchBroadcaster + break + } + } + + // Shutdown signal channel + al.done = make(chan struct{}) + + // Background GC goroutine + go al.gcLoop() +} + // SetConfigSaver registers a callback to persist config changes. func (al *AgentLoop) SetConfigSaver(fn func(*config.Config) error) { al.saveConfig = fn