diff --git a/internal/agent/pruning.go b/internal/agent/pruning.go index fbfddd53..6cec5e05 100644 --- a/internal/agent/pruning.go +++ b/internal/agent/pruning.go @@ -14,8 +14,9 @@ import ( // Context pruning defaults matching TS DEFAULT_CONTEXT_PRUNING_SETTINGS. const ( defaultKeepLastAssistants = 3 - defaultSoftTrimRatio = 0.25 + defaultSoftTrimRatio = 0.3 defaultHardClearRatio = 0.5 + defaultPruningMode = "cache-ttl" defaultMinPrunableToolChars = 50000 defaultSoftTrimMaxChars = 6000 defaultSoftTrimHeadChars = 3000 @@ -172,12 +173,16 @@ func resolvePruningSettings(cfg *config.ContextPruningConfig) *effectivePruningS // for non-ASCII content like Vietnamese/Chinese). When nil, falls back to the // legacy rune_count/charsPerTokenEstimate heuristic so existing tests pass. func pruneContextMessages(msgs []providers.Message, contextWindowTokens int, cfg *config.ContextPruningConfig, tc tokencount.TokenCounter, model string, stats *pipeline.PruneStats) []providers.Message { - // Opt-in: require explicit mode. Matches TS computeEffectiveSettings in settings.ts. - if cfg == nil || cfg.Mode == "" || cfg.Mode == "off" { + // Resolve effective mode: empty defaults to "cache-ttl" (enabled by default). + mode := defaultPruningMode + if cfg != nil && cfg.Mode != "" { + mode = cfg.Mode + } + if mode == "off" { return msgs } - if cfg.Mode != "cache-ttl" { - slog.Warn("context_pruning: unknown mode, disabled", "mode", cfg.Mode) + if mode != "cache-ttl" { + slog.Warn("context_pruning: unknown mode, disabled", "mode", mode) return msgs } if contextWindowTokens <= 0 || len(msgs) == 0 { diff --git a/internal/agent/pruning_test.go b/internal/agent/pruning_test.go index e98d5d8a..714babf7 100644 --- a/internal/agent/pruning_test.go +++ b/internal/agent/pruning_test.go @@ -448,8 +448,8 @@ func TestHasImportantTail_ShortContentNoMatch(t *testing.T) { // Lock current (buggy-vs-TS) behavior before refactoring. Subsequent phases // update these tests to new expected values. -// makeLargeHistoryFixture creates a history with a large tool result for opt-in tests. -// contextWindow=5000 → charWindow=20000, total ~8030 chars → ratio ~40% ≥ 25%. +// makeLargeHistoryFixture creates a history with a large tool result for default-enabled tests. +// contextWindow=5000 → charWindow=20000, total ~8030 chars → ratio ~40% ≥ 30% (softTrimRatio). func makeLargeHistoryFixture() []providers.Message { return []providers.Message{ {Role: "user", Content: "q"}, @@ -461,21 +461,25 @@ func makeLargeHistoryFixture() []providers.Message { } } -func TestPruneContextMessages_NilCfg_NoOp(t *testing.T) { - // Phase 04: nil cfg → no pruning (opt-in). Matches TS design. +func TestPruneContextMessages_NilCfg_DefaultEnabled(t *testing.T) { + // nil cfg defaults to "cache-ttl" mode (enabled by default). + // Tool result 8000 chars > 6000 threshold → trimmed to ~6000. msgs := makeLargeHistoryFixture() got := pruneContextMessages(msgs, 5000, nil, nil, "", nil) - if estimateMessageChars(got[2]) != 8000 { - t.Errorf("nil cfg should be no-op (opt-in). Got %d chars", estimateMessageChars(got[2])) + chars := estimateMessageChars(got[2]) + if chars >= 8000 { + t.Errorf("nil cfg should enable pruning (default cache-ttl). Got %d chars, expected < 8000", chars) } } -func TestPruneContextMessages_EmptyMode_NoOp(t *testing.T) { +func TestPruneContextMessages_EmptyMode_DefaultEnabled(t *testing.T) { + // Empty mode defaults to "cache-ttl" (enabled by default). cfg := &config.ContextPruningConfig{Mode: ""} msgs := makeLargeHistoryFixture() got := pruneContextMessages(msgs, 5000, cfg, nil, "", nil) - if estimateMessageChars(got[2]) != 8000 { - t.Errorf("empty mode should be no-op. Got %d chars", estimateMessageChars(got[2])) + chars := estimateMessageChars(got[2]) + if chars >= 8000 { + t.Errorf("empty mode should enable pruning (default cache-ttl). Got %d chars, expected < 8000", chars) } }