chat 4.30.0 → 4.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,143 @@
1
+ ---
2
+ title: CLI
3
+ description: Scaffold a Chat SDK bot app with a single command.
4
+ ---
5
+
6
+ `create-chat-sdk` creates a minimal Next.js app for Chat SDK bots.
7
+
8
+ The CLI will generate your `Chat` configuration, webhook route, `.env.example` file, dependencies, and optional Web adapter route from the adapter catalog.
9
+
10
+ ## Quick start
11
+
12
+ <CodeBlockTabs defaultValue="npm">
13
+ <CodeBlockTabsList>
14
+ <CodeBlockTabsTrigger value="npm">npm</CodeBlockTabsTrigger>
15
+ <CodeBlockTabsTrigger value="pnpm">pnpm</CodeBlockTabsTrigger>
16
+ <CodeBlockTabsTrigger value="yarn">yarn</CodeBlockTabsTrigger>
17
+ <CodeBlockTabsTrigger value="bun">bun</CodeBlockTabsTrigger>
18
+ </CodeBlockTabsList>
19
+ <CodeBlockTab value="npm">
20
+ ```bash
21
+ npm create chat-sdk@latest my-bot
22
+ ```
23
+ </CodeBlockTab>
24
+ <CodeBlockTab value="pnpm">
25
+ ```bash
26
+ pnpm create chat-sdk@latest my-bot
27
+ ```
28
+ </CodeBlockTab>
29
+ <CodeBlockTab value="yarn">
30
+ ```bash
31
+ yarn create chat-sdk my-bot
32
+ ```
33
+ </CodeBlockTab>
34
+ <CodeBlockTab value="bun">
35
+ ```bash
36
+ bunx create-chat-sdk@latest my-bot
37
+ ```
38
+ </CodeBlockTab>
39
+ </CodeBlockTabs>
40
+
41
+ <Callout type="info">
42
+ `create-chat-sdk` automatically detects when it is being run by Cursor, Claude Code, or another coding agent. In agent environments, pass at least one platform adapter with `--adapter`; the state adapter defaults to `memory`. The CLI runs non-interactively and uses `my-bot` when no project name is provided. Pass `--interactive` to force prompts.
43
+ </Callout>
44
+
45
+ ## Non-interactive usage
46
+
47
+ Pass platform and state adapters with `--adapter`:
48
+
49
+ ```bash
50
+ npm create chat-sdk@latest -- my-bot --adapter slack redis -y
51
+ ```
52
+
53
+ <Callout type="warn">
54
+ With npm, the `--` separator is required because npm consumes flags before it (`-y` is npm's own `--yes`) instead of forwarding them to the CLI. `pnpm create` and `yarn create` forward flags without it.
55
+ </Callout>
56
+
57
+ The interactive prompt lists official adapters by default. Pass `--vendor` to list only vendor-official adapters instead. Automation and coding agents can install any CLI-supported official or vendor adapter directly with `--adapter`.
58
+
59
+ Adapters that require a long-running process, including Matrix and Lark, cannot run on the webhook-only serverless runtime and are not available through the CLI. Add them to an existing project manually instead.
60
+
61
+ <AdapterSlugList />
62
+
63
+ Examples:
64
+
65
+ ```bash
66
+ npm create chat-sdk@latest -- slack-bot --adapter slack memory -y --skip-install
67
+ npm create chat-sdk@latest -- gchat-bot --adapter gchat redis -y --pm pnpm
68
+ npm create chat-sdk@latest -- email-bot --adapter resend postgres -y --no-git
69
+ npm create chat-sdk@latest -- my-bot --adapter slack memory -y --force
70
+ ```
71
+
72
+ ## What gets generated
73
+
74
+ The scaffolded app is webhook-only. It does not include pages, layouts, or a client UI.
75
+
76
+ ```txt
77
+ src/
78
+ lib/bot.ts Bot configuration and handlers
79
+ app/api/webhooks/[platform]/route.ts Dynamic platform webhook route
80
+ app/api/chat/route.ts Web adapter route, only when selected
81
+ app/api/discord/gateway/route.ts Discord Gateway listener, only when selected
82
+ .env.example Required environment variables
83
+ next.config.ts Next.js server config
84
+ vercel.json Cron schedules, only when needed
85
+ .chat-sdk.json Generated file ownership for safe reruns
86
+ package.json Adapter dependencies
87
+ ```
88
+
89
+ Webhook endpoints use the selected adapter slug:
90
+
91
+ ```txt
92
+ /api/webhooks/slack
93
+ /api/webhooks/gchat
94
+ /api/webhooks/discord
95
+ ```
96
+
97
+ When the [Web adapter](/adapters/official/web) is selected, the CLI also creates `/api/chat` for browser chat requests and a small `getUser` auth stub. Replace the stub with your app's real authentication logic so each web conversation can be associated with the correct user.
98
+
99
+ When the [Discord adapter](/adapters/official/discord) is selected, the CLI also creates a Gateway listener at `/api/discord/gateway` and a `vercel.json` cron that calls it. Discord delivers slash commands and button clicks to the webhook route, but regular messages and reactions only arrive over the Gateway WebSocket, so the cron keeps that connection alive and forwards events to `/api/webhooks/discord`. Set a `CRON_SECRET` environment variable to authenticate the cron requests. The generated serverless Gateway cron requires [Vercel Pro or Enterprise](https://vercel.com/docs/cron-jobs/usage-and-pricing) because it runs every nine minutes.
100
+
101
+ ## Reference
102
+
103
+ | Option | Description |
104
+ | --- | --- |
105
+ | `[name]` | Name of the project. |
106
+ | `-d, --description <text>` | Project description. |
107
+ | `--adapter <values...>` | Platform or state adapters to include. |
108
+ | `--vendor` | List only vendor-official adapters in the interactive prompt. |
109
+ | `--pm <manager>` | Package manager to use: `npm`, `yarn`, `pnpm`, or `bun`. |
110
+ | `-y, --yes` | Skip prompts and accept defaults. |
111
+ | `--interactive` | Always prompt, even when a coding agent environment is detected. |
112
+ | `-f, --force` | Overwrite generated files in an existing directory. |
113
+ | `-s, --skip-install` | Skip dependency installation. |
114
+ | `--no-git` | Skip git repository initialization. |
115
+ | `-q, --quiet` | Suppress non-essential output. |
116
+
117
+ Color output follows the [NO_COLOR standard](https://no-color.org/) — set `NO_COLOR=1` to disable colors.
118
+
119
+ ## Customize your bot
120
+
121
+ Most bot behavior lives in `src/lib/bot.ts`. Start there when you want to:
122
+
123
+ - Add or change handlers like `onNewMention`, `onSubscribedMessage`, `onNewMessage`, reactions, actions, or slash commands.
124
+ - Adjust adapter configuration, for example passing explicit credentials or platform-specific options instead of relying only on environment variables.
125
+ - If you selected the memory state adapter, switch to Redis, ioredis, or PostgreSQL before deploying to production.
126
+
127
+ The generated file includes starter handlers for mentions and subscribed thread replies.
128
+
129
+ ## Next steps
130
+
131
+ After scaffolding:
132
+
133
+ ```bash
134
+ cd my-bot
135
+ cp .env.example .env.local
136
+ npm run dev
137
+ ```
138
+
139
+ Fill in the generated environment variables, expose your local server, and configure each selected platform to call its `/api/webhooks/{adapter}` endpoint.
140
+
141
+ ## Resources
142
+
143
+ See guides, templates, and examples on the [resources](/resources) page.
@@ -105,7 +105,7 @@ try {
105
105
  }}
106
106
  />
107
107
 
108
- See the [feature matrix](/docs/adapters) for which features are supported on each platform.
108
+ See the [feature matrix](/docs/platform-adapters) for which features are supported on each platform.
109
109
 
110
110
  ### LockError
111
111
 
@@ -19,14 +19,14 @@ Learn the core patterns for handling incoming events and posting messages back t
19
19
  Connect your bot to chat platforms and persist state across restarts.
20
20
 
21
21
  <Cards>
22
- <Card title="Platform Adapters" description="Platform-specific adapters for Slack, Teams, Google Chat, Discord, Telegram, GitHub, and Linear." href="/docs/adapters" />
23
- <Card title="State Adapters" description="Pluggable state adapters for thread subscriptions, distributed locking, and caching." href="/docs/state" />
22
+ <Card title="Platform Adapters" description="Platform-specific adapters for Slack, Teams, Google Chat, Discord, Telegram, GitHub, and Linear." href="/docs/platform-adapters" />
23
+ <Card title="State Adapters" description="Pluggable state adapters for thread subscriptions, distributed locking, and caching." href="/docs/state-adapters" />
24
24
  </Cards>
25
25
 
26
26
  Browse all official and community adapters on the [Adapters](/adapters) page.
27
27
 
28
28
  ## Resources
29
29
 
30
- - [The Complete Guide to Chat SDK](https://vercel.com/kb/guide/the-complete-guide-to-chat-sdk) — End-to-end walkthrough that takes you from zero to a deployed multi-platform bot, covering adapters, state, handlers, cards, and streaming.
30
+ - [The Complete Guide to Chat SDK](https://vercel.com/kb/guide/the-complete-guide-to-chat-sdk?utm_source=chat-sdk_site&utm_medium=docs&utm_campaign=getting-started&utm_content=the-complete-guide-to-chat-sdk) — End-to-end walkthrough that takes you from zero to a deployed multi-platform bot, covering adapters, state, handlers, cards, and streaming.
31
31
 
32
- See all guides and templates on the [resources](/resources) page.
32
+ See all guides and templates on the [resources](/resources?utm_source=chat-sdk_site&utm_medium=docs&utm_campaign=getting-started&utm_content=resources) page.
package/docs/index.mdx CHANGED
@@ -55,7 +55,7 @@ Each adapter factory auto-detects credentials from environment variables (`SLACK
55
55
  | Microsoft Teams | `@chat-adapter/teams` | Yes | Read-only | Yes | Yes | Native (DMs) / Buffered | Yes |
56
56
  | Google Chat | `@chat-adapter/gchat` | Yes | Yes | Yes | No | Post+Edit | Yes |
57
57
  | Discord | `@chat-adapter/discord` | Yes | Yes | Yes | No | Post+Edit | Yes |
58
- | Telegram | `@chat-adapter/telegram` | Yes | Yes | Partial | No | Private chat drafts / Post+Edit | Yes |
58
+ | Telegram | `@chat-adapter/telegram` | Yes | Yes | Partial | No | Rich drafts / Post+Edit | Yes |
59
59
  | GitHub | `@chat-adapter/github` | Yes | Yes | No | No | Buffered | No |
60
60
  | Linear | `@chat-adapter/linear` | Yes | Yes | No | No | Agent sessions / Post+Edit | No |
61
61
  | WhatsApp | `@chat-adapter/whatsapp` | N/A | Yes | Partial | No | Buffered | Yes |
package/docs/meta.json CHANGED
@@ -3,6 +3,7 @@
3
3
  "pages": [
4
4
  "index",
5
5
  "getting-started",
6
+ "create-chat-sdk",
6
7
  "---Usage---",
7
8
  "usage",
8
9
  "threads-messages-channels",
@@ -14,8 +15,10 @@
14
15
  "...ai",
15
16
  "---Adapters---",
16
17
  "adapters",
18
+ "platform-adapters",
17
19
  "slack-primitives",
18
- "state",
20
+ "teams-primitives",
21
+ "state-adapters",
19
22
  "---Messaging---",
20
23
  "streaming",
21
24
  "direct-messages",
@@ -0,0 +1,148 @@
1
+ ---
2
+ title: Platform Adapters
3
+ description: Platform-specific adapters that connect your bot to any messaging platform.
4
+ type: overview
5
+ prerequisites:
6
+ - /docs/getting-started
7
+ - /docs/adapters
8
+ ---
9
+
10
+ Platform adapters handle webhook verification, message parsing, and API calls for each messaging platform. Install only the adapters you need. Browse all available adapters, including community-built ones, on the [Adapters](/adapters) page.
11
+
12
+ Need a browser chat UI? See the [Web adapter](/adapters/official/web). It speaks the AI SDK UI stream protocol and works with React (`@ai-sdk/react`), Vue (`@ai-sdk/vue`), and Svelte (`@ai-sdk/svelte`), so the same bot serves Slack, Teams, and any browser framework out of the box.
13
+
14
+ Ready to build your own? Follow the [building](/docs/contributing/building) guide.
15
+
16
+ ## Feature matrix
17
+
18
+ <GlobalFeatureMatrix type="platform" />
19
+
20
+ ### Messaging
21
+
22
+ | Feature | [Slack](/adapters/slack) | [Teams](/adapters/teams) | [Google Chat](/adapters/gchat) | [Discord](/adapters/discord) | [Telegram](/adapters/telegram) | [GitHub](/adapters/github) | [Linear](/adapters/linear) | [WhatsApp](/adapters/whatsapp) | [Messenger](/adapters/messenger) |
23
+ |---------|-------|-------|-------------|---------|---------|--------|--------|-----------|-----------|
24
+ | Post message | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> |
25
+ | Edit message | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Warn /> Partial | <Cross /> | <Cross /> |
26
+ | Delete message | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Warn /> Partial | <Cross /> | <Cross /> |
27
+ | File uploads | <Check /> | <Check /> | <Cross /> | <Check /> | <Warn /> Single file/media | <Cross /> | <Cross /> | <Check /> Images, audio, docs | <Cross /> |
28
+ | Streaming | <Check /> Native | <Warn /> Native (DMs) / Buffered | <Warn /> Post+Edit | <Warn /> Post+Edit | <Warn /> Rich drafts / Post+Edit | <Warn /> Buffered | <Warn /> Agent sessions / Post+Edit | <Warn /> Buffered | <Warn /> Buffered |
29
+ | Scheduled messages | <Check /> Native | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> |
30
+
31
+ ### Rich content
32
+
33
+ | Feature | Slack | Teams | Google Chat | Discord | Telegram | GitHub | Linear | WhatsApp | Messenger |
34
+ |---------|-------|-------|-------------|---------|----------|--------|--------|-----------|-----------|
35
+ | Card format | Block Kit | Adaptive Cards | Google Chat Cards | Embeds | Markdown + inline keyboard buttons | GFM Markdown | Markdown | WhatsApp templates | Generic/Button Templates |
36
+ | Buttons | <Check /> | <Check /> | <Check /> | <Check /> | <Warn /> Inline keyboard callbacks | <Cross /> | <Cross /> | <Check /> Interactive replies | <Warn /> Max 3, postback |
37
+ | Link buttons | <Check /> | <Check /> | <Check /> | <Check /> | <Warn /> Inline keyboard URLs | <Cross /> | <Cross /> | <Cross /> | <Check /> |
38
+ | Select menus | <Check /> | <Cross /> | <Check /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> |
39
+ | Tables | <Check /> Block Kit | <Check /> GFM | <Warn /> ASCII | <Check /> GFM | <Warn /> Native messages / ASCII cards | <Check /> GFM | <Check /> GFM | <Cross /> | <Warn /> ASCII |
40
+ | Fields | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Warn /> Template variables | <Warn /> ASCII |
41
+ | Images in cards | <Check /> | <Check /> | <Check /> | <Check /> | <Cross /> | <Check /> | <Cross /> | <Check /> | <Check /> |
42
+ | Modals | <Check /> | <Check /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> |
43
+
44
+ ### Conversations
45
+
46
+ | Feature | Slack | Teams | Google Chat | Discord | Telegram | GitHub | Linear | WhatsApp | Messenger |
47
+ |---------|-------|-------|-------------|---------|----------|--------|--------|-----------|-----------|
48
+ | Slash commands | <Check /> | <Cross /> | <Cross /> | <Check /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> |
49
+ | Mentions | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Cross /> | <Check /> |
50
+ | Add reactions | <Check /> | <Cross /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Cross /> |
51
+ | Remove reactions | <Check /> | <Cross /> | <Check /> | <Check /> | <Check /> | <Warn /> | <Warn /> | <Check /> | <Cross /> |
52
+ | Typing indicator | <Check /> | <Check /> | <Cross /> | <Check /> | <Check /> | <Cross /> | <Warn /> Agent sessions | <Warn /> | <Check /> |
53
+ | DMs | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Cross /> | <Cross /> | <Check /> | <Check /> |
54
+ | Ephemeral messages | <Check /> Native | <Cross /> | <Check /> Native | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> |
55
+ | User lookup ([`getUser`](/docs/api/chat#getuser)) | <Check /> | <Warn /> Cached | <Warn /> Cached | <Check /> | <Warn /> Seen users | <Check /> | <Check /> | <Cross /> | <Cross /> |
56
+ | Parent subject ([`message.subject`](/docs/subject)) | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Check /> | <Check /> | <Cross /> | <Cross /> |
57
+ | Native client ([`.webClient` / `.octokit` / `.linearClient`](/docs/api/chat#getadapter)) | <Check /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Check /> | <Check /> | <Cross /> | <Cross /> |
58
+ | Custom API endpoint (`apiUrl`) | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> |
59
+
60
+ ### Message history
61
+
62
+ | Feature | Slack | Teams | Google Chat | Discord | Telegram | GitHub | Linear | WhatsApp | Messenger |
63
+ |---------|-------|-------|-------------|---------|----------|--------|--------|-----------|-----------|
64
+ | Fetch messages | <Check /> | <Check /> | <Check /> | <Check /> | <Warn /> Cached | <Check /> | <Check /> | <Warn /> Cached sent messages only | <Warn /> Cached sent messages only |
65
+ | Fetch single message | <Check /> | <Cross /> | <Cross /> | <Cross /> | <Warn /> Cached | <Cross /> | <Cross /> | <Cross /> | <Warn /> Cached |
66
+ | Fetch thread info | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> |
67
+ | Fetch channel messages | <Check /> | <Check /> | <Check /> | <Check /> | <Warn /> Cached | <Check /> | <Cross /> | <Cross /> | <Warn /> Cached |
68
+ | List threads | <Check /> | <Check /> | <Check /> | <Check /> | <Cross /> | <Check /> | <Cross /> | <Cross /> | <Cross /> |
69
+ | Fetch channel info | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Cross /> | <Cross /> | <Check /> |
70
+ | Post channel message | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Cross /> | <Cross /> | <Check /> | <Check /> |
71
+
72
+ <Callout type="info">
73
+ <Warn /> indicates partial support. The feature works with limitations. See individual adapter pages for details.
74
+ </Callout>
75
+
76
+ ## How adapters work
77
+
78
+ Each adapter implements a standard interface that the `Chat` class uses to route events and send messages. When a webhook arrives:
79
+
80
+ 1. The adapter verifies the request signature
81
+ 2. Parses the platform-specific payload into a normalized `Message`
82
+ 3. Routes to your handlers via the `Chat` class
83
+ 4. Converts outgoing messages from markdown/AST/cards to the platform's native format
84
+
85
+ ## Using multiple adapters
86
+
87
+ Register multiple [adapters](/adapters) and your event handlers work across all of them:
88
+
89
+ ```typescript title="lib/bot.ts" lineNumbers
90
+ import { Chat } from "chat";
91
+ import { createSlackAdapter } from "@chat-adapter/slack";
92
+ import { createTeamsAdapter } from "@chat-adapter/teams";
93
+ import { createGoogleChatAdapter } from "@chat-adapter/gchat";
94
+ import { createRedisState } from "@chat-adapter/state-redis";
95
+
96
+ const bot = new Chat({
97
+ userName: "mybot",
98
+ adapters: {
99
+ slack: createSlackAdapter(),
100
+ teams: createTeamsAdapter(),
101
+ gchat: createGoogleChatAdapter(),
102
+ },
103
+ state: createRedisState(),
104
+ });
105
+
106
+ // This handler fires for mentions on any platform
107
+ bot.onNewMention(async (thread) => {
108
+ await thread.subscribe();
109
+ await thread.post("Hello!");
110
+ });
111
+ ```
112
+
113
+ Each adapter auto-detects credentials from environment variables, so you only need to pass config when overriding defaults.
114
+
115
+ <Callout type="info">
116
+ The examples above use Redis for state. See [State Adapters](/docs/state-adapters) for all available options.
117
+ </Callout>
118
+
119
+ Each adapter creates a webhook handler accessible via `bot.webhooks.<name>`.
120
+
121
+ ## Customizing an adapter via subclassing
122
+
123
+ Each official adapter exposes its extension surface as `protected` members so you can subclass it to override or extend platform-specific behavior without forking the package. Use this when you need to handle a payload type the built-in adapter doesn't cover, intercept verification, or wrap an existing handler.
124
+
125
+ ```typescript title="lib/custom-telegram.ts" lineNumbers
126
+ import { TelegramAdapter, type TelegramUpdate } from "@chat-adapter/telegram";
127
+ import type { WebhookOptions } from "chat";
128
+
129
+ export class CustomTelegramAdapter extends TelegramAdapter {
130
+ protected override processUpdate(
131
+ update: TelegramUpdate,
132
+ options?: WebhookOptions
133
+ ): void {
134
+ // Handle a payload type the base adapter doesn't, e.g. chat_join_request.
135
+ if ("chat_join_request" in update) {
136
+ this.logger.info("Received chat_join_request", { update });
137
+ return;
138
+ }
139
+ super.processUpdate(update, options);
140
+ }
141
+ }
142
+ ```
143
+
144
+ Construct your subclass anywhere you'd construct the base adapter, for example, `adapters: { telegram: new CustomTelegramAdapter({ ... }) }`. Members marked `private` intentionally remain inaccessible. If you find a hook you need that isn't `protected`, please open an issue.
145
+
146
+ <Callout type="warn">
147
+ The `protected` extension surface is intentionally broader than the public API but is not yet considered fully stable. Method signatures may evolve in minor releases as we learn from real-world subclasses. Pin the adapter version you build against, watch the changelog for the affected adapter, and prefer overriding the smallest hook that solves your problem so upgrades stay easy. If you rely on a particular hook, please open an issue so we can promote it to a stable, documented extension point.
148
+ </Callout>
@@ -8,11 +8,12 @@ related:
8
8
  - /docs/modals
9
9
  - /adapters/official/slack
10
10
  - /adapters/official/discord
11
+ - /adapters/official/telegram
11
12
  ---
12
13
 
13
14
  Slash commands let users invoke your bot with `/command` syntax. Register handlers with `onSlashCommand` to respond.
14
15
 
15
- Slash commands are supported on [Slack](/adapters/official/slack) and [Discord](/adapters/official/discord).
16
+ Slash commands are supported on [Slack](/adapters/official/slack), [Discord](/adapters/official/discord), and [Telegram](/adapters/official/telegram).
16
17
 
17
18
  ## Handle a specific command
18
19
 
@@ -112,6 +113,10 @@ bot.onModalSubmit("feedback_form", async (event) => {
112
113
  });
113
114
  ```
114
115
 
116
+ ## Telegram
117
+
118
+ Telegram supports bot commands such as `/status` and `/status@mybot`. Register handlers with `onSlashCommand`; commands addressed to another bot are ignored as slash commands and continue through the normal message path.
119
+
115
120
  ## Discord
116
121
 
117
122
  Discord slash commands are received via [HTTP Interactions](/adapters/official/discord#http-interactions-vs-gateway) — no Gateway connection is needed. The adapter automatically sends a deferred response to Discord, then resolves it when your handler calls `event.channel.post()`.
@@ -59,7 +59,7 @@ await thread.post(stream);
59
59
  | Platform | Method | Description |
60
60
  |----------|--------|-------------|
61
61
  | Slack | Native streaming API | Uses Slack's `chatStream` for smooth, real-time updates |
62
- | Telegram | Private chat draft previews | Uses Telegram's `sendMessageDraft` in private chats and falls back to post + edit elsewhere |
62
+ | Telegram | Private chat rich draft previews | Uses Telegram's `sendRichMessageDraft` in private chats, persists the final response with `sendRichMessage`, and falls back to post + edit elsewhere |
63
63
  | Teams | Native (DMs) / Buffered (group chats) | Uses the Teams SDK's native `stream.emit()` for direct messages; accumulates chunks and posts one final message when no native streamer is active |
64
64
  | Google Chat | Post + Edit | Posts a message then edits it as chunks arrive |
65
65
  | Discord | Post + Edit | Posts a message then edits it as chunks arrive |
@@ -0,0 +1,255 @@
1
+ ---
2
+ title: Teams Low-Level APIs
3
+ description: Use Teams Activity parsing, Bot Connector calls, Graph reads, formatting, Adaptive Cards, and Task Module helpers without the full Chat runtime.
4
+ type: guide
5
+ prerequisites:
6
+ - /adapters/official/teams
7
+ related:
8
+ - /docs/handling-events
9
+ - /docs/cards
10
+ - /docs/modals
11
+ ---
12
+
13
+ The Teams adapter is the right default for most bots. It validates Bot Framework requests through the Microsoft Teams SDK, parses Activities, stores conversation context, renders Adaptive Cards, reads Graph history, and routes events through `Chat`.
14
+
15
+ Use the low-level Teams subpaths when your app already owns routing, state, sessions, or workflow execution and only needs Teams-specific primitives.
16
+
17
+ | Subpath | Use for |
18
+ |---------|---------|
19
+ | `@chat-adapter/teams/webhook` | Parse Bot Framework Activity JSON, classify common payloads, and extract continuation data |
20
+ | `@chat-adapter/teams/api` | Fetch-based Bot Connector calls for messages, updates, deletes, typing, and conversations |
21
+ | `@chat-adapter/teams/graph` | Fetch-based Microsoft Graph reads for chats, channels, channel messages, and replies |
22
+ | `@chat-adapter/teams/format` | Teams HTML, mention, Markdown-ish, and emoji string helpers |
23
+ | `@chat-adapter/teams/cards` | Runtime-free conversion from simple card objects and input requests to Adaptive Cards |
24
+ | `@chat-adapter/teams/modals` | Runtime-free Task Module Adaptive Card helpers and submit parsing |
25
+
26
+ <Callout type="warning">
27
+ The webhook subpath parses Activities only. It does not verify Microsoft Bot Framework JWTs. For production request validation, use `createTeamsAdapter` or the Microsoft Teams SDK request pipeline before handing the Activity to these helpers.
28
+ </Callout>
29
+
30
+ ## Webhooks
31
+
32
+ Teams sends Bot Framework Activity JSON. `readTeamsWebhook` reads the request body and classifies the Activity, but it intentionally does not perform JWT validation.
33
+
34
+ ```typescript title="app/api/teams/route.ts" lineNumbers
35
+ import { postTeamsMessage } from "@chat-adapter/teams/api";
36
+ import { readTeamsWebhook } from "@chat-adapter/teams/webhook";
37
+
38
+ export async function POST(request: Request) {
39
+ const payload = await readTeamsWebhook(request, {
40
+ botAppId: process.env.TEAMS_APP_ID,
41
+ });
42
+
43
+ if (payload.kind === "message") {
44
+ await postTeamsMessage({
45
+ conversationId: payload.continuation.conversationId,
46
+ credentials: {
47
+ appId: process.env.TEAMS_APP_ID!,
48
+ appPassword: process.env.TEAMS_APP_PASSWORD!,
49
+ tenantId: payload.continuation.tenantId,
50
+ },
51
+ markdownText: `received: ${payload.text}`,
52
+ serviceUrl: payload.continuation.serviceUrl,
53
+ });
54
+ }
55
+
56
+ return new Response(null, { status: 200 });
57
+ }
58
+ ```
59
+
60
+ `parseTeamsWebhookBody` returns typed payloads:
61
+
62
+ | Kind | Teams surface |
63
+ |------|---------------|
64
+ | `message` | Message activities |
65
+ | `message_reaction` | Reaction activities |
66
+ | `card_action` | Adaptive Card actions and `Action.Submit` message activities |
67
+ | `dialog_open` | Task Module `task/fetch` invokes |
68
+ | `dialog_submit` | Task Module `task/submit` invokes |
69
+ | `conversation_update` | Conversation membership and install context updates |
70
+ | `installation_update` | App installation updates |
71
+ | `unsupported` | Valid Activities not normalized by this helper yet |
72
+
73
+ Message-like payloads include `continuation`, which contains provider-native reply context:
74
+
75
+ ```typescript
76
+ type TeamsContinuation = {
77
+ activityId?: string;
78
+ channelId?: string;
79
+ conversationId: string;
80
+ replyToId?: string;
81
+ serviceUrl: string;
82
+ teamId?: string;
83
+ tenantId?: string;
84
+ };
85
+ ```
86
+
87
+ This is not a Chat SDK `Thread`. It is the durable Teams data you need to reply later with `@chat-adapter/teams/api`.
88
+
89
+ ## Bot Connector API
90
+
91
+ The API subpath calls the Bot Framework Connector REST API with `fetch`. It does not import `@microsoft/teams.apps`.
92
+
93
+ ```typescript title="teams.ts" lineNumbers
94
+ import {
95
+ deleteTeamsMessage,
96
+ postTeamsMessage,
97
+ sendTeamsTyping,
98
+ updateTeamsMessage,
99
+ } from "@chat-adapter/teams/api";
100
+
101
+ const credentials = {
102
+ appId: process.env.TEAMS_APP_ID!,
103
+ appPassword: process.env.TEAMS_APP_PASSWORD!,
104
+ tenantId: process.env.TEAMS_APP_TENANT_ID!,
105
+ };
106
+
107
+ const posted = await postTeamsMessage({
108
+ conversationId: "19:abc@thread.tacv2",
109
+ credentials,
110
+ markdownText: "**hello**",
111
+ serviceUrl: "https://smba.trafficmanager.net/teams/",
112
+ });
113
+
114
+ await updateTeamsMessage({
115
+ conversationId: "19:abc@thread.tacv2",
116
+ credentials,
117
+ messageId: posted.id,
118
+ serviceUrl: "https://smba.trafficmanager.net/teams/",
119
+ text: "updated",
120
+ });
121
+
122
+ await sendTeamsTyping({
123
+ conversationId: "19:abc@thread.tacv2",
124
+ credentials,
125
+ serviceUrl: "https://smba.trafficmanager.net/teams/",
126
+ });
127
+
128
+ await deleteTeamsMessage({
129
+ conversationId: "19:abc@thread.tacv2",
130
+ credentials,
131
+ messageId: posted.id,
132
+ serviceUrl: "https://smba.trafficmanager.net/teams/",
133
+ });
134
+ ```
135
+
136
+ Use `accessToken` in `credentials` when your runtime already owns Microsoft token acquisition. A direct `accessToken` must be scoped for the API you call it against — the Bot Connector subpath (`/api`) needs a `https://api.botframework.com/.default` token, while the Graph subpath (`/graph`) needs a `https://graph.microsoft.com/.default` token. Passing the same token to both will fail against one of them. When you supply `appId`/`appPassword` instead, each subpath requests the correct scope for you.
137
+
138
+ ## Graph
139
+
140
+ The Graph subpath reads Teams history with explicit Graph IDs. Unlike `TeamsAdapter`, it does not use the adapter state cache to infer `teamId`, `channelId`, or `chatId`.
141
+
142
+ ```typescript
143
+ import { listTeamsChannelMessages } from "@chat-adapter/teams/graph";
144
+
145
+ const messages = await listTeamsChannelMessages({
146
+ channelId: "19:channel@thread.tacv2",
147
+ credentials: {
148
+ appId: process.env.TEAMS_APP_ID!,
149
+ appPassword: process.env.TEAMS_APP_PASSWORD!,
150
+ tenantId: process.env.TEAMS_APP_TENANT_ID!,
151
+ },
152
+ limit: 25,
153
+ teamId: "19:team@thread.tacv2",
154
+ });
155
+
156
+ const latestText = messages.items[0]?.text;
157
+ ```
158
+
159
+ Graph reads require the same Microsoft Graph permissions as the full adapter. Channel and group-chat reads can use RSC permissions; DM reads require Azure AD application permissions such as `Chat.Read.All`.
160
+
161
+ ## Formatting
162
+
163
+ Teams renders message text as HTML. The format subpath provides small helpers for custom runtimes:
164
+
165
+ ```typescript
166
+ import {
167
+ formatTeamsMention,
168
+ markdownToTeamsHtml,
169
+ teamsHtmlToMarkdown,
170
+ } from "@chat-adapter/teams/format";
171
+
172
+ const html = markdownToTeamsHtml(
173
+ `${formatTeamsMention("Ada")} approved **deploy v2.4.1**`
174
+ );
175
+ const markdown = teamsHtmlToMarkdown("<p>Hello <strong>world</strong></p>");
176
+ ```
177
+
178
+ Use the full `TeamsFormatConverter` from `@chat-adapter/teams` when you need mdast conversion inside Chat SDK.
179
+
180
+ ## Cards
181
+
182
+ The cards subpath converts simple card objects into Adaptive Card JSON without importing the full `chat` JSX runtime.
183
+
184
+ ```typescript title="cards.ts" lineNumbers
185
+ import {
186
+ cardToAdaptiveCard,
187
+ cardToTeamsFallbackText,
188
+ } from "@chat-adapter/teams/cards";
189
+ import { postTeamsMessage } from "@chat-adapter/teams/api";
190
+
191
+ const card = {
192
+ children: [
193
+ { content: "deploy v2.4.1?", type: "text" },
194
+ {
195
+ children: [
196
+ { id: "approve", label: "Approve", style: "primary", type: "button" },
197
+ { id: "deny", label: "Deny", style: "danger", type: "button" },
198
+ ],
199
+ type: "actions",
200
+ },
201
+ ],
202
+ title: "Deployment",
203
+ type: "card",
204
+ } as const;
205
+
206
+ await postTeamsMessage({
207
+ adaptiveCard: cardToAdaptiveCard(card),
208
+ conversationId: payload.continuation.conversationId,
209
+ credentials,
210
+ serviceUrl: payload.continuation.serviceUrl,
211
+ text: cardToTeamsFallbackText(card),
212
+ });
213
+ ```
214
+
215
+ Use the full Chat SDK card JSX when you want cross-platform rendering. Use `@chat-adapter/teams/cards` when you are building a Teams-only runtime and want Adaptive Card output directly.
216
+
217
+ ## Modals
218
+
219
+ Teams Task Modules are invoke-based dialogs backed by Adaptive Cards. The modals subpath builds those cards and parses submit data.
220
+
221
+ ```typescript
222
+ import {
223
+ modalToAdaptiveCard,
224
+ parseTeamsDialogSubmitValues,
225
+ toTeamsTaskModuleResponse,
226
+ } from "@chat-adapter/teams/modals";
227
+
228
+ const modal = {
229
+ callbackId: "deploy",
230
+ children: [
231
+ { content: "Why deploy now?", type: "text" },
232
+ { id: "reason", label: "Reason", type: "text_input" },
233
+ ],
234
+ title: "Deploy",
235
+ type: "modal",
236
+ } as const;
237
+
238
+ const card = modalToAdaptiveCard(modal, { contextId: "deploy-1" });
239
+ const values = parseTeamsDialogSubmitValues(payload.value);
240
+
241
+ return Response.json(
242
+ toTeamsTaskModuleResponse({ action: "update", modal }, { contextId: "deploy-1" })
243
+ );
244
+ ```
245
+
246
+ ## Import Boundaries
247
+
248
+ The low-level Teams subpaths are designed to avoid the full runtime import graph:
249
+
250
+ - no `chat` import
251
+ - no `@chat-adapter/shared` import
252
+ - no `@microsoft/teams.apps` import
253
+ - no full adapter import
254
+
255
+ The package still installs the full Teams adapter dependencies. The subpaths keep your source and bundle imports clean, but they are not a package-size split.
package/docs/testing.mdx CHANGED
@@ -5,7 +5,7 @@ type: guide
5
5
  prerequisites:
6
6
  - /docs/getting-started
7
7
  related:
8
- - /docs/state
8
+ - /docs/state-adapters
9
9
  - /docs/handling-events
10
10
  - /docs/contributing/testing
11
11
  ---