chat 4.28.1 → 4.30.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-BPjXsoIl.d.ts +3158 -0
- package/dist/chunk-HD375J7S.js +128 -0
- package/dist/index.d.ts +6 -3143
- package/dist/index.js +45 -139
- package/dist/{jsx-runtime-DxGwoLu2.d.ts → jsx-runtime-CnDs8rPr.d.ts} +1 -1
- package/dist/jsx-runtime.d.ts +1 -1
- package/docs/adapters.mdx +35 -5
- package/docs/ai/ai-sdk-tools.mdx +227 -0
- package/docs/ai/index.mdx +69 -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/getting-started.mdx +5 -1
- package/docs/handling-events.mdx +10 -7
- package/docs/index.mdx +4 -1
- package/docs/meta.json +4 -0
- package/docs/posting-messages.mdx +3 -1
- package/docs/slack-primitives.mdx +320 -0
- package/docs/slash-commands.mdx +4 -4
- package/docs/streaming.mdx +4 -4
- 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/create-a-discord-support-bot-with-nuxt-and-redis.md +5 -1
- package/resources/guides/how-to-build-a-slack-bot-with-next-js-and-redis.md +5 -1
- package/resources/guides/how-to-build-an-ai-agent-for-slack-with-chat-sdk-and-ai-sdk.md +1 -1
- package/resources/guides/human-in-the-loop-with-chat-sdk-and-workflow-sdk.md +176 -0
- package/resources/guides/liveblocks-chat-sdk-ai-sdk.md +165 -0
- package/resources/guides/run-and-track-deploys-from-slack.md +7 -5
- package/resources/guides/ship-a-github-code-review-bot-with-hono-and-redis.md +5 -1
- package/resources/guides/slack-bot-vercel-blob.md +254 -0
- package/resources/guides/triage-form-submissions-with-chat-sdk.md +3 -1
- package/resources/templates.json +5 -0
package/dist/index.js
CHANGED
|
@@ -61,131 +61,9 @@ import {
|
|
|
61
61
|
toPlainText,
|
|
62
62
|
walkAst
|
|
63
63
|
} from "./chunk-V25FKIIL.js";
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
"text/",
|
|
68
|
-
"application/json",
|
|
69
|
-
"application/xml",
|
|
70
|
-
"application/javascript",
|
|
71
|
-
"application/typescript",
|
|
72
|
-
"application/yaml",
|
|
73
|
-
"application/x-yaml",
|
|
74
|
-
"application/toml"
|
|
75
|
-
];
|
|
76
|
-
function isTextMimeType(mimeType) {
|
|
77
|
-
return TEXT_MIME_PREFIXES.some(
|
|
78
|
-
(prefix) => mimeType === prefix || mimeType.startsWith(prefix)
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
async function attachmentToPart(att) {
|
|
82
|
-
if (att.type === "image") {
|
|
83
|
-
if (att.fetchData) {
|
|
84
|
-
try {
|
|
85
|
-
const buffer = await att.fetchData();
|
|
86
|
-
const mimeType = att.mimeType ?? "image/png";
|
|
87
|
-
return {
|
|
88
|
-
type: "file",
|
|
89
|
-
data: `data:${mimeType};base64,${buffer.toString("base64")}`,
|
|
90
|
-
mediaType: mimeType,
|
|
91
|
-
filename: att.name
|
|
92
|
-
};
|
|
93
|
-
} catch (error) {
|
|
94
|
-
console.error("toAiMessages: failed to fetch image data", error);
|
|
95
|
-
return null;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
return null;
|
|
99
|
-
}
|
|
100
|
-
if (att.type === "file" && att.mimeType && isTextMimeType(att.mimeType)) {
|
|
101
|
-
if (att.fetchData) {
|
|
102
|
-
try {
|
|
103
|
-
const buffer = await att.fetchData();
|
|
104
|
-
return {
|
|
105
|
-
type: "file",
|
|
106
|
-
data: `data:${att.mimeType};base64,${buffer.toString("base64")}`,
|
|
107
|
-
filename: att.name,
|
|
108
|
-
mediaType: att.mimeType
|
|
109
|
-
};
|
|
110
|
-
} catch (error) {
|
|
111
|
-
console.error("toAiMessages: failed to fetch file data", error);
|
|
112
|
-
return null;
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
return null;
|
|
116
|
-
}
|
|
117
|
-
return null;
|
|
118
|
-
}
|
|
119
|
-
async function toAiMessages(messages, options) {
|
|
120
|
-
const includeNames = options?.includeNames ?? false;
|
|
121
|
-
const transformMessage = options?.transformMessage;
|
|
122
|
-
const onUnsupported = options?.onUnsupportedAttachment ?? ((att) => {
|
|
123
|
-
console.warn(
|
|
124
|
-
`toAiMessages: unsupported attachment type "${att.type}"${att.name ? ` (${att.name})` : ""} \u2014 skipped`
|
|
125
|
-
);
|
|
126
|
-
});
|
|
127
|
-
const sorted = [...messages].sort(
|
|
128
|
-
(a, b) => (a.metadata.dateSent?.getTime() ?? 0) - (b.metadata.dateSent?.getTime() ?? 0)
|
|
129
|
-
);
|
|
130
|
-
const filtered = sorted.filter((msg) => msg.text.trim());
|
|
131
|
-
const results = await Promise.all(
|
|
132
|
-
filtered.map(async (msg) => {
|
|
133
|
-
const role = msg.author.isMe ? "assistant" : "user";
|
|
134
|
-
let textContent = includeNames && role === "user" ? `[${msg.author.userName}]: ${msg.text}` : msg.text;
|
|
135
|
-
if (msg.links && msg.links.length > 0) {
|
|
136
|
-
const linkParts = msg.links.map((link2) => {
|
|
137
|
-
const parts = link2.fetchMessage ? [`[Embedded message: ${link2.url}]`] : [link2.url];
|
|
138
|
-
if (link2.title) {
|
|
139
|
-
parts.push(`Title: ${link2.title}`);
|
|
140
|
-
}
|
|
141
|
-
if (link2.description) {
|
|
142
|
-
parts.push(`Description: ${link2.description}`);
|
|
143
|
-
}
|
|
144
|
-
if (link2.siteName) {
|
|
145
|
-
parts.push(`Site: ${link2.siteName}`);
|
|
146
|
-
}
|
|
147
|
-
return parts.join("\n");
|
|
148
|
-
}).join("\n\n");
|
|
149
|
-
textContent += `
|
|
150
|
-
|
|
151
|
-
Links:
|
|
152
|
-
${linkParts}`;
|
|
153
|
-
}
|
|
154
|
-
let aiMessage;
|
|
155
|
-
if (role === "user") {
|
|
156
|
-
const attachmentParts = [];
|
|
157
|
-
for (const att of msg.attachments ?? []) {
|
|
158
|
-
const part = await attachmentToPart(att);
|
|
159
|
-
if (part) {
|
|
160
|
-
attachmentParts.push(part);
|
|
161
|
-
} else if (att.type === "video" || att.type === "audio") {
|
|
162
|
-
onUnsupported(att, msg);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
if (attachmentParts.length > 0) {
|
|
166
|
-
aiMessage = {
|
|
167
|
-
role,
|
|
168
|
-
content: [
|
|
169
|
-
{ type: "text", text: textContent },
|
|
170
|
-
...attachmentParts
|
|
171
|
-
]
|
|
172
|
-
};
|
|
173
|
-
} else {
|
|
174
|
-
aiMessage = { role, content: textContent };
|
|
175
|
-
}
|
|
176
|
-
} else {
|
|
177
|
-
aiMessage = { role, content: textContent };
|
|
178
|
-
}
|
|
179
|
-
if (transformMessage) {
|
|
180
|
-
return { result: await transformMessage(aiMessage, msg), source: msg };
|
|
181
|
-
}
|
|
182
|
-
return { result: aiMessage, source: msg };
|
|
183
|
-
})
|
|
184
|
-
);
|
|
185
|
-
return results.filter(
|
|
186
|
-
(r) => r.result != null
|
|
187
|
-
).map((r) => r.result);
|
|
188
|
-
}
|
|
64
|
+
import {
|
|
65
|
+
toAiMessages
|
|
66
|
+
} from "./chunk-HD375J7S.js";
|
|
189
67
|
|
|
190
68
|
// src/channel.ts
|
|
191
69
|
import { WORKFLOW_DESERIALIZE as WORKFLOW_DESERIALIZE2, WORKFLOW_SERIALIZE as WORKFLOW_SERIALIZE2 } from "@workflow/serde";
|
|
@@ -1626,7 +1504,10 @@ var ThreadImpl = class _ThreadImpl {
|
|
|
1626
1504
|
*/
|
|
1627
1505
|
async handleStream(rawStream, callerOptions) {
|
|
1628
1506
|
const textStream = fromFullStream(rawStream);
|
|
1629
|
-
const options = {
|
|
1507
|
+
const options = {
|
|
1508
|
+
updateIntervalMs: this._streamingUpdateIntervalMs,
|
|
1509
|
+
...callerOptions
|
|
1510
|
+
};
|
|
1630
1511
|
if (this._currentMessage) {
|
|
1631
1512
|
options.recipientUserId = this._currentMessage.author.userId;
|
|
1632
1513
|
options.recipientTeamId = this.extractSlackRecipientTeamId(
|
|
@@ -1655,15 +1536,17 @@ var ThreadImpl = class _ThreadImpl {
|
|
|
1655
1536
|
}
|
|
1656
1537
|
};
|
|
1657
1538
|
const raw = await this.adapter.stream(this.id, wrappedStream, options);
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1539
|
+
if (raw) {
|
|
1540
|
+
const sent = this.createSentMessage(
|
|
1541
|
+
raw.id,
|
|
1542
|
+
{ markdown: accumulated },
|
|
1543
|
+
raw.threadId
|
|
1544
|
+
);
|
|
1545
|
+
if (this._threadHistory) {
|
|
1546
|
+
await this._threadHistory.append(this.id, new Message(sent));
|
|
1547
|
+
}
|
|
1548
|
+
return sent;
|
|
1665
1549
|
}
|
|
1666
|
-
return sent;
|
|
1667
1550
|
}
|
|
1668
1551
|
const textOnlyStream = {
|
|
1669
1552
|
[Symbol.asyncIterator]: () => {
|
|
@@ -2515,9 +2398,13 @@ var Chat = class {
|
|
|
2515
2398
|
/**
|
|
2516
2399
|
* Register a handler for direct messages.
|
|
2517
2400
|
*
|
|
2518
|
-
* Called
|
|
2519
|
-
*
|
|
2520
|
-
* `onNewMention
|
|
2401
|
+
* Called for every message received in a DM thread when at least one
|
|
2402
|
+
* direct message handler is registered. Direct message handlers run before
|
|
2403
|
+
* `onSubscribedMessage`, `onNewMention`, and pattern handlers.
|
|
2404
|
+
*
|
|
2405
|
+
* If no `onDirectMessage` handlers are registered, DMs continue through
|
|
2406
|
+
* normal routing. Unsubscribed DMs fall through to `onNewMention` for
|
|
2407
|
+
* backward compatibility.
|
|
2521
2408
|
*
|
|
2522
2409
|
* @param handler - Handler called for DM messages
|
|
2523
2410
|
*
|
|
@@ -3534,7 +3421,7 @@ var Chat = class {
|
|
|
3534
3421
|
* - Deduplication: Same message may arrive multiple times (e.g., Slack sends
|
|
3535
3422
|
* both `message` and `app_mention` events, GChat sends direct webhook + Pub/Sub)
|
|
3536
3423
|
* - Bot filtering: Messages from the bot itself are skipped
|
|
3537
|
-
* - Concurrency: Controlled by `concurrency` config (drop, queue, debounce, concurrent)
|
|
3424
|
+
* - Concurrency: Controlled by `concurrency` config (drop, queue, debounce, burst, concurrent)
|
|
3538
3425
|
*/
|
|
3539
3426
|
async handleIncomingMessage(adapter, threadId, message) {
|
|
3540
3427
|
setMessageAdapter(message, adapter);
|
|
@@ -3583,7 +3470,7 @@ var Chat = class {
|
|
|
3583
3470
|
await this.handleConcurrent(adapter, threadId, message);
|
|
3584
3471
|
return;
|
|
3585
3472
|
}
|
|
3586
|
-
if (strategy === "queue" || strategy === "debounce") {
|
|
3473
|
+
if (strategy === "queue" || strategy === "debounce" || strategy === "burst") {
|
|
3587
3474
|
await this.handleQueueOrDebounce(
|
|
3588
3475
|
adapter,
|
|
3589
3476
|
threadId,
|
|
@@ -3702,6 +3589,25 @@ var Chat = class {
|
|
|
3702
3589
|
debounceMs
|
|
3703
3590
|
});
|
|
3704
3591
|
await this.debounceLoop(lock, adapter, threadId, lockKey);
|
|
3592
|
+
} else if (strategy === "burst") {
|
|
3593
|
+
await this._stateAdapter.enqueue(
|
|
3594
|
+
lockKey,
|
|
3595
|
+
{
|
|
3596
|
+
message,
|
|
3597
|
+
enqueuedAt: Date.now(),
|
|
3598
|
+
expiresAt: Date.now() + queueEntryTtlMs
|
|
3599
|
+
},
|
|
3600
|
+
maxQueueSize
|
|
3601
|
+
);
|
|
3602
|
+
this.logger.info("message-debouncing", {
|
|
3603
|
+
threadId,
|
|
3604
|
+
lockKey,
|
|
3605
|
+
messageId: message.id,
|
|
3606
|
+
debounceMs
|
|
3607
|
+
});
|
|
3608
|
+
await sleep(debounceMs);
|
|
3609
|
+
await this._stateAdapter.extendLock(lock, DEFAULT_LOCK_TTL_MS);
|
|
3610
|
+
await this.drainQueue(lock, adapter, threadId, lockKey);
|
|
3705
3611
|
} else {
|
|
3706
3612
|
await this.dispatchToHandlers(adapter, threadId, message);
|
|
3707
3613
|
await this.drainQueue(lock, adapter, threadId, lockKey);
|
|
@@ -779,4 +779,4 @@ declare namespace JSX {
|
|
|
779
779
|
}
|
|
780
780
|
}
|
|
781
781
|
|
|
782
|
-
export { type
|
|
782
|
+
export { type SelectOptionElement as $, type ActionsComponent as A, type ButtonComponent as B, type CardChild as C, type DividerComponent as D, type ExternalSelectComponent as E, type FieldComponent as F, type ImageProps as G, type LinkButtonElement as H, type ImageComponent as I, JSX as J, type LinkButtonOptions as K, type LinkButtonComponent as L, type LinkButtonProps as M, type LinkElement as N, type ModalChild as O, type ModalComponent as P, type ModalElement as Q, type ModalOptions as R, type ModalProps as S, type RadioSelectComponent as T, type RadioSelectElement as U, type RadioSelectOptions as V, type SectionComponent as W, type SectionElement as X, type SelectComponent as Y, type SelectElement as Z, type SelectOptionComponent as _, type ActionsElement as a, type SelectOptionProps as a0, type SelectOptions as a1, type SelectProps as a2, Table as a3, type TableAlignment as a4, type TableComponent as a5, type TableElement as a6, type TableOptions as a7, type TableProps as a8, type TextComponent as a9, type TextElement as aa, type TextInputComponent as ab, type TextInputElement as ac, type TextInputOptions as ad, type TextInputProps as ae, type TextProps as af, type TextStyle as ag, cardChildToFallbackText as ah, fromReactElement as ai, fromReactModalElement as aj, isCardElement as ak, isCardLinkProps as al, isJSX as am, isModalElement as an, jsx as ao, jsxDEV as ap, jsxs as aq, toCardElement as ar, toModalElement as as, type ButtonElement as b, type ButtonOptions as c, type ButtonProps as d, type ButtonStyle as e, type CardComponent as f, type CardElement as g, type CardJSXElement as h, type CardJSXProps as i, type CardLinkComponent as j, type CardLinkProps as k, type CardOptions as l, type CardProps as m, type ChatElement as n, type ContainerProps as o, type DividerElement as p, type DividerProps as q, type ExternalSelectElement as r, type ExternalSelectOptions as s, type ExternalSelectProps as t, type FieldElement as u, type FieldProps as v, type FieldsComponent as w, type FieldsElement as x, Fragment as y, type ImageElement as z };
|
package/dist/jsx-runtime.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { A as ActionsComponent, B as ButtonComponent,
|
|
1
|
+
export { A as ActionsComponent, B as ButtonComponent, d as ButtonProps, f as CardComponent, h as CardJSXElement, i as CardJSXProps, j as CardLinkComponent, k as CardLinkProps, m as CardProps, n as ChatElement, o as ContainerProps, D as DividerComponent, q as DividerProps, E as ExternalSelectComponent, t as ExternalSelectProps, F as FieldComponent, v as FieldProps, w as FieldsComponent, y as Fragment, I as ImageComponent, G as ImageProps, J as JSX, L as LinkButtonComponent, M as LinkButtonProps, P as ModalComponent, S as ModalProps, T as RadioSelectComponent, W as SectionComponent, Y as SelectComponent, _ as SelectOptionComponent, a0 as SelectOptionProps, a2 as SelectProps, a5 as TableComponent, a8 as TableProps, a9 as TextComponent, ab as TextInputComponent, ae as TextInputProps, af as TextProps, al as isCardLinkProps, am as isJSX, ao as jsx, ap as jsxDEV, aq as jsxs, ar as toCardElement, as as toModalElement } from './jsx-runtime-CnDs8rPr.js';
|
package/docs/adapters.mdx
CHANGED
|
@@ -8,12 +8,13 @@ prerequisites:
|
|
|
8
8
|
|
|
9
9
|
Adapters handle webhook verification, message parsing, and API calls for each platform. Install only the adapters you need. Browse all available adapters — including community-built ones — on the [Adapters](/adapters) page.
|
|
10
10
|
|
|
11
|
-
Need a browser chat UI? See the [Web adapter](/adapters/web) — it speaks the AI SDK `
|
|
11
|
+
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.
|
|
12
12
|
|
|
13
13
|
Ready to build your own? Follow the [building](/docs/contributing/building) guide.
|
|
14
14
|
|
|
15
15
|
## Feature matrix
|
|
16
16
|
|
|
17
|
+
<GlobalFeatureMatrix type="platform" />
|
|
17
18
|
### Messaging
|
|
18
19
|
|
|
19
20
|
| Feature | [Slack](/adapters/slack) | [Teams](/adapters/teams) | [Google Chat](/adapters/google-chat) | [Discord](/adapters/discord) | [Telegram](/adapters/telegram) | [GitHub](/adapters/github) | [Linear](/adapters/linear) | [WhatsApp](/adapters/whatsapp) | [Messenger](/adapters/messenger) |
|
|
@@ -21,8 +22,8 @@ Ready to build your own? Follow the [building](/docs/contributing/building) guid
|
|
|
21
22
|
| Post message | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> |
|
|
22
23
|
| Edit message | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Warn /> Partial | <Cross /> | <Cross /> |
|
|
23
24
|
| Delete message | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Warn /> Partial | <Cross /> | <Cross /> |
|
|
24
|
-
| File uploads | <Check /> | <Check /> | <Cross /> | <Check /> | <Warn /> Single file | <Cross /> | <Cross /> | <Check /> Images, audio, docs | <Cross /> |
|
|
25
|
-
| Streaming | <Check /> Native | <Warn /> Native (DMs) / Buffered | <Warn /> Post+Edit | <Warn /> Post+Edit | <Warn /> Post+Edit | <Warn /> Buffered | <Warn /> Agent sessions / Post+Edit | <Warn /> Buffered | <Warn /> Buffered |
|
|
25
|
+
| File uploads | <Check /> | <Check /> | <Cross /> | <Check /> | <Warn /> Single file/media | <Cross /> | <Cross /> | <Check /> Images, audio, docs | <Cross /> |
|
|
26
|
+
| Streaming | <Check /> Native | <Warn /> Native (DMs) / Buffered | <Warn /> Post+Edit | <Warn /> Post+Edit | <Warn /> Private chat drafts / Post+Edit | <Warn /> Buffered | <Warn /> Agent sessions / Post+Edit | <Warn /> Buffered | <Warn /> Buffered |
|
|
26
27
|
| Scheduled messages | <Check /> Native | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> |
|
|
27
28
|
|
|
28
29
|
### Rich content
|
|
@@ -46,12 +47,12 @@ Ready to build your own? Follow the [building](/docs/contributing/building) guid
|
|
|
46
47
|
| Mentions | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Cross /> | <Check /> |
|
|
47
48
|
| Add reactions | <Check /> | <Cross /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Cross /> |
|
|
48
49
|
| Remove reactions | <Check /> | <Cross /> | <Check /> | <Check /> | <Check /> | <Warn /> | <Warn /> | <Check /> | <Cross /> |
|
|
49
|
-
| Typing indicator | <Check /> | <Check /> | <Cross /> | <Check /> | <Check /> | <Cross /> | <Warn /> Agent sessions | <
|
|
50
|
+
| Typing indicator | <Check /> | <Check /> | <Cross /> | <Check /> | <Check /> | <Cross /> | <Warn /> Agent sessions | <Warn /> | <Check /> |
|
|
50
51
|
| DMs | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Cross /> | <Cross /> | <Check /> | <Check /> |
|
|
51
52
|
| Ephemeral messages | <Check /> Native | <Cross /> | <Check /> Native | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> |
|
|
52
53
|
| User lookup ([`getUser`](/docs/api/chat#getuser)) | <Check /> | <Warn /> Cached | <Warn /> Cached | <Check /> | <Warn /> Seen users | <Check /> | <Check /> | <Cross /> | <Cross /> |
|
|
53
54
|
| Parent subject ([`message.subject`](/docs/subject)) | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Check /> | <Check /> | <Cross /> | <Cross /> |
|
|
54
|
-
| Native client ([`.
|
|
55
|
+
| Native client ([`.webClient` / `.octokit` / `.linearClient`](/docs/api/chat#getadapter)) | <Check /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Check /> | <Check /> | <Cross /> | <Cross /> |
|
|
55
56
|
| Custom API endpoint (`apiUrl`) | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> |
|
|
56
57
|
|
|
57
58
|
### Message history
|
|
@@ -114,3 +115,32 @@ Each adapter auto-detects credentials from environment variables, so you only ne
|
|
|
114
115
|
</Callout>
|
|
115
116
|
|
|
116
117
|
Each adapter creates a webhook handler accessible via `bot.webhooks.<name>`.
|
|
118
|
+
|
|
119
|
+
## Customizing an adapter via subclassing
|
|
120
|
+
|
|
121
|
+
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.
|
|
122
|
+
|
|
123
|
+
```typescript title="lib/custom-telegram.ts" lineNumbers
|
|
124
|
+
import { TelegramAdapter, type TelegramUpdate } from "@chat-adapter/telegram";
|
|
125
|
+
import type { WebhookOptions } from "chat";
|
|
126
|
+
|
|
127
|
+
export class CustomTelegramAdapter extends TelegramAdapter {
|
|
128
|
+
protected override processUpdate(
|
|
129
|
+
update: TelegramUpdate,
|
|
130
|
+
options?: WebhookOptions
|
|
131
|
+
): void {
|
|
132
|
+
// Handle a payload type the base adapter doesn't, e.g. chat_join_request.
|
|
133
|
+
if ("chat_join_request" in update) {
|
|
134
|
+
this.logger.info("Received chat_join_request", { update });
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
super.processUpdate(update, options);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Construct your subclass anywhere you'd construct the base adapter — for example, `adapters: { telegram: new CustomTelegramAdapter({ ... }) }`. Members marked `private` (internal caches, in-flight runtime state, one-shot warning flags) intentionally remain inaccessible; if you find a hook you need that isn't `protected`, please open an issue.
|
|
143
|
+
|
|
144
|
+
<Callout type="warn">
|
|
145
|
+
The `protected` extension surface is intentionally broader than the public API but is not yet considered fully stable. Method signatures may evolve (renames, parameter changes, new hook splits) 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.
|
|
146
|
+
</Callout>
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: AI SDK Tools
|
|
3
|
+
description: Give an AI agent the ability to operate inside your workspace. Post messages, send DMs, react, edit, delete; all with built-in approval gates.
|
|
4
|
+
type: guide
|
|
5
|
+
prerequisites:
|
|
6
|
+
- /docs/usage
|
|
7
|
+
related:
|
|
8
|
+
- /docs/ai
|
|
9
|
+
- /docs/ai/to-ai-messages
|
|
10
|
+
- /docs/streaming
|
|
11
|
+
- /docs/conversation-history
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
`createChatTools` exposes Chat SDK operations as ready-to-use [AI SDK](https://ai-sdk.dev) tools so an agent can act inside the same workspaces your bot is connected to: read messages, post replies, send DMs, react, edit, delete, and manage thread subscriptions across every adapter you've registered.
|
|
15
|
+
|
|
16
|
+
Write operations require user approval out of the box, toggle them globally or per-tool when you want unattended execution.
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
The tools live in the [`chat/ai`](/docs/ai) subpath of the core `chat` package:
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import { createChatTools } from "chat/ai";
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`ai` and `zod` are optional peer dependencies — install them if you haven't already:
|
|
27
|
+
|
|
28
|
+
<PackageInstall package="ai zod" />
|
|
29
|
+
|
|
30
|
+
<Callout>
|
|
31
|
+
Pair `createChatTools` with [`toAiMessages`](/docs/ai/to-ai-messages)
|
|
32
|
+
to feed prior thread history into the agent before it picks a tool.
|
|
33
|
+
Both ship from the same `chat/ai` subpath, which keeps the optional
|
|
34
|
+
`ai` / `zod` peer deps out of bundles that don't import them.
|
|
35
|
+
</Callout>
|
|
36
|
+
|
|
37
|
+
## Quick start
|
|
38
|
+
|
|
39
|
+
Pass your `Chat` instance and the tools you want into any AI SDK call:
|
|
40
|
+
|
|
41
|
+
```typescript title="lib/agent.ts" lineNumbers
|
|
42
|
+
import { Chat } from "chat";
|
|
43
|
+
import { createChatTools } from "chat/ai";
|
|
44
|
+
import { createSlackAdapter } from "@chat-adapter/slack";
|
|
45
|
+
import { createMemoryState } from "@chat-adapter/state-memory";
|
|
46
|
+
import { generateText } from "ai";
|
|
47
|
+
|
|
48
|
+
const chat = new Chat({
|
|
49
|
+
userName: "mybot",
|
|
50
|
+
adapters: { slack: createSlackAdapter() },
|
|
51
|
+
state: createMemoryState(),
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const result = await generateText({
|
|
55
|
+
model: "anthropic/claude-sonnet-4.6",
|
|
56
|
+
tools: createChatTools({
|
|
57
|
+
chat,
|
|
58
|
+
preset: "messenger",
|
|
59
|
+
requireApproval: false, // unattended script, no human-in-the-loop needed
|
|
60
|
+
}),
|
|
61
|
+
prompt:
|
|
62
|
+
"Post a friendly hello in slack:C0123ABC and react to it with a thumbs up.",
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Each tool resolves the right adapter from the id prefix you give it (`slack:...`, `discord:...`, `gchat:...`), so the same agent can drive any platform your `Chat` instance is wired up to.
|
|
67
|
+
|
|
68
|
+
## Presets
|
|
69
|
+
|
|
70
|
+
Pass `preset` to scope the toolset down to what an agent actually needs.
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
// Read-only — fetch messages, threads, channel info, users
|
|
74
|
+
createChatTools({ chat, preset: "reader" });
|
|
75
|
+
|
|
76
|
+
// Basic posting — read + post + DM + react + typing indicator
|
|
77
|
+
createChatTools({ chat, preset: "messenger" });
|
|
78
|
+
|
|
79
|
+
// Full management — everything including edit, delete, subscriptions
|
|
80
|
+
createChatTools({ chat, preset: "moderator" });
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Presets compose — pass an array to combine them:
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
createChatTools({ chat, preset: ["reader", "messenger"] });
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Omit `preset` entirely to get every tool (same as `'moderator'`).
|
|
90
|
+
|
|
91
|
+
| Preset | Tools included |
|
|
92
|
+
|---|---|
|
|
93
|
+
| `reader` | `fetchMessages`, `fetchChannelMessages`, `fetchThread`, `listThreads`, `getThreadParticipants`, `getChannelInfo`, `getUser` |
|
|
94
|
+
| `messenger` | `fetchMessages`, `fetchThread`, `getChannelInfo`, `getUser`, `postMessage`, `postChannelMessage`, `sendDirectMessage`, `addReaction`, `removeReaction`, `startTyping` |
|
|
95
|
+
| `moderator` | All read tools plus `postMessage`, `postChannelMessage`, `sendDirectMessage`, `editMessage`, `deleteMessage`, `addReaction`, `removeReaction`, `subscribeThread`, `unsubscribeThread`, `startTyping` |
|
|
96
|
+
|
|
97
|
+
## Approval control
|
|
98
|
+
|
|
99
|
+
Write operations (posting, editing, deleting, reacting, subscribing) default to `needsApproval: true`. The AI SDK pauses execution and surfaces an approval request that your application is expected to confirm before the tool runs. This keeps a human in the loop for anything visible to the workspace.
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
// All writes need approval (default)
|
|
103
|
+
createChatTools({ chat });
|
|
104
|
+
|
|
105
|
+
// No approval needed
|
|
106
|
+
createChatTools({ chat, requireApproval: false });
|
|
107
|
+
|
|
108
|
+
// Per-tool — only destructive actions need approval
|
|
109
|
+
createChatTools({
|
|
110
|
+
chat,
|
|
111
|
+
requireApproval: {
|
|
112
|
+
deleteMessage: true,
|
|
113
|
+
editMessage: true,
|
|
114
|
+
sendDirectMessage: false,
|
|
115
|
+
postMessage: false,
|
|
116
|
+
addReaction: false,
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Read tools (`fetchMessages`, `fetchThread`, `getChannelInfo`, …) and the `startTyping` indicator never require approval.
|
|
122
|
+
|
|
123
|
+
## Cherry-picking tools
|
|
124
|
+
|
|
125
|
+
Each tool is also exported as a standalone factory you can hand to `tools` directly:
|
|
126
|
+
|
|
127
|
+
```typescript title="lib/agent.ts" lineNumbers
|
|
128
|
+
import { fetchMessages, postMessage, addReaction } from "chat/ai";
|
|
129
|
+
|
|
130
|
+
const tools = {
|
|
131
|
+
fetchMessages: fetchMessages(chat),
|
|
132
|
+
postMessage: postMessage(chat, { needsApproval: false }),
|
|
133
|
+
addReaction: addReaction(chat, { needsApproval: false }),
|
|
134
|
+
};
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Useful when you want a small, targeted toolset without going through `createChatTools`.
|
|
138
|
+
|
|
139
|
+
## Tool overrides
|
|
140
|
+
|
|
141
|
+
Customize any AI SDK [`tool()`](https://ai-sdk.dev/docs/ai-sdk-core/tools-and-tool-calling) property per tool, keyed by tool name:
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
import type { ChatToolName, ToolOverrides } from "chat/ai";
|
|
145
|
+
|
|
146
|
+
createChatTools({
|
|
147
|
+
chat,
|
|
148
|
+
overrides: {
|
|
149
|
+
postMessage: {
|
|
150
|
+
description: "Reply in the active customer support thread.",
|
|
151
|
+
needsApproval: false,
|
|
152
|
+
},
|
|
153
|
+
deleteMessage: { needsApproval: true },
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
| Property | Type | Description |
|
|
159
|
+
|---|---|---|
|
|
160
|
+
| `description` | `string` | Custom tool description shown to the model |
|
|
161
|
+
| `title` | `string` | Human-readable title |
|
|
162
|
+
| `strict` | `boolean` | Strict mode for input generation |
|
|
163
|
+
| `inputExamples` | `array` | Examples that show the model what tool input should look like |
|
|
164
|
+
| `metadata` | `object` | Tool metadata propagated to tool call and result parts |
|
|
165
|
+
| `needsApproval` | `boolean \| function` | Gate execution behind an approval request |
|
|
166
|
+
| `providerOptions` | `ProviderOptions` | Provider-specific metadata |
|
|
167
|
+
| `onInputStart` | `function` | Callback when argument streaming starts |
|
|
168
|
+
| `onInputDelta` | `function` | Callback on each streaming delta |
|
|
169
|
+
| `onInputAvailable` | `function` | Callback when full input is available |
|
|
170
|
+
| `toModelOutput` | `function` | Custom mapping of tool result to model output |
|
|
171
|
+
|
|
172
|
+
Core properties (`execute`, `inputSchema`, `outputSchema`, and tool-kind fields like `type`, `id`, `args`) cannot be overridden so tool semantics stay stable.
|
|
173
|
+
|
|
174
|
+
## Available tools
|
|
175
|
+
|
|
176
|
+
All ids accept the full Chat SDK form: `slack:C123ABC:1234567890.123456` for a thread, `slack:C123ABC` for a channel, and the platform-native user id (e.g. `U123456` on Slack, `users/123` on Google Chat). The tools auto-detect the adapter from the prefix.
|
|
177
|
+
|
|
178
|
+
### Reading
|
|
179
|
+
|
|
180
|
+
| Tool | Description |
|
|
181
|
+
|---|---|
|
|
182
|
+
| `fetchMessages` | Fetch recent messages from a thread (paginated) |
|
|
183
|
+
| `fetchChannelMessages` | Fetch top-level messages in a channel (not thread replies) |
|
|
184
|
+
| `fetchThread` | Fetch metadata for a thread (channel id, visibility, DM status) |
|
|
185
|
+
| `listThreads` | List recent threads in a channel with their root message |
|
|
186
|
+
| `getThreadParticipants` | Return the unique non-bot participants in a thread |
|
|
187
|
+
| `getChannelInfo` | Fetch channel metadata (name, member count, visibility) |
|
|
188
|
+
| `getUser` | Look up a user's profile by id |
|
|
189
|
+
|
|
190
|
+
### Writing
|
|
191
|
+
|
|
192
|
+
| Tool | Description | Default approval |
|
|
193
|
+
|---|---|---|
|
|
194
|
+
| `postMessage` | Post a reply in an existing thread | required |
|
|
195
|
+
| `postChannelMessage` | Post a top-level message in a channel | required |
|
|
196
|
+
| `sendDirectMessage` | Open a DM with a user and post in it | required |
|
|
197
|
+
| `editMessage` | Edit a message the bot previously posted | required |
|
|
198
|
+
| `deleteMessage` | Delete a message the bot previously posted | required |
|
|
199
|
+
| `addReaction` | Add an emoji reaction to a message | required |
|
|
200
|
+
| `removeReaction` | Remove a previously-added reaction | required |
|
|
201
|
+
| `subscribeThread` | Subscribe the bot to all future messages in a thread | required |
|
|
202
|
+
| `unsubscribeThread` | Stop receiving non-mention messages in a thread | required |
|
|
203
|
+
| `startTyping` | Show a typing indicator in a thread | not gated |
|
|
204
|
+
|
|
205
|
+
## API
|
|
206
|
+
|
|
207
|
+
### `createChatTools(options)`
|
|
208
|
+
|
|
209
|
+
Returns an object of tools, ready to spread into `tools` of any AI SDK call.
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
type ChatToolsOptions = {
|
|
213
|
+
chat: Chat;
|
|
214
|
+
requireApproval?: boolean | Partial<Record<ChatWriteToolName, boolean>>;
|
|
215
|
+
preset?: ChatToolPreset | ChatToolPreset[];
|
|
216
|
+
overrides?: Partial<Record<ChatToolName, ToolOverrides>>;
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
type ChatToolPreset = "reader" | "messenger" | "moderator";
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
| Option | Description |
|
|
223
|
+
|---|---|
|
|
224
|
+
| `chat` | The `Chat` instance the tools dispatch operations against. Required. |
|
|
225
|
+
| `preset` | Preset (or array of presets) restricting which tools are returned. Omit to get every tool. |
|
|
226
|
+
| `requireApproval` | `true` (default), `false`, or per-tool overrides. Read tools and `startTyping` are never gated. |
|
|
227
|
+
| `overrides` | Per-tool customization of any AI SDK `tool()` property except `execute`, `inputSchema`, and `outputSchema`. |
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Overview
|
|
3
|
+
description: AI utilities that ship with Chat SDK — agent tools, message conversion, and supporting types.
|
|
4
|
+
type: overview
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
The `chat/ai` subpath is the home for AI utilities that ship with Chat SDK.
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { createChatTools, toAiMessages } from "chat/ai";
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Add the optional peers if you don't already have them:
|
|
14
|
+
|
|
15
|
+
<PackageInstall package="ai zod" />
|
|
16
|
+
|
|
17
|
+
## What's included
|
|
18
|
+
|
|
19
|
+
| Page | What it gives you |
|
|
20
|
+
|---|---|
|
|
21
|
+
| [AI SDK Tools](/docs/ai/ai-sdk-tools) | `createChatTools` and standalone tool factories that let an agent post messages, send DMs, react, edit, delete, and manage subscriptions across every adapter your `Chat` instance has registered. Built-in approval gates and presets keep writes safe. |
|
|
22
|
+
| [`toAiMessages`](/docs/ai/to-ai-messages) | Convert Chat SDK [`Message[]`](/docs/api/message) into the `{ role, content }[]` shape expected by AI SDK calls. Handles role mapping, attachments, links, sorting, and optional per-message transforms. |
|
|
23
|
+
| [Types](/docs/ai/types) | Reference for every type exported from `chat/ai` — agent message shapes, tool option contracts, presets, approval config, and the binding type that ties tools to your `Chat` instance. |
|
|
24
|
+
|
|
25
|
+
## Typical flow
|
|
26
|
+
|
|
27
|
+
A Chat SDK bot wired to a tool-calling agent usually looks like this:
|
|
28
|
+
|
|
29
|
+
```typescript title="lib/agent.ts" lineNumbers
|
|
30
|
+
import { Chat } from "chat";
|
|
31
|
+
import { createChatTools, toAiMessages } from "chat/ai";
|
|
32
|
+
import { ToolLoopAgent } from "ai";
|
|
33
|
+
|
|
34
|
+
const chat = new Chat({ /* adapters, state, ... */ });
|
|
35
|
+
|
|
36
|
+
const agent = new ToolLoopAgent({
|
|
37
|
+
model: "anthropic/claude-sonnet-4.6",
|
|
38
|
+
instructions: "You operate inside a chat workspace via Chat SDK tools.",
|
|
39
|
+
tools: createChatTools({ chat, preset: "messenger", requireApproval: true }),
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
bot.onSubscribedMessage(async (thread) => {
|
|
43
|
+
const { messages } = await thread.adapter.fetchMessages(thread.id, {
|
|
44
|
+
limit: 20,
|
|
45
|
+
});
|
|
46
|
+
const history = await toAiMessages(messages);
|
|
47
|
+
const result = await agent.stream({ prompt: history });
|
|
48
|
+
await thread.post(result.fullStream);
|
|
49
|
+
});
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
1. [`toAiMessages`](/docs/ai/to-ai-messages) converts messages into an output compatible with AI SDK's `ModelMessage[]`.
|
|
53
|
+
2. [`createChatTools`](/docs/ai/ai-sdk-tools) gives the agent pre-built and fully customizable AI SDK tools.
|
|
54
|
+
3. The streamed response is rendered back into the thread via the standard [`thread.post(stream)`](/docs/streaming) flow.
|
|
55
|
+
|
|
56
|
+
## Backwards compatibility
|
|
57
|
+
|
|
58
|
+
`toAiMessages` and the related `Ai*` types are still re-exported from the top-level `chat` package so older bots keep working. Those re-exports are now flagged with `@deprecated` JSDoc — your editor will surface a hint pointing at `chat/ai`. Migrating is a one-line import change:
|
|
59
|
+
|
|
60
|
+
```diff
|
|
61
|
+
- import { toAiMessages } from "chat";
|
|
62
|
+
+ import { toAiMessages } from "chat/ai";
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Resources
|
|
66
|
+
|
|
67
|
+
- [Human-in-the-Loop with Chat SDK and Workflow SDK](https://vercel.com/kb/guide/human-in-the-loop-with-chat-sdk-and-workflow-sdk) — Pause durable workflows on Slack approval cards using Chat SDK and Workflow SDK. Uses `createWebhook` to suspend workflows until a button click, with patterns for multi-stage approvals, timeouts via durable sleep, and approver validation.
|
|
68
|
+
|
|
69
|
+
See all guides and templates on the [resources](/resources) page.
|