chat 4.27.0 → 4.29.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.
- package/dist/ai/index.d.ts +501 -0
- package/dist/ai/index.js +500 -0
- package/dist/chat-D9UYaaNO.d.ts +3156 -0
- package/dist/chunk-HD375J7S.js +128 -0
- package/dist/{chunk-AN7MRAVW.js → chunk-V25FKIIL.js} +5 -1
- package/dist/index.d.ts +35 -2934
- package/dist/index.js +567 -210
- package/dist/{jsx-runtime-Co9uV6l7.d.ts → jsx-runtime-CFq1K_Ve.d.ts} +11 -1
- package/dist/jsx-runtime.d.ts +1 -1
- package/dist/jsx-runtime.js +1 -1
- package/docs/actions.mdx +52 -1
- package/docs/adapters.mdx +72 -36
- package/docs/ai/ai-sdk-tools.mdx +227 -0
- package/docs/ai/index.mdx +63 -0
- package/docs/ai/meta.json +4 -0
- package/docs/{api → ai}/to-ai-messages.mdx +16 -3
- package/docs/ai/types.mdx +243 -0
- package/docs/api/cards.mdx +4 -0
- package/docs/api/chat.mdx +132 -10
- package/docs/api/index.mdx +6 -6
- package/docs/api/markdown.mdx +28 -5
- package/docs/api/message.mdx +54 -1
- package/docs/api/meta.json +1 -0
- package/docs/api/modals.mdx +50 -0
- package/docs/api/postable-message.mdx +58 -4
- package/docs/api/thread.mdx +11 -3
- package/docs/api/transcripts.mdx +220 -0
- package/docs/cards.mdx +6 -0
- package/docs/concurrency.mdx +58 -15
- package/docs/contributing/building.mdx +74 -2
- package/docs/contributing/testing.mdx +4 -0
- package/docs/conversation-history.mdx +137 -0
- package/docs/direct-messages.mdx +23 -5
- package/docs/ephemeral-messages.mdx +1 -1
- package/docs/error-handling.mdx +15 -3
- package/docs/files.mdx +21 -1
- package/docs/handling-events.mdx +10 -7
- package/docs/index.mdx +8 -5
- package/docs/meta.json +17 -3
- package/docs/modals.mdx +24 -0
- package/docs/posting-messages.mdx +10 -4
- package/docs/slash-commands.mdx +4 -4
- package/docs/streaming.mdx +75 -27
- package/docs/subject.mdx +53 -0
- package/docs/testing.mdx +142 -0
- package/docs/threads-messages-channels.mdx +10 -1
- package/docs/usage.mdx +15 -2
- package/package.json +23 -2
- package/resources/guides/how-to-build-an-ai-agent-for-slack-with-chat-sdk-and-ai-sdk.md +1 -1
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Conversation History
|
|
3
|
+
description: Persist messages per user across every platform — for LLM context, audit, or compliance.
|
|
4
|
+
type: guide
|
|
5
|
+
prerequisites:
|
|
6
|
+
- /docs/state
|
|
7
|
+
related:
|
|
8
|
+
- /docs/handling-events
|
|
9
|
+
- /docs/api/transcripts
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
Bots that hold context across a user's conversations need somewhere to store it. The platform's own message history won't do — a user might talk to your bot in Slack today and Discord tomorrow, and you want the same memory to follow them.
|
|
13
|
+
|
|
14
|
+
`bot.transcripts` keeps a per-user transcript in your state adapter, keyed by a stable identifier you choose (an email, an internal user ID, anything that's the same person no matter where they are).
|
|
15
|
+
|
|
16
|
+
## Setup
|
|
17
|
+
|
|
18
|
+
You opt in by setting two fields on `ChatConfig`:
|
|
19
|
+
|
|
20
|
+
```typescript title="lib/bot.ts" lineNumbers
|
|
21
|
+
import { Chat } from "chat";
|
|
22
|
+
import { createSlackAdapter } from "@chat-adapter/slack";
|
|
23
|
+
import { createDiscordAdapter } from "@chat-adapter/discord";
|
|
24
|
+
import { createRedisState } from "@chat-adapter/state-redis";
|
|
25
|
+
|
|
26
|
+
const bot = new Chat({
|
|
27
|
+
userName: "mybot",
|
|
28
|
+
adapters: {
|
|
29
|
+
slack: createSlackAdapter(),
|
|
30
|
+
discord: createDiscordAdapter(),
|
|
31
|
+
},
|
|
32
|
+
state: createRedisState({ url: process.env.REDIS_URL! }),
|
|
33
|
+
|
|
34
|
+
// Resolve the cross-platform identifier for an inbound message.
|
|
35
|
+
// Return null for messages you don't want to remember.
|
|
36
|
+
identity: ({ author }) => author.email ?? null,
|
|
37
|
+
|
|
38
|
+
// Storage tuning. retention is the list TTL, refreshed on every append.
|
|
39
|
+
transcripts: {
|
|
40
|
+
retention: "30d",
|
|
41
|
+
maxPerUser: 200,
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
`transcripts` and `identity` are paired — set one without the other and the constructor throws. This keeps the API loud rather than silently no-op'ing on every call.
|
|
47
|
+
|
|
48
|
+
## Building LLM context
|
|
49
|
+
|
|
50
|
+
The most common pattern: append the user's message, build a prompt from recent transcript entries, post the reply, append the reply too.
|
|
51
|
+
|
|
52
|
+
```typescript title="lib/bot.ts" lineNumbers
|
|
53
|
+
bot.onSubscribedMessage(async (thread, msg) => {
|
|
54
|
+
await bot.transcripts.append(thread, msg);
|
|
55
|
+
|
|
56
|
+
const recent = await bot.transcripts.list({
|
|
57
|
+
userKey: msg.userKey!,
|
|
58
|
+
limit: 20,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const reply = await generateReply(recent, msg);
|
|
62
|
+
await thread.post(reply);
|
|
63
|
+
|
|
64
|
+
await bot.transcripts.append(
|
|
65
|
+
thread,
|
|
66
|
+
{ role: "assistant", text: reply },
|
|
67
|
+
{ userKey: msg.userKey! }
|
|
68
|
+
);
|
|
69
|
+
});
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
A few things worth knowing:
|
|
73
|
+
|
|
74
|
+
- **`msg.userKey`** is set automatically from your `identity` resolver before your handler runs. If the resolver returned `null`, it stays `undefined` and the `append` call no-ops.
|
|
75
|
+
- **Bot replies are explicit.** The SDK doesn't auto-capture `thread.post()` output — you decide what gets remembered. That's important for retries, intermediate streaming chunks, and anything you don't want feeding back into the model later.
|
|
76
|
+
- **Order is chronological.** `list` returns oldest-first, ready to feed into a model. Set `limit` to keep prompts bounded.
|
|
77
|
+
|
|
78
|
+
## Identity resolution
|
|
79
|
+
|
|
80
|
+
`identity` runs once per inbound message during dispatch. The `author`, `message`, and `adapter` name are all available:
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
identity: async ({ adapter, author, message }) => {
|
|
84
|
+
// Look up by email when the platform exposes it
|
|
85
|
+
if (author.email) {
|
|
86
|
+
return author.email;
|
|
87
|
+
}
|
|
88
|
+
// Or map a platform user to an internal ID
|
|
89
|
+
return await lookupUser(adapter, author.userId);
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Return `null` when you can't resolve a key. The SDK won't fall back to a platform-specific ID — that would silently fragment a user's transcript across platforms, which is exactly what this feature is here to prevent.
|
|
94
|
+
|
|
95
|
+
If your resolver throws, the SDK logs a warning and dispatches the message without a `userKey`. Handlers still run; only the persistence is skipped.
|
|
96
|
+
|
|
97
|
+
## Filtering entries
|
|
98
|
+
|
|
99
|
+
`list` accepts a few filters. They compose, and they're applied after `getList` — useful for narrowing prompts without restructuring storage.
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
// Recent N across all platforms
|
|
103
|
+
await bot.transcripts.list({ userKey: "mike@acme.com", limit: 50 });
|
|
104
|
+
|
|
105
|
+
// Single platform
|
|
106
|
+
await bot.transcripts.list({ userKey: "mike@acme.com", platforms: ["slack"] });
|
|
107
|
+
|
|
108
|
+
// Single thread
|
|
109
|
+
await bot.transcripts.list({
|
|
110
|
+
userKey: "mike@acme.com",
|
|
111
|
+
threadId: "slack:C123:1234.5678",
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// Only the user's own messages
|
|
115
|
+
await bot.transcripts.list({ userKey: "mike@acme.com", roles: ["user"] });
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Deleting a user's transcript
|
|
119
|
+
|
|
120
|
+
For data-subject requests or simple "forget me" flows:
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
await bot.transcripts.delete({ userKey: "mike@acme.com" });
|
|
124
|
+
// → { deleted: 47 }
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
This wipes every entry stored under the key. Single-entry and time-range deletes aren't part of the API — `appendToList` doesn't support them safely under concurrent writes.
|
|
128
|
+
|
|
129
|
+
## Where it's stored
|
|
130
|
+
|
|
131
|
+
`bot.transcripts` is backed by `StateAdapter.appendToList` / `getList` / `delete`. Every built-in state adapter (`memory`, `redis`, `ioredis`, `pg`) supports these primitives, so this works on whichever one you've already configured.
|
|
132
|
+
|
|
133
|
+
Entries are written under the key `transcripts:user:{userKey}` as a capped list. `appendToList` is atomic, so concurrent inbound messages on the same user don't race.
|
|
134
|
+
|
|
135
|
+
## Reference
|
|
136
|
+
|
|
137
|
+
See [Transcripts](/docs/api/transcripts) for full type signatures, configuration options, and the entry shape.
|
package/docs/direct-messages.mdx
CHANGED
|
@@ -1,20 +1,29 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: Direct
|
|
2
|
+
title: Direct Messages
|
|
3
3
|
description: Initiate DM conversations with users programmatically.
|
|
4
4
|
type: guide
|
|
5
5
|
prerequisites:
|
|
6
6
|
- /docs/usage
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
Open direct message conversations with users using `bot.openDM()`.
|
|
9
|
+
Open direct message conversations with users using `bot.openDM()`. For globally recognizable user IDs, the adapter is automatically inferred from the ID format.
|
|
10
10
|
|
|
11
11
|
## DM behavior
|
|
12
12
|
|
|
13
13
|
DMs behave slightly differently from channel messages:
|
|
14
14
|
|
|
15
|
-
- **
|
|
15
|
+
- **Direct message handlers** — if you register `onDirectMessage`, every incoming DM routes there before `onSubscribedMessage`, `onNewMention`, and pattern handlers. This keeps DM-centric flows like WhatsApp conversations, Telegram DMs, and web chat on one consistent handler.
|
|
16
|
+
- **Mention fallback** — if no `onDirectMessage` handlers are registered, DMs continue through normal routing. Unsubscribed DMs are treated as mentions, so existing `onNewMention` bots keep working without requiring the user to @-mention the bot.
|
|
16
17
|
- **Per-conversation threading** — Each top-level DM starts a new conversation. Thread replies within a DM continue the same conversation, giving you the same per-thread isolation as channels.
|
|
17
18
|
|
|
19
|
+
## Handle incoming DMs
|
|
20
|
+
|
|
21
|
+
```typescript title="lib/bot.ts" lineNumbers
|
|
22
|
+
bot.onDirectMessage(async (thread, message) => {
|
|
23
|
+
await thread.post(`You said: ${message.text}`);
|
|
24
|
+
});
|
|
25
|
+
```
|
|
26
|
+
|
|
18
27
|
## Open a DM
|
|
19
28
|
|
|
20
29
|
### From an Author object
|
|
@@ -40,10 +49,19 @@ const dmThread = await bot.openDM("U1234567890"); // Slack
|
|
|
40
49
|
|
|
41
50
|
| Format | Platform |
|
|
42
51
|
|--------|----------|
|
|
43
|
-
| `U...` | Slack |
|
|
52
|
+
| `U...` / `W...` | Slack |
|
|
44
53
|
| `29:...` | Teams |
|
|
45
54
|
| `users/...` | Google Chat |
|
|
46
|
-
|
|
|
55
|
+
| Numeric ID | Discord or Telegram |
|
|
56
|
+
|
|
57
|
+
<Callout type="info">
|
|
58
|
+
Numeric IDs can be ambiguous when multiple numeric-ID adapters are registered. For platforms whose user IDs are not globally distinguishable, call the adapter directly and wrap the returned thread ID with `bot.thread()`.
|
|
59
|
+
</Callout>
|
|
60
|
+
|
|
61
|
+
```typescript title="lib/bot.ts"
|
|
62
|
+
const threadId = await bot.getAdapter("whatsapp").openDM("15551234567");
|
|
63
|
+
const dmThread = bot.thread(threadId);
|
|
64
|
+
```
|
|
47
65
|
|
|
48
66
|
## Check if a thread is a DM
|
|
49
67
|
|
package/docs/error-handling.mdx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: Error
|
|
2
|
+
title: Error Handling
|
|
3
3
|
description: Handle rate limits, unsupported features, and other errors from adapters.
|
|
4
4
|
type: guide
|
|
5
5
|
prerequisites:
|
|
@@ -16,7 +16,19 @@ import { ChatError, RateLimitError, NotImplementedError, LockError } from "chat"
|
|
|
16
16
|
|
|
17
17
|
### ChatError
|
|
18
18
|
|
|
19
|
-
Base error class for all SDK errors. Every error below extends `ChatError`.
|
|
19
|
+
Base error class for all SDK errors. Every error below extends `ChatError`. The `code` property carries a machine-readable identifier you can branch on:
|
|
20
|
+
|
|
21
|
+
| Code | Thrown by | Meaning |
|
|
22
|
+
|------|-----------|---------|
|
|
23
|
+
| `NOT_SUPPORTED` | `bot.openDM`, `bot.getUser` | The resolved adapter doesn't implement this method |
|
|
24
|
+
| `INVALID_THREAD_ID` | `bot.thread`, internal routing | Thread ID does not match the `adapter:channel:thread` shape |
|
|
25
|
+
| `INVALID_CHANNEL_ID` | `bot.channel` | Channel ID does not match the `adapter:channel` shape |
|
|
26
|
+
| `ADAPTER_NOT_FOUND` | `bot.thread`, `bot.channel` | Thread/channel ID references an adapter that wasn't registered on this `Chat` instance |
|
|
27
|
+
| `AMBIGUOUS_USER_ID` | `bot.getUser`, `bot.openDM` | Numeric user ID could match more than one registered adapter (Discord/Telegram/GitHub) |
|
|
28
|
+
| `UNKNOWN_USER_ID_FORMAT` | `bot.getUser`, `bot.openDM` | The `userId` doesn't match any platform's known ID format |
|
|
29
|
+
| `RATE_LIMITED` | Any platform call | Platform returned 429; see `RateLimitError` below |
|
|
30
|
+
| `NOT_IMPLEMENTED` | Any platform call | The adapter doesn't implement this feature; see `NotImplementedError` below |
|
|
31
|
+
| `LOCK_FAILED` | Inbound message routing | Distributed lock was busy; see `LockError` below |
|
|
20
32
|
|
|
21
33
|
<TypeTable
|
|
22
34
|
type={{
|
|
@@ -25,7 +37,7 @@ Base error class for all SDK errors. Every error below extends `ChatError`.
|
|
|
25
37
|
type: 'string',
|
|
26
38
|
},
|
|
27
39
|
code: {
|
|
28
|
-
description: 'Machine-readable error code.',
|
|
40
|
+
description: 'Machine-readable error code (see table above).',
|
|
29
41
|
type: 'string',
|
|
30
42
|
},
|
|
31
43
|
cause: {
|
package/docs/files.mdx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: File
|
|
2
|
+
title: File Uploads
|
|
3
3
|
description: Send and receive files across chat platforms.
|
|
4
4
|
type: guide
|
|
5
5
|
prerequisites:
|
|
@@ -25,6 +25,26 @@ await thread.post({
|
|
|
25
25
|
});
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
+
### Typed attachments
|
|
29
|
+
|
|
30
|
+
Use `attachments` when you already have normalized `Attachment` objects and the adapter supports typed outgoing media. Telegram supports one outgoing attachment per message and uses the native media method for the attachment type:
|
|
31
|
+
|
|
32
|
+
```typescript title="lib/bot.ts" lineNumbers
|
|
33
|
+
await thread.post({
|
|
34
|
+
markdown: "Here's the image:",
|
|
35
|
+
attachments: [
|
|
36
|
+
{
|
|
37
|
+
data: imageBuffer,
|
|
38
|
+
name: "diagram.png",
|
|
39
|
+
mimeType: "image/png",
|
|
40
|
+
type: "image",
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
});
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Outgoing `attachments` are available on `{ raw }`, `{ markdown }`, and `{ ast }` messages. Card messages use `files` for uploads. Use `files` for generic uploads. On Telegram, `files` always upload as documents, while `attachments` preserve image, audio, video, or file media type. Use `data` or `fetchData` for private/authenticated files; URL-only attachments must be public URLs Telegram can fetch directly.
|
|
47
|
+
|
|
28
48
|
### Multiple files
|
|
29
49
|
|
|
30
50
|
```typescript title="lib/bot.ts" lineNumbers
|
package/docs/handling-events.mdx
CHANGED
|
@@ -16,9 +16,10 @@ Chat SDK uses an event-driven architecture. You register handlers for different
|
|
|
16
16
|
|
|
17
17
|
When a message arrives, the SDK evaluates handlers in this order:
|
|
18
18
|
|
|
19
|
-
1. **
|
|
20
|
-
2. **
|
|
21
|
-
3. **
|
|
19
|
+
1. **Direct messages** — if the thread is a DM and any `onDirectMessage` handlers are registered, they fire before `onSubscribedMessage`, `onNewMention`, and pattern handlers.
|
|
20
|
+
2. **Subscribed threads** — if the thread is subscribed, `onSubscribedMessage` fires and no other message handler runs. DMs only reach this step when no `onDirectMessage` handlers are registered.
|
|
21
|
+
3. **Mentions** — if the bot is @-mentioned in an unsubscribed thread, `onNewMention` fires. Unsubscribed DMs without direct handlers are treated as mentions for backward compatibility.
|
|
22
|
+
4. **Pattern matches** — if the message text matches any `onNewMessage` regex patterns, those handlers fire.
|
|
22
23
|
|
|
23
24
|
Reactions, slash commands, actions, and modals have their own dedicated routing and are not affected by subscription state.
|
|
24
25
|
|
|
@@ -68,7 +69,9 @@ bot.onNewMention(async (thread, message) => {
|
|
|
68
69
|
|
|
69
70
|
## Handling subscribed messages
|
|
70
71
|
|
|
71
|
-
`onSubscribedMessage` fires for every new message in a thread your bot has subscribed to. Once subscribed,
|
|
72
|
+
`onSubscribedMessage` fires for every new message in a non-DM thread your bot has subscribed to. Once subscribed, messages (including @-mentions) route here instead of `onNewMention`.
|
|
73
|
+
|
|
74
|
+
If an `onDirectMessage` handler is registered, DM messages route there before subscription routing. Without a direct handler, subscribed DMs route to `onSubscribedMessage`.
|
|
72
75
|
|
|
73
76
|
```typescript title="lib/bot.ts" lineNumbers
|
|
74
77
|
bot.onSubscribedMessage(async (thread, message) => {
|
|
@@ -94,7 +97,7 @@ Messages sent by the bot itself do not trigger this handler. You don't need to f
|
|
|
94
97
|
### Example: Conversational AI with history
|
|
95
98
|
|
|
96
99
|
```typescript title="lib/bot.ts" lineNumbers
|
|
97
|
-
import { toAiMessages } from "chat";
|
|
100
|
+
import { toAiMessages } from "chat/ai";
|
|
98
101
|
|
|
99
102
|
bot.onSubscribedMessage(async (thread, message) => {
|
|
100
103
|
await thread.startTyping();
|
|
@@ -111,7 +114,7 @@ bot.onSubscribedMessage(async (thread, message) => {
|
|
|
111
114
|
});
|
|
112
115
|
```
|
|
113
116
|
|
|
114
|
-
See [`toAiMessages`](/docs/
|
|
117
|
+
See [`toAiMessages`](/docs/ai/to-ai-messages) for all options including multi-user name prefixing, message transforms, and attachment handling.
|
|
115
118
|
|
|
116
119
|
### Example: Unsubscribe on keyword
|
|
117
120
|
|
|
@@ -299,7 +302,7 @@ These handlers are specific to the Slack platform and require the Slack adapter.
|
|
|
299
302
|
|
|
300
303
|
### Handling assistant threads
|
|
301
304
|
|
|
302
|
-
`onAssistantThreadStarted` fires when a user opens a new assistant thread in Slack. Use it with the [Slack Assistants API](/adapters/slack#slack-assistants-api) to set suggested prompts and status indicators.
|
|
305
|
+
`onAssistantThreadStarted` fires when a user opens a new assistant thread in Slack. Use it with the [Slack Assistants API](/adapters/official/slack#slack-assistants-api) to set suggested prompts and status indicators.
|
|
303
306
|
|
|
304
307
|
```typescript title="lib/bot.ts" lineNumbers
|
|
305
308
|
bot.onAssistantThreadStarted(async (event) => {
|
package/docs/index.mdx
CHANGED
|
@@ -4,7 +4,7 @@ description: A unified SDK for building chat bots across Slack, Microsoft Teams,
|
|
|
4
4
|
type: overview
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
Chat SDK is a TypeScript library for building chat bots that work across multiple platforms with a single codebase. Write your bot logic once and deploy it to Slack, Microsoft Teams, Google Chat, Discord, Telegram, GitHub, Linear, and
|
|
7
|
+
Chat SDK is a TypeScript library for building chat bots that work across multiple platforms with a single codebase. Write your bot logic once and deploy it to Slack, Microsoft Teams, Google Chat, Discord, Telegram, GitHub, Linear, WhatsApp, and Messenger.
|
|
8
8
|
|
|
9
9
|
## Why Chat SDK?
|
|
10
10
|
|
|
@@ -52,13 +52,14 @@ Each adapter factory auto-detects credentials from environment variables (`SLACK
|
|
|
52
52
|
| Platform | Package | Mentions | Reactions | Cards | Modals | Streaming | DMs |
|
|
53
53
|
|----------|---------|----------|-----------|-------|--------|-----------|-----|
|
|
54
54
|
| Slack | `@chat-adapter/slack` | Yes | Yes | Yes | Yes | Native | Yes |
|
|
55
|
-
| Microsoft Teams | `@chat-adapter/teams` | Yes | Read-only | Yes |
|
|
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
58
|
| Telegram | `@chat-adapter/telegram` | Yes | Yes | Partial | No | Post+Edit | Yes |
|
|
59
|
-
| GitHub | `@chat-adapter/github` | Yes | Yes | No | No |
|
|
60
|
-
| Linear | `@chat-adapter/linear` | Yes | Yes | No | No |
|
|
61
|
-
| WhatsApp | `@chat-adapter/whatsapp` | N/A | Yes | Partial | No |
|
|
59
|
+
| GitHub | `@chat-adapter/github` | Yes | Yes | No | No | Buffered | No |
|
|
60
|
+
| Linear | `@chat-adapter/linear` | Yes | Yes | No | No | Agent sessions / Post+Edit | No |
|
|
61
|
+
| WhatsApp | `@chat-adapter/whatsapp` | N/A | Yes | Partial | No | Buffered | Yes |
|
|
62
|
+
| Messenger | `@chat-adapter/messenger` | Yes | Receive-only | Partial | No | Buffered | Yes |
|
|
62
63
|
|
|
63
64
|
## AI coding agent support
|
|
64
65
|
|
|
@@ -77,6 +78,7 @@ The SDK is distributed as a set of packages you install based on your needs:
|
|
|
77
78
|
| Package | Description |
|
|
78
79
|
|---------|-------------|
|
|
79
80
|
| `chat` | Core SDK with `Chat` class, types, JSX runtime, and utilities |
|
|
81
|
+
| `chat/ai` | [AI utilities](/docs/ai) — [`createChatTools`](/docs/ai/ai-sdk-tools) for agent operations and [`toAiMessages`](/docs/ai/to-ai-messages) for converting chat history into AI SDK prompts |
|
|
80
82
|
| `@chat-adapter/slack` | Slack adapter |
|
|
81
83
|
| `@chat-adapter/teams` | Microsoft Teams adapter |
|
|
82
84
|
| `@chat-adapter/gchat` | Google Chat adapter |
|
|
@@ -85,6 +87,7 @@ The SDK is distributed as a set of packages you install based on your needs:
|
|
|
85
87
|
| `@chat-adapter/github` | GitHub Issues adapter |
|
|
86
88
|
| `@chat-adapter/linear` | Linear Issues adapter |
|
|
87
89
|
| `@chat-adapter/whatsapp` | WhatsApp Business adapter |
|
|
90
|
+
| `@chat-adapter/messenger` | Facebook Messenger adapter |
|
|
88
91
|
| `@chat-adapter/state-redis` | Redis state adapter (production) |
|
|
89
92
|
| `@chat-adapter/state-ioredis` | ioredis state adapter (alternative) |
|
|
90
93
|
| `@chat-adapter/state-pg` | PostgreSQL state adapter (production) |
|
package/docs/meta.json
CHANGED
|
@@ -8,13 +8,27 @@
|
|
|
8
8
|
"threads-messages-channels",
|
|
9
9
|
"handling-events",
|
|
10
10
|
"posting-messages",
|
|
11
|
+
"error-handling",
|
|
12
|
+
"testing",
|
|
13
|
+
"---AI---",
|
|
14
|
+
"...ai",
|
|
11
15
|
"---Adapters---",
|
|
12
16
|
"adapters",
|
|
13
17
|
"state",
|
|
14
|
-
"---
|
|
18
|
+
"---Messaging---",
|
|
19
|
+
"streaming",
|
|
20
|
+
"direct-messages",
|
|
21
|
+
"ephemeral-messages",
|
|
22
|
+
"files",
|
|
23
|
+
"conversation-history",
|
|
24
|
+
"subject",
|
|
15
25
|
"concurrency",
|
|
16
|
-
"
|
|
17
|
-
"
|
|
26
|
+
"---Interactivity---",
|
|
27
|
+
"cards",
|
|
28
|
+
"modals",
|
|
29
|
+
"actions",
|
|
30
|
+
"slash-commands",
|
|
31
|
+
"emoji",
|
|
18
32
|
"---API Reference---",
|
|
19
33
|
"...api",
|
|
20
34
|
"---Contributing---",
|
package/docs/modals.mdx
CHANGED
|
@@ -59,6 +59,7 @@ The top-level container for the form.
|
|
|
59
59
|
| `submitLabel` | `string` (optional) | Submit button text (defaults to "Submit") |
|
|
60
60
|
| `closeLabel` | `string` (optional) | Cancel button text (defaults to "Cancel") |
|
|
61
61
|
| `notifyOnClose` | `boolean` (optional) | Fire `onModalClose` when user cancels |
|
|
62
|
+
| `callbackUrl` | `string` (optional) | URL to POST form values to on submit |
|
|
62
63
|
| `privateMetadata` | `string` (optional) | Custom context passed through to handlers |
|
|
63
64
|
|
|
64
65
|
### TextInput
|
|
@@ -248,6 +249,29 @@ bot.onModalClose("feedback_form", async (event) => {
|
|
|
248
249
|
});
|
|
249
250
|
```
|
|
250
251
|
|
|
252
|
+
## Callback URLs
|
|
253
|
+
|
|
254
|
+
Like buttons, modals accept a `callbackUrl`. When the modal is submitted, the form values are POSTed to the URL:
|
|
255
|
+
|
|
256
|
+
```tsx title="lib/bot.tsx" lineNumbers
|
|
257
|
+
await event.openModal(
|
|
258
|
+
<Modal callbackUrl={webhook.url} callbackId="intake" title="Request Access" submitLabel="Submit">
|
|
259
|
+
<TextInput id="reason" label="Reason" multiline />
|
|
260
|
+
</Modal>
|
|
261
|
+
);
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
The POST body for modal submissions:
|
|
265
|
+
|
|
266
|
+
```json
|
|
267
|
+
{
|
|
268
|
+
"type": "modal_submit",
|
|
269
|
+
"callbackId": "intake",
|
|
270
|
+
"values": { "reason": "Need access to production logs" },
|
|
271
|
+
"user": { "id": "U123", "name": "alice" }
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
251
275
|
## Pass context with privateMetadata
|
|
252
276
|
|
|
253
277
|
Use `privateMetadata` to carry context from the button click through to the submit handler:
|
|
@@ -24,7 +24,7 @@ This sends the string directly without any formatting conversion.
|
|
|
24
24
|
|
|
25
25
|
## Markdown
|
|
26
26
|
|
|
27
|
-
Pass a `{ markdown }` object to have the SDK
|
|
27
|
+
Pass a `{ markdown }` object to have the SDK render standard markdown on each platform — passed through to Slack's native `markdown_text` field, converted to HTML for Teams, and so on.
|
|
28
28
|
|
|
29
29
|
```typescript title="lib/bot.ts" lineNumbers
|
|
30
30
|
await thread.post({
|
|
@@ -32,7 +32,7 @@ await thread.post({
|
|
|
32
32
|
});
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
-
Under the hood, the SDK parses the markdown into an mdast AST, then each adapter converts it to the platform's format.
|
|
35
|
+
Under the hood, the SDK parses the markdown into an mdast AST, then each adapter handles it natively or converts it to the platform's format.
|
|
36
36
|
|
|
37
37
|
## AST builders
|
|
38
38
|
|
|
@@ -139,7 +139,7 @@ See the [Cards](/docs/cards) page for the full list of card components.
|
|
|
139
139
|
|
|
140
140
|
## Streaming
|
|
141
141
|
|
|
142
|
-
Pass an AI SDK stream to `thread.post()` to stream a message in real time. The SDK uses platform-native streaming where available and falls back to post-then-edit on
|
|
142
|
+
Pass an AI SDK stream to `thread.post()` to stream a message in real time. The SDK uses platform-native streaming where available and falls back to post-then-edit or buffered delivery depending on the platform.
|
|
143
143
|
|
|
144
144
|
```typescript title="lib/bot.ts" lineNumbers
|
|
145
145
|
import { ToolLoopAgent } from "ai";
|
|
@@ -151,7 +151,9 @@ await thread.post(result.fullStream);
|
|
|
151
151
|
|
|
152
152
|
Both `fullStream` and `textStream` are supported. Use `fullStream` with multi-step agents — it preserves paragraph breaks between steps. Any `AsyncIterable<string>` also works for custom streams.
|
|
153
153
|
|
|
154
|
-
For multi-turn conversations, use [`toAiMessages()`](/docs/
|
|
154
|
+
For multi-turn conversations, use [`toAiMessages()`](/docs/ai/to-ai-messages) to convert thread history into the `{ role, content }[]` format expected by AI SDKs.
|
|
155
|
+
|
|
156
|
+
To pass platform-specific streaming options (e.g. Slack task grouping or stop blocks), wrap the stream in a [`StreamingPlan`](/docs/streaming#streaming-with-options) and post that.
|
|
155
157
|
|
|
156
158
|
See the [Streaming](/docs/streaming) page for details on platform behavior and configuration.
|
|
157
159
|
|
|
@@ -166,6 +168,8 @@ await thread.post({
|
|
|
166
168
|
});
|
|
167
169
|
```
|
|
168
170
|
|
|
171
|
+
Use `attachments` on `{ raw }`, `{ markdown }`, or `{ ast }` when an adapter supports typed media uploads, such as Telegram's single image/audio/video/file upload support.
|
|
172
|
+
|
|
169
173
|
See the [Files](/docs/files) page for more on attachments.
|
|
170
174
|
|
|
171
175
|
## Choosing a format
|
|
@@ -178,5 +182,7 @@ See the [Files](/docs/files) page for more on attachments.
|
|
|
178
182
|
| Card (function) | You need buttons, fields, or structured layouts | Approval flows, dashboards |
|
|
179
183
|
| Card (JSX) | Same as above, with JSX syntax preference | Same use cases as function cards |
|
|
180
184
|
| `AsyncIterable` | Streaming AI responses | Chat with LLMs |
|
|
185
|
+
| [`Plan`](/docs/streaming#plan-api) | Step-by-step tasks that mutate after posting | Multi-step agents, deploy progress |
|
|
186
|
+
| [`StreamingPlan`](/docs/streaming#streaming-with-options) | Streaming with platform-specific options | Slack streaming with grouped tasks or stop blocks |
|
|
181
187
|
|
|
182
188
|
For most cases, **AST builders** give the best balance of control and simplicity. Reach for **cards** when you need interactive elements like buttons or dropdowns.
|
package/docs/slash-commands.mdx
CHANGED
|
@@ -6,13 +6,13 @@ prerequisites:
|
|
|
6
6
|
- /docs/getting-started
|
|
7
7
|
related:
|
|
8
8
|
- /docs/modals
|
|
9
|
-
- /adapters/slack
|
|
10
|
-
- /adapters/discord
|
|
9
|
+
- /adapters/official/slack
|
|
10
|
+
- /adapters/official/discord
|
|
11
11
|
---
|
|
12
12
|
|
|
13
13
|
Slash commands let users invoke your bot with `/command` syntax. Register handlers with `onSlashCommand` to respond.
|
|
14
14
|
|
|
15
|
-
Slash commands are supported on [Slack](/adapters/slack) and [Discord](/adapters/discord).
|
|
15
|
+
Slash commands are supported on [Slack](/adapters/official/slack) and [Discord](/adapters/official/discord).
|
|
16
16
|
|
|
17
17
|
## Handle a specific command
|
|
18
18
|
|
|
@@ -114,7 +114,7 @@ bot.onModalSubmit("feedback_form", async (event) => {
|
|
|
114
114
|
|
|
115
115
|
## Discord
|
|
116
116
|
|
|
117
|
-
Discord slash commands are received via [HTTP Interactions](/adapters/discord#
|
|
117
|
+
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()`.
|
|
118
118
|
|
|
119
119
|
### Subcommands
|
|
120
120
|
|