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/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";
|
|
@@ -2515,9 +2393,13 @@ var Chat = class {
|
|
|
2515
2393
|
/**
|
|
2516
2394
|
* Register a handler for direct messages.
|
|
2517
2395
|
*
|
|
2518
|
-
* Called
|
|
2519
|
-
*
|
|
2520
|
-
* `onNewMention
|
|
2396
|
+
* Called for every message received in a DM thread when at least one
|
|
2397
|
+
* direct message handler is registered. Direct message handlers run before
|
|
2398
|
+
* `onSubscribedMessage`, `onNewMention`, and pattern handlers.
|
|
2399
|
+
*
|
|
2400
|
+
* If no `onDirectMessage` handlers are registered, DMs continue through
|
|
2401
|
+
* normal routing. Unsubscribed DMs fall through to `onNewMention` for
|
|
2402
|
+
* backward compatibility.
|
|
2521
2403
|
*
|
|
2522
2404
|
* @param handler - Handler called for DM messages
|
|
2523
2405
|
*
|
|
@@ -3534,7 +3416,7 @@ var Chat = class {
|
|
|
3534
3416
|
* - Deduplication: Same message may arrive multiple times (e.g., Slack sends
|
|
3535
3417
|
* both `message` and `app_mention` events, GChat sends direct webhook + Pub/Sub)
|
|
3536
3418
|
* - Bot filtering: Messages from the bot itself are skipped
|
|
3537
|
-
* - Concurrency: Controlled by `concurrency` config (drop, queue, debounce, concurrent)
|
|
3419
|
+
* - Concurrency: Controlled by `concurrency` config (drop, queue, debounce, burst, concurrent)
|
|
3538
3420
|
*/
|
|
3539
3421
|
async handleIncomingMessage(adapter, threadId, message) {
|
|
3540
3422
|
setMessageAdapter(message, adapter);
|
|
@@ -3583,7 +3465,7 @@ var Chat = class {
|
|
|
3583
3465
|
await this.handleConcurrent(adapter, threadId, message);
|
|
3584
3466
|
return;
|
|
3585
3467
|
}
|
|
3586
|
-
if (strategy === "queue" || strategy === "debounce") {
|
|
3468
|
+
if (strategy === "queue" || strategy === "debounce" || strategy === "burst") {
|
|
3587
3469
|
await this.handleQueueOrDebounce(
|
|
3588
3470
|
adapter,
|
|
3589
3471
|
threadId,
|
|
@@ -3702,6 +3584,25 @@ var Chat = class {
|
|
|
3702
3584
|
debounceMs
|
|
3703
3585
|
});
|
|
3704
3586
|
await this.debounceLoop(lock, adapter, threadId, lockKey);
|
|
3587
|
+
} else if (strategy === "burst") {
|
|
3588
|
+
await this._stateAdapter.enqueue(
|
|
3589
|
+
lockKey,
|
|
3590
|
+
{
|
|
3591
|
+
message,
|
|
3592
|
+
enqueuedAt: Date.now(),
|
|
3593
|
+
expiresAt: Date.now() + queueEntryTtlMs
|
|
3594
|
+
},
|
|
3595
|
+
maxQueueSize
|
|
3596
|
+
);
|
|
3597
|
+
this.logger.info("message-debouncing", {
|
|
3598
|
+
threadId,
|
|
3599
|
+
lockKey,
|
|
3600
|
+
messageId: message.id,
|
|
3601
|
+
debounceMs
|
|
3602
|
+
});
|
|
3603
|
+
await sleep(debounceMs);
|
|
3604
|
+
await this._stateAdapter.extendLock(lock, DEFAULT_LOCK_TTL_MS);
|
|
3605
|
+
await this.drainQueue(lock, adapter, threadId, lockKey);
|
|
3705
3606
|
} else {
|
|
3706
3607
|
await this.dispatchToHandlers(adapter, threadId, message);
|
|
3707
3608
|
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 DividerProps as $, type ActionsComponent as A, type ButtonComponent as B, type CardElement as C, type DividerComponent as D, type ExternalSelectComponent as E, type FieldComponent as F, type LinkButtonOptions as G, type LinkElement as H, type ImageComponent as I, type SectionElement as J, type TableAlignment as K, type LinkButtonComponent as L, type ModalComponent as M, type TableElement as N, type TableOptions as O, type TextElement as P, type TextStyle as Q, type RadioSelectComponent as R, type SectionComponent as S, type TextComponent as T, type ButtonProps as U, type CardJSXElement as V, type CardJSXProps as W, type CardLinkProps as X, type CardProps as Y, type ChatElement as Z, type ContainerProps as _, type CardChild as a, type ExternalSelectProps as a0, type FieldProps as a1, type ImageProps as a2, type LinkButtonProps as a3, type ModalProps as a4, type SelectOptionProps as a5, type SelectProps as a6, type TextInputProps as a7, type TextProps as a8, type ExternalSelectElement as a9, type ExternalSelectOptions as aa, type ModalChild as ab, type ModalElement as ac, type ModalOptions as ad, type RadioSelectElement as ae, type RadioSelectOptions as af, type SelectElement as ag, type SelectOptionElement as ah, type SelectOptions as ai, type TextInputElement as aj, type TextInputOptions as ak, type TableProps as al, type TableComponent as am, isCardLinkProps as an, jsx as ao, jsxs as ap, jsxDEV as aq, Fragment as ar, JSX as as, type CardComponent as b, cardChildToFallbackText as c, type CardLinkComponent as d, type FieldsComponent as e, fromReactElement as f, isJSX as g, Table as h, isCardElement as i, toModalElement as j, fromReactModalElement as k, isModalElement as l, type SelectComponent as m, type SelectOptionComponent as n, type TextInputComponent as o, type ActionsElement as p, type ButtonElement as q, type ButtonOptions as r, type ButtonStyle as s, toCardElement as t, type CardOptions as u, type DividerElement as v, type FieldElement as w, type FieldsElement as x, type ImageElement as y, type LinkButtonElement 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, U as ButtonProps, b as CardComponent, V as CardJSXElement, W as CardJSXProps, d as CardLinkComponent, X as CardLinkProps, Y as CardProps, Z as ChatElement, _ as ContainerProps, D as DividerComponent, $ as DividerProps, E as ExternalSelectComponent, a0 as ExternalSelectProps, F as FieldComponent, a1 as FieldProps, e as FieldsComponent, ar as Fragment, I as ImageComponent, a2 as ImageProps, as as JSX, L as LinkButtonComponent, a3 as LinkButtonProps, M as ModalComponent, a4 as ModalProps, R as RadioSelectComponent, S as SectionComponent, m as SelectComponent, n as SelectOptionComponent, a5 as SelectOptionProps, a6 as SelectProps, am as TableComponent, al as TableProps, T as TextComponent, o as TextInputComponent, a7 as TextInputProps, a8 as TextProps, an as isCardLinkProps, g as isJSX, ao as jsx, aq as jsxDEV, ap as jsxs, t as toCardElement, j as toModalElement } from './jsx-runtime-CFq1K_Ve.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,7 +22,7 @@ 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
|
+
| File uploads | <Check /> | <Check /> | <Cross /> | <Check /> | <Warn /> Single file/media | <Cross /> | <Cross /> | <Check /> Images, audio, docs | <Cross /> |
|
|
25
26
|
| 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 |
|
|
26
27
|
| Scheduled messages | <Check /> Native | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> |
|
|
27
28
|
|
|
@@ -51,7 +52,7 @@ Ready to build your own? Follow the [building](/docs/contributing/building) guid
|
|
|
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,63 @@
|
|
|
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
|
+
```
|
|
@@ -2,18 +2,31 @@
|
|
|
2
2
|
title: toAiMessages
|
|
3
3
|
description: Convert Chat SDK messages to AI SDK conversation format.
|
|
4
4
|
type: reference
|
|
5
|
+
related:
|
|
6
|
+
- /docs/ai
|
|
7
|
+
- /docs/ai/ai-sdk-tools
|
|
8
|
+
- /docs/ai/types
|
|
9
|
+
- /docs/streaming
|
|
5
10
|
---
|
|
6
11
|
|
|
7
|
-
Convert an array of `Message` objects into the `{ role, content }[]` format expected by AI
|
|
12
|
+
Convert an array of [`Message`](/docs/api/message) objects into the `{ role, content }[]` format expected by the AI SDK. The output is structurally compatible with AI SDK's `ModelMessage[]`.
|
|
8
13
|
|
|
9
14
|
```typescript
|
|
10
|
-
import { toAiMessages } from "chat";
|
|
15
|
+
import { toAiMessages } from "chat/ai";
|
|
11
16
|
```
|
|
12
17
|
|
|
18
|
+
<Callout>
|
|
19
|
+
`toAiMessages` is also re-exported from the main `chat` entrypoint
|
|
20
|
+
for backwards compatibility (with a `@deprecated` JSDoc hint), but
|
|
21
|
+
new code should import it from [`chat/ai`](/docs/ai) alongside
|
|
22
|
+
[`createChatTools`](/docs/ai/ai-sdk-tools) and the rest of the AI
|
|
23
|
+
utilities.
|
|
24
|
+
</Callout>
|
|
25
|
+
|
|
13
26
|
## Usage
|
|
14
27
|
|
|
15
28
|
```typescript title="lib/bot.ts" lineNumbers
|
|
16
|
-
import { toAiMessages } from "chat";
|
|
29
|
+
import { toAiMessages } from "chat/ai";
|
|
17
30
|
|
|
18
31
|
bot.onSubscribedMessage(async (thread, message) => {
|
|
19
32
|
const result = await thread.adapter.fetchMessages(thread.id, { limit: 20 });
|