l-ai-chat-client-go/README.md
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

109 lines
3.7 KiB
Markdown

# 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:
```bash
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`:
```text
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:
```text
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:
```text
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.