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,243 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Types
|
|
3
|
+
description: TypeScript types exported from the chat/ai subpath.
|
|
4
|
+
type: reference
|
|
5
|
+
related:
|
|
6
|
+
- /docs/ai
|
|
7
|
+
- /docs/ai/ai-sdk-tools
|
|
8
|
+
- /docs/ai/to-ai-messages
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
Every type exported from `chat/ai`. Pulling these from the subpath keeps the optional `ai` and `zod` peer deps out of bundles that don't import them.
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import type {
|
|
15
|
+
AiMessage,
|
|
16
|
+
AiUserMessage,
|
|
17
|
+
AiAssistantMessage,
|
|
18
|
+
AiMessagePart,
|
|
19
|
+
AiTextPart,
|
|
20
|
+
AiImagePart,
|
|
21
|
+
AiFilePart,
|
|
22
|
+
ToAiMessagesOptions,
|
|
23
|
+
ChatBinding,
|
|
24
|
+
ChatTools,
|
|
25
|
+
ChatToolName,
|
|
26
|
+
ChatToolPreset,
|
|
27
|
+
ChatWriteToolName,
|
|
28
|
+
ApprovalConfig,
|
|
29
|
+
ToolOptions,
|
|
30
|
+
ToolOverrides,
|
|
31
|
+
} from "chat/ai";
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Conversation messages
|
|
35
|
+
|
|
36
|
+
Used by [`toAiMessages`](/docs/ai/to-ai-messages) and any agent prompt you build by hand. The shapes are structurally compatible with AI SDK's `ModelMessage` so the result is directly assignable to `prompt` / `messages`.
|
|
37
|
+
|
|
38
|
+
### AiMessage
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
type AiMessage = AiUserMessage | AiAssistantMessage;
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
A single normalized turn in a conversation — the array form is what AI SDK calls expect.
|
|
45
|
+
|
|
46
|
+
### AiUserMessage
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
interface AiUserMessage {
|
|
50
|
+
role: "user";
|
|
51
|
+
content: string | AiMessagePart[];
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
User content can be plain text, or a multipart array when attachments are present.
|
|
56
|
+
|
|
57
|
+
### AiAssistantMessage
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
interface AiAssistantMessage {
|
|
61
|
+
role: "assistant";
|
|
62
|
+
content: string;
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Assistant turns are always plain strings — `toAiMessages` produces this for any message authored by the bot itself (`author.isMe === true`).
|
|
67
|
+
|
|
68
|
+
### AiMessagePart
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
type AiMessagePart = AiTextPart | AiImagePart | AiFilePart;
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The discriminated union used inside multipart user messages.
|
|
75
|
+
|
|
76
|
+
### AiTextPart
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
interface AiTextPart {
|
|
80
|
+
type: "text";
|
|
81
|
+
text: string;
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### AiImagePart
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
interface AiImagePart {
|
|
89
|
+
type: "image";
|
|
90
|
+
image: DataContent | URL;
|
|
91
|
+
mediaType?: string;
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
`DataContent` matches AI SDK's type — `string | Uint8Array | ArrayBuffer | Buffer`.
|
|
96
|
+
|
|
97
|
+
### AiFilePart
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
interface AiFilePart {
|
|
101
|
+
type: "file";
|
|
102
|
+
data: DataContent | URL;
|
|
103
|
+
filename?: string;
|
|
104
|
+
mediaType: string;
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
`toAiMessages` emits text-like attachments (JSON, XML, YAML, source files, etc.) as file parts.
|
|
109
|
+
|
|
110
|
+
### ToAiMessagesOptions
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
interface ToAiMessagesOptions {
|
|
114
|
+
includeNames?: boolean;
|
|
115
|
+
transformMessage?: (
|
|
116
|
+
aiMessage: AiMessage,
|
|
117
|
+
source: Message
|
|
118
|
+
) => AiMessage | null | Promise<AiMessage | null>;
|
|
119
|
+
onUnsupportedAttachment?: (
|
|
120
|
+
attachment: Attachment,
|
|
121
|
+
message: Message
|
|
122
|
+
) => void;
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
See [`toAiMessages`](/docs/ai/to-ai-messages) for behavior and examples.
|
|
127
|
+
|
|
128
|
+
## Tools
|
|
129
|
+
|
|
130
|
+
Returned by [`createChatTools`](/docs/ai/ai-sdk-tools) and used to configure it.
|
|
131
|
+
|
|
132
|
+
### ChatBinding
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
type ChatBinding = Chat<any, any>;
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Whatever [`Chat`](/docs/api/chat) instance the tools should dispatch operations against. The generics are intentionally loose so any strongly-typed `Chat<TAdapters, TState>` is assignable.
|
|
139
|
+
|
|
140
|
+
### ChatTools
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
type ChatTools = ReturnType<typeof createChatTools>;
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Convenience alias for the object returned by `createChatTools` — handy when you want to type a wrapper or pass the toolset around.
|
|
147
|
+
|
|
148
|
+
### ChatToolPreset
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
type ChatToolPreset = "reader" | "messenger" | "moderator";
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Predefined toolset scopes. See [Presets](/docs/ai/ai-sdk-tools#presets) for the exact tool list per preset.
|
|
155
|
+
|
|
156
|
+
### ChatToolName
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
type ChatToolName =
|
|
160
|
+
| "fetchMessages"
|
|
161
|
+
| "fetchChannelMessages"
|
|
162
|
+
| "fetchThread"
|
|
163
|
+
| "listThreads"
|
|
164
|
+
| "getThreadParticipants"
|
|
165
|
+
| "getChannelInfo"
|
|
166
|
+
| "getUser"
|
|
167
|
+
| "startTyping"
|
|
168
|
+
| "postMessage"
|
|
169
|
+
| "postChannelMessage"
|
|
170
|
+
| "sendDirectMessage"
|
|
171
|
+
| "editMessage"
|
|
172
|
+
| "deleteMessage"
|
|
173
|
+
| "addReaction"
|
|
174
|
+
| "removeReaction"
|
|
175
|
+
| "subscribeThread"
|
|
176
|
+
| "unsubscribeThread";
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
The names of every generated tool. Useful when typing per-tool overrides.
|
|
180
|
+
|
|
181
|
+
### ChatWriteToolName
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
type ChatWriteToolName =
|
|
185
|
+
| "postMessage"
|
|
186
|
+
| "postChannelMessage"
|
|
187
|
+
| "sendDirectMessage"
|
|
188
|
+
| "editMessage"
|
|
189
|
+
| "deleteMessage"
|
|
190
|
+
| "addReaction"
|
|
191
|
+
| "removeReaction"
|
|
192
|
+
| "subscribeThread"
|
|
193
|
+
| "unsubscribeThread";
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
The names of every mutating tool. Useful when wiring per-tool approval overrides.
|
|
197
|
+
|
|
198
|
+
### ApprovalConfig
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
type ApprovalConfig =
|
|
202
|
+
| boolean
|
|
203
|
+
| Partial<Record<ChatWriteToolName, boolean>>;
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Controls the `requireApproval` option:
|
|
207
|
+
|
|
208
|
+
- `true` (default) — every write tool needs approval.
|
|
209
|
+
- `false` — no write tool needs approval.
|
|
210
|
+
- object — per-tool override; unspecified write tools fall back to `true`.
|
|
211
|
+
|
|
212
|
+
### ToolOptions
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
interface ToolOptions {
|
|
216
|
+
needsApproval?: boolean;
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Common options accepted by every standalone write-tool factory (e.g. `postMessage(chat, { needsApproval: false })`).
|
|
221
|
+
|
|
222
|
+
### ToolOverrides
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
type ToolOverrides = Partial<
|
|
226
|
+
Pick<
|
|
227
|
+
Tool,
|
|
228
|
+
| "description"
|
|
229
|
+
| "inputExamples"
|
|
230
|
+
| "metadata"
|
|
231
|
+
| "needsApproval"
|
|
232
|
+
| "onInputAvailable"
|
|
233
|
+
| "onInputDelta"
|
|
234
|
+
| "onInputStart"
|
|
235
|
+
| "providerOptions"
|
|
236
|
+
| "strict"
|
|
237
|
+
| "title"
|
|
238
|
+
| "toModelOutput"
|
|
239
|
+
>
|
|
240
|
+
>;
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Per-tool overrides accepted by `createChatTools({ overrides })`. Core fields like `execute`, `inputSchema`, `outputSchema`, `type`, `id`, and `args` are intentionally excluded so tool semantics stay stable across upgrades.
|
package/docs/api/cards.mdx
CHANGED
|
@@ -100,6 +100,10 @@ Button({ id: "delete", label: "Delete", style: "danger", value: "item-123" })
|
|
|
100
100
|
type: '"action" | "modal"',
|
|
101
101
|
default: '"action"',
|
|
102
102
|
},
|
|
103
|
+
callbackUrl: {
|
|
104
|
+
description: 'URL to POST action data to when this button is clicked.',
|
|
105
|
+
type: 'string',
|
|
106
|
+
},
|
|
103
107
|
}}
|
|
104
108
|
/>
|
|
105
109
|
|
package/docs/api/chat.mdx
CHANGED
|
@@ -74,9 +74,36 @@ bot.onNewMention(async (thread, message) => {
|
|
|
74
74
|
}}
|
|
75
75
|
/>
|
|
76
76
|
|
|
77
|
+
### onDirectMessage
|
|
78
|
+
|
|
79
|
+
Fires for every direct message when registered. Direct message handlers run before `onSubscribedMessage`, `onNewMention`, and pattern handlers. If no direct message handler is registered, unsubscribed DMs fall through to `onNewMention` for backward compatibility.
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
bot.onDirectMessage(async (thread, message, channel) => {
|
|
83
|
+
await thread.post(`Got your DM in ${channel.id}: ${message.text}`);
|
|
84
|
+
});
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
<TypeTable
|
|
88
|
+
type={{
|
|
89
|
+
thread: {
|
|
90
|
+
description: 'The DM thread where the message occurred.',
|
|
91
|
+
type: 'Thread',
|
|
92
|
+
},
|
|
93
|
+
message: {
|
|
94
|
+
description: 'The direct message.',
|
|
95
|
+
type: 'Message',
|
|
96
|
+
},
|
|
97
|
+
channel: {
|
|
98
|
+
description: 'The DM channel.',
|
|
99
|
+
type: 'Channel',
|
|
100
|
+
},
|
|
101
|
+
}}
|
|
102
|
+
/>
|
|
103
|
+
|
|
77
104
|
### onSubscribedMessage
|
|
78
105
|
|
|
79
|
-
Fires for every new message in a subscribed thread. Once subscribed,
|
|
106
|
+
Fires for every new message in a subscribed non-DM thread. Once subscribed, messages (including @-mentions) route here instead of `onNewMention`. DM threads route to `onDirectMessage` first when a direct message handler is registered.
|
|
80
107
|
|
|
81
108
|
```typescript
|
|
82
109
|
bot.onSubscribedMessage(async (thread, message) => {
|
|
@@ -168,7 +195,9 @@ Fires when a user clicks a button or selects an option in a card.
|
|
|
168
195
|
```typescript
|
|
169
196
|
// Single action
|
|
170
197
|
bot.onAction("approve", async (event) => {
|
|
171
|
-
|
|
198
|
+
if (event.thread) {
|
|
199
|
+
await event.thread.post("Approved!");
|
|
200
|
+
}
|
|
172
201
|
});
|
|
173
202
|
|
|
174
203
|
// Multiple actions
|
|
@@ -193,8 +222,8 @@ bot.onAction(async (event) => { /* ... */ });
|
|
|
193
222
|
type: 'Author',
|
|
194
223
|
},
|
|
195
224
|
'event.thread': {
|
|
196
|
-
description: 'The thread containing the card.',
|
|
197
|
-
type: 'Thread',
|
|
225
|
+
description: 'The thread containing the card, or null for view-based actions.',
|
|
226
|
+
type: 'Thread | null',
|
|
198
227
|
},
|
|
199
228
|
'event.triggerId': {
|
|
200
229
|
description: 'Trigger ID for opening modals (platform-specific, may expire quickly).',
|
|
@@ -261,9 +290,47 @@ Returns `ModalResponse | undefined` to control the modal after submission:
|
|
|
261
290
|
- `{ action: "update", modal: ModalElement }` — replace the modal content
|
|
262
291
|
- `{ action: "push", modal: ModalElement }` — push a new modal view onto the stack
|
|
263
292
|
|
|
293
|
+
### onOptionsLoad
|
|
294
|
+
|
|
295
|
+
Fires when an `ExternalSelect` requests options dynamically. The handler is keyed on the select's `id` and must return options synchronously enough for Slack's 3-second budget (the adapter caps the loader at ~2.5s and substitutes an empty result on timeout). Slack-only.
|
|
296
|
+
|
|
297
|
+
```typescript
|
|
298
|
+
bot.onOptionsLoad("assignee", async (event) => {
|
|
299
|
+
const people = await peopleService.search(event.query);
|
|
300
|
+
return people.map((p) => ({ label: p.fullName, value: p.id }));
|
|
301
|
+
});
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
Return an array of `OptionsLoadGroup` (`{ label, options }[]`) instead of a flat array to render grouped headers (e.g. "Recent" / "All"). Slack limits: max 100 groups, max 100 options per group.
|
|
305
|
+
|
|
306
|
+
<TypeTable
|
|
307
|
+
type={{
|
|
308
|
+
'event.actionId': {
|
|
309
|
+
description: 'The id of the select requesting options (matches the id passed to bot.onOptionsLoad).',
|
|
310
|
+
type: 'string',
|
|
311
|
+
},
|
|
312
|
+
'event.query': {
|
|
313
|
+
description: 'The text the user has typed so far.',
|
|
314
|
+
type: 'string',
|
|
315
|
+
},
|
|
316
|
+
'event.user': {
|
|
317
|
+
description: 'The user requesting options.',
|
|
318
|
+
type: 'Author',
|
|
319
|
+
},
|
|
320
|
+
'event.adapter': {
|
|
321
|
+
description: 'The adapter that received this event.',
|
|
322
|
+
type: 'Adapter',
|
|
323
|
+
},
|
|
324
|
+
'event.raw': {
|
|
325
|
+
description: 'Raw platform-specific payload.',
|
|
326
|
+
type: 'unknown',
|
|
327
|
+
},
|
|
328
|
+
}}
|
|
329
|
+
/>
|
|
330
|
+
|
|
264
331
|
### onSlashCommand
|
|
265
332
|
|
|
266
|
-
Fires when a user invokes a `/command` in the message composer. Currently supported on Slack.
|
|
333
|
+
Fires when a user invokes a `/command` in the message composer. Currently supported on Slack and Discord.
|
|
267
334
|
|
|
268
335
|
```typescript
|
|
269
336
|
// Specific command
|
|
@@ -426,12 +493,55 @@ bot.webhooks.teams(request, { waitUntil });
|
|
|
426
493
|
|
|
427
494
|
### getAdapter
|
|
428
495
|
|
|
429
|
-
Get
|
|
496
|
+
Get a typed adapter instance by name.
|
|
430
497
|
|
|
431
498
|
```typescript
|
|
432
499
|
const slack = bot.getAdapter("slack");
|
|
433
500
|
```
|
|
434
501
|
|
|
502
|
+
#### Direct client access
|
|
503
|
+
|
|
504
|
+
Access the platform's typed native API client directly via an SDK-named getter — `.webClient` on Slack, `.linearClient` on Linear, `.octokit` on GitHub:
|
|
505
|
+
|
|
506
|
+
```typescript
|
|
507
|
+
// Slack - full WebClient from @slack/web-api
|
|
508
|
+
const slack = bot.getAdapter("slack").webClient;
|
|
509
|
+
await slack.pins.add({ channel: "C123ABC", timestamp: "1234567890.123456" });
|
|
510
|
+
|
|
511
|
+
// Linear - full LinearClient from @linear/sdk
|
|
512
|
+
const linear = bot.getAdapter("linear").linearClient;
|
|
513
|
+
const issue = await linear.issue("ENG-123");
|
|
514
|
+
const project = await issue.project;
|
|
515
|
+
|
|
516
|
+
// GitHub - full Octokit from @octokit/rest
|
|
517
|
+
const github = bot.getAdapter("github").octokit;
|
|
518
|
+
const { data: pulls } = await github.rest.pulls.list({
|
|
519
|
+
owner: "vercel",
|
|
520
|
+
repo: "chat",
|
|
521
|
+
state: "open",
|
|
522
|
+
});
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
The client uses the credentials from your adapter config. For multi-tenant / multi-workspace adapters (Slack, Linear, GitHub), it returns the client bound to the credentials for the current webhook request context.
|
|
526
|
+
|
|
527
|
+
<Callout type="info">
|
|
528
|
+
The previous `.client` getter still works on all three adapters as a deprecated alias for `.webClient` / `.linearClient` / `.octokit`.
|
|
529
|
+
</Callout>
|
|
530
|
+
|
|
531
|
+
<Callout type="warn">
|
|
532
|
+
Multi-tenant adapters (GitHub App without a fixed installation ID, Linear with per-org OAuth, Slack in multi-workspace mode) require a webhook handler context to resolve credentials when the native client getter is accessed. Calling it outside a handler throws.
|
|
533
|
+
|
|
534
|
+
For Slack, you can also bind a token explicitly outside a webhook with `adapter.withBotToken(token, () => adapter.webClient.…)` — useful for cron jobs or workflows. The same pattern is required when `botToken` is configured as an async resolver function, since `.webClient` resolves the token synchronously.
|
|
535
|
+
|
|
536
|
+
Single-tenant adapters (PAT, API key, static `botToken` string, or a synchronous `botToken` resolver) work anywhere.
|
|
537
|
+
</Callout>
|
|
538
|
+
|
|
539
|
+
| Adapter | Getter | Type |
|
|
540
|
+
|---------|--------|------|
|
|
541
|
+
| Slack | `.webClient` | `WebClient` from `@slack/web-api` |
|
|
542
|
+
| Linear | `.linearClient` | `LinearClient` from `@linear/sdk` |
|
|
543
|
+
| GitHub | `.octokit` | `Octokit` from `@octokit/rest` |
|
|
544
|
+
|
|
435
545
|
### openDM
|
|
436
546
|
|
|
437
547
|
Open a direct message thread with a user.
|
|
@@ -498,10 +608,16 @@ const user = await bot.getUser(message.author);
|
|
|
498
608
|
- **GitHub** — `email` is `null` unless the user made it public, or you authenticated with the `user:email` scope.
|
|
499
609
|
- **Linear** — full profile (incl. email + avatar) for any active workspace member.
|
|
500
610
|
|
|
501
|
-
Fields that aren't available return `undefined`. Numeric user IDs (Discord/Telegram/GitHub) can be ambiguous when multiple of those adapters are registered — call the
|
|
611
|
+
Fields that aren't available return `undefined`. Numeric user IDs (Discord/Telegram/GitHub) can be ambiguous when multiple of those adapters are registered — `bot.getUser` throws a `ChatError` with code `AMBIGUOUS_USER_ID` in that case. Pass an `Author` from a message handler (which already carries the adapter), or call the adapter directly (`adapter.getUser(userId)`).
|
|
502
612
|
</Callout>
|
|
503
613
|
|
|
504
|
-
|
|
614
|
+
`bot.getUser` throws a `ChatError` in three cases. Handle them if your bot runs on multiple platforms:
|
|
615
|
+
|
|
616
|
+
| Code | When |
|
|
617
|
+
|------|------|
|
|
618
|
+
| `NOT_SUPPORTED` | The resolved adapter doesn't implement `getUser` (e.g. WhatsApp) |
|
|
619
|
+
| `AMBIGUOUS_USER_ID` | A numeric user ID could belong to more than one registered adapter (Discord/Telegram/GitHub) |
|
|
620
|
+
| `UNKNOWN_USER_ID_FORMAT` | The `userId` string doesn't match any registered platform's ID format |
|
|
505
621
|
|
|
506
622
|
```typescript
|
|
507
623
|
import { ChatError } from "chat";
|
|
@@ -512,8 +628,14 @@ try {
|
|
|
512
628
|
// User not found on this platform
|
|
513
629
|
}
|
|
514
630
|
} catch (error) {
|
|
515
|
-
if (error instanceof ChatError
|
|
516
|
-
|
|
631
|
+
if (error instanceof ChatError) {
|
|
632
|
+
if (error.code === "NOT_SUPPORTED") {
|
|
633
|
+
// This adapter doesn't support user lookups
|
|
634
|
+
} else if (error.code === "AMBIGUOUS_USER_ID") {
|
|
635
|
+
// Pass message.author or call adapter.getUser(userId) directly
|
|
636
|
+
} else if (error.code === "UNKNOWN_USER_ID_FORMAT") {
|
|
637
|
+
// userId doesn't match any known platform format
|
|
638
|
+
}
|
|
517
639
|
}
|
|
518
640
|
}
|
|
519
641
|
```
|
package/docs/api/index.mdx
CHANGED
|
@@ -20,17 +20,17 @@ import { Chat, root, paragraph, text, Card, Button, emoji } from "chat";
|
|
|
20
20
|
| [`Message`](/docs/api/message) | Normalized message with text, AST, author, and metadata |
|
|
21
21
|
| [`ScheduledMessage`](/docs/api/thread#scheduledmessage) | Returned by `thread.schedule()` / `channel.schedule()` with `cancel()` |
|
|
22
22
|
|
|
23
|
-
## Utilities
|
|
24
|
-
|
|
25
|
-
| Export | Description |
|
|
26
|
-
|--------|-------------|
|
|
27
|
-
| [`toAiMessages`](/docs/api/to-ai-messages) | Convert `Message[]` to AI SDK `{ role, content }[]` format |
|
|
28
|
-
|
|
29
23
|
## Message formats
|
|
30
24
|
|
|
31
25
|
| Export | Description |
|
|
32
26
|
|--------|-------------|
|
|
33
27
|
| [`PostableMessage`](/docs/api/postable-message) | Union type accepted by `thread.post()` |
|
|
28
|
+
| [`Plan`](/docs/api/postable-message#plan) | Step-by-step task list that mutates after posting |
|
|
29
|
+
| [`StreamingPlan`](/docs/api/postable-message#streamingplan) | Wraps an async iterable with platform-specific streaming options |
|
|
34
30
|
| [`Cards`](/docs/api/cards) | Rich card components — `Card`, `Text`, `Button`, `Actions`, etc. |
|
|
35
31
|
| [`Markdown`](/docs/api/markdown) | AST builder functions — `root`, `paragraph`, `text`, `strong`, etc. |
|
|
36
32
|
| [`Modals`](/docs/api/modals) | Modal form components — `Modal`, `TextInput`, `Select`, etc. |
|
|
33
|
+
|
|
34
|
+
## AI utilities
|
|
35
|
+
|
|
36
|
+
`toAiMessages`, `createChatTools`, and the supporting types live in the [`chat/ai`](/docs/ai) subpath — see the [AI section](/docs/ai) for the full reference.
|
package/docs/api/markdown.mdx
CHANGED
|
@@ -15,6 +15,25 @@ import {
|
|
|
15
15
|
} from "chat";
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
+
## Type re-exports
|
|
19
|
+
|
|
20
|
+
The chat package re-exports mdast's union and content types so adapters and downstream code can build exhaustively-typed AST walkers without depending on `mdast` directly:
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
import type { Nodes, Root, Content } from "chat";
|
|
24
|
+
|
|
25
|
+
function render(node: Nodes): string {
|
|
26
|
+
switch (node.type) {
|
|
27
|
+
case "text": return node.value;
|
|
28
|
+
case "strong": return node.children.map(render).join("");
|
|
29
|
+
// ...
|
|
30
|
+
default: throw new Error(`Unhandled: ${node satisfies never}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Adapters use this pattern to make the type checker reject the build when a new mdast node type is introduced upstream.
|
|
36
|
+
|
|
18
37
|
## Node builders
|
|
19
38
|
|
|
20
39
|
### root
|
|
@@ -222,7 +241,7 @@ const value = getNodeValue(node); // string | undefined
|
|
|
222
241
|
|
|
223
242
|
### tableToAscii
|
|
224
243
|
|
|
225
|
-
Render an mdast `Table` node as a padded ASCII table string. Used by adapters that lack native table support (
|
|
244
|
+
Render an mdast `Table` node as a padded ASCII table string. Used by adapters that lack native table support (Google Chat, Discord, Telegram).
|
|
226
245
|
|
|
227
246
|
```typescript
|
|
228
247
|
import { parseMarkdown, tableToAscii, isTableNode } from "chat";
|
|
@@ -261,17 +280,21 @@ The SDK uses mdast as the canonical format and each adapter converts it to the p
|
|
|
261
280
|
|
|
262
281
|
| Feature | Slack | Teams | Google Chat |
|
|
263
282
|
|---------|-------|-------|-------------|
|
|
264
|
-
| Bold |
|
|
283
|
+
| Bold | `**text**` | `**text**` | `*text*` |
|
|
265
284
|
| Italic | `_text_` | `_text_` | `_text_` |
|
|
266
|
-
| Strikethrough |
|
|
285
|
+
| Strikethrough | `~~text~~` | `~~text~~` | `~text~` |
|
|
267
286
|
| Code | `` `code` `` | `` `code` `` | `` `code` `` |
|
|
268
287
|
| Code blocks | ```` ``` ```` | ```` ``` ```` | ```` ``` ```` |
|
|
269
|
-
| Links |
|
|
288
|
+
| Links | `[text](url)` | `[text](url)` | `[text](url)` |
|
|
270
289
|
| Lists | Supported | Supported | Supported |
|
|
271
290
|
| Blockquotes | `>` | `>` | Simulated with `>` prefix |
|
|
272
|
-
| Tables |
|
|
291
|
+
| Tables | Native (markdown_text) | Native GFM | ASCII fallback |
|
|
273
292
|
| Mentions | `<@USER>` | `<at>name</at>` | `<users/{id}>` |
|
|
274
293
|
|
|
294
|
+
<Callout type="info">
|
|
295
|
+
Slack accepts standard markdown via the `markdown_text` field on `chat.postMessage` and friends, so the SDK passes markdown through directly. Incoming Slack messages still arrive as legacy mrkdwn (`*bold*`, `<url|text>`) and are parsed transparently. If you need to send mrkdwn yourself, use `{ raw: "..." }`.
|
|
296
|
+
</Callout>
|
|
297
|
+
|
|
275
298
|
<Callout type="info">
|
|
276
299
|
You don't need to worry about these differences when using the SDK — the AST builders and `parseMarkdown` handle conversion automatically. This table is useful if you're working with `raw` platform payloads or debugging formatting issues.
|
|
277
300
|
</Callout>
|
package/docs/api/message.mdx
CHANGED
|
@@ -54,6 +54,10 @@ import { Message } from "chat";
|
|
|
54
54
|
description: 'Whether the bot was @-mentioned in this message.',
|
|
55
55
|
type: 'boolean | undefined',
|
|
56
56
|
},
|
|
57
|
+
subject: {
|
|
58
|
+
description: 'Resolves the parent resource (issue, PR) this message is about. Returns null on chat platforms. See [Message Subject](/docs/subject).',
|
|
59
|
+
type: 'Promise<MessageSubject | null>',
|
|
60
|
+
},
|
|
57
61
|
}}
|
|
58
62
|
/>
|
|
59
63
|
|
|
@@ -190,7 +194,7 @@ Links found in incoming messages are extracted and exposed as `LinkPreview` obje
|
|
|
190
194
|
/>
|
|
191
195
|
|
|
192
196
|
<Callout type="info">
|
|
193
|
-
When using [`toAiMessages()`](/docs/
|
|
197
|
+
When using [`toAiMessages()`](/docs/ai/to-ai-messages), link metadata is automatically appended to the message content. Embedded message links are labeled as `[Embedded message: ...]` so the AI model understands the context.
|
|
194
198
|
</Callout>
|
|
195
199
|
|
|
196
200
|
### Platform support
|
|
@@ -200,6 +204,55 @@ When using [`toAiMessages()`](/docs/api/to-ai-messages), link metadata is automa
|
|
|
200
204
|
| Slack | URLs from `rich_text` blocks or `<url>` text patterns | Slack message links (`*.slack.com/archives/...`) |
|
|
201
205
|
| Others | Not yet — `links` is always `[]` | — |
|
|
202
206
|
|
|
207
|
+
## MessageSubject
|
|
208
|
+
|
|
209
|
+
Returned by `message.subject` on platforms with parent resources. See [Message Subject](/docs/subject) for usage.
|
|
210
|
+
|
|
211
|
+
<TypeTable
|
|
212
|
+
type={{
|
|
213
|
+
type: {
|
|
214
|
+
description: 'Resource kind, e.g. "issue" or "pull_request".',
|
|
215
|
+
type: 'string',
|
|
216
|
+
},
|
|
217
|
+
id: {
|
|
218
|
+
description: 'Resource identifier (e.g. "ENG-123" or "42").',
|
|
219
|
+
type: 'string',
|
|
220
|
+
},
|
|
221
|
+
title: {
|
|
222
|
+
description: 'Resource title.',
|
|
223
|
+
type: 'string | undefined',
|
|
224
|
+
},
|
|
225
|
+
description: {
|
|
226
|
+
description: 'Full description/body in markdown.',
|
|
227
|
+
type: 'string | undefined',
|
|
228
|
+
},
|
|
229
|
+
status: {
|
|
230
|
+
description: 'Current status (e.g. "In Progress", "open").',
|
|
231
|
+
type: 'string | undefined',
|
|
232
|
+
},
|
|
233
|
+
url: {
|
|
234
|
+
description: 'Web URL to the resource.',
|
|
235
|
+
type: 'string | undefined',
|
|
236
|
+
},
|
|
237
|
+
author: {
|
|
238
|
+
description: 'Resource creator.',
|
|
239
|
+
type: '{ id: string; name: string } | undefined',
|
|
240
|
+
},
|
|
241
|
+
assignee: {
|
|
242
|
+
description: 'Current assignee.',
|
|
243
|
+
type: '{ id: string; name: string } | undefined',
|
|
244
|
+
},
|
|
245
|
+
labels: {
|
|
246
|
+
description: 'Labels/tags.',
|
|
247
|
+
type: 'string[] | undefined',
|
|
248
|
+
},
|
|
249
|
+
raw: {
|
|
250
|
+
description: 'Full platform API response.',
|
|
251
|
+
type: 'unknown',
|
|
252
|
+
},
|
|
253
|
+
}}
|
|
254
|
+
/>
|
|
255
|
+
|
|
203
256
|
## Serialization
|
|
204
257
|
|
|
205
258
|
Messages can be serialized for workflow engines and external systems.
|
package/docs/api/meta.json
CHANGED
package/docs/api/modals.mdx
CHANGED
|
@@ -54,6 +54,10 @@ bot.onAction("open-form", async (event) => {
|
|
|
54
54
|
type: 'boolean',
|
|
55
55
|
default: 'false',
|
|
56
56
|
},
|
|
57
|
+
callbackUrl: {
|
|
58
|
+
description: 'URL to POST form values to when the modal is submitted.',
|
|
59
|
+
type: 'string',
|
|
60
|
+
},
|
|
57
61
|
privateMetadata: {
|
|
58
62
|
description: 'Arbitrary string passed through the modal lifecycle (e.g., JSON context).',
|
|
59
63
|
type: 'string',
|
|
@@ -167,6 +171,52 @@ Select({
|
|
|
167
171
|
}}
|
|
168
172
|
/>
|
|
169
173
|
|
|
174
|
+
## ExternalSelect
|
|
175
|
+
|
|
176
|
+
Dropdown that loads options dynamically from a handler as the user types. Slack-only. Pair with [`bot.onOptionsLoad`](/docs/api/chat#onoptionsload) to supply options. See [Modals → ExternalSelect](/docs/modals#externalselect) for a full example, grouped-options support, and Slack setup notes.
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
ExternalSelect({
|
|
180
|
+
id: "assignee",
|
|
181
|
+
label: "Assignee",
|
|
182
|
+
placeholder: "Search people",
|
|
183
|
+
minQueryLength: 1,
|
|
184
|
+
initialOption: { label: "Alice", value: "U123" },
|
|
185
|
+
})
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
<TypeTable
|
|
189
|
+
type={{
|
|
190
|
+
id: {
|
|
191
|
+
description: 'Input ID — used as the key in event.values.',
|
|
192
|
+
type: 'string',
|
|
193
|
+
},
|
|
194
|
+
label: {
|
|
195
|
+
description: 'Label displayed above the select.',
|
|
196
|
+
type: 'string',
|
|
197
|
+
},
|
|
198
|
+
placeholder: {
|
|
199
|
+
description: 'Placeholder text.',
|
|
200
|
+
type: 'string',
|
|
201
|
+
},
|
|
202
|
+
minQueryLength: {
|
|
203
|
+
description: 'Minimum characters before the loader fires (Slack default: 3).',
|
|
204
|
+
type: 'number',
|
|
205
|
+
},
|
|
206
|
+
initialOption: {
|
|
207
|
+
description: 'Pre-selected option when the modal opens. Unlike static Select where initialOption is a value string, ExternalSelect needs the full label/value object since the loader has not run yet.',
|
|
208
|
+
type: '{ label: string, value: string }',
|
|
209
|
+
},
|
|
210
|
+
optional: {
|
|
211
|
+
description: 'Whether the field can be left empty.',
|
|
212
|
+
type: 'boolean',
|
|
213
|
+
default: 'false',
|
|
214
|
+
},
|
|
215
|
+
}}
|
|
216
|
+
/>
|
|
217
|
+
|
|
218
|
+
The loader registered via `bot.onOptionsLoad("assignee", handler)` returns either a flat `SelectOptionElement[]` or `OptionsLoadGroup[]` (`{ label, options }[]`) for grouped options.
|
|
219
|
+
|
|
170
220
|
## RadioSelect
|
|
171
221
|
|
|
172
222
|
Radio button group for mutually exclusive choices.
|