refactor: align fork structure with upstream to minimize merge conflicts

Three changes to reduce conflict surface when merging upstream:

1. Move Subagents and SkillsFilter from instanceExt back to
   AgentInstance struct (matches upstream field placement)

2. Remove al *AgentLoop parameter from registerSharedTools to match
   upstream signature. Orchestration registration now happens via
   separate registerAllOrchestrationTools method call in NewAgentLoop

3. Move buildToolsSection from context.go to context_ext.go (fork-only
   code that doesn't exist upstream)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
dj-oyu 2026-03-15 19:02:31 +09:00
parent b4021782c1
commit c74dbd4c19
6 changed files with 56 additions and 35 deletions

View file

@ -157,29 +157,6 @@ Your workspace is at: %s
toolsSection, executingRule, toolDiscovery) toolsSection, executingRule, toolDiscovery)
} }
func (cb *ContextBuilder) buildToolsSection() string {
if cb.tools == nil {
return ""
}
summaries := cb.tools.GetSummaries()
if len(summaries) == 0 {
return ""
}
var sb strings.Builder
sb.WriteString("## Available Tools\n\n")
sb.WriteString(
"**CRITICAL**: You MUST use tools to perform actions. Do NOT pretend to execute commands or schedule tasks.\n\n",
)
sb.WriteString("You have access to the following tools:\n\n")
for _, s := range summaries {
sb.WriteString(s)
sb.WriteString("\n")
}
return sb.String()
}
func (cb *ContextBuilder) getDiscoveryRule() string { func (cb *ContextBuilder) getDiscoveryRule() string {
if !cb.toolDiscoveryBM25 && !cb.toolDiscoveryRegex { if !cb.toolDiscoveryBM25 && !cb.toolDiscoveryRegex {
return "" return ""

View file

@ -1,6 +1,10 @@
package agent package agent
import "github.com/sipeed/picoclaw/pkg/tools" import (
"strings"
"github.com/sipeed/picoclaw/pkg/tools"
)
// contextBuilderExt holds fork-specific fields for ContextBuilder. // contextBuilderExt holds fork-specific fields for ContextBuilder.
// Embedded in ContextBuilder so existing field access (cb.workDir, cb.tools, etc.) continues to work. // Embedded in ContextBuilder so existing field access (cb.workDir, cb.tools, etc.) continues to work.
@ -33,6 +37,30 @@ func (cb *ContextBuilder) SetOrchestrationEnabled(enabled bool) {
cb.orchestrationEnabled = enabled cb.orchestrationEnabled = enabled
} }
// buildToolsSection generates the "Available Tools" section for the system prompt.
func (cb *ContextBuilder) buildToolsSection() string {
if cb.tools == nil {
return ""
}
summaries := cb.tools.GetSummaries()
if len(summaries) == 0 {
return ""
}
var sb strings.Builder
sb.WriteString("## Available Tools\n\n")
sb.WriteString(
"**CRITICAL**: You MUST use tools to perform actions. Do NOT pretend to execute commands or schedule tasks.\n\n",
)
sb.WriteString("You have access to the following tools:\n\n")
for _, s := range summaries {
sb.WriteString(s)
sb.WriteString("\n")
}
return sb.String()
}
// extIdentityOverrides returns the orchestration-specific overrides for // extIdentityOverrides returns the orchestration-specific overrides for
// getIdentity: banner prefix, identity string, and plan executing rule. // getIdentity: banner prefix, identity string, and plan executing rule.
// When orchestration is disabled, all return values are empty strings. // When orchestration is disabled, all return values are empty strings.

View file

@ -37,6 +37,8 @@ type AgentInstance struct {
Sessions *session.LegacyAdapter Sessions *session.LegacyAdapter
ContextBuilder *ContextBuilder ContextBuilder *ContextBuilder
Tools *tools.ToolRegistry Tools *tools.ToolRegistry
Subagents *config.SubagentsConfig
SkillsFilter []string
Candidates []providers.FallbackCandidate Candidates []providers.FallbackCandidate
PlanModel string PlanModel string
PlanFallbacks []string PlanFallbacks []string

View file

@ -18,9 +18,6 @@ type instanceExt struct {
// Used by runAgentLoop to wait for spawned subagents before worktree cleanup. // Used by runAgentLoop to wait for spawned subagents before worktree cleanup.
SubagentMgr *tools.SubagentManager SubagentMgr *tools.SubagentManager
Subagents *config.SubagentsConfig
SkillsFilter []string
// Interview staleness tracking: consecutive turns where MEMORY.md was not updated. // Interview staleness tracking: consecutive turns where MEMORY.md was not updated.
interviewStaleCount int interviewStaleCount int
interviewMemoryLen int interviewMemoryLen int

View file

@ -155,8 +155,11 @@ func NewAgentLoop(
// Initialize fork-specific fields (stats, sessions, orchestration, gcLoop). // Initialize fork-specific fields (stats, sessions, orchestration, gcLoop).
al.initLoopExt(cfg, registry, len(enableStats) > 0 && enableStats[0]) al.initLoopExt(cfg, registry, len(enableStats) > 0 && enableStats[0])
// Register shared tools to all agents (needs al for reporter injection). // Register shared tools to all agents.
registerSharedTools(cfg, msgBus, registry, provider, al) registerSharedTools(cfg, msgBus, registry, provider)
// Register fork-specific orchestration tools (needs al for reporter injection).
al.registerAllOrchestrationTools(cfg, registry, provider, msgBus)
return al return al
} }
@ -167,8 +170,6 @@ func registerSharedTools(
msgBus *bus.MessageBus, msgBus *bus.MessageBus,
registry *AgentRegistry, registry *AgentRegistry,
provider providers.LLMProvider, provider providers.LLMProvider,
al *AgentLoop,
) { ) {
for _, agentID := range registry.ListAgentIDs() { for _, agentID := range registry.ListAgentIDs() {
agent, ok := registry.GetAgent(agentID) agent, ok := registry.GetAgent(agentID)
@ -298,9 +299,6 @@ func registerSharedTools(
} }
} }
// Orchestration tools (spawn, subagent, answer, review_plan)
registerOrchestrationTools(cfg, agent, agentID, registry, provider, msgBus, al)
// Update context builder with the complete tools registry // Update context builder with the complete tools registry
agent.ContextBuilder.SetToolsRegistry(agent.Tools) agent.ContextBuilder.SetToolsRegistry(agent.Tools)
} }
@ -644,7 +642,8 @@ func (al *AgentLoop) ReloadProviderAndConfig(
} }
// Ensure shared tools are re-registered on the new registry // Ensure shared tools are re-registered on the new registry
registerSharedTools(cfg, al.bus, registry, provider, al) registerSharedTools(cfg, al.bus, registry, provider)
al.registerAllOrchestrationTools(cfg, registry, provider, al.bus)
// Atomically swap the config and registry under write lock // Atomically swap the config and registry under write lock
// This ensures readers see a consistent pair // This ensures readers see a consistent pair

View file

@ -107,6 +107,24 @@ func (al *AgentLoop) SetHeartbeatThreadUpdater(fn func(int)) {
al.onHeartbeatThreadUpdate = fn al.onHeartbeatThreadUpdate = fn
} }
// registerAllOrchestrationTools iterates all agents and registers orchestration
// tools for those with subagents enabled. Called from NewAgentLoop after
// registerSharedTools, keeping the upstream function signature clean.
func (al *AgentLoop) registerAllOrchestrationTools(
cfg *config.Config,
registry *AgentRegistry,
provider providers.LLMProvider,
msgBus *bus.MessageBus,
) {
for _, agentID := range registry.ListAgentIDs() {
agent, ok := registry.GetAgent(agentID)
if !ok {
continue
}
registerOrchestrationTools(cfg, agent, agentID, registry, provider, msgBus, al)
}
}
// registerOrchestrationTools registers spawn, subagent, answer, and review_plan // registerOrchestrationTools registers spawn, subagent, answer, and review_plan
// tools for agents with orchestration enabled. // tools for agents with orchestration enabled.
func registerOrchestrationTools( func registerOrchestrationTools(