Files
Agent-Native/runtime/session/jsonl.go
T
weichenandCursor 6bc1b08540 Record sessions as an append-only entry tree
Why: Linear transcripts cannot fork, and compaction rewrote history, so resume and branch navigation had nothing to point at.
Evidence: go test -count=1 ./runtime/session/
Impact: Load still returns the model-visible path, including old one-message-per-line files. Compaction appends a summary entry instead of truncating the file.
Refs: feature/pi-headless N1
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-01 10:08:04 +00:00

55 lines
1.4 KiB
Go

package session
import (
"os"
"path/filepath"
"sync"
"agent-native/runtime/llm"
)
// Store is an append-only JSONL transcript. New sessions are a tree of
// entries (id / parentId). Load still returns the model-visible message
// path, and files written as one llm.Message per line remain readable.
type Store struct {
path string
mu sync.Mutex
}
func Open(path string) (*Store, error) {
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
return nil, err
}
f, err := os.OpenFile(path, os.O_CREATE, 0o644)
if err != nil {
return nil, err
}
_ = f.Close()
return &Store{path: path}, nil
}
func (s *Store) Path() string { return s.path }
// Append records one message on the active branch.
func (s *Store) Append(msg llm.Message) error {
s.mu.Lock()
defer s.mu.Unlock()
_, err := s.appendEntryLocked(Entry{Type: TypeMessage, Message: &msg})
return err
}
// Load returns the model-visible messages on the active branch.
// A compaction entry replaces earlier turns with its summary plus the
// retained tail; the file itself is never rewritten.
// Crash residue: a trailing line without a newline that is not valid JSON
// is dropped. A bad line in the middle is still an error.
func (s *Store) Load() ([]llm.Message, error) {
s.mu.Lock()
defer s.mu.Unlock()
entries, err := s.readEntriesLocked()
if err != nil {
return nil, err
}
return visibleMessages(entries, s.leafLocked(entries)), nil
}