chat 4.28.1 → 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/index.d.ts +6 -3143
- package/dist/index.js +31 -130
- package/dist/{jsx-runtime-DxGwoLu2.d.ts → jsx-runtime-CFq1K_Ve.d.ts} +1 -1
- package/dist/jsx-runtime.d.ts +1 -1
- package/docs/adapters.mdx +33 -3
- 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/chat.mdx +50 -10
- package/docs/api/index.mdx +4 -6
- package/docs/api/message.mdx +1 -1
- package/docs/api/meta.json +0 -1
- package/docs/api/postable-message.mdx +3 -3
- package/docs/api/thread.mdx +1 -1
- package/docs/concurrency.mdx +54 -15
- package/docs/contributing/building.mdx +1 -1
- package/docs/contributing/testing.mdx +4 -0
- package/docs/direct-messages.mdx +10 -1
- package/docs/files.mdx +20 -0
- package/docs/handling-events.mdx +10 -7
- package/docs/index.mdx +1 -0
- package/docs/meta.json +3 -0
- package/docs/posting-messages.mdx +3 -1
- package/docs/slash-commands.mdx +4 -4
- package/docs/streaming.mdx +3 -3
- package/docs/subject.mdx +1 -1
- package/docs/testing.mdx +142 -0
- package/docs/threads-messages-channels.mdx +1 -1
- package/docs/usage.mdx +8 -4
- 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
package/docs/meta.json
CHANGED
|
@@ -151,7 +151,7 @@ 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
155
|
|
|
156
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.
|
|
157
157
|
|
|
@@ -168,6 +168,8 @@ await thread.post({
|
|
|
168
168
|
});
|
|
169
169
|
```
|
|
170
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
|
+
|
|
171
173
|
See the [Files](/docs/files) page for more on attachments.
|
|
172
174
|
|
|
173
175
|
## Choosing a format
|
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
|
|
package/docs/streaming.mdx
CHANGED
|
@@ -240,10 +240,10 @@ Adapters that don't support PostableObject editing (e.g. WhatsApp) render the pl
|
|
|
240
240
|
## Streaming with conversation history
|
|
241
241
|
|
|
242
242
|
Combine message history with streaming for multi-turn AI conversations.
|
|
243
|
-
Use [`toAiMessages()`](/docs/
|
|
243
|
+
Use [`toAiMessages()`](/docs/ai/to-ai-messages) to convert chat messages into the `{ role, content }` format expected by AI SDKs:
|
|
244
244
|
|
|
245
245
|
```typescript title="lib/bot.ts" lineNumbers
|
|
246
|
-
import { toAiMessages } from "chat";
|
|
246
|
+
import { toAiMessages } from "chat/ai";
|
|
247
247
|
|
|
248
248
|
bot.onSubscribedMessage(async (thread, message) => {
|
|
249
249
|
// Fetch recent messages for context
|
|
@@ -256,4 +256,4 @@ bot.onSubscribedMessage(async (thread, message) => {
|
|
|
256
256
|
});
|
|
257
257
|
```
|
|
258
258
|
|
|
259
|
-
See the [`toAiMessages`
|
|
259
|
+
See the [`toAiMessages` reference](/docs/ai/to-ai-messages) for all options including `includeNames`, `transformMessage`, and attachment handling.
|
package/docs/subject.mdx
CHANGED
|
@@ -50,4 +50,4 @@ bot.onNewMention(async (thread, message) => {
|
|
|
50
50
|
});
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
-
For anything beyond `message.subject`, access the platform's typed API client via [`bot.getAdapter(
|
|
53
|
+
For anything beyond `message.subject`, access the platform's typed API client via [`bot.getAdapter("github").octokit`](/docs/api/chat#getadapter) or [`bot.getAdapter("linear").linearClient`](/docs/api/chat#getadapter).
|
package/docs/testing.mdx
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Testing
|
|
3
|
+
description: Test your bot handlers and custom adapters with @chat-adapter/tests — Vitest factories, custom matchers, and a setup file.
|
|
4
|
+
type: guide
|
|
5
|
+
prerequisites:
|
|
6
|
+
- /docs/getting-started
|
|
7
|
+
related:
|
|
8
|
+
- /docs/state
|
|
9
|
+
- /docs/handling-events
|
|
10
|
+
- /docs/contributing/testing
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
The [`@chat-adapter/tests`](https://www.npmjs.com/package/@chat-adapter/tests) package gives you Vitest factories, custom matchers, and a setup file for testing bots and custom adapters built on Chat SDK.
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pnpm add -D @chat-adapter/tests
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
`chat` and `vitest` are peer dependencies — they should already be in your project.
|
|
22
|
+
|
|
23
|
+
## Setup file (recommended)
|
|
24
|
+
|
|
25
|
+
Auto-register all matchers by adding the package's setup file to your Vitest config:
|
|
26
|
+
|
|
27
|
+
```typescript title="vitest.config.ts" lineNumbers
|
|
28
|
+
import { defineConfig } from "vitest/config";
|
|
29
|
+
|
|
30
|
+
export default defineConfig({
|
|
31
|
+
test: {
|
|
32
|
+
setupFiles: ["@chat-adapter/tests/setup"],
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Without the setup file, register matchers manually:
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
import { matchers } from "@chat-adapter/tests/matchers";
|
|
41
|
+
expect.extend(matchers);
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Mock factories
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
import {
|
|
48
|
+
createMockAdapter,
|
|
49
|
+
createMockChatInstance,
|
|
50
|
+
createMockState,
|
|
51
|
+
createTestMessage,
|
|
52
|
+
mockLogger,
|
|
53
|
+
} from "@chat-adapter/tests";
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
| Factory | Returns | Notes |
|
|
57
|
+
|---|---|---|
|
|
58
|
+
| `createMockAdapter(name?, overrides?)` | `Adapter` | Every method is `vi.fn()` with sensible defaults |
|
|
59
|
+
| `createMockChatInstance(options?)` | `ChatInstance` | Every `process*` handler is `vi.fn()`; `getState`/`getUserName`/`getLogger` wired up |
|
|
60
|
+
| `createMockState()` | `MockStateAdapter` | In-memory `Map`s for subscriptions, locks, KV, lists, queues; `cache` exposes the underlying map |
|
|
61
|
+
| `createTestMessage(id, text, overrides?)` | `Message` | Markdown text is parsed into the formatted AST |
|
|
62
|
+
| `mockLogger` / `createMockLogger()` | `Logger` | Shared default vs fresh-per-call |
|
|
63
|
+
|
|
64
|
+
## Matchers
|
|
65
|
+
|
|
66
|
+
| Matcher | Asserts |
|
|
67
|
+
|---|---|
|
|
68
|
+
| `expect(adapter).toHavePosted(threadId, textPattern?)` | `adapter.postMessage` was called for this thread |
|
|
69
|
+
| `expect(adapter).toHaveEdited(threadId, messageId, textPattern?)` | `adapter.editMessage` was called for this message |
|
|
70
|
+
| `expect(adapter).toHaveDeleted(threadId, messageId)` | `adapter.deleteMessage` was called for this message |
|
|
71
|
+
| `expect(adapter).toHaveReactedWith(threadId, messageId, emoji)` | `adapter.addReaction` was called with the emoji (string or `EmojiValue.name`) |
|
|
72
|
+
| `expect(adapter).toHaveStartedTyping(threadId)` | `adapter.startTyping` was called for this thread |
|
|
73
|
+
| `expect(adapter).toHavePostedToChannel(channelId, textPattern?)` | `adapter.postChannelMessage` was called for this channel |
|
|
74
|
+
| `expect(chat).toHaveDispatched(handler)` | The named `process*` handler on the mock `ChatInstance` was called |
|
|
75
|
+
| `expect(state).toBeSubscribedTo(threadId)` | `state.isSubscribed(threadId)` resolves to `true` (async — `await expect(...)`) |
|
|
76
|
+
|
|
77
|
+
Text-pattern matchers extract a comparable string from `AdapterPostableMessage` — strings directly, `PostableMarkdown.markdown`, `PostableRaw.raw`, and `PostableCard.fallbackText`. AST-shaped messages and cards without `fallbackText` aren't text-matchable; assert without `textPattern` and inspect `mock.calls` directly.
|
|
78
|
+
|
|
79
|
+
## Bot authors: test your handlers
|
|
80
|
+
|
|
81
|
+
When you're building a bot on top of Chat SDK, the kit lets you exercise your handlers without a real Slack/Teams/etc. webhook on the wire:
|
|
82
|
+
|
|
83
|
+
```typescript title="bot.test.ts"
|
|
84
|
+
import { describe, expect, it } from "vitest";
|
|
85
|
+
import { Chat } from "chat";
|
|
86
|
+
import { createMockAdapter, createMockState } from "@chat-adapter/tests";
|
|
87
|
+
|
|
88
|
+
describe("bot handlers", () => {
|
|
89
|
+
it("replies with a greeting on mention", async () => {
|
|
90
|
+
const slack = createMockAdapter("slack");
|
|
91
|
+
const state = createMockState();
|
|
92
|
+
const bot = new Chat({
|
|
93
|
+
userName: "mybot",
|
|
94
|
+
adapters: { slack },
|
|
95
|
+
state,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
bot.onNewMention(async (thread) => {
|
|
99
|
+
await thread.post("hello there");
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// Drive a synthesized mention through the bot…
|
|
103
|
+
// (use your adapter's webhook path or a thread-level call)
|
|
104
|
+
|
|
105
|
+
expect(slack).toHavePosted("slack:C1:t1", /hello there/);
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Adapter authors: test webhook → dispatch
|
|
111
|
+
|
|
112
|
+
When you're building a custom `Adapter`, the kit gives you a `ChatInstance` mock you can hand to your adapter and assert that webhooks route through the right `process*` hook with the right normalized payload:
|
|
113
|
+
|
|
114
|
+
```typescript title="adapter.test.ts"
|
|
115
|
+
import { describe, expect, it } from "vitest";
|
|
116
|
+
import { createMockChatInstance } from "@chat-adapter/tests";
|
|
117
|
+
import { MyAdapter } from "./adapter";
|
|
118
|
+
|
|
119
|
+
describe("MyAdapter.handleWebhook", () => {
|
|
120
|
+
it("dispatches incoming messages through processMessage", async () => {
|
|
121
|
+
const chat = createMockChatInstance();
|
|
122
|
+
const adapter = new MyAdapter({ /* config */ });
|
|
123
|
+
await adapter.initialize(chat);
|
|
124
|
+
|
|
125
|
+
const request = new Request("https://example.com/webhook", {
|
|
126
|
+
method: "POST",
|
|
127
|
+
body: JSON.stringify({ /* platform-specific payload */ }),
|
|
128
|
+
headers: { "content-type": "application/json" },
|
|
129
|
+
});
|
|
130
|
+
const response = await adapter.handleWebhook(request);
|
|
131
|
+
|
|
132
|
+
expect(response.status).toBe(200);
|
|
133
|
+
expect(chat).toHaveDispatched("processMessage");
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Adapter-specific helpers
|
|
139
|
+
|
|
140
|
+
Helpers that depend on a specific platform's wire format (signed Slack webhooks, Teams claim builders, etc.) live in each adapter's own `/testing` subpath rather than in this kit, so adopting `@chat-adapter/tests` doesn't pull in adapter dependencies you don't use.
|
|
141
|
+
|
|
142
|
+
If you're contributing adapters or core to this repo, see the [Testing adapters contributing guide](/docs/contributing/testing) for hand-rolled patterns used inside `packages/`.
|
|
@@ -40,7 +40,7 @@ await thread.post({
|
|
|
40
40
|
|
|
41
41
|
### Subscribe and unsubscribe
|
|
42
42
|
|
|
43
|
-
Subscriptions persist across restarts (stored in your state adapter). When a thread is subscribed, all messages route to `onSubscribedMessage`.
|
|
43
|
+
Subscriptions persist across restarts (stored in your state adapter). When a non-DM thread is subscribed, all messages route to `onSubscribedMessage`. DM threads route to `onDirectMessage` first when a direct message handler is registered.
|
|
44
44
|
|
|
45
45
|
```typescript title="lib/bot.ts" lineNumbers
|
|
46
46
|
await thread.subscribe();
|
package/docs/usage.mdx
CHANGED
|
@@ -34,7 +34,7 @@ bot.onNewMention(async (thread) => {
|
|
|
34
34
|
```
|
|
35
35
|
|
|
36
36
|
<Callout type="info">
|
|
37
|
-
This example uses Redis. Chat SDK also supports [PostgreSQL](/adapters/postgres) and [ioredis](/adapters/ioredis) as production state adapters. See [State Adapters](/docs/state) for all options.
|
|
37
|
+
This example uses Redis. Chat SDK also supports [PostgreSQL](/adapters/official/postgres) and [ioredis](/adapters/official/ioredis) as production state adapters. See [State Adapters](/docs/state) for all options.
|
|
38
38
|
</Callout>
|
|
39
39
|
|
|
40
40
|
Each adapter factory auto-detects credentials from environment variables (`SLACK_BOT_TOKEN`, `SLACK_SIGNING_SECRET`, `REDIS_URL`, etc.), so you can get started with zero config. Pass explicit values to override.
|
|
@@ -72,6 +72,7 @@ Your event handlers work identically across all registered adapters — the SDK
|
|
|
72
72
|
| `state` | `StateAdapter` | *required* | State adapter for subscriptions and locking |
|
|
73
73
|
| `logger` | `Logger \| LogLevel` | `"info"` | Logger instance or log level (`"debug"`, `"info"`, `"warn"`, `"error"`, `"silent"`) |
|
|
74
74
|
| `dedupeTtlMs` | `number` | `300000` | TTL in ms for message deduplication (5 minutes) |
|
|
75
|
+
| `concurrency` | `"drop" \| "queue" \| "debounce" \| "burst" \| "concurrent" \| ConcurrencyConfig` | `"drop"` | Strategy for overlapping messages on the same thread |
|
|
75
76
|
| `streamingUpdateIntervalMs` | `number` | `500` | Update interval in ms for post+edit streaming |
|
|
76
77
|
| `fallbackStreamingPlaceholderText` | `string \| null` | `"..."` | Placeholder text while streaming starts. Set to `null` to skip |
|
|
77
78
|
| `onLockConflict` | `'drop' \| 'force' \| (threadId, message) => 'drop' \| 'force'` | `"drop"` | Behavior when a thread lock is already held. `'force'` releases the existing lock and re-acquires it, enabling interrupt/steerability for long-running handlers |
|
|
@@ -89,13 +90,16 @@ await slack.setSuggestedPrompts(channelId, threadTs, [
|
|
|
89
90
|
]);
|
|
90
91
|
```
|
|
91
92
|
|
|
92
|
-
For typed access to the platform's native API client
|
|
93
|
+
For typed access to the platform's native API client, use the SDK-named getter on each adapter:
|
|
93
94
|
|
|
94
95
|
```typescript title="lib/bot.ts" lineNumbers
|
|
95
|
-
const
|
|
96
|
-
const
|
|
96
|
+
const slack = bot.getAdapter("slack").webClient; // WebClient
|
|
97
|
+
const linear = bot.getAdapter("linear").linearClient; // LinearClient
|
|
98
|
+
const github = bot.getAdapter("github").octokit; // Octokit
|
|
97
99
|
```
|
|
98
100
|
|
|
101
|
+
The previous `.client` getter still works as a deprecated alias on all three adapters.
|
|
102
|
+
|
|
99
103
|
See [`getAdapter`](/docs/api/chat#getadapter) for multi-tenant constraints.
|
|
100
104
|
|
|
101
105
|
## Webhook routing
|
package/package.json
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chat",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.29.0",
|
|
4
4
|
"description": "Unified chat abstraction for Slack, Teams, Google Chat, and Discord",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=20"
|
|
8
|
+
},
|
|
6
9
|
"main": "./dist/index.js",
|
|
7
10
|
"module": "./dist/index.js",
|
|
8
11
|
"types": "./dist/index.d.ts",
|
|
@@ -11,6 +14,10 @@
|
|
|
11
14
|
"types": "./dist/index.d.ts",
|
|
12
15
|
"import": "./dist/index.js"
|
|
13
16
|
},
|
|
17
|
+
"./ai": {
|
|
18
|
+
"types": "./dist/ai/index.d.ts",
|
|
19
|
+
"import": "./dist/ai/index.js"
|
|
20
|
+
},
|
|
14
21
|
"./jsx-runtime": {
|
|
15
22
|
"types": "./dist/jsx-runtime.d.ts",
|
|
16
23
|
"import": "./dist/jsx-runtime.js"
|
|
@@ -37,9 +44,23 @@
|
|
|
37
44
|
"devDependencies": {
|
|
38
45
|
"@types/mdast": "^4.0.4",
|
|
39
46
|
"@types/node": "^25.3.2",
|
|
47
|
+
"ai": "^6.0.182",
|
|
40
48
|
"tsup": "^8.3.5",
|
|
41
49
|
"typescript": "^5.7.2",
|
|
42
|
-
"vitest": "^4.0.18"
|
|
50
|
+
"vitest": "^4.0.18",
|
|
51
|
+
"zod": "^4.3.6"
|
|
52
|
+
},
|
|
53
|
+
"peerDependencies": {
|
|
54
|
+
"ai": "^6.0.182",
|
|
55
|
+
"zod": "^3.0.0 || ^4.0.0"
|
|
56
|
+
},
|
|
57
|
+
"peerDependenciesMeta": {
|
|
58
|
+
"ai": {
|
|
59
|
+
"optional": true
|
|
60
|
+
},
|
|
61
|
+
"zod": {
|
|
62
|
+
"optional": true
|
|
63
|
+
}
|
|
43
64
|
},
|
|
44
65
|
"repository": {
|
|
45
66
|
"type": "git",
|
|
@@ -86,7 +86,7 @@ Each tool has a `description` (which tells the model when to use it), an `inputS
|
|
|
86
86
|
|
|
87
87
|
Create `lib/bot.ts` with a `ToolLoopAgent` and a `Chat` instance:
|
|
88
88
|
|
|
89
|
-
`import { Chat } from "chat"; import { toAiMessages } from "chat"; import { createSlackAdapter } from "@chat-adapter/slack"; import { createRedisState } from "@chat-adapter/state-redis"; import { ToolLoopAgent } from "ai"; import { tools } from "./tools"; const agent = new ToolLoopAgent({ model: "anthropic/claude-sonnet-4.6", instructions: "You are a helpful AI assistant in a Slack workspace. " + "Answer questions clearly and use your tools when you need " + "real-time data. Keep responses concise and well-formatted for chat.", tools, }); export const bot = new Chat({ userName: "ai-agent", adapters: { slack: createSlackAdapter(), }, state: createRedisState(), }); // Handle first-time mentions bot.onNewMention(async (thread, message) => { await thread.subscribe(); const result = await agent.stream({ prompt: message.text }); await thread.post(result.fullStream); }); // Handle follow-up messages in subscribed threads bot.onSubscribedMessage(async (thread, message) => { const allMessages = []; for await (const msg of thread.allMessages) { allMessages.push(msg); } const history = await toAiMessages(allMessages); const result = await agent.stream({ messages: history }); await thread.post(result.fullStream); });`
|
|
89
|
+
`import { Chat } from "chat"; import { toAiMessages } from "chat/ai"; import { createSlackAdapter } from "@chat-adapter/slack"; import { createRedisState } from "@chat-adapter/state-redis"; import { ToolLoopAgent } from "ai"; import { tools } from "./tools"; const agent = new ToolLoopAgent({ model: "anthropic/claude-sonnet-4.6", instructions: "You are a helpful AI assistant in a Slack workspace. " + "Answer questions clearly and use your tools when you need " + "real-time data. Keep responses concise and well-formatted for chat.", tools, }); export const bot = new Chat({ userName: "ai-agent", adapters: { slack: createSlackAdapter(), }, state: createRedisState(), }); // Handle first-time mentions bot.onNewMention(async (thread, message) => { await thread.subscribe(); const result = await agent.stream({ prompt: message.text }); await thread.post(result.fullStream); }); // Handle follow-up messages in subscribed threads bot.onSubscribedMessage(async (thread, message) => { const allMessages = []; for await (const msg of thread.allMessages) { allMessages.push(msg); } const history = await toAiMessages(allMessages); const result = await agent.stream({ messages: history }); await thread.post(result.fullStream); });`
|
|
90
90
|
|
|
91
91
|
When someone @mentions the bot, `onNewMention` fires. The handler subscribes to the thread (to track future messages in that thread) and streams the agent's response. For follow-up messages, `onSubscribedMessage` retrieves the full thread history using `thread.allMessages`, converts it to the AI SDK message format with `toAiMessages`and passes it to the agent so it has a complete conversation context.
|
|
92
92
|
|