返回 DeepSeek-Reasonix
transcript_metadata.go
根目录 / internal / session / transcript_metadata.go
1 package session
2
3 import (
4 "encoding/json"
5 "reasonix/internal/provider"
6 )
7
8 // Transcript metadata retains only input identities and bounded previews, never
9 // assistant/tool bodies. It remains available when display history is external.
10 type transcriptInput struct {
11 ID string
12 TurnID string
13 Preview string
14 }
15
16 func applyTranscriptMetadata(p *Projection, commit Commit, ev Event) {
17 switch ev.Kind {
18 case "history/replace", "legacy/import":
19 p.TranscriptInputs = nil
20 p.HiddenTurns = nil
21 p.RetractedInputs = nil
22 messages, err := replacementEventMessages(ev, ev.Payload)
23 if err != nil {
24 return
25 }
26 for _, m := range messages {
27 noteTranscriptInput(p, "", m)
28 }
29 case "message/complete", "message/upsert":
30 var body struct {
31 Message provider.Message `json:"message"`
32 }
33 if json.Unmarshal(ev.Payload, &body) != nil {
34 return
35 }
36 if body.Message.LocalOnly || body.Message.Role != provider.RoleAssistant {
37 for i := range p.Turns {
38 if p.Turns[i].MessageID == body.Message.ID {
39 p.Turns[i].MessageID = ""
40 }
41 }
42 }
43 noteTranscriptInput(p, commit.TurnID, body.Message)
44 case "message/retract":
45 ids, err := retractedMessageIDs(ev, ev.Payload)
46 if err != nil {
47 return
48 }
49 for _, id := range ids {
50 for i := 0; i < len(p.TranscriptInputs); i++ {
51 input := p.TranscriptInputs[i]
52 if input.ID != id {
53 continue
54 }
55 if input.TurnID != "" {
56 if p.RetractedInputs == nil {
57 p.RetractedInputs = map[string]string{}
58 }
59 p.RetractedInputs[id] = input.TurnID
60 if p.HiddenTurns == nil {
61 p.HiddenTurns = map[string]bool{}
62 }
63 p.HiddenTurns[input.TurnID] = true
64 }
65 p.TranscriptInputs = append(p.TranscriptInputs[:i], p.TranscriptInputs[i+1:]...)
66 break
67 }
68 for i := range p.Turns {
69 if p.Turns[i].MessageID == id {
70 p.Turns[i].MessageID = ""
71 }
72 }
73 }
74 }
75 }
76
77 func noteTranscriptInput(p *Projection, turnID string, m provider.Message) {
78 // A repair may restore an identity outside any active turn. Retain the
79 // original ownership across retraction/checkpoints rather than attaching
80 // it to the repair's empty (or unrelated) turn.
81 if original, ok := p.RetractedInputs[m.ID]; ok && m.Role == provider.RoleUser {
82 turnID = original
83 delete(p.RetractedInputs, m.ID)
84 }
85 for i := range p.TranscriptInputs {
86 if p.TranscriptInputs[i].ID == m.ID {
87 p.TranscriptInputs[i].Preview = catalogMessagePreview(m)
88 return
89 }
90 }
91 if m.Role != provider.RoleUser {
92 return
93 }
94 p.TranscriptInputs = append(p.TranscriptInputs, transcriptInput{ID: m.ID, TurnID: turnID, Preview: catalogMessagePreview(m)})
95 delete(p.HiddenTurns, turnID)
96 }
97
98 func visibleBoundaryCount(p Projection, endedOnly bool) int {
99 count := 0
100 for _, t := range p.Turns {
101 if !p.HiddenTurns[t.TurnID] && (!endedOnly || t.EndSequence != 0) {
102 count++
103 }
104 }
105 return count
106 }
107
107 lines GO