This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated. # Releases ## @chat-adapter/discord@4.29.0 ### Minor Changes -2ffed48: Adapter internals are now `protected` rather than `private`, so consumers can subclass an adapter to override or extend its behavior (e.g. handling additional Telegram update types by overriding `processUpdate`). ### Patch Changes -e60bc8c: chore: set supported Node versions in engines -b9b17cd: handle slash commands and button interactions in Discord gateway-only mode - Updated dependencies [ac8a207] - Updated dependencies [e60bc8c] - Updated dependencies [add2730] - Updated dependencies [b75eedb] - chat@4.29.0 - @chat-adapter/shared@4.29.0 ## @chat-adapter/gchat@4.29.0 ### Minor Changes -2ffed48: Adapter internals are now `protected` rather than `private`, so consumers can subclass an adapter to override or extend its behavior (e.g. handling additional Telegram update types by overriding `processUpdate`). ### Patch Changes -e60bc8c: chore: set supported Node versions in engines -06fb8e5: Align package shapes with the new `konsistent` conventions. All changes are backwards-compatible — previous type names are kept as deprecated aliases. - `@chat-adapter/gchat`, `@chat-adapter/slack`: moved `*AdapterConfig` (and related sub-types) into a `./types` module; the public re-exports from `index.ts` are unchanged. - `@chat-adapter/slack`: `createSlackAdapter` now accepts `SlackAdapterConfig` directly instead of `Partial<SlackAdapterConfig>`. Every field on the config was already optional, so no call sites need to change. - `@chat-adapter/messenger`: `MessengerAdapterConfig` fields are now optional (the factory still falls back to `FACEBOOK_*` env vars), and `logger` / `userName` live on `MessengerAdapterConfig` directly. The factory signature is now `createMessengerAdapter(config?: MessengerAdapterConfig)`. - `@chat-adapter/web`: renamed `WebAdapterOptions` to `WebAdapterConfig`; the old name is exported as a deprecated alias. - `@chat-adapter/whatsapp`: every field on `WhatsAppAdapterConfig` is optional (the factory still falls back to `WHATSAPP_*` env vars). `createWhatsAppAdapter` is now typed `(config?: WhatsAppAdapterConfig) => WhatsAppAdapter`. - `@chat-adapter/state-memory`: added an empty `MemoryStateAdapterOptions` type so the package matches every other state adapter; `createMemoryState` now accepts an optional argument of that type. - `@chat-adapter/state-ioredis`, `@chat-adapter/state-redis`, `@chat-adapter/state-pg`: the URL- and client-based option shapes were split into named interfaces (`*StateAdapterUrlOptions` / `*StateAdapterClientOptions`) and unified under `*StateAdapterOptions`. The factories now take the union type directly. Old names — `RedisStateClientOptions`, `CreateRedisStateOptions`, `PostgresStateClientOptions`, `CreatePostgresStateOptions`, `IoRedisStateClientOptions` — are kept as deprecated aliases. - Updated dependencies [ac8a207] - Updated dependencies [e60bc8c] - Updated dependencies [add2730] - Updated dependencies [b75eedb] - chat@4.29.0 - @chat-adapter/shared@4.29.0 ## @chat-adapter/github@4.29.0 ### Minor Changes -2f108bd: Rename the typed native client getter on the Slack, GitHub, and Linear adapters to match the underlying SDK class. - `bot.getAdapter("slack").client` is now `bot.getAdapter("slack").webClient` (returns `WebClient` from `@slack/web-api`). - `bot.getAdapter("github").client` is now `bot.getAdapter("github").octokit` (returns `Octokit`). - `bot.getAdapter("linear").client` is now `bot.getAdapter("linear").linearClient` (returns `LinearClient`). The previous `.client` getter is kept as a deprecated alias on all three adapters, so existing code continues to work without changes. -2ffed48: Adapter internals are now `protected` rather than `private`, so consumers can subclass an adapter to override or extend its behavior (e.g. handling additional Telegram update types by overriding `processUpdate`). ### Patch Changes -e60bc8c: chore: set supported Node versions in engines - Updated dependencies [ac8a207] - Updated dependencies [e60bc8c] - Updated dependencies [add2730] - Updated dependencies [b75eedb] - chat@4.29.0 - @chat-adapter/shared@4.29.0 ## @chat-adapter/linear@4.29.0 ### Minor Changes -2f108bd: Rename the typed native client getter on the Slack, GitHub, and Linear adapters to match the underlying SDK class. - `bot.getAdapter("slack").client` is now `bot.getAdapter("slack").webClient` (returns `WebClient` from `@slack/web-api`). - `bot.getAdapter("github").client` is now `bot.getAdapter("github").octokit` (returns `Octokit`). - `bot.getAdapter("linear").client` is now `bot.getAdapter("linear").linearClient` (returns `LinearClient`). The previous `.client` getter is kept as a deprecated alias on all three adapters, so existing code continues to work without changes. -2ffed48: Adapter internals are now `protected` rather than `private`, so consumers can subclass an adapter to override or extend its behavior (e.g. handling additional Telegram update types by overriding `processUpdate`). ### Patch Changes -e60bc8c: chore: set supported Node versions in engines - Updated dependencies [ac8a207] - Updated dependencies [e60bc8c] - Updated dependencies [add2730] - Updated dependencies [b75eedb] - chat@4.29.0 - @chat-adapter/shared@4.29.0 ## @chat-adapter/messenger@4.29.0 ### Minor Changes -2ffed48: Adapter internals are now `protected` rather than `private`, so consumers can subclass an adapter to override or extend its behavior (e.g. handling additional Telegram update types by overriding `processUpdate`). ### Patch Changes -e60bc8c: chore: set supported Node versions in engines -06fb8e5: Align package shapes with the new `konsistent` conventions. All changes are backwards-compatible — previous type names are kept as deprecated aliases. - `@chat-adapter/gchat`, `@chat-adapter/slack`: moved `*AdapterConfig` (and related sub-types) into a `./types` module; the public re-exports from `index.ts` are unchanged. - `@chat-adapter/slack`: `createSlackAdapter` now accepts `SlackAdapterConfig` directly instead of `Partial<SlackAdapterConfig>`. Every field on the config was already optional, so no call sites need to change. - `@chat-adapter/messenger`: `MessengerAdapterConfig` fields are now optional (the factory still falls back to `FACEBOOK_*` env vars), and `logger` / `userName` live on `MessengerAdapterConfig` directly. The factory signature is now `createMessengerAdapter(config?: MessengerAdapterConfig)`. - `@chat-adapter/web`: renamed `WebAdapterOptions` to `WebAdapterConfig`; the old name is exported as a deprecated alias. - `@chat-adapter/whatsapp`: every field on `WhatsAppAdapterConfig` is optional (the factory still falls back to `WHATSAPP_*` env vars). `createWhatsAppAdapter` is now typed `(config?: WhatsAppAdapterConfig) => WhatsAppAdapter`. - `@chat-adapter/state-memory`: added an empty `MemoryStateAdapterOptions` type so the package matches every other state adapter; `createMemoryState` now accepts an optional argument of that type. - `@chat-adapter/state-ioredis`, `@chat-adapter/state-redis`, `@chat-adapter/state-pg`: the URL- and client-based option shapes were split into named interfaces (`*StateAdapterUrlOptions` / `*StateAdapterClientOptions`) and unified under `*StateAdapterOptions`. The factories now take the union type directly. Old names — `RedisStateClientOptions`, `CreateRedisStateOptions`, `PostgresStateClientOptions`, `CreatePostgresStateOptions`, `IoRedisStateClientOptions` — are kept as deprecated aliases. - Updated dependencies [ac8a207] - Updated dependencies [e60bc8c] - Updated dependencies [add2730] - Updated dependencies [b75eedb] - chat@4.29.0 - @chat-adapter/shared@4.29.0 ## @chat-adapter/shared@4.29.0 ### Minor Changes -add2730: support typed Telegram attachment uploads ### Patch Changes -e60bc8c: chore: set supported Node versions in engines - Updated dependencies [ac8a207] - Updated dependencies [e60bc8c] - Updated dependencies [b75eedb] - chat@4.29.0 ## @chat-adapter/slack@4.29.0 ### Minor Changes -2f108bd: Rename the typed native client getter on the Slack, GitHub, and Linear adapters to match the underlying SDK class. - `bot.getAdapter("slack").client` is now `bot.getAdapter("slack").webClient` (returns `WebClient` from `@slack/web-api`). - `bot.getAdapter("github").client` is now `bot.getAdapter("github").octokit` (returns `Octokit`). - `bot.getAdapter("linear").client` is now `bot.getAdapter("linear").linearClient` (returns `LinearClient`). The previous `.client` getter is kept as a deprecated alias on all three adapters, so existing code continues to work without changes. -c46fdb6: Add support for external `installationProvider` and Enterprise Grid org-wide installs. - New optional `installationProvider` config: `{ getInstallation(installationId, isEnterpriseInstall) => Promise<SlackInstallation | null> }`. When set, the adapter resolves bot tokens for incoming events, slash commands, and interactive payloads through the provider instead of the internal `StateAdapter` — useful for hosted token-management systems (e.g. Vercel Connect). The provider is read-only; OAuth callback writes (`setInstallation`, `handleOAuthCallback`) and the `getInstallation`/`deleteInstallation` public methods continue to use internal state, so callers using a provider should manage their own writes. - Enterprise Grid org-wide installs (`is_enterprise_install: true`) are now keyed on `enterprise_id` instead of `team_id` across event_callback, slash command, and interactive payload paths. Multi-workspace deployments using the internal `StateAdapter` for org-wide installs must repopulate installations under the `enterprise_id` key — previously, org-wide events would fall through to a `team_id` lookup that did not match what the OAuth flow had stored. -fdebde7: feat(slack): expose direct `WebClient` access via `adapter.client` `bot.getAdapter("slack").client` now returns a typed `WebClient` from `@slack/web-api`, matching the existing pattern on the Linear and GitHub adapters. The returned client is bound to the bot token for the current request context (multi-workspace) or the configured default token (single-workspace). Use it for any Web API call not covered by the SDK's high-level methods, e.g. `adapter.client.pins.add(...)` or `adapter.client.usergroups.list(...)`. Resolution order: 1. The token from the current `requestContext` — set during webhook handling, or by `adapter.withBotToken(token, fn)`. 2. The default `botToken`, when configured as a static string or a synchronous resolver function. Throws `AuthenticationError` outside of any context in multi-workspace mode, or when `botToken` is configured as an async resolver function. For async tokens, await the token first and bind it explicitly with `adapter.withBotToken(token, () => adapter.client...)`. Also fixes `createSlackAdapter()` silently dropping the `apiUrl` config field. -2ffed48: Adapter internals are now `protected` rather than `private`, so consumers can subclass an adapter to override or extend its behavior (e.g. handling additional Telegram update types by overriding `processUpdate`). ### Patch Changes -e60bc8c: chore: set supported Node versions in engines -06fb8e5: Align package shapes with the new `konsistent` conventions. All changes are backwards-compatible — previous type names are kept as deprecated aliases. - `@chat-adapter/gchat`, `@chat-adapter/slack`: moved `*AdapterConfig` (and related sub-types) into a `./types` module; the public re-exports from `index.ts` are unchanged. - `@chat-adapter/slack`: `createSlackAdapter` now accepts `SlackAdapterConfig` directly instead of `Partial<SlackAdapterConfig>`. Every field on the config was already optional, so no call sites need to change. - `@chat-adapter/messenger`: `MessengerAdapterConfig` fields are now optional (the factory still falls back to `FACEBOOK_*` env vars), and `logger` / `userName` live on `MessengerAdapterConfig` directly. The factory signature is now `createMessengerAdapter(config?: MessengerAdapterConfig)`. - `@chat-adapter/web`: renamed `WebAdapterOptions` to `WebAdapterConfig`; the old name is exported as a deprecated alias. - `@chat-adapter/whatsapp`: every field on `WhatsAppAdapterConfig` is optional (the factory still falls back to `WHATSAPP_*` env vars). `createWhatsAppAdapter` is now typed `(config?: WhatsAppAdapterConfig) => WhatsAppAdapter`. - `@chat-adapter/state-memory`: added an empty `MemoryStateAdapterOptions` type so the package matches every other state adapter; `createMemoryState` now accepts an optional argument of that type. - `@chat-adapter/state-ioredis`, `@chat-adapter/state-redis`, `@chat-adapter/state-pg`: the URL- and client-based option shapes were split into named interfaces (`*StateAdapterUrlOptions` / `*StateAdapterClientOptions`) and unified under `*StateAdapterOptions`. The factories now take the union type directly. Old names — `RedisStateClientOptions`, `CreateRedisStateOptions`, `PostgresStateClientOptions`, `CreatePostgresStateOptions`, `IoRedisStateClientOptions` — are kept as deprecated aliases. -0f0c203: fix(slack): prefer `webhookVerifier` over `signingSecret` and `SLACK_SIGNING_SECRET` When a `webhookVerifier` is configured, it now takes precedence over both the `signingSecret` config field and the `SLACK_SIGNING_SECRET` env var. Previously, a configured `signingSecret` (or env var) would shadow the verifier. - Updated dependencies [ac8a207] - Updated dependencies [e60bc8c] - Updated dependencies [add2730] - Updated dependencies [b75eedb] - chat@4.29.0 - @chat-adapter/shared@4.29.0 ## @chat-adapter/teams@4.29.0 ### Minor Changes -2ffed48: Adapter internals are now `protected` rather than `private`, so consumers can subclass an adapter to override or extend its behavior (e.g. handling additional Telegram update types by overriding `processUpdate`). ### Patch Changes -e60bc8c: chore: set supported Node versions in engines - Updated dependencies [ac8a207] - Updated dependencies [e60bc8c] - Updated dependencies [add2730] - Updated dependencies [b75eedb] - chat@4.29.0 - @chat-adapter/shared@4.29.0 ## @chat-adapter/telegram@4.29.0 ### Minor Changes -add2730: support typed Telegram attachment uploads -2ffed48: Adapter internals are now `protected` rather than `private`, so consumers can subclass an adapter to override or extend its behavior (e.g. handling additional Telegram update types by overriding `processUpdate`). ### Patch Changes -e60bc8c: chore: set supported Node versions in engines -711babe: Handle `video_note` (round video messages) in `extractAttachments`. Previously these messages were silently dropped; now they are returned as `video` attachments with `width`/`height` set to the clip's `length`. - Updated dependencies [ac8a207] - Updated dependencies [e60bc8c] - Updated dependencies [add2730] - Updated dependencies [b75eedb] - chat@4.29.0 - @chat-adapter/shared@4.29.0 ## @chat-adapter/web@4.29.0 ### Minor Changes -2ffed48: Adapter internals are now `protected` rather than `private`, so consumers can subclass an adapter to override or extend its behavior (e.g. handling additional Telegram update types by overriding `processUpdate`). -716e934: Add first-class Vue and Svelte support via new subpath exports `@chat-adapter/web/vue` and `@chat-adapter/web/svelte`. Each exports a `useChat()` factory preconfigured with `DefaultChatTransport`, returning a framework-reactive `Chat` instance from `@ai-sdk/vue` / `@ai-sdk/svelte` respectively. Note: unlike the React subpath which wraps `@ai-sdk/react`'s `useChat` hook and returns destructurable helpers, the Vue and Svelte wrappers return a `Chat` class instance — access `chat.messages`, `chat.sendMessage()`, `chat.status`, and `chat.stop()` directly on the object. ### Patch Changes -e60bc8c: chore: set supported Node versions in engines -06fb8e5: Align package shapes with the new `konsistent` conventions. All changes are backwards-compatible — previous type names are kept as deprecated aliases. - `@chat-adapter/gchat`, `@chat-adapter/slack`: moved `*AdapterConfig` (and related sub-types) into a `./types` module; the public re-exports from `index.ts` are unchanged. - `@chat-adapter/slack`: `createSlackAdapter` now accepts `SlackAdapterConfig` directly instead of `Partial<SlackAdapterConfig>`. Every field on the config was already optional, so no call sites need to change. - `@chat-adapter/messenger`: `MessengerAdapterConfig` fields are now optional (the factory still falls back to `FACEBOOK_*` env vars), and `logger` / `userName` live on `MessengerAdapterConfig` directly. The factory signature is now `createMessengerAdapter(config?: MessengerAdapterConfig)`. - `@chat-adapter/web`: renamed `WebAdapterOptions` to `WebAdapterConfig`; the old name is exported as a deprecated alias. - `@chat-adapter/whatsapp`: every field on `WhatsAppAdapterConfig` is optional (the factory still falls back to `WHATSAPP_*` env vars). `createWhatsAppAdapter` is now typed `(config?: WhatsAppAdapterConfig) => WhatsAppAdapter`. - `@chat-adapter/state-memory`: added an empty `MemoryStateAdapterOptions` type so the package matches every other state adapter; `createMemoryState` now accepts an optional argument of that type. - `@chat-adapter/state-ioredis`, `@chat-adapter/state-redis`, `@chat-adapter/state-pg`: the URL- and client-based option shapes were split into named interfaces (`*StateAdapterUrlOptions` / `*StateAdapterClientOptions`) and unified under `*StateAdapterOptions`. The factories now take the union type directly. Old names — `RedisStateClientOptions`, `CreateRedisStateOptions`, `PostgresStateClientOptions`, `CreatePostgresStateOptions`, `IoRedisStateClientOptions` — are kept as deprecated aliases. - Updated dependencies [ac8a207] - Updated dependencies [e60bc8c] - Updated dependencies [add2730] - Updated dependencies [b75eedb] - chat@4.29.0 - @chat-adapter/shared@4.29.0 ## @chat-adapter/whatsapp@4.29.0 ### Minor Changes -2ffed48: Adapter internals are now `protected` rather than `private`, so consumers can subclass an adapter to override or extend its behavior (e.g. handling additional Telegram update types by overriding `processUpdate`). ### Patch Changes -e60bc8c: chore: set supported Node versions in engines -06fb8e5: Align package shapes with the new `konsistent` conventions. All changes are backwards-compatible — previous type names are kept as deprecated aliases. - `@chat-adapter/gchat`, `@chat-adapter/slack`: moved `*AdapterConfig` (and related sub-types) into a `./types` module; the public re-exports from `index.ts` are unchanged. - `@chat-adapter/slack`: `createSlackAdapter` now accepts `SlackAdapterConfig` directly instead of `Partial<SlackAdapterConfig>`. Every field on the config was already optional, so no call sites need to change. - `@chat-adapter/messenger`: `MessengerAdapterConfig` fields are now optional (the factory still falls back to `FACEBOOK_*` env vars), and `logger` / `userName` live on `MessengerAdapterConfig` directly. The factory signature is now `createMessengerAdapter(config?: MessengerAdapterConfig)`. - `@chat-adapter/web`: renamed `WebAdapterOptions` to `WebAdapterConfig`; the old name is exported as a deprecated alias. - `@chat-adapter/whatsapp`: every field on `WhatsAppAdapterConfig` is optional (the factory still falls back to `WHATSAPP_*` env vars). `createWhatsAppAdapter` is now typed `(config?: WhatsAppAdapterConfig) => WhatsAppAdapter`. - `@chat-adapter/state-memory`: added an empty `MemoryStateAdapterOptions` type so the package matches every other state adapter; `createMemoryState` now accepts an optional argument of that type. - `@chat-adapter/state-ioredis`, `@chat-adapter/state-redis`, `@chat-adapter/state-pg`: the URL- and client-based option shapes were split into named interfaces (`*StateAdapterUrlOptions` / `*StateAdapterClientOptions`) and unified under `*StateAdapterOptions`. The factories now take the union type directly. Old names — `RedisStateClientOptions`, `CreateRedisStateOptions`, `PostgresStateClientOptions`, `CreatePostgresStateOptions`, `IoRedisStateClientOptions` — are kept as deprecated aliases. - Updated dependencies [ac8a207] - Updated dependencies [e60bc8c] - Updated dependencies [add2730] - Updated dependencies [b75eedb] - chat@4.29.0 - @chat-adapter/shared@4.29.0 ## chat@4.29.0 ### Minor Changes -ac8a207: Add `chat/ai` subpath as the home for AI utilities, including `createChatTools` for the Vercel AI SDK and `toAiMessages` for converting chat history into AI SDK prompts. `createChatTools` exposes Chat SDK operations as ready-to-use AI SDK tools so an agent can read messages, post replies, send DMs, react, edit, delete, and manage thread subscriptions across every adapter the supplied `Chat` instance has registered. Write operations require user approval by default and can be toggled globally or per-tool via `requireApproval`. Three presets (`reader`, `messenger`, `moderator`) scope the toolset, and tools can also be cherry-picked from the same subpath. `toAiMessages` (and the `AiMessage` / `AiMessagePart` / `ToAiMessagesOptions` types) now ship from `chat/ai` alongside the tools — keeping the optional `ai` and `zod` peer dependencies out of bundles that don't use them. The previous `chat` re-exports continue to work, but are marked `@deprecated` so editors surface a hint pointing at `chat/ai`; existing code keeps compiling, and migrating is a single import-path change. -b75eedb: add burst concurrency strategy ### Patch Changes -e60bc8c: chore: set supported Node versions in engines ## @chat-adapter/state-ioredis@4.29.0 ### Patch Changes -e60bc8c: chore: set supported Node versions in engines -06fb8e5: Align package shapes with the new `konsistent` conventions. All changes are backwards-compatible — previous type names are kept as deprecated aliases. - `@chat-adapter/gchat`, `@chat-adapter/slack`: moved `*AdapterConfig` (and related sub-types) into a `./types` module; the public re-exports from `index.ts` are unchanged. - `@chat-adapter/slack`: `createSlackAdapter` now accepts `SlackAdapterConfig` directly instead of `Partial<SlackAdapterConfig>`. Every field on the config was already optional, so no call sites need to change. - `@chat-adapter/messenger`: `MessengerAdapterConfig` fields are now optional (the factory still falls back to `FACEBOOK_*` env vars), and `logger` / `userName` live on `MessengerAdapterConfig` directly. The factory signature is now `createMessengerAdapter(config?: MessengerAdapterConfig)`. - `@chat-adapter/web`: renamed `WebAdapterOptions` to `WebAdapterConfig`; the old name is exported as a deprecated alias. - `@chat-adapter/whatsapp`: every field on `WhatsAppAdapterConfig` is optional (the factory still falls back to `WHATSAPP_*` env vars). `createWhatsAppAdapter` is now typed `(config?: WhatsAppAdapterConfig) => WhatsAppAdapter`. - `@chat-adapter/state-memory`: added an empty `MemoryStateAdapterOptions` type so the package matches every other state adapter; `createMemoryState` now accepts an optional argument of that type. - `@chat-adapter/state-ioredis`, `@chat-adapter/state-redis`, `@chat-adapter/state-pg`: the URL- and client-based option shapes were split into named interfaces (`*StateAdapterUrlOptions` / `*StateAdapterClientOptions`) and unified under `*StateAdapterOptions`. The factories now take the union type directly. Old names — `RedisStateClientOptions`, `CreateRedisStateOptions`, `PostgresStateClientOptions`, `CreatePostgresStateOptions`, `IoRedisStateClientOptions` — are kept as deprecated aliases. - Updated dependencies [ac8a207] - Updated dependencies [e60bc8c] - Updated dependencies [b75eedb] - chat@4.29.0 ## @chat-adapter/state-memory@4.29.0 ### Patch Changes -e60bc8c: chore: set supported Node versions in engines -06fb8e5: Align package shapes with the new `konsistent` conventions. All changes are backwards-compatible — previous type names are kept as deprecated aliases. - `@chat-adapter/gchat`, `@chat-adapter/slack`: moved `*AdapterConfig` (and related sub-types) into a `./types` module; the public re-exports from `index.ts` are unchanged. - `@chat-adapter/slack`: `createSlackAdapter` now accepts `SlackAdapterConfig` directly instead of `Partial<SlackAdapterConfig>`. Every field on the config was already optional, so no call sites need to change. - `@chat-adapter/messenger`: `MessengerAdapterConfig` fields are now optional (the factory still falls back to `FACEBOOK_*` env vars), and `logger` / `userName` live on `MessengerAdapterConfig` directly. The factory signature is now `createMessengerAdapter(config?: MessengerAdapterConfig)`. - `@chat-adapter/web`: renamed `WebAdapterOptions` to `WebAdapterConfig`; the old name is exported as a deprecated alias. - `@chat-adapter/whatsapp`: every field on `WhatsAppAdapterConfig` is optional (the factory still falls back to `WHATSAPP_*` env vars). `createWhatsAppAdapter` is now typed `(config?: WhatsAppAdapterConfig) => WhatsAppAdapter`. - `@chat-adapter/state-memory`: added an empty `MemoryStateAdapterOptions` type so the package matches every other state adapter; `createMemoryState` now accepts an optional argument of that type. - `@chat-adapter/state-ioredis`, `@chat-adapter/state-redis`, `@chat-adapter/state-pg`: the URL- and client-based option shapes were split into named interfaces (`*StateAdapterUrlOptions` / `*StateAdapterClientOptions`) and unified under `*StateAdapterOptions`. The factories now take the union type directly. Old names — `RedisStateClientOptions`, `CreateRedisStateOptions`, `PostgresStateClientOptions`, `CreatePostgresStateOptions`, `IoRedisStateClientOptions` — are kept as deprecated aliases. - Updated dependencies [ac8a207] - Updated dependencies [e60bc8c] - Updated dependencies [b75eedb] - chat@4.29.0 ## @chat-adapter/state-pg@4.29.0 ### Patch Changes -e60bc8c: chore: set supported Node versions in engines -06fb8e5: Align package shapes with the new `konsistent` conventions. All changes are backwards-compatible — previous type names are kept as deprecated aliases. - `@chat-adapter/gchat`, `@chat-adapter/slack`: moved `*AdapterConfig` (and related sub-types) into a `./types` module; the public re-exports from `index.ts` are unchanged. - `@chat-adapter/slack`: `createSlackAdapter` now accepts `SlackAdapterConfig` directly instead of `Partial<SlackAdapterConfig>`. Every field on the config was already optional, so no call sites need to change. - `@chat-adapter/messenger`: `MessengerAdapterConfig` fields are now optional (the factory still falls back to `FACEBOOK_*` env vars), and `logger` / `userName` live on `MessengerAdapterConfig` directly. The factory signature is now `createMessengerAdapter(config?: MessengerAdapterConfig)`. - `@chat-adapter/web`: renamed `WebAdapterOptions` to `WebAdapterConfig`; the old name is exported as a deprecated alias. - `@chat-adapter/whatsapp`: every field on `WhatsAppAdapterConfig` is optional (the factory still falls back to `WHATSAPP_*` env vars). `createWhatsAppAdapter` is now typed `(config?: WhatsAppAdapterConfig) => WhatsAppAdapter`. - `@chat-adapter/state-memory`: added an empty `MemoryStateAdapterOptions` type so the package matches every other state adapter; `createMemoryState` now accepts an optional argument of that type. - `@chat-adapter/state-ioredis`, `@chat-adapter/state-redis`, `@chat-adapter/state-pg`: the URL- and client-based option shapes were split into named interfaces (`*StateAdapterUrlOptions` / `*StateAdapterClientOptions`) and unified under `*StateAdapterOptions`. The factories now take the union type directly. Old names — `RedisStateClientOptions`, `CreateRedisStateOptions`, `PostgresStateClientOptions`, `CreatePostgresStateOptions`, `IoRedisStateClientOptions` — are kept as deprecated aliases. - Updated dependencies [ac8a207] - Updated dependencies [e60bc8c] - Updated dependencies [b75eedb] - chat@4.29.0 ## @chat-adapter/state-redis@4.29.0 ### Patch Changes -e60bc8c: chore: set supported Node versions in engines -06fb8e5: Align package shapes with the new `konsistent` conventions. All changes are backwards-compatible — previous type names are kept as deprecated aliases. - `@chat-adapter/gchat`, `@chat-adapter/slack`: moved `*AdapterConfig` (and related sub-types) into a `./types` module; the public re-exports from `index.ts` are unchanged. - `@chat-adapter/slack`: `createSlackAdapter` now accepts `SlackAdapterConfig` directly instead of `Partial<SlackAdapterConfig>`. Every field on the config was already optional, so no call sites need to change. - `@chat-adapter/messenger`: `MessengerAdapterConfig` fields are now optional (the factory still falls back to `FACEBOOK_*` env vars), and `logger` / `userName` live on `MessengerAdapterConfig` directly. The factory signature is now `createMessengerAdapter(config?: MessengerAdapterConfig)`. - `@chat-adapter/web`: renamed `WebAdapterOptions` to `WebAdapterConfig`; the old name is exported as a deprecated alias. - `@chat-adapter/whatsapp`: every field on `WhatsAppAdapterConfig` is optional (the factory still falls back to `WHATSAPP_*` env vars). `createWhatsAppAdapter` is now typed `(config?: WhatsAppAdapterConfig) => WhatsAppAdapter`. - `@chat-adapter/state-memory`: added an empty `MemoryStateAdapterOptions` type so the package matches every other state adapter; `createMemoryState` now accepts an optional argument of that type. - `@chat-adapter/state-ioredis`, `@chat-adapter/state-redis`, `@chat-adapter/state-pg`: the URL- and client-based option shapes were split into named interfaces (`*StateAdapterUrlOptions` / `*StateAdapterClientOptions`) and unified under `*StateAdapterOptions`. The factories now take the union type directly. Old names — `RedisStateClientOptions`, `CreateRedisStateOptions`, `PostgresStateClientOptions`, `CreatePostgresStateOptions`, `IoRedisStateClientOptions` — are kept as deprecated aliases. - Updated dependencies [ac8a207] - Updated dependencies [e60bc8c] - Updated dependencies [b75eedb] - chat@4.29.0 ## @chat-adapter/tests@4.29.0 ### Patch Changes -0adf3ad: Add `@chat-adapter/tests` — Vitest factories, matchers, and setup utilities for Chat SDK adapter and bot authors. - **Factories**: `createMockAdapter`, `createMockChatInstance`, `createMockState` (with working in-memory subscriptions/locks/KV/queues), `createTestMessage`, `mockLogger`/`createMockLogger`. - **Matchers**: `toHavePosted(threadId, textPattern?)`, `toHaveDispatched(handler)`, `toBeSubscribedTo(threadId)`. - **Setup file**: `@chat-adapter/tests/setup` registers all matchers via `expect.extend` — drop into `vitest.config.ts` `setupFiles`. `chat` and `vitest` are peer dependencies. Adapter-specific helpers (e.g. signed Slack webhook builders) belong in each adapter's own `/testing` subpath, not in this kit. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@chat-adapter/slack
Slack adapter for Chat SDK. Configure single-workspace or multi-workspace OAuth deployments.
Installation
pnpm add @chat-adapter/slack
Single-workspace mode
For bots deployed to a single Slack workspace. The adapter auto-detects SLACK_BOT_TOKEN and SLACK_SIGNING_SECRET from environment variables:
import { Chat } from "chat";
import { createSlackAdapter } from "@chat-adapter/slack";
const bot = new Chat({
userName: "mybot",
adapters: {
slack: createSlackAdapter(),
},
});
bot.onNewMention(async (thread, message) => {
await thread.post("Hello from Slack!");
});
Token rotation
botToken accepts a function returning a string or Promise<string> — the resolver is invoked per API call, so it composes with Slack token rotation (12-hour TTL) or lazy fetch from a secret manager:
createSlackAdapter({
botToken: async () => await secrets.get("slack-bot-token"),
});
If the resolver is expensive (e.g. a vault round-trip), implement caching inside the resolver itself.
Custom webhook verification
Pass webhookVerifier to replace the built-in HMAC check — useful when verification runs in a proxy or signing layer ahead of your handler:
createSlackAdapter({
webhookVerifier: async (request, body) => {
if (!(await myProxy.verify(request))) {
throw new Error("invalid");
}
return true; // or return a string to substitute the verified body
},
});
If both signingSecret and webhookVerifier are set, webhookVerifier wins — it also takes precedence over the SLACK_SIGNING_SECRET env var, so an env-configured deployment can't silently shadow a verifier you wired up. When using webhookVerifier, you are responsible for replay/timestamp protection — the built-in 5-minute timestamp tolerance only applies to the signingSecret path.
Multi-workspace mode
For apps installed across multiple Slack workspaces via OAuth, omit botToken and provide OAuth credentials instead. The adapter resolves tokens dynamically from your state adapter using the team_id from incoming webhooks — or enterprise_id for Enterprise Grid org-wide installs (is_enterprise_install: true).
When you pass any auth-related config (like clientId), the adapter won't fall back to env vars for other auth fields, preventing accidental mixing of auth modes.
import { createSlackAdapter } from "@chat-adapter/slack";
import { createRedisState } from "@chat-adapter/state-redis";
const slackAdapter = createSlackAdapter({
clientId: process.env.SLACK_CLIENT_ID!,
clientSecret: process.env.SLACK_CLIENT_SECRET!,
});
const bot = new Chat({
userName: "mybot",
adapters: { slack: slackAdapter },
state: createRedisState(),
});
OAuth callback
The adapter handles the full Slack OAuth V2 exchange. Point your OAuth redirect URL to a route that calls handleOAuthCallback:
import { slackAdapter } from "@/lib/bot";
export async function GET(request: Request) {
const { teamId } = await slackAdapter.handleOAuthCallback(request, {
redirectUri: process.env.SLACK_REDIRECT_URI,
});
return new Response(`Installed for team ${teamId}!`);
}
If your install flow uses a specific redirect URI, pass the same value here that you used during the authorize step. This is especially useful when one app supports multiple redirect URLs. When no option is provided, the adapter still falls back to redirect_uri on the callback request URL.
Using the adapter outside webhooks
During webhook handling, the adapter resolves tokens automatically from team_id. Outside that context (e.g. cron jobs or background workers), use getInstallation and withBotToken:
const install = await slackAdapter.getInstallation(teamId);
if (!install) throw new Error("Workspace not installed");
await slackAdapter.withBotToken(install.botToken, async () => {
const thread = bot.thread("slack:C12345:1234567890.123456");
await thread.post("Hello from a cron job!");
});
withBotToken uses AsyncLocalStorage under the hood, so concurrent calls with different tokens are isolated.
Removing installations
await slackAdapter.deleteInstallation(teamId);
Token encryption
Pass a base64-encoded 32-byte key as encryptionKey to encrypt bot tokens at rest using AES-256-GCM:
openssl rand -base64 32
When encryptionKey is set, setInstallation() encrypts the token before storing and getInstallation() decrypts it transparently.
External installation provider
For deployments that manage Slack tokens in an external system (e.g. Vercel Connect), pass installationProvider to bypass the internal state adapter when resolving tokens for incoming webhooks:
createSlackAdapter({
clientId: process.env.SLACK_CLIENT_ID!,
clientSecret: process.env.SLACK_CLIENT_SECRET!,
installationProvider: {
getInstallation: async (installationId, isEnterpriseInstall) => {
// installationId is enterprise_id when isEnterpriseInstall is true,
// otherwise team_id. Return null if not found.
return await myTokenStore.lookup(installationId, isEnterpriseInstall);
},
},
});
When configured, the provider's getInstallation is called for every webhook event, slash command, and interactive payload. It is read-only — the adapter's setInstallation, deleteInstallation, and handleOAuthCallback continue to write to the internal state adapter, so callers using a provider should manage their own writes through their external system.
Socket mode
For environments behind firewalls that can't expose public HTTP endpoints, the adapter supports Slack Socket Mode. Instead of receiving webhooks, the adapter connects to Slack over a WebSocket.
import { Chat } from "chat";
import { createSlackAdapter } from "@chat-adapter/slack";
const bot = new Chat({
userName: "mybot",
adapters: {
slack: createSlackAdapter({
mode: "socket",
appToken: process.env.SLACK_APP_TOKEN!,
botToken: process.env.SLACK_BOT_TOKEN!,
}),
},
});
Slack app setup for socket mode
- Go to your app's settings at api.slack.com/apps
- Navigate to Socket Mode and enable it
- Generate an App-Level Token with the
connections:writescope — this is yourSLACK_APP_TOKEN(xapp-...) - Event subscriptions and interactivity still need to be configured, but no public request URL is required
Socket mode is not compatible with multi-workspace OAuth (
clientId/clientSecret). It's designed for single-workspace deployments.
Socket mode on serverless (Vercel)
Socket mode requires a persistent WebSocket connection, which doesn't fit the request/response model of serverless functions. The adapter provides a forwarding mechanism to bridge this gap:
- A cron job periodically starts a transient socket listener
- The listener connects via WebSocket, acks events immediately, and forwards them as HTTP requests to your webhook endpoint
- Your existing webhook route processes the forwarded events normally
// api/slack/socket-mode/route.ts
import { after } from "next/server";
import { bot } from "@/lib/bot";
export const maxDuration = 800;
export async function GET(request: Request) {
const authHeader = request.headers.get("authorization");
if (authHeader !== `Bearer ${process.env.CRON_SECRET}`) {
return new Response("Unauthorized", { status: 401 });
}
await bot.initialize();
const slack = bot.getAdapter("slack");
const webhookUrl = `https://${process.env.VERCEL_URL}/api/webhooks/slack`;
return slack.startSocketModeListener(
{ waitUntil: (task: Promise<unknown>) => after(() => task) },
600_000, // 10 minutes
undefined,
webhookUrl
);
}
Schedule the cron job to run every 9 minutes (overlapping with the 10-minute listener duration) to maintain continuous coverage:
// vercel.json
{
"crons": [
{
"path": "/api/slack/socket-mode",
"schedule": "*/9 * * * *"
}
]
}
Forwarded events are authenticated using the socketForwardingSecret config option (defaults to SLACK_SOCKET_FORWARDING_SECRET env var, falling back to appToken).
Slack app setup
1. Create a Slack app from manifest
- Go to api.slack.com/apps
- Click Create New App then From an app manifest
- Select your workspace and paste the following manifest:
display_information:
name: My Bot
description: A bot built with chat-sdk
features:
bot_user:
display_name: My Bot
always_online: true
oauth_config:
scopes:
bot:
- app_mentions:read
- channels:history
- channels:read
- chat:write
- groups:history
- groups:read
- im:history
- im:read
- mpim:history
- mpim:read
- reactions:read
- reactions:write
- users:read
settings:
event_subscriptions:
request_url: https://your-domain.com/api/webhooks/slack
bot_events:
- app_mention
- message.channels
- message.groups
- message.im
- message.mpim
- member_joined_channel
- assistant_thread_started
- assistant_thread_context_changed
interactivity:
is_enabled: true
request_url: https://your-domain.com/api/webhooks/slack
org_deploy_enabled: false
socket_mode_enabled: false
token_rotation_enabled: false
- Replace
https://your-domain.com/api/webhooks/slackwith your deployed webhook URL - Click Create
2. Get credentials
After creating the app, go to Basic Information → App Credentials and copy:
- Signing Secret as
SLACK_SIGNING_SECRET - Client ID as
SLACK_CLIENT_ID(multi-workspace only) - Client Secret as
SLACK_CLIENT_SECRET(multi-workspace only)
Single workspace: Go to OAuth & Permissions, click Install to Workspace, and copy the Bot User OAuth Token (xoxb-...) as SLACK_BOT_TOKEN.
Multi-workspace: Enable Manage Distribution under Basic Information and set up an OAuth redirect URL pointing to your callback route.
3. Configure slash commands (optional)
- Go to Slash Commands in your app settings
- Click Create New Command
- Set Command (e.g.,
/feedback) - Set Request URL to
https://your-domain.com/api/webhooks/slack - Add a description and click Save
Configuration
All options are auto-detected from environment variables when not provided. You can call createSlackAdapter() with no arguments if the env vars are set.
| Option | Required | Description |
|---|---|---|
botToken |
No | Bot token (xoxb-...) or a function returning one (sync or async) for rotation/lazy fetch. Auto-detected from SLACK_BOT_TOKEN |
signingSecret |
No* | Signing secret for webhook verification. Auto-detected from SLACK_SIGNING_SECRET |
webhookVerifier |
No* | Custom verifier (request, body) => unknown | Promise<unknown> used in place of signingSecret. Returning a string substitutes the verified body for downstream parsing |
mode |
No | Connection mode: "webhook" (default) or "socket" |
appToken |
No** | App-level token (xapp-...) for socket mode. Auto-detected from SLACK_APP_TOKEN |
socketForwardingSecret |
No | Shared secret for authenticating forwarded socket events. Auto-detected from SLACK_SOCKET_FORWARDING_SECRET, falls back to appToken |
clientId |
No | App client ID for multi-workspace OAuth. Auto-detected from SLACK_CLIENT_ID |
clientSecret |
No | App client secret for multi-workspace OAuth. Auto-detected from SLACK_CLIENT_SECRET |
encryptionKey |
No | AES-256-GCM key for encrypting stored tokens. Auto-detected from SLACK_ENCRYPTION_KEY |
installationKeyPrefix |
No | Prefix for the state key used to store workspace installations. Defaults to slack:installation. The full key is {prefix}:{teamId} (or {prefix}:{enterpriseId} for Enterprise Grid org-wide installs) |
installationProvider |
No | External installation lookup { getInstallation(installationId, isEnterpriseInstall) => Promise<SlackInstallation | null> }. When set, bypasses the internal state adapter for token resolution on incoming webhooks. Read-only — manage your own writes externally |
apiUrl |
No | Override the Slack Web API base URL (e.g. for GovSlack or a self-hosted gateway). Auto-detected from SLACK_API_URL |
logger |
No | Logger instance (defaults to ConsoleLogger("info")) |
*signingSecret is required for webhook mode — either via config, SLACK_SIGNING_SECRET env var, or a webhookVerifier.
**appToken is required for socket mode — either via config or SLACK_APP_TOKEN env var.
Environment variables
SLACK_BOT_TOKEN=xoxb-... # Single-workspace only
SLACK_SIGNING_SECRET=... # Required for webhook mode
SLACK_APP_TOKEN=xapp-... # Required for socket mode
SLACK_SOCKET_FORWARDING_SECRET=... # Optional, for socket event forwarding auth
SLACK_CLIENT_ID=... # Multi-workspace only
SLACK_CLIENT_SECRET=... # Multi-workspace only
SLACK_ENCRYPTION_KEY=... # Optional, for token encryption
SLACK_API_URL=... # Optional, for GovSlack or a self-hosted gateway
Features
Messaging
| Feature | Supported |
|---|---|
| Post message | Yes |
| Edit message | Yes |
| Delete message | Yes |
| File uploads | Yes |
| Streaming | Native API |
| Scheduled messages | Yes (native, with cancel) |
Rich content
| Feature | Supported |
|---|---|
| Card format | Block Kit |
| Buttons | Yes |
| Link buttons | Yes |
| Select menus | Yes |
| Tables | Block Kit |
| Fields | Yes |
| Images in cards | Yes |
| Modals | Yes |
Conversations
| Feature | Supported |
|---|---|
| Slash commands | Yes |
| Mentions | Yes |
| Add reactions | Yes |
| Remove reactions | Yes |
| Typing indicator | Yes |
| DMs | Yes |
| Ephemeral messages | Yes (native) |
Message history
| Feature | Supported |
|---|---|
| Fetch messages | Yes |
| Fetch single message | Yes |
| Fetch thread info | Yes |
| Fetch channel messages | Yes |
| List threads | Yes |
| Fetch channel info | Yes |
| Post channel message | Yes |
Platform-specific
| Feature | Supported |
|---|---|
| Assistants API | Yes |
| Member joined channel | Yes |
| App Home tab | Yes |
Direct WebClient access
Use adapter.webClient to get a typed WebClient from @slack/web-api
for any Web API call that isn't wrapped by the SDK's high-level methods.
import type { SlackAdapter } from "@chat-adapter/slack";
bot.onAction("pin-this", async (event) => {
const slack = bot.getAdapter("slack") as SlackAdapter;
await slack.webClient.pins.add({
channel: event.thread!.channel.id.replace(/^slack:/, ""),
timestamp: event.messageId,
});
});
The returned client is bound to the bot token resolved in this order:
- The token from the current request context — set automatically during
webhook handling, or by
adapter.withBotToken(token, fn). - The default
botToken, when configured as a static string or a synchronous resolver function.
adapter.webClient throws AuthenticationError outside of any context
in multi-workspace mode, or when botToken is configured as an async
resolver function. For both cases, await the token first and bind it
explicitly:
const install = await slackAdapter.getInstallation(teamId);
if (!install) throw new Error("Workspace not installed");
await slackAdapter.withBotToken(install.botToken, async () => {
const me = await slackAdapter.webClient.auth.test();
console.log("Bot user:", me.user_id);
});
The previous
.clientgetter still works as a deprecated alias for.webClient.
Internal API calls (postMessage, editMessage, fetchMessages, etc.) are
unaffected — they continue to resolve tokens through the same async path
they always have.
Slack Assistants API
The adapter supports Slack's Assistants API for building AI-powered assistant experiences. This enables suggested prompts, status indicators, and thread titles in assistant DM threads.
Event handlers
Register handlers on the Chat instance:
bot.onAssistantThreadStarted(async (event) => {
const slack = bot.getAdapter("slack") as SlackAdapter;
await slack.setSuggestedPrompts(event.channelId, event.threadTs, [
{ title: "Summarize", message: "Summarize this channel" },
{ title: "Draft", message: "Help me draft a message" },
]);
});
bot.onAssistantContextChanged(async (event) => {
// User navigated to a different channel with the assistant panel open
});
Adapter methods
The SlackAdapter exposes these methods for the Assistants API:
| Method | Description |
|---|---|
setSuggestedPrompts(channelId, threadTs, prompts, title?) |
Show prompt suggestions in the thread |
setAssistantStatus(channelId, threadTs, status) |
Show a thinking/status indicator |
setAssistantTitle(channelId, threadTs, title) |
Set the thread title (shown in History) |
publishHomeView(userId, view) |
Publish a Home tab view for a user |
startTyping(threadId, status) |
Show a custom loading status (requires assistant:write scope) |
Required scopes and events
Add these to your Slack app manifest for Assistants API support:
oauth_config:
scopes:
bot:
- assistant:write
settings:
event_subscriptions:
bot_events:
- assistant_thread_started
- assistant_thread_context_changed
Stream with stop blocks
When streaming in an assistant thread, attach Block Kit elements to the final message by wrapping the stream in a StreamingPlan and passing endWith:
import { StreamingPlan } from "chat";
await thread.post(
new StreamingPlan(textStream, {
endWith: [
{ type: "actions", elements: [{ type: "button", text: { type: "plain_text", text: "Retry" }, action_id: "retry" }] },
],
})
);
Troubleshooting
handleOAuthCallback throws "Adapter not initialized"
- Call
await bot.initialize()beforehandleOAuthCallback()in your callback route. - In a Next.js app, this ensures:
- state adapter is connected
- the Slack adapter is attached to Chat
- installation writes succeed
const slackAdapter = bot.getAdapter("slack");
await bot.initialize();
await slackAdapter.handleOAuthCallback(request);
"Invalid signature" error
- Verify
SLACK_SIGNING_SECRETis correct - Check that the request timestamp is within 5 minutes (clock sync issue)
- If using a custom
webhookVerifier, the error also surfaces when the verifier throws or returns a falsy value
Bot not responding to messages
- Verify event subscriptions are configured
- Check that the bot has been added to the channel
- Ensure the webhook URL is correct and accessible
License
MIT