This page explains why Chat SDK Go is shaped the way it is: the model in
brief, the design goals, how it maps to
Vercel Chat SDK, and what it leaves out on
purpose (non-goals and intentional gaps).
The full record is in CONTEXT.md and the
ADRs.
The runtime coordinates conversations; it does not own your product.
chat.Chat receives deliveries through adapters, over webhooks or over a
listener such as Slack Socket Mode. It normalizes platform payloads into
events, dedupes them, serializes work per thread with token-owned lock
leases, and routes to your single-slot handlers. Everything your product
stores — transcripts, user records, workflow state — lives in your database,
keyed by the opaque ThreadID.
Adapters own the platform boundary. Transports, signature verification,
payload normalization, outbound rendering, rate-limit retries, and platform
quirks live inside the adapter. Platform-specific power is reached
deliberately via typed adapter access (chat.AdapterAs), never by making raw
platform structs the normal API.
State is required and small. Subscriptions, dedupe marks, and locks — that is all. Memory for development; Redis, Postgres, or NATS for production.
Events are broader than messages. A slash command and a button click are normalized events with their own hooks, not messages. All events ride the same dispatch spine.
Semantic compatibility, not feature parity. Vercel Chat SDK's conversation model is the precedent; its TypeScript API shapes are not. Where Go idioms or operational safety argue otherwise, this SDK deliberately diverges and documents the divergence.
- Go-native API built around
context.Context,net/http, small interfaces, and explicit errors. - Slack-first vertical slice before claiming multi-platform portability.
- One inbound pipeline for each adapter, whatever the transport: a Slack payload gives the same event over HTTP and over Socket Mode, and Socket Mode lets a bot run with no public URL (ADR 0017).
- Required runtime state for subscriptions, dedupe, and locks: memory for tests and local development; Redis, Postgres, or NATS JetStream for horizontally scaled production deployments.
- Thread-oriented application code: handle a message, subscribe the thread, reply to the thread.
- Platform escape hatches without making raw platform structs the normal API.
- Vercel Chat SDK behavior as the default precedent unless it is non-idiomatic in Go or outside the documented scope.
Chat SDK Go follows Vercel Chat SDK's conversation semantics where they fit Go, built outward from a production-shaped Slack slice. It is not a TypeScript API port and does not promise full feature parity. If you know Vercel Chat SDK, this table maps each concept to its status here:
The behavioral differences that matter when porting handler code — single-slot hooks, fail-fast construction, explicit subscriptions, application-owned history — are documented on the affected symbols' GoDoc and in the reference.
These are deliberate boundaries, each recorded in an ADR. Most are permanent: they mark what belongs to your application. Streaming is the exception; it is deferred, out of the core today but not ruled out.
- Streaming token transport in the core runtime — ADR 0011 defers token streaming and pub/sub transports out of core (without foreclosing a future optional capability); long generation is ack-then-work (ADR 0002) posting one finished message.
- LLM routing and prompt orchestration — the runtime coordinates
conversations; LLM calls, prompt assembly, and generation pipelines are
application concerns inside handlers
(ADR 0011 classifies generation and
stream persistence as app/LLM concerns;
CONTEXT.mddefines the runtime boundary). - A generative-UI card DSL —
ADR 0004 rejected a cross-platform
card model as lossy; platform-native payloads ship opaquely via
NativeContentPosterinstead. - RAG and embeddings — ADR 0009 keeps embeddings, summaries, and RAG corpora as Thread Application State in the application's own database keyed by Thread ID.
- Durable transcript persistence in
chat.State— ADR 0009 rejected baking a message store into runtime state;chat.Statestays subscriptions, dedupe, and locks. - App-user auth orchestration — ADR 0006 scopes the install store to platform-tenant credentials; account linking, login prompts, and OAuth web flows are Application Identity and stay app-owned.
These are smaller things the current scope leaves out on purpose. They are not bugs.
API shape:
- no TypeScript API compatibility
- no full Vercel Chat SDK feature parity
- no multiple handlers per routing hook
- no lazy runtime initialization
- no dedicated
OnDirectMessagehook - no public proactive
OpenDM, except adapter behavior needed for explicit ephemeral fallback - no pattern handlers
- no middleware
Messages and content:
- no edit, delete, reaction, or other outbound mutation APIs in the portable surface beyond what a native interaction response needs
- no JSX cards, files, or typed Block Kit / Adaptive Card payload builders in
the portable surface (native Block Kit content ships as an opaque payload
via
NativeContentPoster) - no Slack shortcuts or Block Kit workflow steps (
block_actionsbuttons and menus are routed as Interaction Events) - no synchronous modal
view_submissionresponse (modal open viaviews.openships; the synchronousresponse_actionis incompatible with ack-then-work and is deferred)
Slack-only applications that own their runtime can call Slack's edit,
delete, reaction, and file methods through the low-level
slackapi package
(ADR 0016).
State and history:
- no history persistence APIs:
HistoryReaderis a storage-free live read-through, implemented by the Slack and Linear adapters - no thread application state APIs
Linear:
- no Linear personal API key mode, and no single-install static access token
(pre-exchanged access tokens are supported through the multi-tenant
InstallStore) - no Linear streaming, reactions, or Markdown conversion
Operations:
- no built-in OAuth web flow: authorize/callback/token-exchange routes and install storage are application-owned (ADR 0006)
- no built-in HTTP server or router integrations: the application mounts
each webhook handler, and runs each listener with
Chat.Listen - no bundled metrics framework, exporters, or scrape endpoint (an optional
no-op
Observerseam is provided; OpenTelemetry stays out of the core import graph) - no live Slack end-to-end test in CI
- no adapter marketplace/package conventions
CONTEXT.mdis the project's vocabulary and architecture document. Read it top to bottom to understand the system. ItsLanguagesection defines every domain term (with the synonyms to avoid), grouped by area. ItsRelationshipssection is the closest thing to a formal specification of the runtime's invariants. ItsFlagged ambiguitiessection explains where and why the design diverges from Vercel Chat SDK.docs/adr/holds the Architecture Decision Records. Every significant decision has one, including decisions not to build something:
