Files
vercel__chat/AGENTS.md
Felix Arntz 06fb8e59ef chore: set up konsistent with basic initial config for package, adapter, and state adapter conventions (#466)
* set up konsistent with basic initial config

* add konsistent to CI

* fix konsistent.json formatting

* fix(gchat): move GoogleChatAdapterConfig to ./types for konsistent

* fix(slack): move SlackAdapterConfig to ./types and drop Partial wrapper from createSlackAdapter

* fix(messenger): name createMessengerAdapter parameter MessengerAdapterConfig

* fix(web): rename WebAdapterOptions to WebAdapterConfig and import Adapter type in index.ts

* fix(whatsapp): align WhatsAppAdapterConfig and creator with konsistent + map kebab to PascalCase

* fix(state-memory): add MemoryStateAdapterOptions type for konsistent

* fix(state-ioredis): unify URL and client options under IoRedisStateAdapterOptions

* fix(state-redis): unify URL and client options under RedisStateAdapterOptions

* fix(state-pg): unify URL and client options under PostgresStateAdapterOptions

* chore: changeset for konsistent convention alignment

* chore: drop CHANGELOG.md from konsistent's required files list

CHANGELOG.md is generated automatically by changesets on each release —
it's never hand-authored and doesn't exist for a package until its first
release lands. Requiring it as a convention check makes CI fail
indefinitely for any newly added package, with no honest fix available
(an empty placeholder is just noise that gets overwritten on first
release).

* docs: document konsistent and package shape conventions

* fix(web): use WebAdapterConfig in WebAdapter field types after main merge

The merge of main into konsistent brought in PR #475's `protected`
field modifiers on top of the WebAdapterOptions → WebAdapterConfig
rename, leaving two stale references to the (un-imported) old name.
Switch them to WebAdapterConfig and update a stale JSDoc reference
in als.ts to match.

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
Co-authored-by: Ben Sabic <27636870+bensabic@users.noreply.github.com>
2026-05-11 19:29:05 +10:00

6.5 KiB

CLAUDE.md

Guidance for Claude Code (claude.ai/code) when working in this repository.

Build commands

pnpm install
pnpm build           # Build all packages (Turborepo)
pnpm typecheck       # Type-check all packages
pnpm check           # Lint and format check (ultracite/biome)
pnpm fix             # Auto-fix lint/format issues
pnpm knip            # Check for unused exports/dependencies
pnpm konsistent      # Enforce adapter/state-package shape conventions (see .github/konsistent.json)
pnpm test            # Run all tests
pnpm validate        # knip + check + typecheck + test + build. ALWAYS run before declaring a task done.
pnpm dev             # Watch mode

# Per-package
pnpm --filter chat test
pnpm --filter @chat-adapter/slack build
pnpm --filter docs dev    # Preview the docs site (chat-sdk.dev) locally

Code style

  • Install dependencies with pnpm add, not by editing package.json by hand.
  • sample-messages.md files in adapter packages contain real-world webhook logs — useful when writing parsers or fixtures.
  • Commits must be signed and verified — see .github/CONTRIBUTING.md.
  • We follow Conventional Commits (feat:, fix:, docs:, chore:, optionally scoped). The release workflow's auto-PR uses chore(release): version packages — don't reuse that exact subject for unrelated commits.
  • See the Ultracite section at the bottom for the in-code style rules Biome doesn't catch automatically.

Architecture

pnpm monorepo, Turborepo orchestrated. All packages are ESM ("type": "module"), TypeScript, bundled with tsup.

Packages

  • packages/chat — core SDK (chat npm package): Chat class, types, mdast-based markdown utilities
  • packages/adapter-{slack,teams,gchat,discord,telegram,whatsapp,github,linear,zoom} — platform adapters
  • packages/adapter-shared — utilities shared across adapters
  • packages/state-{memory,redis,ioredis,pg} — state adapters
  • packages/integration-tests — integration tests against real platform APIs
  • apps/docs — fumadocs-based docs site (chat-sdk.dev)
  • examples/nextjs-chat — example Next.js app

Core concepts

  1. Chat (packages/chat/src/chat.ts) — main entry point; coordinates adapters and handlers.
  2. Adapter — platform-specific implementation: webhook verification + parsing, normalized format conversion, FormatConverter for markdown ↔ platform AST.
  3. StateAdapter — persistence for subscriptions, distributed locks, key/value cache, lists, and queues.
  4. Thread — conversation thread with post(), subscribe(), startTyping(), setState(), etc.
  5. Message — normalized message: text, formatted (mdast AST), raw (platform-specific).

Thread ID format

{adapter}:{channel}:{thread} — e.g. slack:C123ABC:1234567890.123456. Some adapters base64-encode IDs that contain delimiters (Teams, Google Chat).

Webhook flow

  1. Platform → /api/webhooks/{platform}
  2. Adapter verifies, parses, calls chat.handleIncomingMessage()
  3. Chat acquires a thread lock, then routes to onSubscribedMessage, onNewMention, or onNewMessage handlers depending on context.
  4. Handler receives Thread and Message.

Formatting

Messages use mdast as the canonical format. Each adapter's FormatConverter provides:

  • toAst(platformText) — platform → mdast
  • fromAst(ast) — mdast → platform
  • renderPostable(message) — PostableMessage → platform string

Testing

packages/chat/src/mock-adapter.ts exports test utilities:

  • createMockAdapter(name) — Adapter with vi.fn() mocks
  • createMockState() — in-memory subscriptions/locks/cache
  • createTestMessage(id, text, overrides?)
  • mockLogger

For production-traffic-driven testing, see packages/integration-tests/fixtures/replay/README.md (recording / export / replay workflow). Recordings are tagged with the deployed git SHA and exported via pnpm recording:list / pnpm recording:export <session-id> from examples/nextjs-chat.

Documentation

User-facing docs live in apps/docs/content/docs/ (rendered at chat-sdk.dev/docs). When changing behavior, public APIs, or env vars, update the relevant page in the same PR. Preview locally with pnpm --filter docs dev.

Releases

Uses Changesets with fixed versioning (every package shares one version). Every PR that changes a package's behavior must include a changeset (pnpm changeset). Full rules in .github/CONTRIBUTING.md.

Environment variables

Key env vars (see turbo.json for the full list):

  • SLACK_BOT_TOKEN, SLACK_SIGNING_SECRET
  • TEAMS_APP_ID, TEAMS_APP_PASSWORD, TEAMS_APP_TENANT_ID
  • GOOGLE_CHAT_CREDENTIALS or GOOGLE_CHAT_USE_ADC
  • WHATSAPP_ACCESS_TOKEN, WHATSAPP_APP_SECRET, WHATSAPP_PHONE_NUMBER_ID, WHATSAPP_VERIFY_TOKEN
  • REDIS_URL — Redis state adapter
  • POSTGRES_URL / DATABASE_URL — PostgreSQL state adapter
  • BOT_USERNAME — default bot username

Community health files

  • .github/CONTRIBUTING.md — dev setup, signed commits, conventional commits, changesets, docs preview, building your own adapter
  • .github/SUPPORT.md — where to send help/usage questions
  • .github/SECURITY.md — private vulnerability reporting
  • .github/ISSUE_TEMPLATE/ — bug, feature, docs, and adapter-request templates
  • .github/CODEOWNERS — @vercel/chat-sdk owns everything; release plumbing is locked to @cramforce

Ultracite code standards

This project uses Ultracite (Biome-based). pnpm check / pnpm fix run it across the monorepo. Most issues auto-fix.

Beyond what Biome enforces:

  • Type safety — prefer unknown over any; as const for literals; named constants over magic numbers.
  • Modern JS/TS — for...of over .forEach; ?. and ??; const by default; template literals; destructure.
  • Async — always await returned promises; no async Promise executors.
  • React — function components only; hooks at top level; stable keys (prefer IDs over indices); no components defined inside other components; semantic HTML and ARIA; <Image> over <img>; ref-as-prop in React 19+.
  • Errors — throw Error objects with descriptive messages; early returns over nested conditionals; no console.log / debugger / alert in shipped code.
  • Performance — top-level regex literals; no spread-in-accumulator loops; specific imports over barrel files.
  • Security — rel="noopener" on target="_blank"; avoid dangerouslySetInnerHTML; never eval() or assign to document.cookie.