返回 DeepSeek-Reasonix
session_listing_projection_update.go
根目录 / internal / agent / session_listing_projection_update.go
1 package agent
2
3 import (
4 "fmt"
5 "strings"
6 )
7
8 // UpdateSessionListingProjectionIfCurrent publishes counts decoded from one
9 // persisted transcript generation together with the runtime's acknowledged
10 // model identity. It rechecks both the transcript digest and its sidecar
11 // identity while holding the save lock, so an autosave that landed after the
12 // caller's decode cannot receive the stale projection.
13 func UpdateSessionListingProjectionIfCurrent(sessionPath, model, identity, preview string, turns int, markActivity bool, expected PersistedState) (bool, error) {
14 return updateSessionListingProjectionIfCurrent(sessionPath, model, identity, preview, turns, markActivity, expected, nil, false)
15 }
16
17 // UpdateOwnedSessionListingProjectionIfCurrent also fences the runtime that
18 // saved the transcript. Model-only changes need not change its digest, so the
19 // transcript CAS alone cannot reject a retired runtime's delayed publication.
20 func UpdateOwnedSessionListingProjectionIfCurrent(sessionPath, model, identity, preview string, turns int, markActivity bool, expected PersistedState, authority *SessionWriteAuthority) (bool, error) {
21 return updateSessionListingProjectionIfCurrent(sessionPath, model, identity, preview, turns, markActivity, expected, authority, true)
22 }
23
24 func updateSessionListingProjectionIfCurrent(sessionPath, model, identity, preview string, turns int, markActivity bool, expected PersistedState, authority *SessionWriteAuthority, requireAuthority bool) (bool, error) {
25 if strings.TrimSpace(sessionPath) == "" {
26 return false, fmt.Errorf("empty session path")
27 }
28 unlockSave := lockSessionSavePath(sessionPath)
29 defer unlockSave()
30 unlockFile, err := lockSessionFile(sessionPath)
31 if err != nil {
32 return false, fmt.Errorf("lock session file: %w", err)
33 }
34 defer unlockFile()
35 _, current, _, err := loadSessionDisplayMessagesUnlocked(sessionPath)
36 if err != nil {
37 return false, err
38 }
39 if current.DigestHex != expected.DigestHex || current.RevisionKnown != expected.RevisionKnown ||
40 current.RevisionKnown && current.Revision != expected.Revision {
41 return false, nil
42 }
43
44 unlockMeta, err := LockSessionMetaPath(sessionPath)
45 if err != nil {
46 return false, err
47 }
48 defer unlockMeta()
49 if requireAuthority {
50 unlockAuthority, err := authority.lockCurrentLease(sessionPath)
51 if err != nil {
52 return false, err
53 }
54 defer unlockAuthority()
55 }
56 meta, err := ensureBranchMetaUnlocked(sessionPath)
57 if err != nil {
58 return false, err
59 }
60 digest := strings.TrimSpace(meta.ContentDigest)
61 if expected.RevisionKnown {
62 if meta.Revision != expected.Revision || digest != expected.DigestHex {
63 return false, nil
64 }
65 } else if meta.Revision != 0 || digest != "" {
66 return false, nil
67 }
68 if strings.TrimSpace(model) != "" {
69 setMetaModelSelection(&meta, model, &identity)
70 }
71 meta.Preview = preview
72 meta.Turns = turns
73 meta.SchemaVersion = BranchMetaCountsVersion
74 stampSessionListingProjection(&meta)
75 if err := saveBranchMeta(sessionPath, meta, markActivity); err != nil {
76 return false, err
77 }
78 return true, nil
79 }
80
80 lines GO