From fb662b02d32edf9108e780b445b4a5e198ee3c42 Mon Sep 17 00:00:00 2001 From: dj-oyu <68707227+dj-oyu@users.noreply.github.com> Date: Fri, 13 Mar 2026 16:06:41 +0900 Subject: [PATCH] refactor: extract loop.go fork fields into loopExt embedded struct Move fork-specific AgentLoop fields to loop_ext.go via embedded struct: - stats, sessions, orchBroadcaster, orchReporter - planStartPending, planClearHistory, sessionLocks, activeTasks - done, saveConfig, onHeartbeatThreadUpdate - SetConfigSaver, SetHeartbeatThreadUpdater methods Co-Authored-By: Claude Opus 4.6 --- pkg/agent/loop.go | 52 +++++++++---------------------------------- pkg/agent/loop_ext.go | 46 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 42 deletions(-) create mode 100644 pkg/agent/loop_ext.go diff --git a/pkg/agent/loop.go b/pkg/agent/loop.go index 259f2cd28..89037d8dc 100644 --- a/pkg/agent/loop.go +++ b/pkg/agent/loop.go @@ -39,6 +39,8 @@ import ( ) type AgentLoop struct { + loopExt // fork-specific fields (see loop_ext.go) + bus *bus.MessageBus cfg *config.Config @@ -47,8 +49,6 @@ type AgentLoop struct { state *state.Manager - stats *stats.Tracker // nil when --stats not passed - running atomic.Bool summarizing sync.Map @@ -67,16 +67,6 @@ type AgentLoop struct { providerCache map[string]providers.LLMProvider - planStartPending bool // set by /plan start to trigger LLM execution - - planClearHistory bool // set by /plan start clear to wipe history on transition - - sessionLocks sync.Map // sessionKey → *sessionSemaphore - - activeTasks sync.Map // sessionKey → *activeTask - - sessions *SessionTracker - lastSystemPrompt atomic.Value // string — last system prompt sent to LLM promptDirty atomic.Bool // true = rebuild needed on next GetSystemPrompt read @@ -84,16 +74,6 @@ type AgentLoop struct { OnStateChange func() // called on plan/session/skills mutations OnUserMessage func() // called when a real user message is processed - - saveConfig func(*config.Config) error - - onHeartbeatThreadUpdate func(int) - - orchBroadcaster *orch.Broadcaster // nil when --orchestration not set - - orchReporter orch.AgentReporter // always non-nil (Noop when disabled) - - done chan struct{} // closed by Close() to stop background goroutines } // processOptions configures how a message is processed @@ -186,6 +166,14 @@ func NewAgentLoop( } al := &AgentLoop{ + loopExt: loopExt{ + stats: statsTracker, + sessions: NewSessionTracker(), + orchBroadcaster: orchBroadcaster, + orchReporter: orchReporter, + done: make(chan struct{}), + }, + bus: msgBus, cfg: cfg, @@ -194,22 +182,12 @@ func NewAgentLoop( state: stateManager, - stats: statsTracker, - summarizing: sync.Map{}, fallback: fallbackChain, providerCache: providerCache, - sessions: NewSessionTracker(), - - orchBroadcaster: orchBroadcaster, - - orchReporter: orchReporter, - - done: make(chan struct{}), - cmdRegistry: commands.NewRegistry(commands.BuiltinDefinitions()), } @@ -222,16 +200,6 @@ func NewAgentLoop( return al } -func (al *AgentLoop) SetConfigSaver(fn func(*config.Config) error) { - al.saveConfig = fn -} - -// SetHeartbeatThreadUpdater registers a callback to apply runtime heartbeat thread updates. - -func (al *AgentLoop) SetHeartbeatThreadUpdater(fn func(int)) { - al.onHeartbeatThreadUpdate = fn -} - // registerSharedTools registers tools that are shared across all agents (web, message, spawn). func registerSharedTools( cfg *config.Config, diff --git a/pkg/agent/loop_ext.go b/pkg/agent/loop_ext.go new file mode 100644 index 000000000..67d99a89b --- /dev/null +++ b/pkg/agent/loop_ext.go @@ -0,0 +1,46 @@ +package agent + +import ( + "sync" + + "github.com/sipeed/picoclaw/pkg/config" + "github.com/sipeed/picoclaw/pkg/orch" + "github.com/sipeed/picoclaw/pkg/stats" +) + +// loopExt holds fork-specific fields for AgentLoop. +// Embedded in AgentLoop so existing field access (al.stats, al.sessions, etc.) continues to work. +// Upstream additions to AgentLoop won't conflict with these fields. +type loopExt struct { + stats *stats.Tracker // nil when --stats not passed + + sessions *SessionTracker + + orchBroadcaster *orch.Broadcaster // nil when --orchestration not set + + orchReporter orch.AgentReporter // always non-nil (Noop when disabled) + + planStartPending bool // set by /plan start to trigger LLM execution + + planClearHistory bool // set by /plan start clear to wipe history on transition + + sessionLocks sync.Map // sessionKey → *sessionSemaphore + + activeTasks sync.Map // sessionKey → *activeTask + + done chan struct{} // closed by Close() to stop background goroutines + + saveConfig func(*config.Config) error + + onHeartbeatThreadUpdate func(int) +} + +// SetConfigSaver registers a callback to persist config changes. +func (al *AgentLoop) SetConfigSaver(fn func(*config.Config) error) { + al.saveConfig = fn +} + +// SetHeartbeatThreadUpdater registers a callback to apply runtime heartbeat thread updates. +func (al *AgentLoop) SetHeartbeatThreadUpdater(fn func(int)) { + al.onHeartbeatThreadUpdate = fn +}