返回 DeepSeek-Reasonix
session_persist_observer.go
根目录 / internal / agent / session_persist_observer.go
1 package agent
2
3 import (
4 "os"
5
6 "reasonix/internal/store"
7 )
8
9 // SessionPersistEvent is emitted only after the authoritative transcript,
10 // event log, metadata ledger, and derived display read model have completed
11 // their save boundary. Observers must only enqueue work and return immediately.
12 type SessionPersistEvent struct {
13 Path string
14 Revision int64
15 ContentDigest string
16 MessageCount int
17 AppendFrom int
18 Rewrite bool
19 Removed bool
20 }
21
22 type SessionPersistObserver interface {
23 EnqueueSessionPersist(SessionPersistEvent) bool
24 }
25
26 func (s *Session) SetPersistObserver(observer SessionPersistObserver) {
27 if s == nil {
28 return
29 }
30 s.mu.Lock()
31 s.persistObserver = observer
32 s.mu.Unlock()
33 }
34
35 func (s *Session) notifyPersisted(path string, rewrite bool, appendFrom int) {
36 if s == nil {
37 return
38 }
39 s.mu.RLock()
40 observer := s.persistObserver
41 messageCount := len(s.Messages)
42 s.mu.RUnlock()
43 if observer == nil {
44 return
45 }
46 state, known := s.PersistedState(path)
47 event := SessionPersistEvent{Path: path, MessageCount: messageCount, AppendFrom: appendFrom, Rewrite: rewrite}
48 if known {
49 event.Revision = state.Revision
50 event.ContentDigest = state.DigestHex
51 }
52 observer.EnqueueSessionPersist(event)
53 }
54
55 // SaveIfAbsent persists a newly imported or copied session without replacing a
56 // destination another runtime created after the caller's scan. It runs under
57 // an ephemeral session lease: the destination is lease-protected for the
58 // duration of the save, so an import or fork never races another runtime's
59 // writer even before the artifact-existence check.
60 func (s *Session) SaveIfAbsent(path string) error {
61 err := s.SaveWithEphemeralWriter(path, func(target string) error {
62 return s.withSessionSaveLocks(target, func() error {
63 if sessionArtifactExists(target) {
64 return os.ErrExist
65 }
66 return s.saveLocked(target, sessionSaveSnapshot)
67 })
68 })
69 if err == nil {
70 s.notifyPersisted(path, false, -1)
71 }
72 return err
73 }
74
75 func (s *Session) saveObserved(path string, mode sessionSaveMode) error {
76 appendFrom := -1
77 if mode == sessionSaveSnapshot {
78 if index, err := LoadSessionDisplayIndex(store.SessionDisplayIndex(path)); err == nil {
79 appendFrom = index.MessageCount
80 }
81 }
82 err := s.save(path, mode)
83 if err == nil && !mode.defersProjection() {
84 s.notifyPersisted(path, mode != sessionSaveSnapshot, appendFrom)
85 }
86 return err
87 }
88
88 lines GO