chat 4.32.0 → 4.33.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.
@@ -148,6 +148,35 @@ declare const ADAPTERS: {
148
148
  readonly slug: "agentphone";
149
149
  readonly type: "platform";
150
150
  };
151
+ readonly "cloudflare-agents": {
152
+ readonly description: "Cloudflare Agents state adapter for Chat SDK. Stores subscriptions, locks, queues, and history in Durable Object SQLite via ChatSdkStateAgent sub-agents.";
153
+ readonly env: {
154
+ readonly config: readonly ["parent", "agent", "name", "shardKey", "keyShard"];
155
+ readonly notes: "No environment variables are required. State is stored in Durable Object SQLite via ChatSdkStateAgent sub-agents; add the parent Agent to your Durable Object migration and re-export ChatSdkStateAgent from the Worker entry point.";
156
+ };
157
+ readonly factoryExport: "createChatSdkState";
158
+ readonly group: "vendor-official";
159
+ readonly name: "Cloudflare Agents";
160
+ readonly packageName: "agents";
161
+ readonly peerDeps: readonly [];
162
+ readonly slug: "cloudflare-agents";
163
+ readonly type: "state";
164
+ };
165
+ readonly dial: {
166
+ readonly description: "SMS, MMS, iMessage, and inbound voice-call transcripts for Chat SDK, built and maintained by Dial. One handler answers phone traffic the same way it answers Slack/Teams/Discord, with HMAC-signed webhooks and replies over @getdial/sdk.";
167
+ readonly env: {
168
+ readonly config: readonly ["apiBaseUrl", "botName", "logger"];
169
+ readonly optional: readonly [EnvVar, EnvVar, EnvVar];
170
+ readonly required: readonly [EnvVar, EnvVar];
171
+ };
172
+ readonly factoryExport: "createDialAdapter";
173
+ readonly group: "vendor-official";
174
+ readonly name: "Dial";
175
+ readonly packageName: "@getdial/chat-sdk-adapter";
176
+ readonly peerDeps: readonly [];
177
+ readonly slug: "dial";
178
+ readonly type: "platform";
179
+ };
151
180
  readonly discord: {
152
181
  readonly description: "Create Discord bots with slash commands, threads, and rich embeds.";
153
182
  readonly env: {
@@ -172,7 +201,7 @@ declare const ADAPTERS: {
172
201
  readonly label: "GitHub App";
173
202
  readonly vars: readonly [EnvVar, EnvVar];
174
203
  }];
175
- readonly optional: readonly [EnvVar, EnvVar, EnvVar];
204
+ readonly optional: readonly [EnvVar, EnvVar, EnvVar, EnvVar];
176
205
  readonly required: readonly [EnvVar];
177
206
  };
178
207
  readonly factoryExport: "createGitHubAdapter";
@@ -363,6 +392,27 @@ declare const ADAPTERS: {
363
392
  readonly slug: "novu";
364
393
  readonly type: "platform";
365
394
  };
