chat 4.25.0 → 4.27.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-OPV5U4WG.js → chunk-AN7MRAVW.js} +39 -0
- package/dist/index.d.ts +235 -6
- package/dist/index.js +353 -76
- package/dist/{jsx-runtime-DxATbnrP.d.ts → jsx-runtime-Co9uV6l7.d.ts} +39 -5
- package/dist/jsx-runtime.d.ts +1 -1
- package/dist/jsx-runtime.js +1 -1
- package/docs/adapters.mdx +30 -30
- package/docs/api/cards.mdx +5 -0
- package/docs/api/chat.mdx +95 -1
- package/docs/api/message.mdx +5 -1
- package/docs/api/modals.mdx +1 -1
- package/docs/api/thread.mdx +23 -1
- package/docs/cards.mdx +6 -0
- package/docs/contributing/publishing.mdx +33 -0
- package/docs/files.mdx +1 -0
- package/docs/getting-started.mdx +2 -12
- package/docs/meta.json +0 -2
- package/docs/modals.mdx +74 -2
- package/docs/state.mdx +1 -1
- package/docs/streaming.mdx +13 -5
- package/docs/threads-messages-channels.mdx +34 -0
- package/docs/usage.mdx +2 -2
- package/package.json +3 -2
- package/resources/guides/create-a-discord-support-bot-with-nuxt-and-redis.md +180 -0
- package/resources/guides/how-to-build-a-slack-bot-with-next-js-and-redis.md +134 -0
- package/resources/guides/how-to-build-an-ai-agent-for-slack-with-chat-sdk-and-ai-sdk.md +220 -0
- package/resources/guides/run-and-track-deploys-from-slack.md +270 -0
- package/resources/guides/ship-a-github-code-review-bot-with-hono-and-redis.md +147 -0
- package/resources/guides/triage-form-submissions-with-chat-sdk.md +178 -0
- package/resources/templates.json +19 -0
- package/docs/adapters/whatsapp.mdx +0 -222
- package/docs/guides/code-review-hono.mdx +0 -241
- package/docs/guides/discord-nuxt.mdx +0 -227
- package/docs/guides/durable-chat-sessions-nextjs.mdx +0 -331
- package/docs/guides/meta.json +0 -10
- package/docs/guides/scheduled-posts-neon.mdx +0 -447
- package/docs/guides/slack-nextjs.mdx +0 -234
|
@@ -371,7 +371,7 @@ declare function cardChildToFallbackText(child: CardChild): string | null;
|
|
|
371
371
|
* Modal elements for form dialogs.
|
|
372
372
|
*/
|
|
373
373
|
|
|
374
|
-
type ModalChild = TextInputElement | SelectElement | RadioSelectElement | TextElement | FieldsElement;
|
|
374
|
+
type ModalChild = TextInputElement | SelectElement | ExternalSelectElement | RadioSelectElement | TextElement | FieldsElement;
|
|
375
375
|
interface ModalElement {
|
|
376
376
|
callbackId: string;
|
|
377
377
|
children: ModalChild[];
|
|
@@ -402,6 +402,15 @@ interface SelectElement {
|
|
|
402
402
|
placeholder?: string;
|
|
403
403
|
type: "select";
|
|
404
404
|
}
|
|
405
|
+
interface ExternalSelectElement {
|
|
406
|
+
id: string;
|
|
407
|
+
initialOption?: SelectOptionElement;
|
|
408
|
+
label: string;
|
|
409
|
+
minQueryLength?: number;
|
|
410
|
+
optional?: boolean;
|
|
411
|
+
placeholder?: string;
|
|
412
|
+
type: "external_select";
|
|
413
|
+
}
|
|
405
414
|
interface SelectOptionElement {
|
|
406
415
|
description?: string;
|
|
407
416
|
label: string;
|
|
@@ -446,6 +455,15 @@ interface SelectOptions {
|
|
|
446
455
|
placeholder?: string;
|
|
447
456
|
}
|
|
448
457
|
declare function Select(options: SelectOptions): SelectElement;
|
|
458
|
+
interface ExternalSelectOptions {
|
|
459
|
+
id: string;
|
|
460
|
+
initialOption?: SelectOptionElement;
|
|
461
|
+
label: string;
|
|
462
|
+
minQueryLength?: number;
|
|
463
|
+
optional?: boolean;
|
|
464
|
+
placeholder?: string;
|
|
465
|
+
}
|
|
466
|
+
declare function ExternalSelect(options: ExternalSelectOptions): ExternalSelectElement;
|
|
449
467
|
declare function SelectOption(options: {
|
|
450
468
|
label: string;
|
|
451
469
|
value: string;
|
|
@@ -574,6 +592,18 @@ interface SelectProps {
|
|
|
574
592
|
optional?: boolean;
|
|
575
593
|
placeholder?: string;
|
|
576
594
|
}
|
|
595
|
+
/** Props for ExternalSelect component in JSX */
|
|
596
|
+
interface ExternalSelectProps {
|
|
597
|
+
id: string;
|
|
598
|
+
initialOption?: {
|
|
599
|
+
label: string;
|
|
600
|
+
value: string;
|
|
601
|
+
};
|
|
602
|
+
label: string;
|
|
603
|
+
minQueryLength?: number;
|
|
604
|
+
optional?: boolean;
|
|
605
|
+
placeholder?: string;
|
|
606
|
+
}
|
|
577
607
|
/** Props for SelectOption component in JSX */
|
|
578
608
|
interface SelectOptionProps {
|
|
579
609
|
description?: string;
|
|
@@ -586,9 +616,9 @@ interface TableProps {
|
|
|
586
616
|
rows: string[][];
|
|
587
617
|
}
|
|
588
618
|
/** Union of all valid JSX props */
|
|
589
|
-
type CardJSXProps = CardProps | TextProps | ButtonProps | LinkButtonProps | CardLinkProps | ImageProps | FieldProps | ContainerProps | DividerProps | ModalProps | TextInputProps | SelectProps | SelectOptionProps | TableProps;
|
|
619
|
+
type CardJSXProps = CardProps | TextProps | ButtonProps | LinkButtonProps | CardLinkProps | ImageProps | FieldProps | ContainerProps | DividerProps | ModalProps | TextInputProps | SelectProps | ExternalSelectProps | SelectOptionProps | TableProps;
|
|
590
620
|
/** Component function type with proper overloads */
|
|
591
|
-
type CardComponentFunction = typeof Card | typeof Text | typeof Button | typeof LinkButton | typeof CardLink | typeof Image | typeof Field | typeof Divider | typeof Section | typeof Actions | typeof Fields | typeof Modal | typeof TextInput | typeof Select | typeof RadioSelect | typeof SelectOption | typeof Table;
|
|
621
|
+
type CardComponentFunction = typeof Card | typeof Text | typeof Button | typeof LinkButton | typeof CardLink | typeof Image | typeof Field | typeof Divider | typeof Section | typeof Actions | typeof Fields | typeof Modal | typeof TextInput | typeof Select | typeof ExternalSelect | typeof RadioSelect | typeof SelectOption | typeof Table;
|
|
592
622
|
/**
|
|
593
623
|
* Represents a JSX element from the chat JSX runtime.
|
|
594
624
|
* This is the type returned when using JSX syntax with chat components.
|
|
@@ -600,7 +630,7 @@ interface CardJSXElement<P extends CardJSXProps = CardJSXProps> {
|
|
|
600
630
|
type: CardComponentFunction;
|
|
601
631
|
}
|
|
602
632
|
/** Union of all element types that can be produced by chat components */
|
|
603
|
-
type ChatElement = CardJSXElement | CardElement | TextElement | ButtonElement | LinkButtonElement | LinkElement | ImageElement | DividerElement | ActionsElement | SectionElement | FieldsElement | FieldElement | ModalElement | TextInputElement | SelectElement | SelectOptionElement | RadioSelectElement | TableElement;
|
|
633
|
+
type ChatElement = CardJSXElement | CardElement | TextElement | ButtonElement | LinkButtonElement | LinkElement | ImageElement | DividerElement | ActionsElement | SectionElement | FieldsElement | FieldElement | ModalElement | TextInputElement | SelectElement | ExternalSelectElement | SelectOptionElement | RadioSelectElement | TableElement;
|
|
604
634
|
interface CardComponent {
|
|
605
635
|
(options?: CardOptions): CardElement;
|
|
606
636
|
(props: CardProps): ChatElement;
|
|
@@ -668,6 +698,10 @@ interface SelectComponent {
|
|
|
668
698
|
(options: SelectOptions): SelectElement;
|
|
669
699
|
(props: SelectProps): ChatElement;
|
|
670
700
|
}
|
|
701
|
+
interface ExternalSelectComponent {
|
|
702
|
+
(options: ExternalSelectOptions): ExternalSelectElement;
|
|
703
|
+
(props: ExternalSelectProps): ChatElement;
|
|
704
|
+
}
|
|
671
705
|
interface SelectOptionComponent {
|
|
672
706
|
(options: {
|
|
673
707
|
label: string;
|
|
@@ -735,4 +769,4 @@ declare namespace JSX {
|
|
|
735
769
|
}
|
|
736
770
|
}
|
|
737
771
|
|
|
738
|
-
export { type
|
|
772
|
+
export { type CardProps as $, type ActionsComponent as A, type ButtonComponent as B, type ChatElement as C, type DividerComponent as D, type ExternalSelectComponent as E, type FieldComponent as F, type FieldsElement as G, type ImageElement as H, type ImageComponent as I, type LinkButtonElement as J, type LinkButtonOptions as K, type LinkButtonComponent as L, type ModalElement as M, type LinkElement as N, type SectionElement as O, type TableAlignment as P, type TableElement as Q, type RadioSelectComponent as R, type SelectOptionElement as S, type TextComponent as T, type TableOptions as U, type TextElement as V, type TextStyle as W, type ButtonProps as X, type CardJSXElement as Y, type CardJSXProps as Z, type CardLinkProps as _, type CardElement as a, type ContainerProps as a0, type DividerProps as a1, type ExternalSelectProps as a2, type FieldProps as a3, type ImageProps as a4, type LinkButtonProps as a5, type ModalProps as a6, type SelectOptionProps as a7, type SelectProps as a8, type TextInputProps as a9, type TextProps as aa, type ExternalSelectElement as ab, type ExternalSelectOptions as ac, type ModalChild as ad, type ModalOptions as ae, type RadioSelectElement as af, type RadioSelectOptions as ag, type SelectElement 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 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, type SectionComponent as j, Table as k, toModalElement as l, fromReactModalElement as m, isModalElement as n, type ModalComponent as o, type SelectComponent as p, type SelectOptionComponent as q, type TextInputComponent as r, type ActionsElement as s, toCardElement as t, type ButtonElement as u, type ButtonOptions as v, type ButtonStyle as w, type CardOptions as x, type DividerElement as y, type FieldElement 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, X as ButtonProps, c as CardComponent, Y as CardJSXElement, Z as CardJSXProps, e as CardLinkComponent, _ as CardLinkProps, $ as CardProps, C as ChatElement, a0 as ContainerProps, D as DividerComponent, a1 as DividerProps, E as ExternalSelectComponent, a2 as ExternalSelectProps, F as FieldComponent, a3 as FieldProps, f as FieldsComponent, ar as Fragment, I as ImageComponent, a4 as ImageProps, as as JSX, L as LinkButtonComponent, a5 as LinkButtonProps, o as ModalComponent, a6 as ModalProps, R as RadioSelectComponent, j as SectionComponent, p as SelectComponent, q as SelectOptionComponent, a7 as SelectOptionProps, a8 as SelectProps, am as TableComponent, al as TableProps, T as TextComponent, r as TextInputComponent, a9 as TextInputProps, aa as TextProps, an as isCardLinkProps, h as isJSX, ao as jsx, aq as jsxDEV, ap as jsxs, t as toCardElement, l as toModalElement } from './jsx-runtime-Co9uV6l7.js';
|
package/dist/jsx-runtime.js
CHANGED
package/docs/adapters.mdx
CHANGED
|
@@ -16,55 +16,55 @@ Ready to build your own? Follow the [building](/docs/contributing/building) guid
|
|
|
16
16
|
|
|
17
17
|
| 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) |
|
|
18
18
|
|---------|-------|-------|-------------|---------|---------|--------|--------|-----------|
|
|
19
|
-
| Post message |
|
|
20
|
-
| Edit message |
|
|
21
|
-
| Delete message |
|
|
22
|
-
| File uploads |
|
|
23
|
-
| Streaming |
|
|
24
|
-
| Scheduled messages |
|
|
19
|
+
| Post message | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> |
|
|
20
|
+
| Edit message | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> |
|
|
21
|
+
| Delete message | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Cross /> |
|
|
22
|
+
| File uploads | <Check /> | <Check /> | <Cross /> | <Check /> | <Warn /> Single file | <Cross /> | <Cross /> | <Check /> Images, audio, docs |
|
|
23
|
+
| Streaming | <Check /> Native | <Warn /> Post+Edit | <Warn /> Post+Edit | <Warn /> Post+Edit | <Warn /> Post+Edit | <Cross /> | <Cross /> | <Cross /> |
|
|
24
|
+
| Scheduled messages | <Check /> Native | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> |
|
|
25
25
|
|
|
26
26
|
### Rich content
|
|
27
27
|
|
|
28
28
|
| Feature | Slack | Teams | Google Chat | Discord | Telegram | GitHub | Linear | WhatsApp |
|
|
29
29
|
|---------|-------|-------|-------------|---------|----------|--------|--------|-----------|
|
|
30
30
|
| Card format | Block Kit | Adaptive Cards | Google Chat Cards | Embeds | Markdown + inline keyboard buttons | GFM Markdown | Markdown | WhatsApp templates |
|
|
31
|
-
| Buttons |
|
|
32
|
-
| Link buttons |
|
|
33
|
-
| Select menus |
|
|
34
|
-
| Tables |
|
|
35
|
-
| Fields |
|
|
36
|
-
| Images in cards |
|
|
37
|
-
| Modals |
|
|
31
|
+
| Buttons | <Check /> | <Check /> | <Check /> | <Check /> | <Warn /> Inline keyboard callbacks | <Cross /> | <Cross /> | <Check /> Interactive replies |
|
|
32
|
+
| Link buttons | <Check /> | <Check /> | <Check /> | <Check /> | <Warn /> Inline keyboard URLs | <Cross /> | <Cross /> | <Cross /> |
|
|
33
|
+
| Select menus | <Check /> | <Cross /> | <Check /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> |
|
|
34
|
+
| Tables | <Check /> Block Kit | <Check /> GFM | <Warn /> ASCII | <Check /> GFM | <Warn /> ASCII | <Check /> GFM | <Check /> GFM | <Cross /> |
|
|
35
|
+
| Fields | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Warn /> Template variables |
|
|
36
|
+
| Images in cards | <Check /> | <Check /> | <Check /> | <Check /> | <Cross /> | <Check /> | <Cross /> | <Check /> |
|
|
37
|
+
| Modals | <Check /> | <Check /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> |
|
|
38
38
|
|
|
39
39
|
### Conversations
|
|
40
40
|
|
|
41
41
|
| Feature | Slack | Teams | Google Chat | Discord | Telegram | GitHub | Linear | WhatsApp |
|
|
42
42
|
|---------|-------|-------|-------------|---------|----------|--------|--------|-----------|
|
|
43
|
-
| Slash commands |
|
|
44
|
-
| Mentions |
|
|
45
|
-
| Add reactions |
|
|
46
|
-
| Remove reactions |
|
|
47
|
-
| Typing indicator |
|
|
48
|
-
| DMs |
|
|
49
|
-
| Ephemeral messages |
|
|
43
|
+
| Slash commands | <Check /> | <Cross /> | <Cross /> | <Check /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> |
|
|
44
|
+
| Mentions | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Cross /> |
|
|
45
|
+
| Add reactions | <Check /> | <Cross /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Cross /> |
|
|
46
|
+
| Remove reactions | <Check /> | <Cross /> | <Check /> | <Check /> | <Check /> | <Warn /> | <Warn /> | <Cross /> |
|
|
47
|
+
| Typing indicator | <Cross /> | <Check /> | <Cross /> | <Check /> | <Check /> | <Cross /> | <Cross /> | <Cross /> |
|
|
48
|
+
| DMs | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Cross /> | <Cross /> | <Check /> |
|
|
49
|
+
| Ephemeral messages | <Check /> Native | <Cross /> | <Check /> Native | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> |
|
|
50
50
|
|
|
51
51
|
### Message history
|
|
52
52
|
|
|
53
53
|
| Feature | Slack | Teams | Google Chat | Discord | Telegram | GitHub | Linear | WhatsApp |
|
|
54
54
|
|---------|-------|-------|-------------|---------|----------|--------|--------|-----------|
|
|
55
|
-
| Fetch messages |
|
|
56
|
-
| Fetch single message |
|
|
57
|
-
| Fetch thread info |
|
|
58
|
-
| Fetch channel messages |
|
|
59
|
-
| List threads |
|
|
60
|
-
| Fetch channel info |
|
|
61
|
-
| Post channel message |
|
|
55
|
+
| Fetch messages | <Check /> | <Check /> | <Check /> | <Check /> | <Warn /> Cached | <Check /> | <Check /> | <Warn /> Cached sent messages only |
|
|
56
|
+
| Fetch single message | <Check /> | <Cross /> | <Cross /> | <Cross /> | <Warn /> Cached | <Cross /> | <Cross /> | <Warn /> Cached sent messages only |
|
|
57
|
+
| Fetch thread info | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Cross /> |
|
|
58
|
+
| Fetch channel messages | <Check /> | <Check /> | <Check /> | <Check /> | <Warn /> Cached | <Check /> | <Cross /> | <Warn /> Cached sent messages only |
|
|
59
|
+
| List threads | <Check /> | <Check /> | <Check /> | <Check /> | <Cross /> | <Check /> | <Cross /> | <Cross /> |
|
|
60
|
+
| Fetch channel info | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Cross /> | <Cross /> |
|
|
61
|
+
| Post channel message | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Cross /> | <Cross /> | <Check /> |
|
|
62
62
|
|
|
63
63
|
<Callout type="info">
|
|
64
|
-
|
|
64
|
+
<Warn /> indicates partial support — the feature works with limitations. See individual adapter pages for details.
|
|
65
65
|
</Callout>
|
|
66
66
|
|
|
67
|
-
## How
|
|
67
|
+
## How adapters work
|
|
68
68
|
|
|
69
69
|
Each adapter implements a standard interface that the `Chat` class uses to route events and send messages. When a webhook arrives:
|
|
70
70
|
|
|
@@ -73,7 +73,7 @@ Each adapter implements a standard interface that the `Chat` class uses to route
|
|
|
73
73
|
3. Routes to your handlers via the `Chat` class
|
|
74
74
|
4. Converts outgoing messages from markdown/AST/cards to the platform's native format
|
|
75
75
|
|
|
76
|
-
## Using multiple
|
|
76
|
+
## Using multiple adapters
|
|
77
77
|
|
|
78
78
|
Register multiple [adapters](/adapters) and your event handlers work across all of them:
|
|
79
79
|
|
package/docs/api/cards.mdx
CHANGED
|
@@ -95,6 +95,11 @@ Button({ id: "delete", label: "Delete", style: "danger", value: "item-123" })
|
|
|
95
95
|
description: 'Optional payload sent with the action callback.',
|
|
96
96
|
type: 'string',
|
|
97
97
|
},
|
|
98
|
+
actionType: {
|
|
99
|
+
description: 'Hints to adapters like Teams that this button will open a modal via event.openModal().',
|
|
100
|
+
type: '"action" | "modal"',
|
|
101
|
+
default: '"action"',
|
|
102
|
+
},
|
|
98
103
|
}}
|
|
99
104
|
/>
|
|
100
105
|
|
package/docs/api/chat.mdx
CHANGED
|
@@ -255,7 +255,8 @@ bot.onModalSubmit("feedback", async (event) => {
|
|
|
255
255
|
|
|
256
256
|
Returns `ModalResponse | undefined` to control the modal after submission:
|
|
257
257
|
|
|
258
|
-
- `{ action: "close" }` — close the
|
|
258
|
+
- `{ action: "close" }` — close the current view (goes back one level in the stack)
|
|
259
|
+
- `{ action: "clear" }` — close all views and dismiss the modal entirely
|
|
259
260
|
- `{ action: "errors", errors: { fieldId: "message" } }` — show validation errors
|
|
260
261
|
- `{ action: "update", modal: ModalElement }` — replace the modal content
|
|
261
262
|
- `{ action: "push", modal: ModalElement }` — push a new modal view onto the stack
|
|
@@ -443,6 +444,89 @@ await dm.post("Hello via DM!");
|
|
|
443
444
|
const dm = await bot.openDM(message.author);
|
|
444
445
|
```
|
|
445
446
|
|
|
447
|
+
### getUser
|
|
448
|
+
|
|
449
|
+
Look up user information by user ID. Returns a `UserInfo` object with name, email, avatar, and bot status, or `null` if the user was not found. Supported on Slack, Microsoft Teams, Discord, Google Chat, GitHub, Linear, and Telegram. Other adapters will throw `NOT_SUPPORTED`.
|
|
450
|
+
|
|
451
|
+
```typescript
|
|
452
|
+
const user = await bot.getUser("U123456");
|
|
453
|
+
console.log(user?.email); // "alice@company.com"
|
|
454
|
+
console.log(user?.fullName); // "Alice Smith"
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
```typescript
|
|
458
|
+
// Or with an Author object from a message handler
|
|
459
|
+
const user = await bot.getUser(message.author);
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
<TypeTable
|
|
463
|
+
type={{
|
|
464
|
+
userId: {
|
|
465
|
+
description: 'Platform-specific user ID.',
|
|
466
|
+
type: 'string',
|
|
467
|
+
},
|
|
468
|
+
userName: {
|
|
469
|
+
description: 'Username/handle.',
|
|
470
|
+
type: 'string',
|
|
471
|
+
},
|
|
472
|
+
fullName: {
|
|
473
|
+
description: 'Display name / full name.',
|
|
474
|
+
type: 'string',
|
|
475
|
+
},
|
|
476
|
+
isBot: {
|
|
477
|
+
description: 'Whether the user is a bot.',
|
|
478
|
+
type: 'boolean',
|
|
479
|
+
},
|
|
480
|
+
email: {
|
|
481
|
+
description: 'Email address (requires scopes on some platforms).',
|
|
482
|
+
type: 'string | undefined',
|
|
483
|
+
},
|
|
484
|
+
avatarUrl: {
|
|
485
|
+
description: 'Profile image URL.',
|
|
486
|
+
type: 'string | undefined',
|
|
487
|
+
},
|
|
488
|
+
}}
|
|
489
|
+
/>
|
|
490
|
+
|
|
491
|
+
<Callout type="info">
|
|
492
|
+
**Per-platform constraints:**
|
|
493
|
+
- **Slack** — requires both `users:read` and `users:read.email` scopes (the email scope must be granted at OAuth install time).
|
|
494
|
+
- **Discord** — bot tokens never see email (the `email` OAuth scope only applies in user-context auth).
|
|
495
|
+
- **Telegram** — bots can only look up users who have previously messaged them.
|
|
496
|
+
- **Microsoft Teams** — only works for users who previously interacted with the bot (cached from webhook activity). `avatarUrl` is not returned (Graph API requires a separate photo call).
|
|
497
|
+
- **Google Chat** — same caching constraint as Teams: only users seen in prior webhooks.
|
|
498
|
+
- **GitHub** — `email` is `null` unless the user made it public, or you authenticated with the `user:email` scope.
|
|
499
|
+
- **Linear** — full profile (incl. email + avatar) for any active workspace member.
|
|
500
|
+
|
|
501
|
+
Fields that aren't available return `undefined`. Numeric user IDs (Discord/Telegram/GitHub) can be ambiguous when multiple of those adapters are registered — call the platform's adapter directly (`adapter.getUser(userId)`) in that case.
|
|
502
|
+
</Callout>
|
|
503
|
+
|
|
504
|
+
Adapters that don't support user lookups will throw a `ChatError` with code `NOT_SUPPORTED`. Handle both cases if your bot runs on multiple platforms:
|
|
505
|
+
|
|
506
|
+
```typescript
|
|
507
|
+
import { ChatError } from "chat";
|
|
508
|
+
|
|
509
|
+
try {
|
|
510
|
+
const user = await bot.getUser(userId);
|
|
511
|
+
if (!user) {
|
|
512
|
+
// User not found on this platform
|
|
513
|
+
}
|
|
514
|
+
} catch (error) {
|
|
515
|
+
if (error instanceof ChatError && error.code === "NOT_SUPPORTED") {
|
|
516
|
+
// This adapter doesn't support user lookups
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
### thread
|
|
522
|
+
|
|
523
|
+
Get a Thread handle by its thread ID. Useful for posting to threads outside of webhook contexts (e.g. cron jobs, external triggers).
|
|
524
|
+
|
|
525
|
+
```typescript
|
|
526
|
+
const thread = bot.thread("slack:C123ABC:1234567890.123456");
|
|
527
|
+
await thread.post("Hello from a cron job!");
|
|
528
|
+
```
|
|
529
|
+
|
|
446
530
|
### channel
|
|
447
531
|
|
|
448
532
|
Get a Channel by its channel ID.
|
|
@@ -475,3 +559,13 @@ Get a `JSON.parse` reviver that deserializes `Thread` and `Message` objects from
|
|
|
475
559
|
const data = JSON.parse(payload, bot.reviver());
|
|
476
560
|
await data.thread.post("Hello from workflow!");
|
|
477
561
|
```
|
|
562
|
+
|
|
563
|
+
There is also a standalone `reviver` export that works without a `Chat` instance. This is useful in Vercel Workflow functions where importing the full Chat instance (with its adapter dependencies) is not possible:
|
|
564
|
+
|
|
565
|
+
```typescript
|
|
566
|
+
import { reviver } from "chat";
|
|
567
|
+
|
|
568
|
+
const data = JSON.parse(payload, reviver) as { thread: Thread; message: Message };
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
The standalone reviver uses lazy adapter resolution - the adapter is looked up from the Chat singleton when first accessed. Call `chat.registerSingleton()` before using thread methods like `post()` (typically inside a `"use step"` function).
|
package/docs/api/message.mdx
CHANGED
|
@@ -149,6 +149,10 @@ All adapters return `false` if the bot ID isn't known yet. This is a safe defaul
|
|
|
149
149
|
description: 'Fetch the attachment data. Handles platform auth automatically.',
|
|
150
150
|
type: '() => Promise<Buffer> | undefined',
|
|
151
151
|
},
|
|
152
|
+
fetchMetadata: {
|
|
153
|
+
description: 'Platform-specific IDs for reconstructing fetchData after serialization (e.g. WhatsApp mediaId, Telegram fileId).',
|
|
154
|
+
type: 'Record<string, string> | undefined',
|
|
155
|
+
},
|
|
152
156
|
}}
|
|
153
157
|
/>
|
|
154
158
|
|
|
@@ -208,4 +212,4 @@ const json = message.toJSON();
|
|
|
208
212
|
const restored = Message.fromJSON(json);
|
|
209
213
|
```
|
|
210
214
|
|
|
211
|
-
The serialized format converts `Date` fields to ISO strings and omits non-serializable fields like `data` buffers and `fetchData` functions.
|
|
215
|
+
The serialized format converts `Date` fields to ISO strings and omits non-serializable fields like `data` buffers and `fetchData` functions. The `fetchMetadata` field is preserved so that adapters can reconstruct `fetchData` when the message is rehydrated from a queue.
|
package/docs/api/modals.mdx
CHANGED
|
@@ -4,7 +4,7 @@ description: Modal form components for collecting user input.
|
|
|
4
4
|
type: reference
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
Modals display form dialogs that collect structured user input. Currently supported on Slack.
|
|
7
|
+
Modals display form dialogs that collect structured user input. Currently supported on Slack and Teams.
|
|
8
8
|
|
|
9
9
|
```typescript
|
|
10
10
|
import { Modal, TextInput, Select, RadioSelect, SelectOption } from "chat";
|
package/docs/api/thread.mdx
CHANGED
|
@@ -4,7 +4,7 @@ description: Represents a conversation thread with methods for posting, subscrib
|
|
|
4
4
|
type: reference
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
A `Thread` is provided to your event handlers and represents a conversation thread on any platform. You
|
|
7
|
+
A `Thread` is provided to your event handlers and represents a conversation thread on any platform. You can also create thread handles directly using `chat.thread()` or `chat.openDM()`.
|
|
8
8
|
|
|
9
9
|
## Properties
|
|
10
10
|
|
|
@@ -114,6 +114,28 @@ await scheduled.cancel();
|
|
|
114
114
|
Streaming and file uploads are not supported in scheduled messages.
|
|
115
115
|
</Callout>
|
|
116
116
|
|
|
117
|
+
## getParticipants
|
|
118
|
+
|
|
119
|
+
Get the unique human participants in a thread. Returns deduplicated authors, excluding all bots. Useful for subscribing only to 1:1 conversations and unsubscribing when others join.
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
const participants = await thread.getParticipants();
|
|
123
|
+
|
|
124
|
+
// Subscribe only when one person is talking to the bot
|
|
125
|
+
if (participants.length === 1) {
|
|
126
|
+
await thread.subscribe();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Unsubscribe when the thread becomes a group conversation
|
|
130
|
+
if (participants.length > 1) {
|
|
131
|
+
await thread.unsubscribe();
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
<Callout type="warn">
|
|
136
|
+
Each call fetches the full message history to find all participants. On threads with long history this makes multiple API calls to the platform. Consider checking `message.author` against a known set before calling `getParticipants()` on every incoming message.
|
|
137
|
+
</Callout>
|
|
138
|
+
|
|
117
139
|
## subscribe / unsubscribe
|
|
118
140
|
|
|
119
141
|
Manage thread subscriptions. Subscribed threads route all messages to `onSubscribedMessage` handlers.
|
package/docs/cards.mdx
CHANGED
|
@@ -109,6 +109,12 @@ The `id` maps to your `onAction` handler. Optional `value` passes extra data:
|
|
|
109
109
|
<Button id="report" value="bug">Report Bug</Button>
|
|
110
110
|
```
|
|
111
111
|
|
|
112
|
+
Set `actionType="modal"` to indicate the button opens a [modal](/docs/modals). The button still triggers your `onAction` handler, where you call `event.openModal()` — this prop tells adapters like Teams to wire up the button for dialog opening:
|
|
113
|
+
|
|
114
|
+
```tsx title="lib/bot.tsx"
|
|
115
|
+
<Button id="open-feedback" actionType="modal">Give Feedback</Button>
|
|
116
|
+
```
|
|
117
|
+
|
|
112
118
|
### CardLink
|
|
113
119
|
|
|
114
120
|
Inline hyperlink rendered as text. Unlike `LinkButton` (which must be inside `Actions`), `CardLink` can be placed directly in a card alongside other content.
|
|
@@ -159,3 +159,36 @@ You should see your exported symbols (`createMatrixAdapter`, `MatrixAdapter`, et
|
|
|
159
159
|
- Watch the [Chat SDK changelog](https://github.com/vercel/chat/releases) for new features and breaking changes
|
|
160
160
|
- Run your test suite against new Chat SDK releases before they ship to catch compatibility issues early
|
|
161
161
|
- When the `Adapter` interface adds new optional methods, consider implementing them to keep your adapter feature-complete
|
|
162
|
+
|
|
163
|
+
## Listing on chat-sdk.dev
|
|
164
|
+
|
|
165
|
+
Community adapters can be listed on the [Adapters](https://chat-sdk.dev/adapters) page by opening a PR that adds an entry to `apps/docs/adapters.json` in the [Chat SDK repo](https://github.com/vercel/chat). Your adapter's README is fetched from GitHub at build time and rendered on its dedicated page.
|
|
166
|
+
|
|
167
|
+
### Pin your README to a commit or tag
|
|
168
|
+
|
|
169
|
+
<Callout type="warn">
|
|
170
|
+
The `readme` field **must** reference a specific commit SHA or tag — not a branch name like `main`.
|
|
171
|
+
</Callout>
|
|
172
|
+
|
|
173
|
+
The docs site re-renders on every deploy, so an unpinned `readme` would serve whatever currently sits at your default branch — including edits made after the listing PR was reviewed. Pinning freezes the rendered content at the state we approved; new content goes live through a follow-up PR that bumps the ref.
|
|
174
|
+
|
|
175
|
+
```json title="apps/docs/adapters.json"
|
|
176
|
+
{
|
|
177
|
+
"name": "My Adapter",
|
|
178
|
+
"slug": "my-adapter",
|
|
179
|
+
"type": "platform",
|
|
180
|
+
"community": true,
|
|
181
|
+
"packageName": "chat-adapter-my-thing",
|
|
182
|
+
"readme": "https://github.com/your-org/chat-adapter-my-thing/tree/v1.2.0"
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Accepted `readme` formats:
|
|
187
|
+
|
|
188
|
+
| Format | Example |
|
|
189
|
+
|--------|---------|
|
|
190
|
+
| Repo root at a tag | `https://github.com/owner/repo/tree/v1.0.0` |
|
|
191
|
+
| Repo root at a commit | `https://github.com/owner/repo/tree/abc1234...` |
|
|
192
|
+
| Subpath in a monorepo | `https://github.com/owner/repo/tree/<ref>/packages/adapter` |
|
|
193
|
+
|
|
194
|
+
Unpinned refs (e.g., `tree/main`, or omitting `/tree/<ref>` entirely) will emit a build warning and are rejected during PR review.
|
package/docs/files.mdx
CHANGED
|
@@ -75,3 +75,4 @@ bot.onSubscribedMessage(async (thread, message) => {
|
|
|
75
75
|
| `width` | `number` (optional) | Image width |
|
|
76
76
|
| `height` | `number` (optional) | Image height |
|
|
77
77
|
| `fetchData` | `() => Promise<Buffer>` (optional) | Download the file data |
|
|
78
|
+
| `fetchMetadata` | `Record<string, string>` (optional) | Platform-specific IDs for reconstructing `fetchData` after serialization |
|
package/docs/getting-started.mdx
CHANGED
|
@@ -14,7 +14,7 @@ Learn the core patterns for handling incoming events and posting messages back t
|
|
|
14
14
|
<Card title="Posting Messages" description="Different ways to render and send messages with thread.post()." href="/docs/posting-messages" />
|
|
15
15
|
</Cards>
|
|
16
16
|
|
|
17
|
-
##
|
|
17
|
+
## Adapters
|
|
18
18
|
|
|
19
19
|
Connect your bot to chat platforms and persist state across restarts.
|
|
20
20
|
|
|
@@ -25,14 +25,4 @@ Connect your bot to chat platforms and persist state across restarts.
|
|
|
25
25
|
|
|
26
26
|
Browse all official and community adapters on the [Adapters](/adapters) page.
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
Step-by-step tutorials to get up and running on your platform of choice.
|
|
31
|
-
|
|
32
|
-
<Cards>
|
|
33
|
-
<Card title="Slack bot with Next.js and Redis" description="Build a Slack bot from scratch using Chat SDK, Next.js, and Redis." href="/docs/guides/slack-nextjs" />
|
|
34
|
-
<Card title="Durable chat sessions with Next.js, Workflow, and Redis" description="Build a bot whose thread sessions survive restarts by combining Chat SDK with Workflow." href="/docs/guides/durable-chat-sessions-nextjs" />
|
|
35
|
-
<Card title="Schedule Slack posts with Next.js, Workflow, and Neon" description="Build durable scheduled posts backed by Neon and Workflow timers." href="/docs/guides/scheduled-posts-neon" />
|
|
36
|
-
<Card title="Code review GitHub bot with Hono and Redis" description="Build a GitHub bot that reviews pull requests using AI SDK, Vercel Sandbox, and Chat SDK." href="/docs/guides/code-review-hono" />
|
|
37
|
-
<Card title="Discord support bot with Nuxt and Redis" description="Build a Discord support bot using Chat SDK, Nuxt, and AI SDK." href="/docs/guides/discord-nuxt" />
|
|
38
|
-
</Cards>
|
|
28
|
+
Step-by-step guides and starter templates are available on the [Resources](/resources) page.
|
package/docs/meta.json
CHANGED
package/docs/modals.mdx
CHANGED
|
@@ -6,7 +6,7 @@ prerequisites:
|
|
|
6
6
|
- /docs/actions
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
Modals open form dialogs in response to button clicks or [slash commands](/docs/slash-commands). They support text inputs, dropdowns, radio buttons, and server-side validation. Currently supported on Slack.
|
|
9
|
+
Modals open form dialogs in response to button clicks or [slash commands](/docs/slash-commands). They support text inputs, dropdowns, radio buttons, and server-side validation. Currently supported on Slack and Teams.
|
|
10
10
|
|
|
11
11
|
## Open a modal
|
|
12
12
|
|
|
@@ -87,6 +87,77 @@ A dropdown for selecting a single option.
|
|
|
87
87
|
| `initialOption` | `string` (optional) | Pre-selected value |
|
|
88
88
|
| `optional` | `boolean` (optional) | Allow empty submission |
|
|
89
89
|
|
|
90
|
+
### ExternalSelect
|
|
91
|
+
|
|
92
|
+
A dropdown that loads its options dynamically from a handler as the user types. Useful for large or remote-backed option sets (people, tickets, records) where a static `<Select>` would be impractical. Slack-only.
|
|
93
|
+
|
|
94
|
+
| Prop | Type | Description |
|
|
95
|
+
|------|------|-------------|
|
|
96
|
+
| `id` | `string` | Field identifier (key in `event.values`) |
|
|
97
|
+
| `label` | `string` | Field label |
|
|
98
|
+
| `placeholder` | `string` (optional) | Placeholder text |
|
|
99
|
+
| `minQueryLength` | `number` (optional) | Minimum characters before the loader fires (Slack default: 3) |
|
|
100
|
+
| `initialOption` | `{ label, value }` (optional) | Pre-selected option when the modal opens (must match an option returned by the loader). For static `<Select>`, `initialOption` is just the value string — for `<ExternalSelect>` it's the full `{ label, value }` object since the loader hasn't run yet. |
|
|
101
|
+
| `optional` | `boolean` (optional) | Allow empty submission |
|
|
102
|
+
|
|
103
|
+
Register the loader with `onOptionsLoad`:
|
|
104
|
+
|
|
105
|
+
```tsx title="lib/bot.tsx" lineNumbers
|
|
106
|
+
import { ExternalSelect, Modal } from "chat";
|
|
107
|
+
|
|
108
|
+
bot.onAction("assign", async (event) => {
|
|
109
|
+
await event.openModal(
|
|
110
|
+
<Modal callbackId="assign_form" title="Assign to…">
|
|
111
|
+
<ExternalSelect
|
|
112
|
+
id="assignee"
|
|
113
|
+
label="Assignee"
|
|
114
|
+
placeholder="Search people"
|
|
115
|
+
minQueryLength={1}
|
|
116
|
+
/>
|
|
117
|
+
</Modal>
|
|
118
|
+
);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
bot.onOptionsLoad("assignee", async (event) => {
|
|
122
|
+
const people = await peopleService.search(event.query);
|
|
123
|
+
return people.map((p) => ({ label: p.fullName, value: p.id }));
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
bot.onModalSubmit("assign_form", async (event) => {
|
|
127
|
+
const assigneeId = event.values.assignee;
|
|
128
|
+
// …
|
|
129
|
+
});
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
The selected value arrives in `event.values` on submit just like a static `<Select>`.
|
|
133
|
+
|
|
134
|
+
#### Grouped options
|
|
135
|
+
|
|
136
|
+
Return an array of groups instead of a flat options array to render headers between sections (e.g. "Recent" / "All"):
|
|
137
|
+
|
|
138
|
+
```tsx
|
|
139
|
+
bot.onOptionsLoad("assignee", async (event) => {
|
|
140
|
+
const [recent, all] = await Promise.all([
|
|
141
|
+
peopleService.recent(event.user.userId),
|
|
142
|
+
peopleService.search(event.query),
|
|
143
|
+
]);
|
|
144
|
+
return [
|
|
145
|
+
{ label: "Recent", options: recent.map((p) => ({ label: p.fullName, value: p.id })) },
|
|
146
|
+
{ label: "All", options: all.map((p) => ({ label: p.fullName, value: p.id })) },
|
|
147
|
+
];
|
|
148
|
+
});
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Slack limits: max 100 groups, max 100 options per group, group label max 75 characters.
|
|
152
|
+
|
|
153
|
+
<Callout type="warn">
|
|
154
|
+
Slack requires a response within 3 seconds for options requests. The adapter caps the loader at ~2.5s and returns an empty result on timeout — keep your loader fast (cache, prefetch, or narrow the query server-side).
|
|
155
|
+
</Callout>
|
|
156
|
+
|
|
157
|
+
<Callout type="info">
|
|
158
|
+
**Slack setup:** `ExternalSelect` uses Slack's `block_suggestion` payload, which is dispatched to the **Options Load URL**. In your [Slack app settings](https://api.slack.com/apps) go to **Interactivity & Shortcuts** → **Select Menus** and set the **Options Load URL** to the same endpoint as your Interactivity Request URL (e.g. `https://your-domain.com/api/webhooks/slack`). Without this, typing into an external select will silently return no results.
|
|
159
|
+
</Callout>
|
|
160
|
+
|
|
90
161
|
### RadioSelect
|
|
91
162
|
|
|
92
163
|
A radio button group for mutually exclusive options.
|
|
@@ -142,7 +213,8 @@ bot.onModalSubmit("feedback_form", async (event) => {
|
|
|
142
213
|
|
|
143
214
|
| Response | Description |
|
|
144
215
|
|----------|-------------|
|
|
145
|
-
| `undefined` or `{ action: "close" }` | Close the
|
|
216
|
+
| `undefined` or `{ action: "close" }` | Close the current view (goes back one level in the stack) |
|
|
217
|
+
| `{ action: "clear" }` | Close all views and dismiss the modal entirely |
|
|
146
218
|
| `{ action: "errors", errors: { fieldId: "message" } }` | Show validation errors on specific fields |
|
|
147
219
|
| `{ action: "update", modal: ModalElement }` | Replace the modal content |
|
|
148
220
|
| `{ action: "push", modal: ModalElement }` | Push a new modal view onto the stack |
|
package/docs/state.mdx
CHANGED
|
@@ -8,7 +8,7 @@ prerequisites:
|
|
|
8
8
|
|
|
9
9
|
State adapters handle persistent storage for thread subscriptions, distributed locks (to prevent duplicate processing), and caching. You must provide a state adapter when creating a `Chat` instance. Browse all available state adapters on the [Adapters](/adapters) page.
|
|
10
10
|
|
|
11
|
-
## What state
|
|
11
|
+
## What state adapters manage
|
|
12
12
|
|
|
13
13
|
### Thread subscriptions
|
|
14
14
|
|