Files
cursor-byok/internal/backend/forwarder/checkpoint_memory.go
2026-06-30 10:38:52 +08:00

148 lines
4.3 KiB
Go

package forwarder
import (
"fmt"
"strings"
"time"
runtimecore "cursor/internal/backend/agent/core"
)
func (service *Service) replaceCheckpointConversation(stream *ActiveStream, conversation *ConversationFile) error {
if stream == nil {
return fmt.Errorf("active stream is required")
}
if conversation == nil {
return fmt.Errorf("checkpoint conversation is required")
}
stream.mu.Lock()
stream.CheckpointConversation = cloneConversationFile(conversation)
stream.mu.Unlock()
return nil
}
func checkpointConversationInitialized(stream *ActiveStream) bool {
if stream == nil {
return false
}
stream.mu.Lock()
defer stream.mu.Unlock()
return stream.CheckpointConversation != nil
}
func (service *Service) appendCheckpointEntries(stream *ActiveStream, entries []HistoryEntry) error {
if len(entries) == 0 {
return nil
}
if stream == nil {
return fmt.Errorf("active stream is required")
}
stream.mu.Lock()
defer stream.mu.Unlock()
if stream.CheckpointConversation == nil {
return fmt.Errorf("checkpoint conversation is not initialized")
}
appendEntriesInPlace(stream.CheckpointConversation, entries)
return nil
}
func (service *Service) snapshotCheckpointConversation(stream *ActiveStream) (*ConversationFile, []runtimecore.PendingExec, []runtimecore.PendingInteraction, error) {
if stream == nil {
return nil, nil, nil, fmt.Errorf("active stream is required")
}
stream.mu.Lock()
defer stream.mu.Unlock()
if stream.CheckpointConversation == nil {
return nil, nil, nil, fmt.Errorf("checkpoint conversation is not initialized")
}
conversation := cloneConversationFile(stream.CheckpointConversation)
pendingExecs := make([]runtimecore.PendingExec, 0, len(stream.PendingExecs))
for _, pending := range stream.PendingExecs {
pendingExecs = append(pendingExecs, pending)
}
pendingInteractions := make([]runtimecore.PendingInteraction, 0, len(stream.PendingInteractions))
for _, pending := range stream.PendingInteractions {
pendingInteractions = append(pendingInteractions, pending)
}
return conversation, pendingExecs, pendingInteractions, nil
}
func (service *Service) appendConversationEntries(stream *ActiveStream, conversationID string, entries []HistoryEntry) ([]HistoryEntry, error) {
if len(entries) == 0 {
return nil, nil
}
if stream == nil {
return nil, fmt.Errorf("active stream is required")
}
stream.mu.Lock()
if stream.CheckpointConversation == nil {
stream.mu.Unlock()
return nil, fmt.Errorf("checkpoint conversation is not initialized")
}
working := cloneConversationFile(stream.CheckpointConversation)
if service.store != nil {
persisted, assigned, err := service.store.AppendEntries(conversationID, resetEntrySequences(entries))
if err != nil {
stream.mu.Unlock()
return nil, err
}
if persisted != nil {
working = persisted
} else {
appendEntriesInPlace(working, assigned)
}
stream.CheckpointConversation = working
stream.UpdatedAt = time.Now().UTC()
stream.mu.Unlock()
return assigned, nil
}
assigned := appendEntriesInPlace(working, entries)
stream.CheckpointConversation = working
stream.UpdatedAt = time.Now().UTC()
stream.mu.Unlock()
return assigned, nil
}
func resetEntrySequences(entries []HistoryEntry) []HistoryEntry {
if len(entries) == 0 {
return nil
}
reset := make([]HistoryEntry, 0, len(entries))
for _, entry := range entries {
next := entry
next.Seq = 0
reset = append(reset, next)
}
return reset
}
func firstWorkspacePath(paths []string) string {
for _, path := range paths {
if strings.TrimSpace(path) != "" {
return strings.TrimSpace(path)
}
}
return ""
}
func (service *Service) updateConversationMetaAndCheckpoint(stream *ActiveStream, conversationID string, update func(*ConversationFile) error) (*ConversationFile, error) {
if stream == nil {
return nil, fmt.Errorf("active stream is required")
}
stream.mu.Lock()
defer stream.mu.Unlock()
if stream.CheckpointConversation == nil {
return nil, fmt.Errorf("checkpoint conversation is not initialized")
}
conversation := cloneConversationFile(stream.CheckpointConversation)
if err := update(conversation); err != nil {
return nil, err
}
if err := service.syncConversationRecord(conversationID, conversation); err != nil {
return nil, err
}
stream.CheckpointConversation = conversation
stream.UpdatedAt = time.Now().UTC()
return cloneConversationFile(conversation), nil
}