返回 DeepSeek-Reasonix
sync.go
根目录 / internal / event / sync.go
1 package event
2
3 import (
4 "sync"
5
6 "reasonix/internal/evidence"
7 "reasonix/internal/nilutil"
8 )
9
10 // Sync wraps a Sink so concurrent Emit calls are serialized. The base Sink
11 // contract assumes serial emission — the agent's run loop emits one event at a
12 // time. Background jobs (internal/jobs) emit from their own goroutines, which can
13 // overlap a running turn's emission; wrapping the session sink once in Sync keeps
14 // the serial-Emit invariant every sink relies on (an SSE writer, a webview
15 // EventsEmit, a TUI channel) without each having to lock. A nil sink yields
16 // Discard.
17 func Sync(s Sink) Sink {
18 if nilutil.IsNil(s) {
19 return Discard
20 }
21 return &syncSink{inner: s}
22 }
23
24 type syncSink struct {
25 mu sync.Mutex
26 inner Sink
27 }
28
29 var _ OptionalSinkCapabilities = (*syncSink)(nil)
30
31 func (s *syncSink) Emit(e Event) {
32 s.mu.Lock()
33 defer s.mu.Unlock()
34 s.inner.Emit(e)
35 }
36
37 func (s *syncSink) RecordDelegationAudit(a evidence.DelegationAudit) {
38 s.mu.Lock()
39 defer s.mu.Unlock()
40 RecordDelegationAudit(s.inner, a)
41 }
42
43 func (s *syncSink) RecordReadinessAudit(a evidence.ReadinessAudit) {
44 s.mu.Lock()
45 defer s.mu.Unlock()
46 if rs, ok := s.inner.(ReadinessAuditSink); ok {
47 rs.RecordReadinessAudit(a)
48 }
49 }
50
51 func (s *syncSink) RecordAnchorSafetyAudit(a AnchorSafetyAudit) {
52 s.mu.Lock()
53 defer s.mu.Unlock()
54 RecordAnchorSafetyAudit(s.inner, a)
55 }
56
57 func (s *syncSink) RecordTurnCompletion() {
58 s.mu.Lock()
59 defer s.mu.Unlock()
60 if ts, ok := s.inner.(TurnCompletionSink); ok {
61 ts.RecordTurnCompletion()
62 }
63 }
64
65 func (s *syncSink) RecordProtocolRecovery(a ProtocolRecoveryAudit) {
66 s.mu.Lock()
67 defer s.mu.Unlock()
68 if rs, ok := s.inner.(ProtocolRecoveryAuditSink); ok {
69 rs.RecordProtocolRecovery(a)
70 }
71 }
72
73 func (s *syncSink) RecordContractShadow(a ContractShadowAudit) {
74 s.mu.Lock()
75 defer s.mu.Unlock()
76 if rs, ok := s.inner.(ContractShadowAuditSink); ok {
77 rs.RecordContractShadow(a)
78 }
79 }
80
81 func (s *syncSink) RecordCompletionReport(a CompletionReportAudit) {
82 s.mu.Lock()
83 defer s.mu.Unlock()
84 if rs, ok := s.inner.(CompletionReportAuditSink); ok {
85 rs.RecordCompletionReport(a)
86 }
87 }
88
89 func (s *syncSink) RecordOutcomeProgress(sample evidence.OutcomeSample) {
90 s.mu.Lock()
91 defer s.mu.Unlock()
92 if op, ok := s.inner.(OutcomeProgressSink); ok {
93 op.RecordOutcomeProgress(sample)
94 }
95 }
96
97 func (s *syncSink) RecordMemoryRecall(a MemoryRecallAudit) {
98 s.mu.Lock()
99 defer s.mu.Unlock()
100 if mr, ok := s.inner.(MemoryRecallSink); ok {
101 mr.RecordMemoryRecall(a)
102 }
103 }
104
105 func (s *syncSink) RecordDelegationAdmission(a DelegationAdmissionAudit) {
106 s.mu.Lock()
107 defer s.mu.Unlock()
108 if da, ok := s.inner.(DelegationAdmissionSink); ok {
109 da.RecordDelegationAdmission(a)
110 }
111 }
112
113 func (s *syncSink) RecordWorkspaceMutation(m WorkspaceMutation) {
114 s.mu.Lock()
115 defer s.mu.Unlock()
116 RecordWorkspaceMutation(s.inner, m)
117 }
118
119 func (s *syncSink) RecordRunBudget(sample RunBudgetSample) {
120 s.mu.Lock()
121 defer s.mu.Unlock()
122 RecordRunBudget(s.inner, sample)
123 }
124
125 func (s *syncSink) RecordSubagentLifecycle(info SubagentLifecycleInfo) {
126 s.mu.Lock()
127 defer s.mu.Unlock()
128 RecordSubagentLifecycle(s.inner, info)
129 }
130
130 lines GO