l-ai-chat-client-go/ack_tracker.go
Kaien 9ac1206f2e Add reusable live chat Go SDK
Provide a standalone agent client for l-ai-chat with service-token auth, safe at-least-once subscriptions, ordered cumulative ACK tracking, and self-contained gRPC fake tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-09 21:17:31 +03:00

68 lines
1.3 KiB
Go

package chatclient
type deliveryToken struct {
_ byte
}
type ackEntry struct {
seq int64
token *deliveryToken
done bool
}
type ackTracker struct {
entries []ackEntry
index map[*deliveryToken]int
}
func newACKTracker() ackTracker {
return ackTracker{index: make(map[*deliveryToken]int)}
}
func (t *ackTracker) add(seq int64, token *deliveryToken) {
t.index[token] = len(t.entries)
t.entries = append(t.entries, ackEntry{seq: seq, token: token})
}
func (t *ackTracker) mark(token *deliveryToken) (int64, bool, bool) {
i, ok := t.index[token]
if !ok {
return 0, false, false
}
if t.entries[i].done {
return t.safeBoundary(), true, true
}
t.entries[i].done = true
return t.safeBoundary(), true, true
}
func (t *ackTracker) safeBoundary() int64 {
var boundary int64
for i := range t.entries {
if !t.entries[i].done {
break
}
boundary = t.entries[i].seq
}
return boundary
}
func (t *ackTracker) removeConfirmed(lastAck int64) {
n := 0
for n < len(t.entries) {
if !t.entries[n].done || t.entries[n].seq > lastAck {
break
}
delete(t.index, t.entries[n].token)
n++
}
if n == 0 {
return
}
copy(t.entries, t.entries[n:])
t.entries = t.entries[:len(t.entries)-n]
for i := range t.entries {
t.index[t.entries[i].token] = i
}
}