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.
@@ -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
- type JSXElement = CardJSXElement;
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
- interface Element extends JSXElement {
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 FieldProps as $, Actions as A, Button as B, type CardElement as C, Divider as D, type ImageElement as E, Field as F, type LinkButtonElement as G, type LinkButtonOptions as H, Image as I, type LinkElement as J, type SectionElement as K, LinkButton as L, type ModalElement as M, type TableAlignment as N, type TableElement as O, type TableOptions as P, type TextElement as Q, RadioSelect as R, Section as S, Text as T, type TextStyle as U, type ButtonProps as V, type CardJSXProps as W, type CardLinkProps as X, type CardProps as Y, type ContainerProps as Z, type DividerProps as _, type CardJSXElement as a, type ImageProps as a0, type LinkButtonProps as a1, type TextProps as a2, type ModalChild as a3, type ModalOptions as a4, type RadioSelectElement as a5, type RadioSelectOptions as a6, type SelectElement as a7, type SelectOptionElement as a8, type SelectOptions as a9, type TextInputElement as aa, type TextInputOptions as ab, type ModalProps as ac, type TextInputProps as ad, type SelectProps as ae, type SelectOptionProps as af, isCardLinkProps as ag, jsx as ah, jsxs as ai, jsxDEV as aj, Fragment as ak, JSX as al, type CardChild as b, Card as c, cardChildToFallbackText as d, CardLink as e, Fields as f, fromReactElement as g, isJSX as h, isCardElement as i, Table as j, toModalElement as k, fromReactModalElement as l, isModalElement as m, Modal as n, Select as o, SelectOption as p, TextInput 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 };
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 };
@@ -1 +1 @@
1
- export { V as ButtonProps, a as CardJSXElement, W as CardJSXProps, X as CardLinkProps, Y as CardProps, Z as ContainerProps, _ as DividerProps, $ as FieldProps, ak as Fragment, a0 as ImageProps, al as JSX, a1 as LinkButtonProps, ac as ModalProps, af as SelectOptionProps, ae as SelectProps, ad as TextInputProps, a2 as TextProps, ag as isCardLinkProps, h as isJSX, ah as jsx, aj as jsxDEV, ai as jsxs, t as toCardElement, k as toModalElement } from './jsx-runtime-Bokk9xw5.js';
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';
@@ -7,7 +7,7 @@ import {
7
7
  jsxs,
8
8
  toCardElement,
9
9
  toModalElement
10
- } from "./chunk-7S5DLTN2.js";
10
+ } from "./chunk-Q32GJ2PR.js";
11
11
  export {
12
12
  Fragment,
13
13
  isCardLinkProps,
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 |
@@ -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
 
@@ -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* | Azure Bot App Password. Auto-detected from `TEAMS_APP_PASSWORD` |
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
- *`appId` and `appPassword` are required — either via config or env vars.
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 `TEAMS_APP_PASSWORD` are correct
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, like the AI SDK's `textStream`. The SDK streams the message in real time using platform-native APIs where available.
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
- import { streamText } from "ai";
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
- const result = streamText({ model, prompt: message.text });
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.
@@ -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.textStream);
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`
@@ -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 `AsyncIterable<string>` (like the AI SDK's `textStream`) 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.
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 { streamText } from "ai";
145
+ import { ToolLoopAgent } from "ai";
146
146
 
147
- const result = streamText({ model, prompt: message.text });
148
- await thread.post(result.textStream);
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
@@ -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.textStream);
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.textStream);
201
+ await thread.post(response.fullStream);
130
202
  });
131
203
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chat",
3
- "version": "4.16.0",
3
+ "version": "4.17.0",
4
4
  "description": "Unified chat abstraction for Slack, Teams, Google Chat, and Discord",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",