chat 4.16.0 → 4.17.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/{chunk-7S5DLTN2.js → chunk-Q32GJ2PR.js} +29 -1
- package/dist/chunk-Q32GJ2PR.js.map +1 -0
- package/dist/index.d.ts +115 -38
- package/dist/index.js +91 -21
- package/dist/index.js.map +1 -1
- package/dist/{jsx-runtime-Bokk9xw5.d.ts → jsx-runtime-BJENDuXl.d.ts} +90 -8
- package/dist/jsx-runtime.d.ts +1 -1
- package/dist/jsx-runtime.js +1 -1
- package/docs/actions.mdx +1 -1
- package/docs/adapters/slack.mdx +2 -0
- package/docs/adapters/teams.mdx +61 -3
- package/docs/api/postable-message.mdx +12 -4
- package/docs/api/thread.mdx +2 -2
- package/docs/handling-events.mdx +28 -1
- package/docs/posting-messages.mdx +7 -4
- package/docs/streaming.mdx +76 -4
- package/package.json +1 -1
- package/dist/chunk-7S5DLTN2.js.map +0 -1
|
@@ -499,12 +499,12 @@ interface CardProps {
|
|
|
499
499
|
}
|
|
500
500
|
/** Props for Text component in JSX */
|
|
501
501
|
interface TextProps {
|
|
502
|
-
children?: string | number;
|
|
502
|
+
children?: string | number | (string | number | undefined)[];
|
|
503
503
|
style?: TextStyle;
|
|
504
504
|
}
|
|
505
505
|
/** Props for Button component in JSX */
|
|
506
506
|
interface ButtonProps {
|
|
507
|
-
children?: string | number;
|
|
507
|
+
children?: string | number | (string | number | undefined)[];
|
|
508
508
|
id: string;
|
|
509
509
|
label?: string;
|
|
510
510
|
style?: ButtonStyle;
|
|
@@ -512,14 +512,14 @@ interface ButtonProps {
|
|
|
512
512
|
}
|
|
513
513
|
/** Props for LinkButton component in JSX */
|
|
514
514
|
interface LinkButtonProps {
|
|
515
|
-
children?: string | number;
|
|
515
|
+
children?: string | number | (string | number | undefined)[];
|
|
516
516
|
label?: string;
|
|
517
517
|
style?: ButtonStyle;
|
|
518
518
|
url: string;
|
|
519
519
|
}
|
|
520
520
|
/** Props for CardLink component in JSX */
|
|
521
521
|
interface CardLinkProps {
|
|
522
|
-
children?: string | number;
|
|
522
|
+
children?: string | number | (string | number | undefined)[];
|
|
523
523
|
label?: string;
|
|
524
524
|
url: string;
|
|
525
525
|
}
|
|
@@ -588,7 +588,87 @@ interface CardJSXElement<P extends CardJSXProps = CardJSXProps> {
|
|
|
588
588
|
props: P;
|
|
589
589
|
type: CardComponentFunction;
|
|
590
590
|
}
|
|
591
|
-
|
|
591
|
+
/** Union of all element types that can be produced by chat components */
|
|
592
|
+
type ChatElement = CardJSXElement | CardElement | TextElement | ButtonElement | LinkButtonElement | LinkElement | ImageElement | DividerElement | ActionsElement | SectionElement | FieldsElement | FieldElement | ModalElement | TextInputElement | SelectElement | SelectOptionElement | RadioSelectElement;
|
|
593
|
+
interface CardComponent {
|
|
594
|
+
(options?: CardOptions): CardElement;
|
|
595
|
+
(props: CardProps): ChatElement;
|
|
596
|
+
}
|
|
597
|
+
interface TextComponent {
|
|
598
|
+
(content: string, options?: {
|
|
599
|
+
style?: TextStyle;
|
|
600
|
+
}): TextElement;
|
|
601
|
+
(props: TextProps): ChatElement;
|
|
602
|
+
}
|
|
603
|
+
interface ButtonComponent {
|
|
604
|
+
(options: ButtonOptions): ButtonElement;
|
|
605
|
+
(props: ButtonProps): ChatElement;
|
|
606
|
+
}
|
|
607
|
+
interface LinkButtonComponent {
|
|
608
|
+
(options: LinkButtonOptions): LinkButtonElement;
|
|
609
|
+
(props: LinkButtonProps): ChatElement;
|
|
610
|
+
}
|
|
611
|
+
interface CardLinkComponent {
|
|
612
|
+
(options: {
|
|
613
|
+
url: string;
|
|
614
|
+
label: string;
|
|
615
|
+
}): LinkElement;
|
|
616
|
+
(props: CardLinkProps): ChatElement;
|
|
617
|
+
}
|
|
618
|
+
interface ImageComponent {
|
|
619
|
+
(options: {
|
|
620
|
+
url: string;
|
|
621
|
+
alt?: string;
|
|
622
|
+
}): ImageElement;
|
|
623
|
+
(props: ImageProps): ChatElement;
|
|
624
|
+
}
|
|
625
|
+
interface FieldComponent {
|
|
626
|
+
(options: {
|
|
627
|
+
label: string;
|
|
628
|
+
value: string;
|
|
629
|
+
}): FieldElement;
|
|
630
|
+
(props: FieldProps): ChatElement;
|
|
631
|
+
}
|
|
632
|
+
interface DividerComponent {
|
|
633
|
+
(): DividerElement;
|
|
634
|
+
(props: DividerProps): ChatElement;
|
|
635
|
+
}
|
|
636
|
+
interface SectionComponent {
|
|
637
|
+
(children: CardChild[]): SectionElement;
|
|
638
|
+
(props: ContainerProps): ChatElement;
|
|
639
|
+
}
|
|
640
|
+
interface ActionsComponent {
|
|
641
|
+
(children: (ButtonElement | LinkButtonElement | SelectElement | RadioSelectElement)[]): ActionsElement;
|
|
642
|
+
(props: ContainerProps): ChatElement;
|
|
643
|
+
}
|
|
644
|
+
interface FieldsComponent {
|
|
645
|
+
(children: FieldElement[]): FieldsElement;
|
|
646
|
+
(props: ContainerProps): ChatElement;
|
|
647
|
+
}
|
|
648
|
+
interface ModalComponent {
|
|
649
|
+
(options: ModalOptions): ModalElement;
|
|
650
|
+
(props: ModalProps): ChatElement;
|
|
651
|
+
}
|
|
652
|
+
interface TextInputComponent {
|
|
653
|
+
(options: TextInputOptions): TextInputElement;
|
|
654
|
+
(props: TextInputProps): ChatElement;
|
|
655
|
+
}
|
|
656
|
+
interface SelectComponent {
|
|
657
|
+
(options: SelectOptions): SelectElement;
|
|
658
|
+
(props: SelectProps): ChatElement;
|
|
659
|
+
}
|
|
660
|
+
interface SelectOptionComponent {
|
|
661
|
+
(options: {
|
|
662
|
+
label: string;
|
|
663
|
+
value: string;
|
|
664
|
+
description?: string;
|
|
665
|
+
}): SelectOptionElement;
|
|
666
|
+
(props: SelectOptionProps): ChatElement;
|
|
667
|
+
}
|
|
668
|
+
interface RadioSelectComponent {
|
|
669
|
+
(options: RadioSelectOptions): RadioSelectElement;
|
|
670
|
+
(props: SelectProps): ChatElement;
|
|
671
|
+
}
|
|
592
672
|
/**
|
|
593
673
|
* Type guard to check if props match CardLinkProps
|
|
594
674
|
*/
|
|
@@ -627,12 +707,14 @@ declare function toModalElement(jsxElement: unknown): ModalElement | null;
|
|
|
627
707
|
*/
|
|
628
708
|
declare function isJSX(value: unknown): boolean;
|
|
629
709
|
declare namespace JSX {
|
|
630
|
-
|
|
631
|
-
}
|
|
710
|
+
type Element = ChatElement;
|
|
632
711
|
type IntrinsicElements = {};
|
|
712
|
+
interface IntrinsicAttributes {
|
|
713
|
+
key?: string | number;
|
|
714
|
+
}
|
|
633
715
|
interface ElementChildrenAttribute {
|
|
634
716
|
children: {};
|
|
635
717
|
}
|
|
636
718
|
}
|
|
637
719
|
|
|
638
|
-
export { type
|
|
720
|
+
export { type DividerProps as $, type ActionsComponent as A, type ButtonComponent as B, type CardElement as C, type DividerComponent as D, type ImageElement as E, type FieldComponent as F, type LinkButtonElement as G, type LinkButtonOptions as H, type ImageComponent as I, type LinkElement as J, type SectionElement as K, type LinkButtonComponent as L, type ModalElement as M, type TableAlignment as N, type TableElement as O, type TableOptions as P, type TextElement as Q, type RadioSelectComponent as R, type SectionComponent as S, type TextComponent as T, type TextStyle as U, type ButtonProps as V, type CardJSXElement as W, type CardJSXProps as X, type CardLinkProps as Y, type CardProps as Z, type ContainerProps as _, type ChatElement as a, type FieldProps as a0, type ImageProps as a1, type LinkButtonProps as a2, type ModalProps as a3, type SelectOptionProps as a4, type SelectProps as a5, type TextInputProps as a6, type TextProps as a7, type ModalChild as a8, type ModalOptions as a9, type RadioSelectElement as aa, type RadioSelectOptions as ab, type SelectElement as ac, type SelectOptionElement as ad, type SelectOptions as ae, type TextInputElement as af, type TextInputOptions as ag, isCardLinkProps as ah, jsx as ai, jsxs as aj, jsxDEV as ak, Fragment as al, JSX as am, type CardChild as b, type CardComponent as c, cardChildToFallbackText as d, type CardLinkComponent as e, type FieldsComponent as f, fromReactElement as g, isJSX as h, isCardElement as i, Table as j, toModalElement as k, fromReactModalElement as l, isModalElement as m, type ModalComponent as n, type SelectComponent as o, type SelectOptionComponent as p, type TextInputComponent as q, type ActionsElement as r, type ButtonElement as s, toCardElement as t, type ButtonOptions as u, type ButtonStyle as v, type CardOptions as w, type DividerElement as x, type FieldElement as y, type FieldsElement as z };
|
package/dist/jsx-runtime.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { V as ButtonProps,
|
|
1
|
+
export { A as ActionsComponent, B as ButtonComponent, V as ButtonProps, c as CardComponent, W as CardJSXElement, X as CardJSXProps, e as CardLinkComponent, Y as CardLinkProps, Z as CardProps, a as ChatElement, _ as ContainerProps, D as DividerComponent, $ as DividerProps, F as FieldComponent, a0 as FieldProps, f as FieldsComponent, al as Fragment, I as ImageComponent, a1 as ImageProps, am as JSX, L as LinkButtonComponent, a2 as LinkButtonProps, n as ModalComponent, a3 as ModalProps, R as RadioSelectComponent, S as SectionComponent, o as SelectComponent, p as SelectOptionComponent, a4 as SelectOptionProps, a5 as SelectProps, T as TextComponent, q as TextInputComponent, a6 as TextInputProps, a7 as TextProps, ah as isCardLinkProps, h as isJSX, ai as jsx, ak as jsxDEV, aj as jsxs, t as toCardElement, k as toModalElement } from './jsx-runtime-BJENDuXl.js';
|
package/dist/jsx-runtime.js
CHANGED
package/docs/actions.mdx
CHANGED
|
@@ -46,7 +46,7 @@ The `event` object passed to action handlers:
|
|
|
46
46
|
| `actionId` | `string` | The `id` from the Button or Select component |
|
|
47
47
|
| `value` | `string` (optional) | The `value` from the Button or selected option |
|
|
48
48
|
| `user` | `Author` | The user who clicked |
|
|
49
|
-
| `thread` | `Thread` | The thread containing the card |
|
|
49
|
+
| `thread` | `Thread \| null` | The thread containing the card (null for view-based actions like home tab buttons) |
|
|
50
50
|
| `messageId` | `string` | The message containing the card |
|
|
51
51
|
| `threadId` | `string` | Thread ID |
|
|
52
52
|
| `adapter` | `Adapter` | The platform adapter |
|
package/docs/adapters/slack.mdx
CHANGED
|
@@ -139,6 +139,7 @@ settings:
|
|
|
139
139
|
request_url: https://your-domain.com/api/webhooks/slack
|
|
140
140
|
bot_events:
|
|
141
141
|
- app_mention
|
|
142
|
+
- member_joined_channel
|
|
142
143
|
- message.channels
|
|
143
144
|
- message.groups
|
|
144
145
|
- message.im
|
|
@@ -219,6 +220,7 @@ SLACK_ENCRYPTION_KEY=... # Optional, for token encryption
|
|
|
219
220
|
| Message history | Yes |
|
|
220
221
|
| Assistants API | Yes |
|
|
221
222
|
| App Home tab | Yes |
|
|
223
|
+
| Member joined channel | Yes |
|
|
222
224
|
|
|
223
225
|
## Slack Assistants API
|
|
224
226
|
|
package/docs/adapters/teams.mdx
CHANGED
|
@@ -140,12 +140,67 @@ All options are auto-detected from environment variables when not provided.
|
|
|
140
140
|
| Option | Required | Description |
|
|
141
141
|
|--------|----------|-------------|
|
|
142
142
|
| `appId` | No* | Azure Bot App ID. Auto-detected from `TEAMS_APP_ID` |
|
|
143
|
-
| `appPassword` | No
|
|
143
|
+
| `appPassword` | No** | Azure Bot App Password. Auto-detected from `TEAMS_APP_PASSWORD` |
|
|
144
|
+
| `certificate` | No** | Certificate-based authentication config |
|
|
145
|
+
| `federated` | No** | Federated (workload identity) authentication config |
|
|
144
146
|
| `appType` | No | `"MultiTenant"` or `"SingleTenant"` (default: `"MultiTenant"`) |
|
|
145
147
|
| `appTenantId` | For SingleTenant | Azure AD Tenant ID. Auto-detected from `TEAMS_APP_TENANT_ID` |
|
|
146
148
|
| `logger` | No | Logger instance (defaults to `ConsoleLogger("info")`) |
|
|
147
149
|
|
|
148
|
-
|
|
150
|
+
\*`appId` is required — either via config or `TEAMS_APP_ID` env var.
|
|
151
|
+
|
|
152
|
+
\*\*Exactly one authentication method is required: `appPassword`, `certificate`, or `federated`.
|
|
153
|
+
|
|
154
|
+
### Authentication methods
|
|
155
|
+
|
|
156
|
+
The adapter supports three mutually exclusive authentication methods. When no explicit auth is provided, `TEAMS_APP_PASSWORD` is auto-detected from environment variables.
|
|
157
|
+
|
|
158
|
+
#### Client secret (default)
|
|
159
|
+
|
|
160
|
+
The simplest option — provide `appPassword` directly or set `TEAMS_APP_PASSWORD`:
|
|
161
|
+
|
|
162
|
+
```typescript title="lib/bot.ts"
|
|
163
|
+
createTeamsAdapter({
|
|
164
|
+
appPassword: "your_app_password_here",
|
|
165
|
+
});
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
#### Certificate
|
|
169
|
+
|
|
170
|
+
Authenticate with a PEM certificate. Provide either `certificateThumbprint` or `x5c` (public certificate for subject-name validation):
|
|
171
|
+
|
|
172
|
+
```typescript title="lib/bot.ts"
|
|
173
|
+
createTeamsAdapter({
|
|
174
|
+
certificate: {
|
|
175
|
+
certificatePrivateKey: "-----BEGIN RSA PRIVATE KEY-----\n...",
|
|
176
|
+
certificateThumbprint: "AB1234...", // hex-encoded thumbprint
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Or with subject-name validation:
|
|
182
|
+
|
|
183
|
+
```typescript title="lib/bot.ts"
|
|
184
|
+
createTeamsAdapter({
|
|
185
|
+
certificate: {
|
|
186
|
+
certificatePrivateKey: "-----BEGIN RSA PRIVATE KEY-----\n...",
|
|
187
|
+
x5c: "-----BEGIN CERTIFICATE-----\n...",
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
#### Federated (workload identity)
|
|
193
|
+
|
|
194
|
+
For environments with managed identities (e.g. Azure Kubernetes Service, GitHub Actions):
|
|
195
|
+
|
|
196
|
+
```typescript title="lib/bot.ts"
|
|
197
|
+
createTeamsAdapter({
|
|
198
|
+
federated: {
|
|
199
|
+
clientId: "your_managed_identity_client_id_here",
|
|
200
|
+
clientAudience: "api://AzureADTokenExchange", // optional, this is the default
|
|
201
|
+
},
|
|
202
|
+
});
|
|
203
|
+
```
|
|
149
204
|
|
|
150
205
|
## Environment variables
|
|
151
206
|
|
|
@@ -212,7 +267,10 @@ Alternatively, configure the bot in Azure to receive all messages.
|
|
|
212
267
|
|
|
213
268
|
### "Unauthorized" error
|
|
214
269
|
|
|
215
|
-
- Verify `TEAMS_APP_ID` and
|
|
270
|
+
- Verify `TEAMS_APP_ID` and your chosen auth credential are correct
|
|
271
|
+
- For client secret auth, check that `TEAMS_APP_PASSWORD` is valid
|
|
272
|
+
- For certificate auth, ensure the private key and thumbprint/x5c match what's registered in Azure AD
|
|
273
|
+
- For federated auth, verify the managed identity client ID and audience are correct
|
|
216
274
|
- For SingleTenant apps, ensure `TEAMS_APP_TENANT_ID` is set
|
|
217
275
|
- Check that the messaging endpoint URL is correct in Azure
|
|
218
276
|
|
|
@@ -7,7 +7,7 @@ type: reference
|
|
|
7
7
|
`PostableMessage` is the union of all message formats accepted by `thread.post()` and `sent.edit()`.
|
|
8
8
|
|
|
9
9
|
```typescript
|
|
10
|
-
type PostableMessage = AdapterPostableMessage | AsyncIterable<string>;
|
|
10
|
+
type PostableMessage = AdapterPostableMessage | AsyncIterable<string | StreamChunk | StreamEvent>;
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
## String
|
|
@@ -135,15 +135,23 @@ await thread.post({
|
|
|
135
135
|
|
|
136
136
|
## AsyncIterable (streaming)
|
|
137
137
|
|
|
138
|
-
An async iterable of strings,
|
|
138
|
+
An async iterable of strings, `StreamChunk` objects, or stream events. The SDK streams the message in real time using platform-native APIs where available.
|
|
139
|
+
|
|
140
|
+
You can yield structured `StreamChunk` objects for rich content like task progress cards on platforms that support it (Slack). See [Streaming](/docs/streaming#structured-streaming-chunks-slack-only) for details.
|
|
141
|
+
|
|
142
|
+
Both AI SDK stream types are supported:
|
|
139
143
|
|
|
140
144
|
```typescript
|
|
141
|
-
|
|
145
|
+
// fullStream (recommended) — preserves step boundaries in multi-step agents
|
|
146
|
+
const result = await agent.stream({ prompt: message.text });
|
|
147
|
+
await thread.post(result.fullStream);
|
|
142
148
|
|
|
143
|
-
|
|
149
|
+
// textStream — plain string chunks
|
|
144
150
|
await thread.post(result.textStream);
|
|
145
151
|
```
|
|
146
152
|
|
|
153
|
+
When using `fullStream`, the SDK auto-detects `text-delta` and `step-finish` events, extracting text and inserting paragraph breaks between agent steps.
|
|
154
|
+
|
|
147
155
|
## FileUpload
|
|
148
156
|
|
|
149
157
|
Used in the `files` field of any structured message format.
|
package/docs/api/thread.mdx
CHANGED
|
@@ -54,8 +54,8 @@ await thread.post({ ast: root([paragraph([text("Hello")])]) });
|
|
|
54
54
|
// Card
|
|
55
55
|
await thread.post(Card({ title: "Hi", children: [Text("Hello")] }));
|
|
56
56
|
|
|
57
|
-
// Stream
|
|
58
|
-
await thread.post(result.
|
|
57
|
+
// Stream (fullStream recommended for multi-step agents)
|
|
58
|
+
await thread.post(result.fullStream);
|
|
59
59
|
```
|
|
60
60
|
|
|
61
61
|
**Parameters:** `message: string | PostableMessage | CardJSXElement`
|
package/docs/handling-events.mdx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Handling Events
|
|
3
|
-
description: Register handlers for mentions, messages, reactions, and platform-specific events.
|
|
3
|
+
description: Register handlers for mentions, messages, reactions, member joins, and platform-specific events.
|
|
4
4
|
type: guide
|
|
5
5
|
prerequisites:
|
|
6
6
|
- /docs/getting-started
|
|
@@ -373,3 +373,30 @@ The `event` object includes:
|
|
|
373
373
|
| `userId` | `string` | User who opened the Home tab |
|
|
374
374
|
| `channelId` | `string` | Channel context |
|
|
375
375
|
| `adapter` | `Adapter` | The Slack adapter |
|
|
376
|
+
|
|
377
|
+
### Handling member joined channel
|
|
378
|
+
|
|
379
|
+
`onMemberJoinedChannel` fires when a user joins a Slack channel. Use it to post welcome messages or onboard users automatically.
|
|
380
|
+
|
|
381
|
+
```typescript title="lib/bot.ts" lineNumbers
|
|
382
|
+
bot.onMemberJoinedChannel(async (event) => {
|
|
383
|
+
// Only post when the bot itself joins
|
|
384
|
+
if (event.userId !== event.adapter.botUserId) {
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
await event.adapter.postMessage(
|
|
389
|
+
event.channelId,
|
|
390
|
+
"Hello! I'm now available in this channel. Mention me to get started."
|
|
391
|
+
);
|
|
392
|
+
});
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
The `event` object includes:
|
|
396
|
+
|
|
397
|
+
| Property | Type | Description |
|
|
398
|
+
|----------|------|-------------|
|
|
399
|
+
| `adapter` | `Adapter` | The Slack adapter |
|
|
400
|
+
| `channelId` | `string` | The channel that was joined |
|
|
401
|
+
| `userId` | `string` | The user who joined |
|
|
402
|
+
| `inviterId` | `string` (optional) | The user who invited them |
|
|
@@ -139,15 +139,18 @@ See the [Cards](/docs/cards) page for the full list of card components.
|
|
|
139
139
|
|
|
140
140
|
## Streaming
|
|
141
141
|
|
|
142
|
-
Pass an
|
|
142
|
+
Pass an AI SDK stream to `thread.post()` to stream a message in real time. The SDK uses platform-native streaming where available and falls back to post-then-edit on other platforms.
|
|
143
143
|
|
|
144
144
|
```typescript title="lib/bot.ts" lineNumbers
|
|
145
|
-
import {
|
|
145
|
+
import { ToolLoopAgent } from "ai";
|
|
146
146
|
|
|
147
|
-
const
|
|
148
|
-
await
|
|
147
|
+
const agent = new ToolLoopAgent({ model, instructions: "You are a helpful assistant." });
|
|
148
|
+
const result = await agent.stream({ prompt: message.text });
|
|
149
|
+
await thread.post(result.fullStream);
|
|
149
150
|
```
|
|
150
151
|
|
|
152
|
+
Both `fullStream` and `textStream` are supported. Use `fullStream` with multi-step agents — it preserves paragraph breaks between steps. Any `AsyncIterable<string>` also works for custom streams.
|
|
153
|
+
|
|
151
154
|
See the [Streaming](/docs/streaming) page for details on platform behavior and configuration.
|
|
152
155
|
|
|
153
156
|
## Attachments and files
|
package/docs/streaming.mdx
CHANGED
|
@@ -6,11 +6,11 @@ prerequisites:
|
|
|
6
6
|
- /docs/usage
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
Chat SDK accepts any `AsyncIterable<string>` as a message, enabling real-time streaming of AI responses and other incremental content to chat platforms.
|
|
9
|
+
Chat SDK accepts any `AsyncIterable<string>` as a message, enabling real-time streaming of AI responses and other incremental content to chat platforms. For platforms with native streaming support (Slack), you can also stream structured `StreamChunk` objects for rich content like task progress cards and plan updates.
|
|
10
10
|
|
|
11
11
|
## AI SDK integration
|
|
12
12
|
|
|
13
|
-
Pass an AI SDK `textStream` directly to `thread.post()`:
|
|
13
|
+
Pass an AI SDK `fullStream` or `textStream` directly to `thread.post()`:
|
|
14
14
|
|
|
15
15
|
```typescript title="lib/bot.ts" lineNumbers
|
|
16
16
|
import { ToolLoopAgent } from "ai";
|
|
@@ -22,10 +22,24 @@ const agent = new ToolLoopAgent({
|
|
|
22
22
|
|
|
23
23
|
bot.onNewMention(async (thread, message) => {
|
|
24
24
|
const result = await agent.stream({ prompt: message.text });
|
|
25
|
-
await thread.post(result.
|
|
25
|
+
await thread.post(result.fullStream);
|
|
26
26
|
});
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
+
### Why `fullStream` over `textStream`?
|
|
30
|
+
|
|
31
|
+
When AI SDK agents make tool calls between text steps, `textStream` concatenates all text without separators — `"hello.how are you?"` instead of `"hello.\n\nhow are you?"`. The `fullStream` contains explicit `step-finish` events that Chat SDK uses to inject paragraph breaks between steps automatically.
|
|
32
|
+
|
|
33
|
+
Both stream types are auto-detected:
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
// Recommended: fullStream preserves step boundaries
|
|
37
|
+
await thread.post(result.fullStream);
|
|
38
|
+
|
|
39
|
+
// Also works: textStream for single-step generation
|
|
40
|
+
await thread.post(result.textStream);
|
|
41
|
+
```
|
|
42
|
+
|
|
29
43
|
## Custom streams
|
|
30
44
|
|
|
31
45
|
Any async iterable works:
|
|
@@ -90,6 +104,64 @@ When streaming content that contains GFM tables (e.g. from an LLM), the SDK auto
|
|
|
90
104
|
|
|
91
105
|
This happens transparently — no configuration needed.
|
|
92
106
|
|
|
107
|
+
## Structured streaming chunks (Slack only)
|
|
108
|
+
|
|
109
|
+
For Slack's native streaming API, you can yield `StreamChunk` objects alongside plain text for rich content:
|
|
110
|
+
|
|
111
|
+
```typescript title="lib/bot.ts" lineNumbers
|
|
112
|
+
import type { StreamChunk } from "chat";
|
|
113
|
+
|
|
114
|
+
const stream = (async function* () {
|
|
115
|
+
yield { type: "markdown_text", text: "Searching..." } satisfies StreamChunk;
|
|
116
|
+
|
|
117
|
+
yield {
|
|
118
|
+
type: "task_update",
|
|
119
|
+
id: "search-1",
|
|
120
|
+
title: "Searching documents",
|
|
121
|
+
status: "in_progress",
|
|
122
|
+
} satisfies StreamChunk;
|
|
123
|
+
|
|
124
|
+
// ... do work ...
|
|
125
|
+
|
|
126
|
+
yield {
|
|
127
|
+
type: "task_update",
|
|
128
|
+
id: "search-1",
|
|
129
|
+
title: "Searching documents",
|
|
130
|
+
status: "complete",
|
|
131
|
+
output: "Found 3 results",
|
|
132
|
+
} satisfies StreamChunk;
|
|
133
|
+
|
|
134
|
+
yield { type: "markdown_text", text: "Here are your results..." } satisfies StreamChunk;
|
|
135
|
+
})();
|
|
136
|
+
|
|
137
|
+
await thread.post(stream);
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Chunk types
|
|
141
|
+
|
|
142
|
+
| Type | Fields | Description |
|
|
143
|
+
|------|--------|-------------|
|
|
144
|
+
| `markdown_text` | `text` | Streamed text content |
|
|
145
|
+
| `task_update` | `id`, `title`, `status`, `output?` | Tool/step progress cards (`pending`, `in_progress`, `complete`, `error`) |
|
|
146
|
+
| `plan_update` | `title` | Plan title updates |
|
|
147
|
+
|
|
148
|
+
### Task display mode
|
|
149
|
+
|
|
150
|
+
Control how `task_update` chunks render in Slack by passing `taskDisplayMode` in stream options:
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
await thread.stream(stream, {
|
|
154
|
+
taskDisplayMode: "plan", // Group all tasks into a single plan block
|
|
155
|
+
});
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
| Mode | Description |
|
|
159
|
+
|------|-------------|
|
|
160
|
+
| `"timeline"` | Individual task cards shown inline with text (default) |
|
|
161
|
+
| `"plan"` | All tasks grouped into a single plan block |
|
|
162
|
+
|
|
163
|
+
Adapters without structured chunk support extract text from `markdown_text` chunks and ignore other types.
|
|
164
|
+
|
|
93
165
|
## Stop blocks (Slack only)
|
|
94
166
|
|
|
95
167
|
When streaming in Slack, you can attach Block Kit elements to the final message using `stopBlocks`. This is useful for adding action buttons after a streamed response completes:
|
|
@@ -126,6 +198,6 @@ bot.onSubscribedMessage(async (thread, message) => {
|
|
|
126
198
|
}));
|
|
127
199
|
|
|
128
200
|
const response = await agent.stream({ prompt: history });
|
|
129
|
-
await thread.post(response.
|
|
201
|
+
await thread.post(response.fullStream);
|
|
130
202
|
});
|
|
131
203
|
```
|