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) <noreply@anthropic.com>
This commit is contained in:
dj-oyu 2026-03-15 18:37:38 +09:00
parent 396186e712
commit 9387263dfd
2 changed files with 36 additions and 41 deletions

View file

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

View file

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