Go to file
Kaien 11e52d5910 chore(proto): update chat proto ready marker
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-09 22:31:55 +03:00
proto chore(proto): update chat proto ready marker 2026-08-09 22:31:55 +03:00
.gitmodules Add reusable live chat Go SDK 2026-08-09 21:17:31 +03:00
ack_tracker.go Harden Stage 5 subscription safety checks 2026-08-09 21:29:41 +03:00
auth.go Add reusable live chat Go SDK 2026-08-09 21:17:31 +03:00
client.go chore(module): use Gitea SDK import path 2026-08-09 22:22:50 +03:00
doc.go Add reusable live chat Go SDK 2026-08-09 21:17:31 +03:00
errors.go Add reusable live chat Go SDK 2026-08-09 21:17:31 +03:00
example_test.go chore(module): use Gitea SDK import path 2026-08-09 22:22:50 +03:00
go.mod chore(module): use Gitea SDK import path 2026-08-09 22:22:50 +03:00
go.sum Add reusable live chat Go SDK 2026-08-09 21:17:31 +03:00
history.go chore(module): use Gitea SDK import path 2026-08-09 22:22:50 +03:00
options.go Add reusable live chat Go SDK 2026-08-09 21:17:31 +03:00
README.md Harden Stage 5 subscription safety checks 2026-08-09 21:29:41 +03:00
subscription_test.go chore(module): use Gitea SDK import path 2026-08-09 22:22:50 +03:00
subscription.go chore(module): use Gitea SDK import path 2026-08-09 22:22:50 +03:00

l-ai-chat-client-go

Reusable Go SDK for l-ai-chat agent/service clients. It uses the authoritative l-ai-chat-proto generated Go API through the proto/l-ai-chat-proto submodule and does not copy protobuf files manually.

Install / Build

Clone with submodules:

git clone --recurse-submodules <repo-url> l-ai-chat-client-go
cd l-ai-chat-client-go
go test ./...

On this workstation the server currently consumes proto commit f1f44e4ed276316ef17f8deccac54da457654312. The local sibling ../l-ai-chat-proto checkout is older, so the SDK submodule is pinned to the same proto worktree commit as ../l-ai-chat.

Transport and Auth

New uses TLS by default and automatically adds service auth metadata to unary RPCs, Subscribe, reconnects, and Acknowledge:

authorization: Bearer <token>

Use WithInsecureTransport() only for trusted/internal networks. The service token travels over this gRPC transport; plaintext mode is suitable only for trusted/internal network until service-to-service TLS/mTLS is deployed.

Tokens come from a TokenProvider; StaticToken is the simple provider. Empty tokens and provider errors fail closed. Tokens are not included in SDK errors.

Delivery Guarantee

Agent delivery is durable at-least-once:

  • receiving a message does not mean processing succeeded;
  • the server durable cursor changes only after successful Acknowledge;
  • the SDK acknowledges only after MarkProcessed;
  • unacked messages replay after reconnect;
  • handlers with external side effects must be idempotent.

The server derives the durable consumer identity from the authenticated agent/service token. The SDK does not accept an arbitrary consumer id.

Cumulative ACK Warning

Acknowledge(last_ack_sequence=N) is cumulative. If deliveries are:

100, 104, 111

and 104 finishes before 100, do not acknowledge 104 yet: that would mark 100 processed too. Use Subscription.MarkProcessed; the SDK tracks actual delivery order, not seq+1, so global sequence gaps inside a topic are safe.

Expected ACK behavior:

104 done -> no ACK
100 done -> ACK 104
111 done -> ACK 111

Subscription Lifecycle

Subscribe opens one server stream for one topic and waits for the first ready=true marker before returning. READY is not exposed as a chat message. Each successful stream registration is a new SDK generation. Deliveries carry internal generation/subscription fencing, so an old Delivery cannot ACK a new stream after reconnect.

Transient stream failures (Unavailable, ResourceExhausted, DeadlineExceeded, EOF/transport reset) reconnect with bounded exponential backoff and jitter. Reconnect does not send a local receive cursor; the server starts from the durable ACK cursor and replays unacked messages.

Aborted means the server intentionally replaced this active subscription because another stream for the same (consumer, topic) connected. The SDK treats that as terminal by default to avoid reconnect ping-pong between two replicas of one logical consumer. HA/leader election belongs above this SDK.

First-time (consumer, topic) subscriptions start at the current topic head. Use History for historical context. Existing consumers resume from the durable ACK cursor. Delivery.Replay means redelivery/backlog context, not an error.

gRPC Surface

The SDK wraps implemented read/agent RPCs:

  • ListTopics
  • GetTopic
  • GetMessage
  • History
  • PostMessage using server-side AgentPostMessage
  • Subscribe
  • Subscription.MarkProcessed

Generic PostMessage, CreateTopic, and UpdateTopic are intentionally not exposed as ergonomic helpers because the current server returns Unimplemented for those fail-closed gRPC paths.

Lifecycle Notes

Client owns and closes the gRPC connection it creates in New. If callers provide an existing connection with WithClientConn, the SDK uses it but does not close it; callers keep ownership of that connection.

Subscription.Run is sequential: it calls the handler for one delivery at a time and calls MarkProcessed only after the handler returns nil. Handler errors stop Run and do not ACK that delivery. Automatic reconnect cancels the subscription stream generation, but it cannot undo external handler side effects; handlers should honor their context and remain idempotent.

Calling MarkProcessed more than once for the same delivery is harmless. The first call may advance the safe ACK boundary; later duplicate calls do not add tracker entries, move the cursor backward, or ACK a caller-mutated sequence.