diff --git a/pkg/agent/loop.go b/pkg/agent/loop.go index 36b4d52..dc9b883 100644 --- a/pkg/agent/loop.go +++ b/pkg/agent/loop.go @@ -75,10 +75,8 @@ func run(ctx context.Context, cfg agentConfig, state state) error { for { // Proactive context gate: strip cold material before calling the backend. - if s, ok := state.(*Session); ok { - if budget := contextBudget(ctx, cfg.Backend); budget > 0 && s.estimateTokens() > budget { - s.stripColdResults() - } + if budget := contextBudget(ctx, cfg.Backend); budget > 0 && state.estimateTokens() > budget { + state.stripCold() } sc := StepContext{ diff --git a/pkg/agent/loop_maxsteps_helpers_test.go b/pkg/agent/loop_maxsteps_helpers_test.go index a53481f..88d996b 100644 --- a/pkg/agent/loop_maxsteps_helpers_test.go +++ b/pkg/agent/loop_maxsteps_helpers_test.go @@ -69,3 +69,13 @@ func (s *simpleState) update(msg backend.Message, results []toolResult) { }) } } + +func (s *simpleState) estimateTokens() int { + chars := 0 + for _, m := range s.msgs { + chars += len(m.Content) + } + return chars / 4 +} + +func (s *simpleState) stripCold() {} diff --git a/pkg/agent/session.go b/pkg/agent/session.go index ee77fef..780d2c5 100644 --- a/pkg/agent/session.go +++ b/pkg/agent/session.go @@ -531,6 +531,9 @@ func (s *Session) stripColdResults() { } } +// stripCold implements the state interface. +func (s *Session) stripCold() { s.stripColdResults() } + // contextDebug returns a multi-line breakdown of the history. func (s *Session) contextDebug() string { var sb strings.Builder diff --git a/pkg/agent/state.go b/pkg/agent/state.go index 33148d5..f3be429 100644 --- a/pkg/agent/state.go +++ b/pkg/agent/state.go @@ -40,4 +40,9 @@ type state interface { // Called once per loop iteration after Act completes. update(assistant backend.Message, results []toolResult) + // estimateTokens returns a rough token count of the current history. + estimateTokens() int + + // stripCold collapses large tool-result messages in-place to reduce context size. + stripCold() }