Keep tool pairs and fold reminders into the last result
Why: Old tool results were filling the window, compaction could split a tool group or commit a stale summary, and reminders arrived as extra user messages. Evidence: Compaction retries a conflict and opens the circuit after three failures; clearing keeps the last five tool results; a stale file write is rejected; RPC reports started, queued, or handled. Impact: Requests over the context window replace older tool results with a placeholder. Rewriting an existing file now requires a read in the same run. Subagent text over 32000 characters is capped. Refs: feature/context-engineering Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -67,15 +67,35 @@ func (s *Store) ReadMeta() map[string]string {
|
||||
return meta
|
||||
}
|
||||
|
||||
const maxCompactAttempts = 3
|
||||
|
||||
// Compact appends a compaction entry. The file keeps every earlier record.
|
||||
// Load hides the summarized prefix behind the summary plus the last
|
||||
// keepLastN non-system messages. Returns false when there is nothing to fold.
|
||||
// A session change during summary is retried from the new transcript.
|
||||
// Three consecutive summary failures or conflicts trip the breaker and
|
||||
// leave the file unchanged.
|
||||
func (s *Store) Compact(keepLastN int, summarize func(messages []llm.Message) (string, error)) (bool, error) {
|
||||
var last error
|
||||
for attempt := 0; attempt < maxCompactAttempts; attempt++ {
|
||||
done, err, retry := s.compactOnce(keepLastN, summarize)
|
||||
if !retry {
|
||||
return done, err
|
||||
}
|
||||
last = err
|
||||
}
|
||||
if last == nil {
|
||||
last = fmt.Errorf("compaction conflict: session changed during summary")
|
||||
}
|
||||
return false, fmt.Errorf("compaction circuit open: %w", last)
|
||||
}
|
||||
|
||||
func (s *Store) compactOnce(keepLastN int, summarize func(messages []llm.Message) (string, error)) (bool, error, bool) {
|
||||
s.mu.Lock()
|
||||
entries, err := s.readEntriesLocked()
|
||||
if err != nil {
|
||||
s.mu.Unlock()
|
||||
return false, err
|
||||
return false, err, false
|
||||
}
|
||||
leaf := s.leafLocked(entries)
|
||||
generation := len(entries)
|
||||
@@ -90,7 +110,7 @@ func (s *Store) Compact(keepLastN int, summarize func(messages []llm.Message) (s
|
||||
}
|
||||
}
|
||||
if len(messages) <= keepLastN+1 {
|
||||
return false, nil
|
||||
return false, nil, false
|
||||
}
|
||||
rest := messages
|
||||
restEntries := msgEntries
|
||||
@@ -100,13 +120,13 @@ func (s *Store) Compact(keepLastN int, summarize func(messages []llm.Message) (s
|
||||
}
|
||||
tailStart := len(rest) - keepLastN
|
||||
if tailStart <= 0 {
|
||||
return false, nil
|
||||
return false, nil, false
|
||||
}
|
||||
tailStart = alignToolTail(rest, tailStart)
|
||||
head := rest[:tailStart]
|
||||
summary, err := summarize(head)
|
||||
if err != nil {
|
||||
return false, err
|
||||
return false, err, true
|
||||
}
|
||||
var tailIDs []string
|
||||
for _, e := range restEntries[tailStart:] {
|
||||
@@ -116,13 +136,13 @@ func (s *Store) Compact(keepLastN int, summarize func(messages []llm.Message) (s
|
||||
defer s.mu.Unlock()
|
||||
now, err := s.readEntriesLocked()
|
||||
if err != nil {
|
||||
return false, err
|
||||
return false, err, false
|
||||
}
|
||||
if len(now) != generation {
|
||||
return false, fmt.Errorf("compaction conflict: session changed during summary")
|
||||
return false, fmt.Errorf("compaction conflict: session changed during summary"), true
|
||||
}
|
||||
_, err = s.appendEntryLocked(Entry{Type: TypeCompaction, Summary: summary, TailIDs: tailIDs})
|
||||
return err == nil, err
|
||||
return err == nil, err, false
|
||||
}
|
||||
|
||||
// alignToolTail 让保留尾巴从一条非 tool 消息开始,避免把 tool_calls 和结果拆开。
|
||||
|
||||
Reference in New Issue
Block a user