395
+ readonly photon: {
396
+ readonly description: "iMessage adapter for Chat SDK, built and maintained by Photon. Cloud, self-hosted, and on-device (macOS) iMessage over spectrum-ts.";
397
+ readonly env: {
398
+ readonly config: readonly ["clients", "logger"];
399
+ readonly credentialModes: readonly [{
400
+ readonly label: "Spectrum Cloud";
401
+ readonly vars: readonly [EnvVar, EnvVar];
402
+ }, {
403
+ readonly label: "Self-hosted";
404
+ readonly vars: readonly [EnvVar, EnvVar];
405
+ }];
406
+ readonly optional: readonly [EnvVar, EnvVar, EnvVar];
407
+ };
408
+ readonly factoryExport: "createiMessageAdapter";
409
+ readonly group: "vendor-official";
410
+ readonly name: "Photon";
411
+ readonly packageName: "@photon-ai/chat-adapter-imessage";
412
+ readonly peerDeps: readonly [];
413
+ readonly slug: "photon";
414
+ readonly type: "platform";
415
+ };
366
416
  readonly postgres: {
367
417
  readonly description: "Production state adapter using PostgreSQL for persistence and distributed locking.";
368
418
  readonly env: {
@@ -544,6 +594,27 @@ declare const ADAPTERS: {
544
594
  readonly slug: "whatsapp";
545
595
  readonly type: "platform";
546
596
  };
597
+ readonly x: {
598
+ readonly description: "Reply to public mentions and hold direct message conversations on X (Twitter) with the X API v2.";
599
+ readonly env: {
600
+ readonly credentialModes: readonly [{
601
+ readonly label: "Static access token";
602
+ readonly vars: readonly [EnvVar];
603
+ }, {
604
+ readonly label: "Managed OAuth refresh";
605
+ readonly vars: readonly [EnvVar, EnvVar];
606
+ }];
607
+ readonly optional: readonly [EnvVar, EnvVar, EnvVar, EnvVar, EnvVar];
608
+ readonly required: readonly [EnvVar];
609
+ };
610
+ readonly factoryExport: "createXAdapter";
611
+ readonly group: "official";
612
+ readonly name: "X";
613
+ readonly packageName: "@chat-adapter/x";
614
+ readonly peerDeps: readonly [];
615
+ readonly slug: "x";
616
+ readonly type: "platform";
617
+ };
547
618
  readonly zernio: {
548
619
  readonly description: "Unified social media DM adapter covering Instagram, Facebook, Telegram, WhatsApp, X/Twitter, Bluesky, and Reddit through a single integration.";
549
620
  readonly env: {
@@ -40,6 +40,54 @@ var ADAPTERS = {
40
40
  slug: "agentphone",
41
41
  type: "platform"
42
42
  },
43
+ "cloudflare-agents": {
44
+ description: "Cloudflare Agents state adapter for Chat SDK. Stores subscriptions, locks, queues, and history in Durable Object SQLite via ChatSdkStateAgent sub-agents.",
45
+ env: {
46
+ config: ["parent", "agent", "name", "shardKey", "keyShard"],
47
+ notes: "No environment variables are required. State is stored in Durable Object SQLite via ChatSdkStateAgent sub-agents; add the parent Agent to your Durable Object migration and re-export ChatSdkStateAgent from the Worker entry point."
48
+ },
49
+ factoryExport: "createChatSdkState",
50
+ group: "vendor-official",
51
+ name: "Cloudflare Agents",
52
+ packageName: "agents",
53
+ peerDeps: [],
54
+ slug: "cloudflare-agents",
55
+ type: "state"
56
+ },
57
+ dial: {
58
+ description: "SMS, MMS, iMessage, and inbound voice-call transcripts for Chat SDK, built and maintained by Dial. One handler answers phone traffic the same way it answers Slack/Teams/Discord, with HMAC-signed webhooks and replies over @getdial/sdk.",
59
+ env: {
60
+ config: ["apiBaseUrl", "botName", "logger"],
61
+ optional: [
62
+ secretEnv(
63
+ "DIAL_WEBHOOK_SECRET",
64
+ "HMAC-SHA256 signing secret from the Dial webhook subscription; enables signature verification of inbound requests."
65
+ ),
66
+ env(
67
+ "DIAL_API_URL",
68
+ "Override the Dial API host. Defaults to https://api.getdial.ai."
69
+ ),
70
+ env(
71
+ "BOT_USERNAME",
72
+ 'Display name Chat SDK uses for the bot. Defaults to "bot".'
73
+ )
74
+ ],
75
+ required: [
76
+ secretEnv("DIAL_API_KEY", "Dial API key (sk_live_\u2026)."),
77
+ env(
78
+ "DIAL_FROM_NUMBER_ID",
79
+ "Dial's ID of the phone number the bot sends from."
80
+ )
81
+ ]
82
+ },
83
+ factoryExport: "createDialAdapter",
84
+ group: "vendor-official",
85
+ name: "Dial",
86
+ packageName: "@getdial/chat-sdk-adapter",
87
+ peerDeps: [],
88
+ slug: "dial",
89
+ type: "platform"
90
+ },
43
91
  discord: {
44
92
  description: "Create Discord bots with slash commands, threads, and rich embeds.",
45
93
  env: {
@@ -86,6 +134,10 @@ var ADAPTERS = {
86
134
  "Installation ID for single-installation app deployments."
87
135
  ),
88
136
  env("GITHUB_BOT_USERNAME", "Bot username for mention detection."),
137
+ env(
138
+ "GITHUB_BOT_USER_ID",
139
+ "Numeric bot user ID for self-message detection; recommended on serverless and with Vercel Connect to prevent reply loops."
140
+ ),
89
141
  urlEnv("GITHUB_API_URL", "Override the GitHub API base URL.")
90
142
  ],
91
143
  required: [secretEnv("GITHUB_WEBHOOK_SECRET", "Webhook signing secret.")]
@@ -439,6 +491,58 @@ var ADAPTERS = {
439
491
  slug: "novu",
440
492
  type: "platform"
441
493
  },
494
+ photon: {
495
+ description: "iMessage adapter for Chat SDK, built and maintained by Photon. Cloud, self-hosted, and on-device (macOS) iMessage over spectrum-ts.",
496
+ env: {
497
+ config: ["clients", "logger"],
498
+ credentialModes: [
499
+ {
500
+ label: "Spectrum Cloud",
501
+ vars: [
502
+ env("IMESSAGE_PROJECT_ID", "Spectrum Cloud project ID."),
503
+ secretEnv(
504
+ "IMESSAGE_PROJECT_SECRET",
505
+ "Spectrum Cloud project secret."
506
+ )
507
+ ]
508
+ },
509
+ {
510
+ label: "Self-hosted",
511
+ vars: [
512
+ urlEnv(
513
+ "IMESSAGE_SERVER_URL",
514
+ "gRPC host:port of a self-hosted iMessage server (not an https URL)."
515
+ ),
516
+ secretEnv(
517
+ "IMESSAGE_API_KEY",
518
+ "Auth token for the self-hosted server."
519
+ )
520
+ ]
521
+ }
522
+ ],
523
+ optional: [
524
+ env(
525
+ "IMESSAGE_LOCAL",
526
+ 'Set to "false" for cloud/self-host; on-device (local, macOS) mode is the default.'
527
+ ),
528
+ secretEnv(
529
+ "IMESSAGE_WEBHOOK_SECRET",
530
+ "Per-webhook signing secret for verifying Spectrum Cloud deliveries."
531
+ ),
532
+ env(
533
+ "IMESSAGE_PHONE",
534
+ "Routing/identity phone for multi-number setups."
535
+ )
536
+ ]
537
+ },
538
+ factoryExport: "createiMessageAdapter",
539
+ group: "vendor-official",
540
+ name: "Photon",
541
+ packageName: "@photon-ai/chat-adapter-imessage",
542
+ peerDeps: [],
543
+ slug: "photon",
544
+ type: "platform"
545
+ },
442
546
  postgres: {
443
547
  description: "Production state adapter using PostgreSQL for persistence and distributed locking.",
444
548
  env: {
@@ -735,6 +839,64 @@ var ADAPTERS = {
735
839
  slug: "whatsapp",
736
840
  type: "platform"
737
841
  },
842
+ x: {
843
+ description: "Reply to public mentions and hold direct message conversations on X (Twitter) with the X API v2.",
844
+ env: {
845
+ credentialModes: [
846
+ {
847
+ label: "Static access token",
848
+ vars: [
849
+ secretEnv(
850
+ "X_USER_ACCESS_TOKEN",
851
+ "OAuth 2.0 user-context access token for outbound API calls."
852
+ )
853
+ ]
854
+ },
855
+ {
856
+ label: "Managed OAuth refresh",
857
+ vars: [
858
+ env("X_CLIENT_ID", "OAuth 2.0 client ID."),
859
+ secretEnv(
860
+ "X_REFRESH_TOKEN",
861
+ "OAuth 2.0 refresh token (requires the offline.access scope)."
862
+ )
863
+ ]
864
+ }
865
+ ],
866
+ optional: [
867
+ secretEnv(
868
+ "X_CLIENT_SECRET",
869
+ "OAuth 2.0 client secret for confidential clients."
870
+ ),
871
+ secretEnv(
872
+ "X_ENCRYPTION_KEY",
873
+ "AES-256-GCM key for encrypting stored OAuth tokens."
874
+ ),
875
+ env(
876
+ "X_USER_ID",
877
+ "Bot account user ID. Fetched from /2/users/me when omitted."
878
+ ),
879
+ env(
880
+ "X_USERNAME",
881
+ "Bot account handle used for mention detection. Fetched when omitted."
882
+ ),
883
+ urlEnv("X_API_BASE_URL", "Override the X API base URL.")
884
+ ],
885
+ required: [
886
+ secretEnv(
887
+ "X_CONSUMER_SECRET",
888
+ "App API key secret used for webhook CRC and signature verification."
889
+ )
890
+ ]
891
+ },
892
+ factoryExport: "createXAdapter",
893
+ group: "official",
894
+ name: "X",
895
+ packageName: "@chat-adapter/x",
896
+ peerDeps: [],
897
+ slug: "x",
898
+ type: "platform"
899
+ },
738
900
  zernio: {
739
901
  description: "Unified social media DM adapter covering Instagram, Facebook, Telegram, WhatsApp, X/Twitter, Bluesky, and Reddit through a single integration.",
740
902
  env: {
@@ -1,10 +1,10 @@
1
- import { x as Chat, w as ChannelVisibility } from '../chat-Dm1vQU3i.js';
2
- export { d as AiAssistantMessage, e as AiFilePart, f as AiImagePart, g as AiMessage, h as AiMessagePart, i as AiTextPart, j as AiUserMessage, aZ as ToAiMessagesOptions, b7 as toAiMessages } from '../chat-Dm1vQU3i.js';
1
+ import { C as Chat, a as ChannelVisibility } from '../messages-x-XVhW8B.js';
2
+ export { A as AiAssistantMessage, b as AiFilePart, c as AiImagePart, d as AiMessage, e as AiMessagePart, f as AiTextPart, g as AiUserMessage, T as ToAiMessagesOptions, t as toAiMessages } from '../messages-x-XVhW8B.js';
3
3
  import * as ai from 'ai';
4
4
  import { Tool } from 'ai';
5
5
  import '@workflow/serde';
6
6
  import 'mdast';
7
- import '../jsx-runtime-CzthIo1o.js';
7
+ import '../jsx-runtime-LBOLBP4o.js';
8
8
 
9
9
  /**
10
10
  * The Chat instance used by all tools to dispatch operations.
package/dist/index.d.ts CHANGED
@@ -1,9 +1,9 @@
1
- import { ay as PostableObject, b as Adapter, az as PostableObjectContext, aN as StreamChunk, aU as ThreadHistoryCache, aV as ThreadHistoryConfig, aO as StreamEvent, L as EmojiFormats, M as EmojiMapConfig, N as EmojiValue, b4 as WellKnownEmoji, G as CustomEmojiMap, c as AdapterPostableMessage } from './chat-Dm1vQU3i.js';
2
- export { A as ActionEvent, a as ActionHandler, d as AiAssistantMessage, e as AiFilePart, f as AiImagePart, g as AiMessage, h as AiMessagePart, i as AiTextPart, j as AiUserMessage, k as AppHomeOpenedEvent, l as AppHomeOpenedHandler, m as AppendInput, n as AppendOptions, o as AssistantContextChangedEvent, p as AssistantContextChangedHandler, q as AssistantThreadStartedEvent, r as AssistantThreadStartedHandler, s as Attachment, t as Author, C as Channel, u as ChannelImpl, v as ChannelInfo, w as ChannelVisibility, x as Chat, y as ChatConfig, z as ChatInstance, B as ConcurrencyConfig, D as ConcurrencyStrategy, E as ConsoleLogger, F as CountQuery, H as DeleteTarget, I as DirectMessageHandler, J as DurationString, K as Emoji, O as EphemeralMessage, P as FetchDirection, Q as FetchOptions, R as FetchResult, S as FileUpload, T as FormattedContent, U as IdentityContext, V as IdentityResolver, W as LinkPreview, X as ListQuery, Y as ListThreadsOptions, Z as ListThreadsResult, _ as Lock, $ as LockScope, a0 as LockScopeContext, a1 as LogLevel, a2 as Logger, a3 as MarkdownTextChunk, a4 as MemberJoinedChannelEvent, a5 as MemberJoinedChannelHandler, a6 as MentionHandler, a7 as Message, a8 as MessageContext, a9 as MessageData, aa as MessageHandler, ab as MessageMetadata, ac as MessageSubject, ad as ModalClearResponse, ae as ModalCloseEvent, af as ModalCloseHandler, ag as ModalCloseResponse, ah as ModalErrorsResponse, ai as ModalPushResponse, aj as ModalResponse, ak as ModalSubmitEvent, al as ModalSubmitHandler, am as ModalUpdateResponse, an as OptionsLoadEvent, ao as OptionsLoadGroup, ap as OptionsLoadHandler, aq as OptionsLoadResult, ar as PlanUpdateChunk, as as PostEphemeralOptions, at as Postable, au as PostableAst, av as PostableCard, aw as PostableMarkdown, ax as PostableMessage, aA as PostableRaw, aB as QueueEntry, aC as RawMessage, aD as ReactionEvent, aE as ReactionHandler, aF as ScheduledMessage, aG as SentMessage, aH as SerializedChannel, aI as SerializedMessage, aJ as SerializedThread, aK as SlashCommandEvent, aL as SlashCommandHandler, aM as StateAdapter, aP as StreamOptions, aQ as SubscribedMessageHandler, aR as THREAD_STATE_TTL_MS, aS as TaskUpdateChunk, aT as Thread, aW as ThreadImpl, aX as ThreadInfo, aY as ThreadSummary, aZ as ToAiMessagesOptions, a_ as TranscriptEntry, a$ as TranscriptRole, b0 as TranscriptsApi, b1 as TranscriptsConfig, b2 as UserInfo, b3 as WebhookOptions, b5 as deriveChannelId, b6 as isPostableObject, b7 as toAiMessages } from './chat-Dm1vQU3i.js';
1
+ import { P as PostableObject, h as Adapter, i as PostableObjectContext, S as StreamChunk, j as ThreadHistoryCache, k as ThreadHistoryConfig, l as StreamEvent, E as EmojiFormats, m as EmojiMapConfig, n as EmojiValue, W as WellKnownEmoji, o as CustomEmojiMap, p as AdapterPostableMessage } from './messages-x-XVhW8B.js';
2
+ export { q as ActionEvent, r as ActionHandler, A as AiAssistantMessage, b as AiFilePart, c as AiImagePart, d as AiMessage, e as AiMessagePart, f as AiTextPart, g as AiUserMessage, s as AppHomeOpenedEvent, u as AppHomeOpenedHandler, v as AppendInput, w as AppendOptions, x as AssistantContextChangedEvent, y as AssistantContextChangedHandler, z as AssistantThreadStartedEvent, B as AssistantThreadStartedHandler, D as Attachment, F as Author, G as Channel, H as ChannelImpl, I as ChannelInfo, a as ChannelVisibility, C as Chat, J as ChatConfig, K as ChatInstance, L as ConcurrencyConfig, M as ConcurrencyStrategy, N as ConsoleLogger, O as CountQuery, Q as DeleteTarget, R as DirectMessageHandler, U as DurationString, V as Emoji, X as EphemeralMessage, Y as FetchDirection, Z as FetchOptions, _ as FetchResult, $ as FileUpload, a0 as FormattedContent, a1 as IdentityContext, a2 as IdentityResolver, a3 as LinkPreview, a4 as ListQuery, a5 as ListThreadsOptions, a6 as ListThreadsResult, a7 as Lock, a8 as LockScope, a9 as LockScopeContext, aa as LogLevel, ab as Logger, ac as MarkdownTextChunk, ad as MemberJoinedChannelEvent, ae as MemberJoinedChannelHandler, af as MentionHandler, ag as Message, ah as MessageContext, ai as MessageData, aj as MessageHandler, ak as MessageMetadata, al as MessageSubject, am as ModalClearResponse, an as ModalCloseEvent, ao as ModalCloseHandler, ap as ModalCloseResponse, aq as ModalErrorsResponse, ar as ModalPushResponse, as as ModalResponse, at as ModalSubmitEvent, au as ModalSubmitHandler, av as ModalUpdateResponse, aw as OptionsLoadEvent, ax as OptionsLoadGroup, ay as OptionsLoadHandler, az as OptionsLoadResult, aA as PlanUpdateChunk, aB as PostEphemeralOptions, aC as Postable, aD as PostableAst, aE as PostableCard, aF as PostableMarkdown, aG as PostableMessage, aH as PostableRaw, aI as QueueEntry, aJ as RawMessage, aK as ReactionEvent, aL as ReactionHandler, aM as ScheduledMessage, aN as SentMessage, aO as SerializedChannel, aP as SerializedMessage, aQ as SerializedThread, aR as SlashCommandEvent, aS as SlashCommandHandler, aT as StateAdapter, aU as StreamOptions, aV as SubscribedMessageHandler, aW as THREAD_STATE_TTL_MS, aX as TaskUpdateChunk, aY as Thread, aZ as ThreadImpl, a_ as ThreadInfo, a$ as ThreadSummary, T as ToAiMessagesOptions, b0 as TranscriptEntry, b1 as TranscriptRole, b2 as TranscriptsApi, b3 as TranscriptsConfig, b4 as UserInfo, b5 as WebhookOptions, b6 as deriveChannelId, b7 as isPostableObject, t as toAiMessages } from './messages-x-XVhW8B.js';
3
3
  import { Root, List, Content, Blockquote, Code, Emphasis, InlineCode, Delete, Link, ListItem, Paragraph, Strong, TableCell, Table as Table$1, TableRow, Text } from 'mdast';
4
4
  export { Blockquote, Code, Content, Delete, Emphasis, InlineCode, Link, List, ListItem, Table as MdastTable, Nodes, Paragraph, Root, Strong, TableCell, TableRow, Text } from 'mdast';
5
- import { g as CardElement, C as CardChild, A as ActionsComponent, B as ButtonComponent, f as CardComponent, j as CardLinkComponent, a9 as TextComponent, D as DividerComponent, E as ExternalSelectComponent, F as FieldComponent, w as FieldsComponent, I as ImageComponent, L as LinkButtonComponent, P as ModalComponent, T as RadioSelectComponent, W as SectionComponent, Y as SelectComponent, _ as SelectOptionComponent, a3 as Table$2, ab as TextInputComponent, ah as cardChildToFallbackText$1, ai as fromReactElement$1, aj as fromReactModalElement$1, ak as isCardElement$1, am as isJSX$1, an as isModalElement$1, ar as toCardElement$1, as as toModalElement$1 } from './jsx-runtime-CzthIo1o.js';
6
- export { a as ActionsElement, b as ButtonElement, c as ButtonOptions, d as ButtonProps, e as ButtonStyle, h as CardJSXElement, i as CardJSXProps, k as CardLinkProps, l as CardOptions, m as CardProps, n as ChatElement, o as ContainerProps, p as DividerElement, q as DividerProps, r as ExternalSelectElement, s as ExternalSelectOptions, t as ExternalSelectProps, u as FieldElement, v as FieldProps, x as FieldsElement, z as ImageElement, G as ImageProps, H as LinkButtonElement, K as LinkButtonOptions, M as LinkButtonProps, N as LinkElement, O as ModalChild, Q as ModalElement, R as ModalOptions, S as ModalProps, U as RadioSelectElement, V as RadioSelectOptions, X as SectionElement, Z as SelectElement, $ as SelectOptionElement, a0 as SelectOptionProps, a1 as SelectOptions, a2 as SelectProps, a4 as TableAlignment, a6 as TableElement, a7 as TableOptions, aa as TextElement, ac as TextInputElement, ad as TextInputOptions, ae as TextInputProps, af as TextProps, ag as TextStyle } from './jsx-runtime-CzthIo1o.js';
5
+ import { C as CardElement, a as CardChild, A as ActionsComponent, B as ButtonComponent, b as CardComponent, c as CardLinkComponent, T as TextComponent, D as DividerComponent, E as ExternalSelectComponent, F as FieldComponent, d as FieldsComponent, I as ImageComponent, L as LinkButtonComponent, M as ModalComponent, R as RadioSelectComponent, S as SectionComponent, e as SelectComponent, f as SelectOptionComponent, g as Table$2, h as TextInputComponent, i as cardChildToFallbackText$1, j as fromReactElement$1, k as fromReactModalElement$1, l as isCardElement$1, m as isJSX$1, n as isModalElement$1, t as toCardElement$1, o as toModalElement$1 } from './jsx-runtime-LBOLBP4o.js';
6
+ export { p as ActionsElement, q as ButtonElement, r as ButtonOptions, s as ButtonProps, u as ButtonStyle, v as CardJSXElement, w as CardJSXProps, x as CardLinkProps, y as CardOptions, z as CardProps, G as ChatElement, H as ContainerProps, J as DividerElement, K as DividerProps, N as ExternalSelectElement, O as ExternalSelectOptions, P as ExternalSelectProps, Q as FieldElement, U as FieldProps, V as FieldsElement, W as ImageElement, X as ImageProps, Y as LinkButtonElement, Z as LinkButtonOptions, _ as LinkButtonProps, $ as LinkElement, a0 as ModalChild, a1 as ModalElement, a2 as ModalOptions, a3 as ModalProps, a4 as RadioSelectElement, a5 as RadioSelectOptions, a6 as SectionElement, a7 as SelectElement, a8 as SelectOptionElement, a9 as SelectOptionProps, aa as SelectOptions, ab as SelectProps, ac as TableAlignment, ad as TableElement, ae as TableOptions, af as TextElement, ag as TextInputElement, ah as TextInputOptions, ai as TextInputProps, aj as TextProps, ak as TextStyle } from './jsx-runtime-LBOLBP4o.js';
7
7
  import '@workflow/serde';
8
8
 
9
9
  /**
@@ -354,7 +354,7 @@ declare const defaultEmojiResolver: EmojiResolver;
354
354
  * // Returns: "Thanks! 👍"
355
355
  * ```
356
356
  */
357
- declare function convertEmojiPlaceholders(text: string, platform: "slack" | "gchat" | "teams" | "discord" | "messenger" | "github" | "linear" | "whatsapp", resolver?: EmojiResolver): string;
357
+ declare function convertEmojiPlaceholders(text: string, platform: "slack" | "gchat" | "teams" | "discord" | "messenger" | "github" | "linear" | "whatsapp" | "x", resolver?: EmojiResolver): string;
358
358
  /** Base emoji object with well-known emoji as EmojiValue singletons */
359
359
  type BaseEmojiHelper = {
360
360
  [K in WellKnownEmoji]: EmojiValue;
package/dist/index.js CHANGED
@@ -2119,7 +2119,7 @@ var SLACK_USER_ID_REGEX = /^[UW][A-Z0-9]+$/;
2119
2119
  var DISCORD_SNOWFLAKE_REGEX = /^\d{17,19}$/;
2120
2120
  var LINEAR_UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
2121
2121
  var NUMERIC_REGEX = /^\d+$/;
2122
- var DEDUPE_TTL_MS = 5 * 60 * 1e3;
2122
+ var DEDUPE_TTL_MS = 10 * 60 * 1e3;
2123
2123
  var MODAL_CONTEXT_TTL_MS = 24 * 60 * 60 * 1e3;
2124
2124
  var Chat = class {
2125
2125
  /**
@@ -3535,7 +3535,7 @@ var Chat = class {
3535
3535
  DEFAULT_LOCK_TTL_MS
3536
3536
  );
3537
3537
  if (!lock) {
3538
- const effectiveMaxSize = strategy === "debounce" ? 1 : maxQueueSize;
3538
+ const effectiveMaxSize = maxQueueSize;
3539
3539
  const depth = await this._stateAdapter.queueDepth(lockKey);
3540
3540
  if (depth >= effectiveMaxSize && strategy !== "debounce" && onQueueFull === "drop-newest") {
3541
3541
  this.logger.info("message-dropped", {
@@ -3580,7 +3580,7 @@ var Chat = class {
3580
3580
  enqueuedAt: Date.now(),
3581
3581
  expiresAt: Date.now() + queueEntryTtlMs
3582
3582
  },
3583
- 1
3583
+ maxQueueSize
3584
3584
  );
3585
3585
  this.logger.info("message-debouncing", {
3586
3586
  threadId,
@@ -3623,37 +3623,54 @@ var Chat = class {
3623
3623
  */
3624
3624
  async debounceLoop(lock, adapter, threadId, lockKey) {
3625
3625
  const { debounceMs } = this._concurrencyConfig;
3626
+ const skipped = [];
3626
3627
  while (true) {
3627
3628
  await sleep(debounceMs);
3628
3629
  await this._stateAdapter.extendLock(lock, DEFAULT_LOCK_TTL_MS);
3629
- const entry = await this._stateAdapter.dequeue(lockKey);
3630
- if (!entry) {
3630
+ const pending = [];
3631
+ while (true) {
3632
+ const entry = await this._stateAdapter.dequeue(lockKey);
3633
+ if (!entry) {
3634
+ break;
3635
+ }
3636
+ const msg = this.rehydrateMessage(entry.message, adapter);
3637
+ if (Date.now() <= entry.expiresAt) {
3638
+ pending.push({ message: msg, expiresAt: entry.expiresAt });
3639
+ } else {
3640
+ this.logger.info("message-expired", {
3641
+ threadId,
3642
+ lockKey,
3643
+ messageId: msg.id
3644
+ });
3645
+ }
3646
+ }
3647
+ if (pending.length === 0) {
3631
3648
  break;
3632
3649
  }
3633
- const msg = this.rehydrateMessage(entry.message, adapter);
3634
- if (Date.now() > entry.expiresAt) {
3635
- this.logger.info("message-expired", {
3636
- threadId,
3637
- lockKey,
3638
- messageId: msg.id
3639
- });
3640
- continue;
3650
+ const latest = pending.at(-1);
3651
+ if (!latest) {
3652
+ break;
3641
3653
  }
3654
+ skipped.push(...pending.slice(0, -1).map((entry) => entry.message));
3642
3655
  const depth = await this._stateAdapter.queueDepth(lockKey);
3643
3656
  if (depth > 0) {
3657
+ skipped.push(latest.message);
3644
3658
  this.logger.info("message-superseded", {
3645
3659
  threadId,
3646
3660
  lockKey,
3647
- droppedId: msg.id
3661
+ droppedId: latest.message.id
3648
3662
  });
3649
3663
  continue;
3650
3664
  }
3651
3665
  this.logger.info("message-dequeued", {
3652
3666
  threadId,
3653
3667
  lockKey,
3654
- messageId: msg.id
3668
+ messageId: latest.message.id
3669
+ });
3670
+ await this.dispatchToHandlers(adapter, threadId, latest.message, {
3671
+ skipped,
3672
+ totalSinceLastHandler: skipped.length + 1
3655
3673
  });
3656
- await this.dispatchToHandlers(adapter, threadId, msg);
3657
3674
  break;
3658
3675
  }
3659
3676
  }
@@ -3814,7 +3831,7 @@ var Chat = class {
3814
3831
  );
3815
3832
  return;
3816
3833
  }
3817
- if (message.isMention || hasMention) {
3834
+ if (message.isMention || hasMention && this.mentionHandlers.length > 0) {
3818
3835
  this.logger.debug("Bot mentioned", {
3819
3836
  threadId,
3820
3837
  text: message.text.slice(0, 100)
@@ -4486,6 +4503,8 @@ function convertEmojiPlaceholders(text2, platform, resolver = defaultEmojiResolv
4486
4503
  return resolver.toGChat(emojiName);
4487
4504
  case "whatsapp":
4488
4505
  return resolver.toGChat(emojiName);
4506
+ case "x":
4507
+ return resolver.toGChat(emojiName);
4489
4508
  default:
4490
4509
  return resolver.toGChat(emojiName);
4491
4510
  }
@@ -784,4 +784,4 @@ declare namespace JSX {
784
784
  }
785
785
  }
786
786
 
787
- export { type SelectOptionElement as $, type ActionsComponent as A, type ButtonComponent as B, type CardChild as C, type DividerComponent as D, type ExternalSelectComponent as E, type FieldComponent as F, type ImageProps as G, type LinkButtonElement as H, type ImageComponent as I, JSX as J, type LinkButtonOptions as K, type LinkButtonComponent as L, type LinkButtonProps as M, type LinkElement as N, type ModalChild as O, type ModalComponent as P, type ModalElement as Q, type ModalOptions as R, type ModalProps as S, type RadioSelectComponent as T, type RadioSelectElement as U, type RadioSelectOptions as V, type SectionComponent as W, type SectionElement as X, type SelectComponent as Y, type SelectElement as Z, type SelectOptionComponent as _, type ActionsElement as a, type SelectOptionProps as a0, type SelectOptions as a1, type SelectProps as a2, Table as a3, type TableAlignment as a4, type TableComponent as a5, type TableElement as a6, type TableOptions as a7, type TableProps as a8, type TextComponent as a9, type TextElement as aa, type TextInputComponent as ab, type TextInputElement as ac, type TextInputOptions as ad, type TextInputProps as ae, type TextProps as af, type TextStyle as ag, cardChildToFallbackText as ah, fromReactElement as ai, fromReactModalElement as aj, isCardElement as ak, isCardLinkProps as al, isJSX as am, isModalElement as an, jsx as ao, jsxDEV as ap, jsxs as aq, toCardElement as ar, toModalElement as as, type ButtonElement as b, type ButtonOptions as c, type ButtonProps as d, type ButtonStyle as e, type CardComponent as f, type CardElement as g, type CardJSXElement as h, type CardJSXProps as i, type CardLinkComponent as j, type CardLinkProps as k, type CardOptions as l, type CardProps as m, type ChatElement as n, type ContainerProps as o, type DividerElement as p, type DividerProps as q, type ExternalSelectElement as r, type ExternalSelectOptions as s, type ExternalSelectProps as t, type FieldElement as u, type FieldProps as v, type FieldsComponent as w, type FieldsElement as x, Fragment as y, type ImageElement as z };
787
+ export { type LinkElement 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 ChatElement as G, type ContainerProps as H, type ImageComponent as I, type DividerElement as J, type DividerProps as K, type LinkButtonComponent as L, type ModalComponent as M, type ExternalSelectElement as N, type ExternalSelectOptions as O, type ExternalSelectProps as P, type FieldElement as Q, type RadioSelectComponent as R, type SectionComponent as S, type TextComponent as T, type FieldProps as U, type FieldsElement as V, type ImageElement as W, type ImageProps as X, type LinkButtonElement as Y, type LinkButtonOptions as Z, type LinkButtonProps as _, type CardChild as a, type ModalChild as a0, type ModalElement as a1, type ModalOptions as a2, type ModalProps as a3, type RadioSelectElement as a4, type RadioSelectOptions as a5, type SectionElement as a6, type SelectElement as a7, type SelectOptionElement as a8, type SelectOptionProps as a9, type SelectOptions as aa, type SelectProps as ab, type TableAlignment as ac, type TableElement as ad, type TableOptions as ae, type TextElement as af, type TextInputElement as ag, type TextInputOptions as ah, type TextInputProps as ai, type TextProps as aj, type TextStyle as ak, Fragment as al, JSX as am, type TableComponent as an, type TableProps as ao, isCardLinkProps as ap, jsx as aq, jsxDEV as ar, jsxs as as, type CardComponent as b, type CardLinkComponent as c, type FieldsComponent as d, type SelectComponent as e, type SelectOptionComponent as f, Table as g, type TextInputComponent as h, cardChildToFallbackText as i, fromReactElement as j, fromReactModalElement as k, isCardElement as l, isJSX as m, isModalElement as n, toModalElement as o, type ActionsElement as p, type ButtonElement as q, type ButtonOptions as r, type ButtonProps as s, toCardElement as t, type ButtonStyle as u, type CardJSXElement as v, type CardJSXProps as w, type CardLinkProps as x, type CardOptions as y, type CardProps as z };
@@ -1 +1 @@
1
- export { A as ActionsComponent, B as ButtonComponent, d as ButtonProps, f as CardComponent, h as CardJSXElement, i as CardJSXProps, j as CardLinkComponent, k as CardLinkProps, m as CardProps, n as ChatElement, o as ContainerProps, D as DividerComponent, q as DividerProps, E as ExternalSelectComponent, t as ExternalSelectProps, F as FieldComponent, v as FieldProps, w as FieldsComponent, y as Fragment, I as ImageComponent, G as ImageProps, J as JSX, L as LinkButtonComponent, M as LinkButtonProps, P as ModalComponent, S as ModalProps, T as RadioSelectComponent, W as SectionComponent, Y as SelectComponent, _ as SelectOptionComponent, a0 as SelectOptionProps, a2 as SelectProps, a5 as TableComponent, a8 as TableProps, a9 as TextComponent, ab as TextInputComponent, ae as TextInputProps, af as TextProps, al as isCardLinkProps, am as isJSX, ao as jsx, ap as jsxDEV, aq as jsxs, ar as toCardElement, as as toModalElement } from './jsx-runtime-CzthIo1o.js';
1
+ export { A as ActionsComponent, B as ButtonComponent, s as ButtonProps, b as CardComponent, v as CardJSXElement, w as CardJSXProps, c as CardLinkComponent, x as CardLinkProps, z as CardProps, G as ChatElement, H as ContainerProps, D as DividerComponent, K as DividerProps, E as ExternalSelectComponent, P as ExternalSelectProps, F as FieldComponent, U as FieldProps, d as FieldsComponent, al as Fragment, I as ImageComponent, X as ImageProps, am as JSX, L as LinkButtonComponent, _ as LinkButtonProps, M as ModalComponent, a3 as ModalProps, R as RadioSelectComponent, S as SectionComponent, e as SelectComponent, f as SelectOptionComponent, a9 as SelectOptionProps, ab as SelectProps, an as TableComponent, ao as TableProps, T as TextComponent, h as TextInputComponent, ai as TextInputProps, aj as TextProps, ap as isCardLinkProps, m as isJSX, aq as jsx, ar as jsxDEV, as as jsxs, t as toCardElement, o as toModalElement } from './jsx-runtime-LBOLBP4o.js';
@@ -1,6 +1,177 @@
1
1
  import { WORKFLOW_SERIALIZE, WORKFLOW_DESERIALIZE } from '@workflow/serde';
2
2
  import { Root } from 'mdast';
3
- import { n as ChatElement, g as CardElement, Q as ModalElement, $ as SelectOptionElement } from './jsx-runtime-CzthIo1o.js';
3
+ import { G as ChatElement, C as CardElement, a1 as ModalElement, a8 as SelectOptionElement } from './jsx-runtime-LBOLBP4o.js';
4
+
5
+ /**
6
+ * Message class with serialization support for workflow engines.
7
+ */
8
+
9
+ /**
10
+ * Input data for creating a Message instance.
11
+ * Use this interface when constructing Message objects.
12
+ */
13
+ interface MessageData<TRawMessage = unknown> {
14
+ /** Attachments */
15
+ attachments: Attachment[];
16
+ /** Message author */
17
+ author: Author;
18
+ /** Structured formatting as an AST (mdast Root) */
19
+ formatted: FormattedContent;
20
+ /** Unique message ID */
21
+ id: string;
22
+ /** Whether the bot is @-mentioned in this message */
23
+ isMention?: boolean;
24
+ /** Links found in the message */
25
+ links?: LinkPreview[];
26
+ /** Message metadata */
27
+ metadata: MessageMetadata;
28
+ /** Platform-specific raw payload (escape hatch) */
29
+ raw: TRawMessage;
30
+ /** Plain text content (all formatting stripped) */
31
+ text: string;
32
+ /** Thread this message belongs to */
33
+ threadId: string;
34
+ }
35
+ /**
36
+ * Serialized message data for passing to external systems (e.g., workflow engines).
37
+ * Dates are converted to ISO strings, and non-serializable fields are omitted.
38
+ */
39
+ interface SerializedMessage {
40
+ _type: "chat:Message";
41
+ attachments: Array<{
42
+ type: "image" | "file" | "video" | "audio";
43
+ url?: string;
44
+ name?: string;
45
+ mimeType?: string;
46
+ size?: number;
47
+ width?: number;
48
+ height?: number;
49
+ fetchMetadata?: Record<string, string>;
50
+ }>;
51
+ author: {
52
+ userId: string;
53
+ userName: string;
54
+ fullName: string;
55
+ isBot: boolean | "unknown";
56
+ isMe: boolean;
57
+ };
58
+ formatted: Root;
59
+ id: string;
60
+ isMention?: boolean;
61
+ links?: Array<{
62
+ url: string;
63
+ title?: string;
64
+ description?: string;
65
+ imageUrl?: string;
66
+ siteName?: string;
67
+ }>;
68
+ metadata: {
69
+ dateSent: string;
70
+ edited: boolean;
71
+ editedAt?: string;
72
+ };
73
+ raw: unknown;
74
+ text: string;
75
+ threadId: string;
76
+ }
77
+ /**
78
+ * A chat message with serialization support for workflow engines.
79
+ *
80
+ * @example
81
+ * ```typescript
82
+ * // Create a message
83
+ * const message = new Message({
84
+ * id: "msg-1",
85
+ * threadId: "slack:C123:1234.5678",
86
+ * text: "Hello world",
87
+ * formatted: parseMarkdown("Hello world"),
88
+ * raw: {},
89
+ * author: { userId: "U123", userName: "user", fullName: "User", isBot: false, isMe: false },
90
+ * metadata: { dateSent: new Date(), edited: false },
91
+ * attachments: [],
92
+ * });
93
+ *
94
+ * // Serialize for workflow
95
+ * const serialized = message.toJSON();
96
+ * ```
97
+ */
98
+ declare class Message<TRawMessage = unknown> {
99
+ /** Unique message ID */
100
+ readonly id: string;
101
+ /** Thread this message belongs to */
102
+ readonly threadId: string;
103
+ /** Plain text content (all formatting stripped) */
104
+ text: string;
105
+ /**
106
+ * Structured formatting as an AST (mdast Root).
107
+ * This is the canonical representation - use this for processing.
108
+ * Use `stringifyMarkdown(message.formatted)` to get markdown string.
109
+ */
110
+ formatted: FormattedContent;
111
+ /** Platform-specific raw payload (escape hatch) */
112
+ raw: TRawMessage;
113
+ /** Message author */
114
+ author: Author;
115
+ /** Message metadata */
116
+ metadata: MessageMetadata;
117
+ /** Attachments */
118
+ attachments: Attachment[];
119
+ /**
120
+ * Whether the bot is @-mentioned in this message.
121
+ *
122
+ * This is set by the Chat SDK before passing the message to handlers.
123
+ * It checks for `@username` in the message text using the adapter's
124
+ * configured `userName` and optional `botUserId`.
125
+ *
126
+ * @example
127
+ * ```typescript
128
+ * chat.onSubscribedMessage(async (thread, message) => {
129
+ * if (message.isMention) {
130
+ * await thread.post("You mentioned me!");
131
+ * }
132
+ * });
133
+ * ```
134
+ */
135
+ isMention?: boolean;
136
+ /**
137
+ * Cross-platform user key for this message's author.
138
+ *
139
+ * Set by the Chat SDK before passing the message to handlers, when
140
+ * `ChatConfig.identity` is configured. `undefined` if no resolver is
141
+ * configured; `undefined` (i.e. absent) when the resolver returned null.
142
+ *
143
+ * Used by the Transcripts API to look up / append per-user transcripts.
144
+ */
145
+ userKey?: string;
146
+ /** Links found in the message */
147
+ links: LinkPreview[];
148
+ private _subjectPromise?;
149
+ get subject(): Promise<MessageSubject | null>;
150
+ constructor(data: MessageData<TRawMessage>);
151
+ /**
152
+ * Serialize the message to a plain JSON object.
153
+ * Use this to pass message data to external systems like workflow engines.
154
+ *
155
+ * Note: Attachment `data` (Buffer) and `fetchData` (function) are omitted
156
+ * as they're not serializable.
157
+ */
158
+ toJSON(): SerializedMessage;
159
+ /**
160
+ * Reconstruct a Message from serialized JSON data.
161
+ * Converts ISO date strings back to Date objects.
162
+ */
163
+ static fromJSON<TRawMessage = unknown>(json: SerializedMessage): Message<TRawMessage>;
164
+ /**
165
+ * Serialize a Message instance for @workflow/serde.
166
+ * This static method is automatically called by workflow serialization.
167
+ */
168
+ static [WORKFLOW_SERIALIZE](instance: Message): SerializedMessage;
169
+ /**
170
+ * Deserialize a Message from @workflow/serde.
171
+ * This static method is automatically called by workflow deserialization.
172
+ */
173
+ static [WORKFLOW_DESERIALIZE](data: SerializedMessage): Message;
174
+ }
4
175
 
5
176
  interface ThreadHistoryConfig {
6
177
  /** Maximum messages to keep per thread (default: 100) */
@@ -2298,259 +2469,6 @@ interface TranscriptsApi {
2298
2469
  list(query: ListQuery): Promise<TranscriptEntry[]>;
2299
2470
  }
2300
2471
 
2301
- /**
2302
- * Message class with serialization support for workflow engines.
2303
- */
2304
-
2305
- /**
2306
- * Input data for creating a Message instance.
2307
- * Use this interface when constructing Message objects.
2308
- */
2309
- interface MessageData<TRawMessage = unknown> {
2310
- /** Attachments */
2311
- attachments: Attachment[];
2312
- /** Message author */
2313
- author: Author;
2314
- /** Structured formatting as an AST (mdast Root) */
2315
- formatted: FormattedContent;
2316
- /** Unique message ID */
2317
- id: string;
2318
- /** Whether the bot is @-mentioned in this message */
2319
- isMention?: boolean;
2320
- /** Links found in the message */
2321
- links?: LinkPreview[];
2322
- /** Message metadata */
2323
- metadata: MessageMetadata;
2324
- /** Platform-specific raw payload (escape hatch) */
2325
- raw: TRawMessage;
2326
- /** Plain text content (all formatting stripped) */
2327
- text: string;
2328
- /** Thread this message belongs to */
2329
- threadId: string;
2330
- }
2331
- /**
2332
- * Serialized message data for passing to external systems (e.g., workflow engines).
2333
- * Dates are converted to ISO strings, and non-serializable fields are omitted.
2334
- */
2335
- interface SerializedMessage {
2336
- _type: "chat:Message";
2337
- attachments: Array<{
2338
- type: "image" | "file" | "video" | "audio";
2339
- url?: string;
2340
- name?: string;
2341
- mimeType?: string;
2342
- size?: number;
2343
- width?: number;
2344
- height?: number;
2345
- fetchMetadata?: Record<string, string>;
2346
- }>;
2347
- author: {
2348
- userId: string;
2349
- userName: string;
2350
- fullName: string;
2351
- isBot: boolean | "unknown";
2352
- isMe: boolean;
2353
- };
2354
- formatted: Root;
2355
- id: string;
2356
- isMention?: boolean;
2357
- links?: Array<{
2358
- url: string;
2359
- title?: string;
2360
- description?: string;
2361
- imageUrl?: string;
2362
- siteName?: string;
2363
- }>;
2364
- metadata: {
2365
- dateSent: string;
2366
- edited: boolean;
2367
- editedAt?: string;
2368
- };
2369
- raw: unknown;
2370
- text: string;
2371
- threadId: string;
2372
- }
2373
- /**
2374
- * A chat message with serialization support for workflow engines.
2375
- *
2376
- * @example
2377
- * ```typescript
2378
- * // Create a message
2379
- * const message = new Message({
2380
- * id: "msg-1",
2381
- * threadId: "slack:C123:1234.5678",
2382
- * text: "Hello world",
2383
- * formatted: parseMarkdown("Hello world"),
2384
- * raw: {},
2385
- * author: { userId: "U123", userName: "user", fullName: "User", isBot: false, isMe: false },
2386
- * metadata: { dateSent: new Date(), edited: false },
2387
- * attachments: [],
2388
- * });
2389
- *
2390
- * // Serialize for workflow
2391
- * const serialized = message.toJSON();
2392
- * ```
2393
- */
2394
- declare class Message<TRawMessage = unknown> {
2395
- /** Unique message ID */
2396
- readonly id: string;
2397
- /** Thread this message belongs to */
2398
- readonly threadId: string;
2399
- /** Plain text content (all formatting stripped) */
2400
- text: string;
2401
- /**
2402
- * Structured formatting as an AST (mdast Root).
2403
- * This is the canonical representation - use this for processing.
2404
- * Use `stringifyMarkdown(message.formatted)` to get markdown string.
2405
- */
2406
- formatted: FormattedContent;
2407
- /** Platform-specific raw payload (escape hatch) */
2408
- raw: TRawMessage;
2409
- /** Message author */
2410
- author: Author;
2411
- /** Message metadata */
2412
- metadata: MessageMetadata;
2413
- /** Attachments */
2414
- attachments: Attachment[];
2415
- /**
2416
- * Whether the bot is @-mentioned in this message.
2417
- *
2418
- * This is set by the Chat SDK before passing the message to handlers.
2419
- * It checks for `@username` in the message text using the adapter's
2420
- * configured `userName` and optional `botUserId`.
2421
- *
2422
- * @example
2423
- * ```typescript
2424
- * chat.onSubscribedMessage(async (thread, message) => {
2425
- * if (message.isMention) {
2426
- * await thread.post("You mentioned me!");
2427
- * }
2428
- * });
2429
- * ```
2430
- */
2431
- isMention?: boolean;
2432
- /**
2433
- * Cross-platform user key for this message's author.
2434
- *
2435
- * Set by the Chat SDK before passing the message to handlers, when
2436
- * `ChatConfig.identity` is configured. `undefined` if no resolver is
2437
- * configured; `undefined` (i.e. absent) when the resolver returned null.
2438
- *
2439
- * Used by the Transcripts API to look up / append per-user transcripts.
2440
- */
2441
- userKey?: string;
2442
- /** Links found in the message */
2443
- links: LinkPreview[];
2444
- private _subjectPromise?;
2445
- get subject(): Promise<MessageSubject | null>;
2446
- constructor(data: MessageData<TRawMessage>);
2447
- /**
2448
- * Serialize the message to a plain JSON object.
2449
- * Use this to pass message data to external systems like workflow engines.
2450
- *
2451
- * Note: Attachment `data` (Buffer) and `fetchData` (function) are omitted
2452
- * as they're not serializable.
2453
- */
2454
- toJSON(): SerializedMessage;
2455
- /**
2456
- * Reconstruct a Message from serialized JSON data.
2457
- * Converts ISO date strings back to Date objects.
2458
- */
2459
- static fromJSON<TRawMessage = unknown>(json: SerializedMessage): Message<TRawMessage>;
2460
- /**
2461
- * Serialize a Message instance for @workflow/serde.
2462
- * This static method is automatically called by workflow serialization.
2463
- */
2464
- static [WORKFLOW_SERIALIZE](instance: Message): SerializedMessage;
2465
- /**
2466
- * Deserialize a Message from @workflow/serde.
2467
- * This static method is automatically called by workflow deserialization.
2468
- */
2469
- static [WORKFLOW_DESERIALIZE](data: SerializedMessage): Message;
2470
- }
2471
-
2472
- /**
2473
- * Content part types structurally identical to AI SDK's TextPart, ImagePart,
2474
- * FilePart so that AiMessage[] is directly assignable to ModelMessage[].
2475
- * @see https://ai-sdk.dev/docs/reference/ai-sdk-core/model-message
2476
- */
2477
- /** Matches AI SDK's DataContent */
2478
- type DataContent = string | Uint8Array | ArrayBuffer | Buffer;
2479
- interface AiTextPart {
2480
- text: string;
2481
- type: "text";
2482
- }
2483
- interface AiImagePart {
2484
- image: DataContent | URL;
2485
- mediaType?: string;
2486
- type: "image";
2487
- }
2488
- interface AiFilePart {
2489
- data: DataContent | URL;
2490
- filename?: string;
2491
- mediaType: string;
2492
- type: "file";
2493
- }
2494
- type AiMessagePart = AiTextPart | AiImagePart | AiFilePart;
2495
- /**
2496
- * A message formatted for AI SDK consumption.
2497
- *
2498
- * This is a discriminated union matching AI SDK's ModelMessage type:
2499
- * - User messages can have text, image, and file parts
2500
- * - Assistant messages have string content only
2501
- */
2502
- type AiMessage = AiUserMessage | AiAssistantMessage;
2503
- interface AiUserMessage {
2504
- content: string | AiMessagePart[];
2505
- role: "user";
2506
- }
2507
- interface AiAssistantMessage {
2508
- content: string;
2509
- role: "assistant";
2510
- }
2511
- /**
2512
- * Options for converting messages to AI SDK format.
2513
- */
2514
- interface ToAiMessagesOptions {
2515
- /** When true, prefixes user messages with "[username]: " for multi-user context */
2516
- includeNames?: boolean;
2517
- /**
2518
- * Called when an attachment type is not supported (video, audio).
2519
- * Defaults to `console.warn`.
2520
- */
2521
- onUnsupportedAttachment?: (attachment: Attachment, message: Message) => void;
2522
- /**
2523
- * Called for each message after default processing (text, links, attachments).
2524
- * Return the message (modified or as-is) to include it, or `null` to skip it.
2525
- *
2526
- * @param aiMessage - The processed AI message
2527
- * @param source - The original chat Message
2528
- * @returns The message to include, or null to skip
2529
- */
2530
- transformMessage?: (aiMessage: AiMessage, source: Message) => AiMessage | null | Promise<AiMessage | null>;
2531
- }
2532
- /**
2533
- * Convert chat SDK messages to AI SDK conversation format.
2534
- *
2535
- * - Filters out messages with empty/whitespace-only text
2536
- * - Maps `author.isMe === true` to `"assistant"`, otherwise `"user"`
2537
- * - Uses `message.text` for content
2538
- * - Appends link metadata when available
2539
- * - Includes image attachments and text files as `FilePart`
2540
- * - Uses `fetchData()` when available to include attachment data inline (base64)
2541
- * - Warns on unsupported attachment types (video, audio)
2542
- *
2543
- * Works with `FetchResult.messages`, `thread.recentMessages`, or collected iterables.
2544
- *
2545
- * @example
2546
- * ```typescript
2547
- * const result = await thread.adapter.fetchMessages(thread.id, { limit: 20 });
2548
- * const history = await toAiMessages(result.messages);
2549
- * const response = await agent.stream({ prompt: history });
2550
- * ```
2551
- */
2552
- declare function toAiMessages(messages: Message[], options?: ToAiMessagesOptions): Promise<AiMessage[]>;
2553
-
2554
2472
  /** Filter can be EmojiValue objects, emoji names, or raw emoji formats */
2555
2473
  type EmojiFilter = EmojiValue | string;
2556
2474
  /**
@@ -3156,4 +3074,86 @@ declare class Chat<TAdapters extends Record<string, Adapter> = Record<string, Ad
3156
3074
  private runHandlers;
3157
3075
  }
3158
3076
 
3159
- export { type LockScope as $, type ActionEvent as A, type ConcurrencyConfig as B, type Channel as C, type ConcurrencyStrategy as D, ConsoleLogger as E, type CountQuery as F, type CustomEmojiMap as G, type DeleteTarget as H, type DirectMessageHandler as I, type DurationString as J, type Emoji as K, type EmojiFormats as L, type EmojiMapConfig as M, type EmojiValue as N, type EphemeralMessage as O, type FetchDirection as P, type FetchOptions as Q, type FetchResult as R, type FileUpload as S, type FormattedContent as T, type IdentityContext as U, type IdentityResolver as V, type LinkPreview as W, type ListQuery as X, type ListThreadsOptions as Y, type ListThreadsResult as Z, type Lock as _, type ActionHandler as a, type TranscriptRole as a$, type LockScopeContext as a0, type LogLevel as a1, type Logger as a2, type MarkdownTextChunk as a3, type MemberJoinedChannelEvent as a4, type MemberJoinedChannelHandler as a5, type MentionHandler as a6, Message as a7, type MessageContext as a8, type MessageData as a9, type PostableRaw as aA, type QueueEntry as aB, type RawMessage as aC, type ReactionEvent as aD, type ReactionHandler as aE, type ScheduledMessage as aF, type SentMessage as aG, type SerializedChannel as aH, type SerializedMessage as aI, type SerializedThread as aJ, type SlashCommandEvent as aK, type SlashCommandHandler as aL, type StateAdapter as aM, type StreamChunk as aN, type StreamEvent as aO, type StreamOptions as aP, type SubscribedMessageHandler as aQ, THREAD_STATE_TTL_MS as aR, type TaskUpdateChunk as aS, type Thread as aT, ThreadHistoryCache as aU, type ThreadHistoryConfig as aV, ThreadImpl as aW, type ThreadInfo as aX, type ThreadSummary as aY, type ToAiMessagesOptions as aZ, type TranscriptEntry as a_, type MessageHandler as aa, type MessageMetadata as ab, type MessageSubject as ac, type ModalClearResponse as ad, type ModalCloseEvent as ae, type ModalCloseHandler as af, type ModalCloseResponse as ag, type ModalErrorsResponse as ah, type ModalPushResponse as ai, type ModalResponse as aj, type ModalSubmitEvent as ak, type ModalSubmitHandler as al, type ModalUpdateResponse as am, type OptionsLoadEvent as an, type OptionsLoadGroup as ao, type OptionsLoadHandler as ap, type OptionsLoadResult as aq, type PlanUpdateChunk as ar, type PostEphemeralOptions as as, type Postable as at, type PostableAst as au, type PostableCard as av, type PostableMarkdown as aw, type PostableMessage as ax, type PostableObject as ay, type PostableObjectContext as az, type Adapter as b, type TranscriptsApi as b0, type TranscriptsConfig as b1, type UserInfo as b2, type WebhookOptions as b3, type WellKnownEmoji as b4, deriveChannelId as b5, isPostableObject as b6, toAiMessages as b7, type AdapterPostableMessage as c, type AiAssistantMessage as d, type AiFilePart as e, type AiImagePart as f, type AiMessage as g, type AiMessagePart as h, type AiTextPart as i, type AiUserMessage as j, type AppHomeOpenedEvent as k, type AppHomeOpenedHandler as l, type AppendInput as m, type AppendOptions as n, type AssistantContextChangedEvent as o, type AssistantContextChangedHandler as p, type AssistantThreadStartedEvent as q, type AssistantThreadStartedHandler as r, type Attachment as s, type Author as t, ChannelImpl as u, type ChannelInfo as v, type ChannelVisibility as w, Chat as x, type ChatConfig as y, type ChatInstance as z };
3077
+ /**
3078
+ * Content part types structurally identical to AI SDK's TextPart, ImagePart,
3079
+ * FilePart so that AiMessage[] is directly assignable to ModelMessage[].
3080
+ * @see https://ai-sdk.dev/docs/reference/ai-sdk-core/model-message
3081
+ */
3082
+ /** Matches AI SDK's DataContent */
3083
+ type DataContent = string | Uint8Array | ArrayBuffer | Buffer;
3084
+ interface AiTextPart {
3085
+ text: string;
3086
+ type: "text";
3087
+ }
3088
+ interface AiImagePart {
3089
+ image: DataContent | URL;
3090
+ mediaType?: string;
3091
+ type: "image";
3092
+ }
3093
+ interface AiFilePart {
3094
+ data: DataContent | URL;
3095
+ filename?: string;
3096
+ mediaType: string;
3097
+ type: "file";
3098
+ }
3099
+ type AiMessagePart = AiTextPart | AiImagePart | AiFilePart;
3100
+ /**
3101
+ * A message formatted for AI SDK consumption.
3102
+ *
3103
+ * This is a discriminated union matching AI SDK's ModelMessage type:
3104
+ * - User messages can have text, image, and file parts
3105
+ * - Assistant messages have string content only
3106
+ */
3107
+ type AiMessage = AiUserMessage | AiAssistantMessage;
3108
+ interface AiUserMessage {
3109
+ content: string | AiMessagePart[];
3110
+ role: "user";
3111
+ }
3112
+ interface AiAssistantMessage {
3113
+ content: string;
3114
+ role: "assistant";
3115
+ }
3116
+ /**
3117
+ * Options for converting messages to AI SDK format.
3118
+ */
3119
+ interface ToAiMessagesOptions {
3120
+ /** When true, prefixes user messages with "[username]: " for multi-user context */
3121
+ includeNames?: boolean;
3122
+ /**
3123
+ * Called when an attachment type is not supported (video, audio).
3124
+ * Defaults to `console.warn`.
3125
+ */
3126
+ onUnsupportedAttachment?: (attachment: Attachment, message: Message) => void;
3127
+ /**
3128
+ * Called for each message after default processing (text, links, attachments).
3129
+ * Return the message (modified or as-is) to include it, or `null` to skip it.
3130
+ *
3131
+ * @param aiMessage - The processed AI message
3132
+ * @param source - The original chat Message
3133
+ * @returns The message to include, or null to skip
3134
+ */
3135
+ transformMessage?: (aiMessage: AiMessage, source: Message) => AiMessage | null | Promise<AiMessage | null>;
3136
+ }
3137
+ /**
3138
+ * Convert chat SDK messages to AI SDK conversation format.
3139
+ *
3140
+ * - Filters out messages with empty/whitespace-only text
3141
+ * - Maps `author.isMe === true` to `"assistant"`, otherwise `"user"`
3142
+ * - Uses `message.text` for content
3143
+ * - Appends link metadata when available
3144
+ * - Includes image attachments and text files as `FilePart`
3145
+ * - Uses `fetchData()` when available to include attachment data inline (base64)
3146
+ * - Warns on unsupported attachment types (video, audio)
3147
+ *
3148
+ * Works with `FetchResult.messages`, `thread.recentMessages`, or collected iterables.
3149
+ *
3150
+ * @example
3151
+ * ```typescript
3152
+ * const result = await thread.adapter.fetchMessages(thread.id, { limit: 20 });
3153
+ * const history = await toAiMessages(result.messages);
3154
+ * const response = await agent.stream({ prompt: history });
3155
+ * ```
3156
+ */
3157
+ declare function toAiMessages(messages: Message[], options?: ToAiMessagesOptions): Promise<AiMessage[]>;
3158
+
3159
+ export { type FileUpload as $, type AiAssistantMessage as A, type AssistantThreadStartedHandler as B, Chat as C, type Attachment as D, type EmojiFormats as E, type Author as F, type Channel as G, ChannelImpl as H, type ChannelInfo as I, type ChatConfig as J, type ChatInstance as K, type ConcurrencyConfig as L, type ConcurrencyStrategy as M, ConsoleLogger as N, type CountQuery as O, type PostableObject as P, type DeleteTarget as Q, type DirectMessageHandler as R, type StreamChunk as S, type ToAiMessagesOptions as T, type DurationString as U, type Emoji as V, type WellKnownEmoji as W, type EphemeralMessage as X, type FetchDirection as Y, type FetchOptions as Z, type FetchResult as _, type ChannelVisibility as a, type ThreadSummary as a$, type FormattedContent as a0, type IdentityContext as a1, type IdentityResolver as a2, type LinkPreview as a3, type ListQuery as a4, type ListThreadsOptions as a5, type ListThreadsResult as a6, type Lock as a7, type LockScope as a8, type LockScopeContext as a9, type PlanUpdateChunk as aA, type PostEphemeralOptions as aB, type Postable as aC, type PostableAst as aD, type PostableCard as aE, type PostableMarkdown as aF, type PostableMessage as aG, type PostableRaw as aH, type QueueEntry as aI, type RawMessage as aJ, type ReactionEvent as aK, type ReactionHandler as aL, type ScheduledMessage as aM, type SentMessage as aN, type SerializedChannel as aO, type SerializedMessage as aP, type SerializedThread as aQ, type SlashCommandEvent as aR, type SlashCommandHandler as aS, type StateAdapter as aT, type StreamOptions as aU, type SubscribedMessageHandler as aV, THREAD_STATE_TTL_MS as aW, type TaskUpdateChunk as aX, type Thread as aY, ThreadImpl as aZ, type ThreadInfo as a_, type LogLevel as aa, type Logger as ab, type MarkdownTextChunk as ac, type MemberJoinedChannelEvent as ad, type MemberJoinedChannelHandler as ae, type MentionHandler as af, Message as ag, type MessageContext as ah, type MessageData as ai, type MessageHandler as aj, type MessageMetadata as ak, type MessageSubject as al, type ModalClearResponse as am, type ModalCloseEvent as an, type ModalCloseHandler as ao, type ModalCloseResponse as ap, type ModalErrorsResponse as aq, type ModalPushResponse as ar, type ModalResponse as as, type ModalSubmitEvent as at, type ModalSubmitHandler as au, type ModalUpdateResponse as av, type OptionsLoadEvent as aw, type OptionsLoadGroup as ax, type OptionsLoadHandler as ay, type OptionsLoadResult as az, type AiFilePart as b, type TranscriptEntry as b0, type TranscriptRole as b1, type TranscriptsApi as b2, type TranscriptsConfig as b3, type UserInfo as b4, type WebhookOptions as b5, deriveChannelId as b6, isPostableObject as b7, type AiImagePart as c, type AiMessage as d, type AiMessagePart as e, type AiTextPart as f, type AiUserMessage as g, type Adapter as h, type PostableObjectContext as i, ThreadHistoryCache as j, type ThreadHistoryConfig as k, type StreamEvent as l, type EmojiMapConfig as m, type EmojiValue as n, type CustomEmojiMap as o, type AdapterPostableMessage as p, type ActionEvent as q, type ActionHandler as r, type AppHomeOpenedEvent as s, toAiMessages as t, type AppHomeOpenedHandler as u, type AppendInput as v, type AppendOptions as w, type AssistantContextChangedEvent as x, type AssistantContextChangedHandler as y, type AssistantThreadStartedEvent as z };
package/docs/api/chat.mdx CHANGED
@@ -27,9 +27,9 @@ const bot = new Chat(config);
27
27
  type: 'Record<string, Adapter>',
28
28
  },
29
29
  dedupeTtlMs: {
30
- description: 'TTL for message deduplication entries in milliseconds. Increase if webhook cold starts cause platform retries after the default window.',
30
+ description: 'TTL for message deduplication entries in milliseconds. The default outlives the last Slack event retry (~5 minutes after original delivery); increase it if your platform redelivers later than that.',
31
31
  type: 'number',
32
- default: '300000',
32
+ default: '600000',
33
33
  },
34
34
  state: {
35
35
  description: 'State adapter for subscriptions, locking, and caching.',
@@ -98,6 +98,20 @@ When the [Web adapter](/adapters/official/web) is selected, the CLI also creates
98
98
 
99
99
  When the [Discord adapter](/adapters/official/discord) is selected, the CLI also creates a Gateway listener at `/api/discord/gateway` and a `vercel.json` cron that calls it. Discord delivers slash commands and button clicks to the webhook route, but regular messages and reactions only arrive over the Gateway WebSocket, so the cron keeps that connection alive and forwards events to `/api/webhooks/discord`. Set a `CRON_SECRET` environment variable to authenticate the cron requests. The generated serverless Gateway cron requires [Vercel Pro or Enterprise](https://vercel.com/docs/cron-jobs/usage-and-pricing) because it runs every nine minutes.
100
100
 
101
+ ## Vercel Connect
102
+
103
+ [Vercel Connect](/docs/vercel-connect) lets the Slack, GitHub, and Linear adapters authenticate with a connector instead of stored provider secrets. Pass `--connect` — or choose **Vercel Connect** at the interactive auth-mode prompt — to scaffold Connect wiring for any selected Slack, GitHub, or Linear adapter:
104
+
105
+ ```bash
106
+ npm create chat-sdk@latest -- my-bot --adapter slack --connect -y
107
+ ```
108
+
109
+ The generated `src/lib/bot.ts` spreads the matching helper from `@vercel/connect/chat` into the adapter factory, `@vercel/connect` is added to dependencies, and `.env.example` lists each connector UID (for example `SLACK_CONNECTOR`) in place of native secrets.
110
+
111
+ <Callout type="info">
112
+ Vercel Connect provides `VERCEL_OIDC_TOKEN` at runtime. For local development, run `vercel link` then `vercel env pull` to populate it. Connect forwards inbound webhooks only to deployed URLs, so test webhook delivery against a Vercel deployment (such as a preview) rather than localhost.
113
+ </Callout>
114
+
101
115
  ## Reference
102
116
 
103
117
  | Option | Description |
@@ -106,6 +120,7 @@ When the [Discord adapter](/adapters/official/discord) is selected, the CLI also
106
120
  | `-d, --description <text>` | Project description. |
107
121
  | `--adapter <values...>` | Platform or state adapters to include. |
108
122
  | `--vendor` | List only vendor-official adapters in the interactive prompt. |
123
+ | `--connect` | Authenticate Slack, GitHub, and Linear adapters with Vercel Connect. |
109
124
  | `--pm <manager>` | Package manager to use: `npm`, `yarn`, `pnpm`, or `bun`. |
110
125
  | `-y, --yes` | Skip prompts and accept defaults. |
111
126
  | `--interactive` | Always prompt, even when a coding agent environment is detected. |
package/docs/meta.json CHANGED
@@ -9,6 +9,7 @@
9
9
  "threads-messages-channels",
10
10
  "handling-events",
11
11
  "posting-messages",
12
+ "vercel-connect",
12
13
  "error-handling",
13
14
  "testing",
14
15
  "---AI---",
package/docs/usage.mdx CHANGED
@@ -71,7 +71,7 @@ Your event handlers work identically across all registered adapters — the SDK
71
71
  | `adapters` | `Record<string, Adapter>` | *required* | Map of adapter name to adapter instance |
72
72
  | `state` | `StateAdapter` | *required* | State adapter for subscriptions and locking |
73
73
  | `logger` | `Logger \| LogLevel` | `"info"` | Logger instance or log level (`"debug"`, `"info"`, `"warn"`, `"error"`, `"silent"`) |
74
- | `dedupeTtlMs` | `number` | `300000` | TTL in ms for message deduplication (5 minutes) |
74
+ | `dedupeTtlMs` | `number` | `600000` | TTL in ms for message deduplication (10 minutes) |
75
75
  | `concurrency` | `"drop" \| "queue" \| "debounce" \| "burst" \| "concurrent" \| ConcurrencyConfig` | `"drop"` | Strategy for overlapping messages on the same thread |
76
76
  | `streamingUpdateIntervalMs` | `number` | `500` | Update interval in ms for post+edit streaming |
77
77
  | `fallbackStreamingPlaceholderText` | `string \| null` | `"..."` | Placeholder text while streaming starts. Set to `null` to skip |
@@ -0,0 +1,162 @@
1
+ ---
2
+ title: Vercel Connect
3
+ description: Authenticate Slack, GitHub, and Linear adapters with Vercel Connect — short-lived runtime tokens for outbound calls and OIDC-verified inbound webhooks, with no stored provider secrets.
4
+ type: overview
5
+ related:
6
+ - /docs/usage
7
+ - /adapters/official/slack
8
+ - /adapters/official/github
9
+ - /adapters/official/linear
10
+ ---
11
+
12
+ [Vercel Connect](https://vercel.com/docs/connect) lets your bot use a registered connector for adapter authentication instead of storing long-lived provider secrets. You register a connector once, link it to your project and environments, and your code requests scoped, short-lived tokens at runtime.
13
+
14
+ The `@vercel/connect/chat` subpath ships a helper per platform that you spread into the matching `create*Adapter` factory:
15
+
16
+ - `connectSlackAdapter`
17
+ - `connectGitHubAdapter`
18
+ - `connectLinearAdapter`
19
+
20
+ Each helper wires both directions of traffic:
21
+
22
+ - **Outbound** (your bot calls the provider API) — a function-form token field that resolves a fresh, short-lived token per call via `getToken`.
23
+ - **Inbound** (the provider calls your bot) — a `webhookVerifier` that validates the Vercel OIDC token Connect attaches to [trigger-forwarded](https://vercel.com/docs/connect/concepts/triggers) webhooks, replacing the provider's native signature check.
24
+
25
+ <Callout type="info">
26
+ Vercel Connect is in beta. Features and behavior, including available connectors and trigger forwarding, may change before general availability.
27
+ </Callout>
28
+
29
+ ## Install
30
+
31
+ ```bash
32
+ pnpm add @vercel/connect
33
+ ```
34
+
35
+ `@vercel/connect` reads the deployment's OIDC token automatically. For local development, run `vercel link` followed by `vercel env pull` to download a short-lived token into `.env.local`.
36
+
37
+ ## Adapter support
38
+
39
+ | Adapter | Helper | Outbound field |
40
+ |---------|--------|----------------|
41
+ | [Slack](/adapters/official/slack) | `connectSlackAdapter` | `botToken` |
42
+ | [GitHub](/adapters/official/github) | `connectGitHubAdapter` | `installationToken` |
43
+ | [Linear](/adapters/official/linear) | `connectLinearAdapter` | `accessToken` |
44
+
45
+ Each helper accepts `(connector, params?, options?)`, where `params` is the [`getToken`](https://vercel.com/docs/connect/ts-sdk-reference) parameters minus `subject` (pinned to `{ type: "app" }`), letting you pass through `installationId`, `scopes`, or `validityBufferMs`.
46
+
47
+ ## Set up a connector
48
+
49
+ 1. Create a connector for the provider in the [Vercel dashboard](https://vercel.com/d?to=%2F%5Bteam%5D%2F~%2Fconnect) or with the CLI, enabling trigger forwarding so inbound webhooks reach your project:
50
+
51
+ ```bash
52
+ vercel connect create slack --name acme-slack --triggers
53
+ ```
54
+
55
+ 2. Attach your project and register your Chat SDK webhook route (`/api/webhooks/{platform}`) as the trigger destination:
56
+
57
+ ```bash
58
+ vercel connect attach slack/acme-slack \
59
+ --project my-bot --environment production \
60
+ --triggers --trigger-path /api/webhooks/slack
61
+ ```
62
+
63
+ 3. Pull a development token locally (deployments get `VERCEL_OIDC_TOKEN` automatically):
64
+
65
+ ```bash
66
+ vercel link
67
+ vercel env pull
68
+ ```
69
+
70
+ ## Wire up the adapter
71
+
72
+ Spread the helper into the adapter factory. The webhook route is unchanged — Connect forwards verified events to the same handler.
73
+
74
+ ```typescript title="lib/bot.ts" lineNumbers
75
+ import { Chat } from "chat";
76
+ import { createSlackAdapter } from "@chat-adapter/slack";
77
+ import { createRedisState } from "@chat-adapter/state-redis";
78
+ import { connectSlackAdapter } from "@vercel/connect/chat";
79
+
80
+ export const bot = new Chat({
81
+ userName: "mybot",
82
+ adapters: {
83
+ slack: createSlackAdapter({
84
+ ...connectSlackAdapter("slack/acme-slack"),
85
+ }),
86
+ },
87
+ state: createRedisState(),
88
+ });
89
+ ```
90
+
91
+ Replace `slack/acme-slack` with your connector UID from the Connect dashboard or `vercel connect list`. Omit `signingSecret` / `SLACK_SIGNING_SECRET` when using the helper — the OIDC `webhookVerifier` is the freshness boundary.
92
+
93
+ ### GitHub
94
+
95
+ ```typescript title="lib/bot.ts" lineNumbers
96
+ import { createGitHubAdapter } from "@chat-adapter/github";
97
+ import { connectGitHubAdapter } from "@vercel/connect/chat";
98
+
99
+ createGitHubAdapter({
100
+ ...connectGitHubAdapter("github/acme-github"),
101
+ userName: "my-bot[bot]",
102
+ });
103
+ ```
104
+
105
+ `installationToken` is the installation access token a GitHub App would normally mint via its private-key JWT exchange — the adapter uses it directly and skips that exchange.
106
+
107
+ ### Linear
108
+
109
+ ```typescript title="lib/bot.ts" lineNumbers
110
+ import { createLinearAdapter } from "@chat-adapter/linear";
111
+ import { connectLinearAdapter } from "@vercel/connect/chat";
112
+
113
+ createLinearAdapter({
114
+ ...connectLinearAdapter("linear/acme-linear"),
115
+ mode: "agent-sessions",
116
+ });
117
+ ```
118
+
119
+ Use `mode: "agent-sessions"` for app-actor installs. For outbound calls outside webhook handling (cron jobs, workflows), wrap them in `withInstallation()` so a request-scoped client is bound:
120
+
121
+ ```typescript title="lib/jobs.ts" lineNumbers
122
+ await linear.withInstallation("org-id", async () => {
123
+ await linear.postMessage("linear:issue-id", "Hello from a background job");
124
+ });
125
+ ```
126
+
127
+ ## Custom webhook verification
128
+
129
+ Each helper attaches a default verifier that matches the deployment's project and environment automatically (`projectId` defaults to `VERCEL_PROJECT_ID`, `environment` to `VERCEL_TARGET_ENV` then `VERCEL_ENV`), so production, preview, and development each accept only their own tokens. Verification fails closed — if those values are absent, every request is rejected, and the issuer is pinned to `https://oidc.vercel.com`.
130
+
131
+ To add constraints (for example to accept multiple environments), build a verifier with `createConnectWebhookVerifier` and override the field:
132
+
133
+ ```typescript title="lib/bot.ts" lineNumbers
134
+ import {
135
+ connectSlackAdapter,
136
+ createConnectWebhookVerifier,
137
+ } from "@vercel/connect/chat";
138
+
139
+ createSlackAdapter({
140
+ ...connectSlackAdapter("slack/acme-slack"),
141
+ webhookVerifier: createConnectWebhookVerifier({
142
+ environment: ["production", "preview"],
143
+ }),
144
+ });
145
+ ```
146
+
147
+ <Callout type="warn">
148
+ Avoid hardcoding `environment: "production"` unless you only forward to production — it would reject preview and development deployments.
149
+ </Callout>
150
+
151
+ ## Notes and limitations
152
+
153
+ - **App-scoped tokens.** The helpers act as the application itself (`subject: { type: "app" }`). End-user OAuth is a separate concern.
154
+ - **Freshness and replay.** OIDC verification replaces each provider's native signature (and timestamp) check, so request freshness relies on the short-lived OIDC token's expiry rather than a signed timestamp, and there is no built-in delivery de-duplication. Keep your webhook handlers idempotent.
155
+ - **Socket Mode is incompatible.** Connect trigger forwarding is HTTP-only; it doesn't apply to the Slack adapter's Socket Mode.
156
+ - **Testing.** Connect forwards to deployed URLs, not `localhost` — test against a preview or development deployment.
157
+
158
+ ## Related resources
159
+
160
+ - [Vercel Connect overview](https://vercel.com/docs/connect)
161
+ - [Vercel Connect triggers](https://vercel.com/docs/connect/concepts/triggers)
162
+ - [`@vercel/connect` SDK reference](https://vercel.com/docs/connect/ts-sdk-reference)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chat",
3
- "version": "4.32.0",
3
+ "version": "4.33.0",
4
4
  "description": "Chat SDK — universal TypeScript toolkit for building multi-platform chat bots and AI agents on Slack, Teams, Google Chat, Discord, WhatsApp, and more",
5
5
  "type": "module",
6
6
  "engines": {