diff --git a/pkg/agent/context.go b/pkg/agent/context.go index da5b49915..69dba7cae 100644 --- a/pkg/agent/context.go +++ b/pkg/agent/context.go @@ -22,12 +22,12 @@ import ( ) type ContextBuilder struct { - workspace string - skillsLoader *skills.SkillsLoader - memory *MemoryStore - splitOnMarker bool - skillCatalogCfg config.SkillCatalogConfig - promptRegistry *PromptRegistry + workspace string + skillsLoader *skills.SkillsLoader + memory *MemoryStore + splitOnMarker bool + skillCatalogCfg config.SkillCatalogConfig + promptRegistry *PromptRegistry // Cache for system prompt to avoid rebuilding on every call. // This fixes issue #607: repeated reprocessing of the entire context. @@ -208,7 +208,6 @@ func (cb *ContextBuilder) BuildSystemPromptParts() []PromptPart { }) } - // Memory context memoryContext := cb.memory.GetMemoryContext() if memoryContext != "" { @@ -306,7 +305,7 @@ func (cb *ContextBuilder) EstimateSystemTokens(summary string, activeSkills []st // (EstimateSystemTokens assumes a non-continuation turn). if skillsSummary := cb.skillsLoader.BuildSkillsSummary(); skillsSummary != "" { totalChars += utf8.RuneCountInString(skillsSummary) + 80 // header overhead - totalChars += 7 // separator + totalChars += 7 // separator } if skillsText := cb.buildActiveSkillsContext(activeSkills); skillsText != "" { @@ -697,23 +696,24 @@ func (cb *ContextBuilder) BuildMessagesFromPrompt(req PromptBuildRequest) []prov } // Determine whether to inject the skill catalog. - // Both skip behaviours are opt-in via config (default: always include). + // Both skip behaviors are opt-in via config (default: always include). isToolContinuation := len(req.History) > 0 && req.History[len(req.History)-1].Role == "tool" isFirstTurn := len(req.History) == 0 isAfterCompaction := req.Summary != "" skipForTools := cb.skillCatalogCfg.SkipOnTools && isToolContinuation - skipForSubsequent := cb.skillCatalogCfg.SkipOnSubsequent && !isFirstTurn && !isAfterCompaction && !isToolContinuation + skipForSubsequent := cb.skillCatalogCfg.SkipOnSubsequent && + !isFirstTurn && !isAfterCompaction && !isToolContinuation if !skipForTools && !skipForSubsequent { if skillsSummary := cb.skillsLoader.BuildSkillsSummary(); skillsSummary != "" { catalogPart := PromptPart{ - ID: "capability.skill_catalog", - Layer: PromptLayerCapability, - Slot: PromptSlotSkillCatalog, - Source: PromptSource{ID: PromptSourceSkillCatalog, Name: "skill:index"}, - Title: "skill catalog", + ID: "capability.skill_catalog", + Layer: PromptLayerCapability, + Slot: PromptSlotSkillCatalog, + Source: PromptSource{ID: PromptSourceSkillCatalog, Name: "skill:index"}, + Title: "skill catalog", Content: fmt.Sprintf("# Skills\n\nThe following skills extend your capabilities. To use a skill, read its SKILL.md file using the read_file tool.\n\n%s", skillsSummary), - Stable: true, - Cache: PromptCacheEphemeral, + Stable: true, + Cache: PromptCacheEphemeral, } stringParts = append(stringParts, catalogPart.Content) contentBlocks = append(contentBlocks, promptContentBlock(catalogPart, &providers.CacheControl{Type: "ephemeral"})) diff --git a/pkg/agent/context_cache_test.go b/pkg/agent/context_cache_test.go index c561a13d5..c54422d60 100644 --- a/pkg/agent/context_cache_test.go +++ b/pkg/agent/context_cache_test.go @@ -680,8 +680,8 @@ func TestSkillCatalogInjectionPolicy(t *testing.T) { t.Error("turn > 1, no summary: catalog should be skipped") } if !contains(cb.BuildMessagesFromPrompt(PromptBuildRequest{ - History: []providers.Message{userMsg, assistantMsg}, - Summary: "prior conversation summary", + History: []providers.Message{userMsg, assistantMsg}, + Summary: "prior conversation summary", })) { t.Error("after compaction: catalog should be re-injected") } diff --git a/pkg/config/config.go b/pkg/config/config.go index 8305a6914..6ebdc533b 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -285,9 +285,9 @@ type AgentDefaults struct { SteeringMode string `json:"steering_mode,omitempty" env:"PICOCLAW_AGENTS_DEFAULTS_STEERING_MODE"` // "one-at-a-time" (default) or "all" MaxParallelTurns int `json:"max_parallel_turns,omitempty" env:"PICOCLAW_AGENTS_DEFAULTS_MAX_PARALLEL_TURNS"` // Max concurrent turns (0 or 1 = sequential) SubTurn SubTurnConfig `json:"subturn" envPrefix:"PICOCLAW_AGENTS_DEFAULTS_SUBTURN_"` - ToolFeedback ToolFeedbackConfig `json:"tool_feedback,omitempty"` - SplitOnMarker bool `json:"split_on_marker" env:"PICOCLAW_AGENTS_DEFAULTS_SPLIT_ON_MARKER"` // split messages on <|[SPLIT]|> marker - SkillCatalog SkillCatalogConfig `json:"skill_catalog,omitempty"` + ToolFeedback ToolFeedbackConfig `json:"tool_feedback,omitempty"` + SplitOnMarker bool `json:"split_on_marker" env:"PICOCLAW_AGENTS_DEFAULTS_SPLIT_ON_MARKER"` // split messages on <|[SPLIT]|> marker + SkillCatalog SkillCatalogConfig `json:"skill_catalog,omitempty"` ContextManager string `json:"context_manager,omitempty" env:"PICOCLAW_AGENTS_DEFAULTS_CONTEXT_MANAGER"` ContextManagerConfig json.RawMessage `json:"context_manager_config,omitempty" env:"PICOCLAW_AGENTS_DEFAULTS_CONTEXT_MANAGER_CONFIG"` MaxLLMRetries int `json:"max_llm_retries,omitempty" env:"PICOCLAW_AGENTS_DEFAULTS_MAX_LLM_RETRIES"`