agent/loop: send full compacted history instead of recent-only tail

After compaction, the loop was discarding cold+warm zones and only
sending the last 8 messages to the backend. This caused the agent to
lose context after manual or auto compaction.

Remove the recentHistory path entirely: state.history() already
returns the compacted (small) history post-compact. The TaskState
render block remains as additional steering injected after the system
prompt.

Removed: StepContext struct, recentHistory from state interface and
Session.
This commit is contained in:
Levi Neely 2026-05-23 13:16:41 +02:00
parent 9d6800a51c
commit 19877f0a06
4 changed files with 5 additions and 40 deletions

View File

@ -81,36 +81,18 @@ func run(ctx context.Context, cfg agentConfig, state state) error {
// sync.Map is required because execOne may run in concurrent goroutines.
var resultCache sync.Map
// stepCtx carries per-turn context assembled once at the top of each iteration.
type StepContext struct {
Goal string
TaskState *TaskState
Recent []backend.Message // last N messages (hot window)
}
for {
// Proactive context gate: strip cold material before calling the backend.
if budget := contextBudget(ctx, cfg.Backend); budget > 0 && state.estimateTokens() > budget {
state.stripCold()
}
sc := StepContext{
Goal: cfg.preamble,
TaskState: state.taskState(),
Recent: state.recentHistory(hotTailSize),
history := state.history()
if cfg.preamble != "" {
history = append([]backend.Message{{Role: "system", Content: cfg.preamble}}, history...)
}
var history []backend.Message
if sc.TaskState != nil {
history = sc.Recent
} else {
history = state.history()
}
if sc.Goal != "" {
history = append([]backend.Message{{Role: "system", Content: sc.Goal}}, history...)
}
if sc.TaskState != nil {
if msg := sc.TaskState.render(); msg != "" && len(history) > 0 {
if ts := state.taskState(); ts != nil {
if msg := ts.render(); msg != "" && len(history) > 0 {
// Insert after system prompt, before conversation.
history = append([]backend.Message{history[0], {Role: "user", Content: msg}}, history[1:]...)
}

View File

@ -48,13 +48,6 @@ func (s *simpleState) history() []backend.Message {
return s.msgs
}
func (s *simpleState) recentHistory(n int) []backend.Message {
if n >= len(s.msgs) {
return s.msgs
}
return s.msgs[len(s.msgs)-n:]
}
func (s *simpleState) taskState() *TaskState { return nil }
func (s *simpleState) updateTaskState(TaskState) {}

View File

@ -158,13 +158,6 @@ func (s *Session) history() []backend.Message {
return s.messages
}
func (s *Session) recentHistory(n int) []backend.Message {
if n >= len(s.messages) {
return s.messages
}
return s.messages[len(s.messages)-n:]
}
func (s *Session) taskState() *TaskState {
return s.TaskState
}

View File

@ -27,9 +27,6 @@ type state interface {
// The first entry is always the initial user message containing the goal.
history() []backend.Message
// recentHistory returns the last n messages from the history.
recentHistory(n int) []backend.Message
// taskState returns the current structured task state, or nil if not set.
taskState() *TaskState