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.
Files changed (37) hide show
  1. package/dist/{chunk-OPV5U4WG.js → chunk-AN7MRAVW.js} +39 -0
  2. package/dist/index.d.ts +235 -6
  3. package/dist/index.js +353 -76
  4. package/dist/{jsx-runtime-DxATbnrP.d.ts → jsx-runtime-Co9uV6l7.d.ts} +39 -5
  5. package/dist/jsx-runtime.d.ts +1 -1
  6. package/dist/jsx-runtime.js +1 -1
  7. package/docs/adapters.mdx +30 -30
  8. package/docs/api/cards.mdx +5 -0
  9. package/docs/api/chat.mdx +95 -1
  10. package/docs/api/message.mdx +5 -1
  11. package/docs/api/modals.mdx +1 -1
  12. package/docs/api/thread.mdx +23 -1
  13. package/docs/cards.mdx +6 -0
  14. package/docs/contributing/publishing.mdx +33 -0
  15. package/docs/files.mdx +1 -0
  16. package/docs/getting-started.mdx +2 -12
  17. package/docs/meta.json +0 -2
  18. package/docs/modals.mdx +74 -2
  19. package/docs/state.mdx +1 -1
  20. package/docs/streaming.mdx +13 -5
  21. package/docs/threads-messages-channels.mdx +34 -0
  22. package/docs/usage.mdx +2 -2
  23. package/package.json +3 -2
  24. package/resources/guides/create-a-discord-support-bot-with-nuxt-and-redis.md +180 -0
  25. package/resources/guides/how-to-build-a-slack-bot-with-next-js-and-redis.md +134 -0
  26. package/resources/guides/how-to-build-an-ai-agent-for-slack-with-chat-sdk-and-ai-sdk.md +220 -0
  27. package/resources/guides/run-and-track-deploys-from-slack.md +270 -0
  28. package/resources/guides/ship-a-github-code-review-bot-with-hono-and-redis.md +147 -0
  29. package/resources/guides/triage-form-submissions-with-chat-sdk.md +178 -0
  30. package/resources/templates.json +19 -0
  31. package/docs/adapters/whatsapp.mdx +0 -222
  32. package/docs/guides/code-review-hono.mdx +0 -241
  33. package/docs/guides/discord-nuxt.mdx +0 -227
  34. package/docs/guides/durable-chat-sessions-nextjs.mdx +0 -331
  35. package/docs/guides/meta.json +0 -10
  36. package/docs/guides/scheduled-posts-neon.mdx +0 -447
  37. package/docs/guides/slack-nextjs.mdx +0 -234
@@ -582,6 +582,7 @@ function cardChildToFallbackText(child) {
582
582
  var VALID_MODAL_CHILD_TYPES = [
583
583
  "text_input",
584
584
  "select",
585
+ "external_select",
585
586
  "radio_select",
586
587
  "text",
587
588
  "fields"
@@ -640,6 +641,17 @@ function Select(options) {
640
641
  optional: options.optional
641
642
  };
642
643
  }
644
+ function ExternalSelect(options) {
645
+ return {
646
+ type: "external_select",
647
+ id: options.id,
648
+ initialOption: options.initialOption,
649
+ label: options.label,
650
+ placeholder: options.placeholder,
651
+ minQueryLength: options.minQueryLength,
652
+ optional: options.optional
653
+ };
654
+ }
643
655
  function SelectOption(options) {
644
656
  return {
645
657
  label: options.label,
@@ -675,6 +687,7 @@ var modalComponentMap = /* @__PURE__ */ new Map([
675
687
  [Modal, "Modal"],
676
688
  [TextInput, "TextInput"],
677
689
  [Select, "Select"],
690
+ [ExternalSelect, "ExternalSelect"],
678
691
  [RadioSelect, "RadioSelect"],
679
692
  [SelectOption, "SelectOption"]
680
693
  ]);
@@ -729,6 +742,15 @@ function fromReactModalElement(element) {
729
742
  initialOption: props.initialOption,
730
743
  optional: props.optional
731
744
  });
745
+ case "ExternalSelect":
746
+ return ExternalSelect({
747
+ id: props.id,
748
+ initialOption: props.initialOption,
749
+ label: props.label,
750
+ placeholder: props.placeholder,
751
+ minQueryLength: props.minQueryLength,
752
+ optional: props.optional
753
+ });
732
754
  case "RadioSelect":
733
755
  return RadioSelect({
734
756
  id: props.id,
@@ -823,6 +845,9 @@ function isTextInputProps(props) {
823
845
  function isSelectProps(props) {
824
846
  return "id" in props && "label" in props && !("value" in props);
825
847
  }
848
+ function isExternalSelectProps(props) {
849
+ return "id" in props && "label" in props && !("value" in props) && !("children" in props);
850
+ }
826
851
  function isSelectOptionProps(props) {
827
852
  return "label" in props && "value" in props && !("id" in props);
828
853
  }
@@ -939,6 +964,19 @@ function resolveJSXElement(element) {
939
964
  options: processedChildren
940
965
  });
941
966
  }
967
+ if (type === ExternalSelect) {
968
+ if (!isExternalSelectProps(props)) {
969
+ throw new Error("ExternalSelect requires 'id' and 'label' props");
970
+ }
971
+ return ExternalSelect({
972
+ id: props.id,
973
+ initialOption: props.initialOption,
974
+ label: props.label,
975
+ placeholder: props.placeholder,
976
+ minQueryLength: props.minQueryLength,
977
+ optional: props.optional
978
+ });
979
+ }
942
980
  if (type === RadioSelect) {
943
981
  if (!isSelectProps(props)) {
944
982
  throw new Error("RadioSelect requires 'id' and 'label' props");
@@ -1096,6 +1134,7 @@ export {
1096
1134
  Modal,
1097
1135
  TextInput,
1098
1136
  Select,
1137
+ ExternalSelect,
1099
1138
  SelectOption,
1100
1139
  RadioSelect,
1101
1140
  fromReactModalElement,
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { WORKFLOW_SERIALIZE, WORKFLOW_DESERIALIZE } from '@workflow/serde';
2
2
  import { Root, List, Content, Blockquote, Code, Emphasis, InlineCode, Delete, Link, ListItem, Paragraph, Strong, TableCell, Table as Table$1, TableRow, Text } from 'mdast';
3
- export { Blockquote, Code, Content, Delete, Emphasis, InlineCode, Link, List, ListItem, Table as MdastTable, Paragraph, Root, Strong, TableCell, TableRow, Text } from 'mdast';
4
- import { C as ChatElement, a as CardElement, M as ModalElement, b as CardChild, A as ActionsComponent, B as ButtonComponent, c as CardComponent, d as cardChildToFallbackText$1, e as CardLinkComponent, T as TextComponent, D as DividerComponent, F as FieldComponent, f as FieldsComponent, g as fromReactElement$1, I as ImageComponent, i as isCardElement$1, h as isJSX$1, L as LinkButtonComponent, S as SectionComponent, j as Table$2, t as toCardElement$1, k as toModalElement$1, l as fromReactModalElement$1, m as isModalElement$1, n as ModalComponent, R as RadioSelectComponent, o as SelectComponent, p as SelectOptionComponent, q as TextInputComponent } from './jsx-runtime-DxATbnrP.js';
5
- export { r as ActionsElement, s as ButtonElement, u as ButtonOptions, V as ButtonProps, v as ButtonStyle, W as CardJSXElement, X as CardJSXProps, Y as CardLinkProps, w as CardOptions, Z as CardProps, _ as ContainerProps, x as DividerElement, $ as DividerProps, y as FieldElement, a0 as FieldProps, z as FieldsElement, E as ImageElement, a1 as ImageProps, G as LinkButtonElement, H as LinkButtonOptions, a2 as LinkButtonProps, J as LinkElement, a8 as ModalChild, a9 as ModalOptions, a3 as ModalProps, aa as RadioSelectElement, ab as RadioSelectOptions, K as SectionElement, ac as SelectElement, ad as SelectOptionElement, a4 as SelectOptionProps, ae as SelectOptions, a5 as SelectProps, N as TableAlignment, O as TableElement, P as TableOptions, Q as TextElement, af as TextInputElement, ag as TextInputOptions, a6 as TextInputProps, a7 as TextProps, U as TextStyle } from './jsx-runtime-DxATbnrP.js';
3
+ export { Blockquote, Code, Content, Delete, Emphasis, InlineCode, Link, List, ListItem, Table as MdastTable, Nodes, Paragraph, Root, Strong, TableCell, TableRow, Text } from 'mdast';
4
+ import { C as ChatElement, a as CardElement, M as ModalElement, S as SelectOptionElement, b as CardChild, A as ActionsComponent, B as ButtonComponent, c as CardComponent, d as cardChildToFallbackText$1, e as CardLinkComponent, T as TextComponent, D as DividerComponent, F as FieldComponent, f as FieldsComponent, g as fromReactElement$1, I as ImageComponent, i as isCardElement$1, h as isJSX$1, L as LinkButtonComponent, j as SectionComponent, k as Table$2, t as toCardElement$1, l as toModalElement$1, m as fromReactModalElement$1, n as isModalElement$1, E as ExternalSelectComponent, o as ModalComponent, R as RadioSelectComponent, p as SelectComponent, q as SelectOptionComponent, r as TextInputComponent } from './jsx-runtime-Co9uV6l7.js';
5
+ export { s as ActionsElement, u as ButtonElement, v as ButtonOptions, X as ButtonProps, w as ButtonStyle, Y as CardJSXElement, Z as CardJSXProps, _ as CardLinkProps, x as CardOptions, $ as CardProps, a0 as ContainerProps, y as DividerElement, a1 as DividerProps, ab as ExternalSelectElement, ac as ExternalSelectOptions, a2 as ExternalSelectProps, z as FieldElement, a3 as FieldProps, G as FieldsElement, H as ImageElement, a4 as ImageProps, J as LinkButtonElement, K as LinkButtonOptions, a5 as LinkButtonProps, N as LinkElement, ad as ModalChild, ae as ModalOptions, a6 as ModalProps, af as RadioSelectElement, ag as RadioSelectOptions, O as SectionElement, ah as SelectElement, a7 as SelectOptionProps, ai as SelectOptions, a8 as SelectProps, P as TableAlignment, Q as TableElement, U as TableOptions, V as TextElement, aj as TextInputElement, ak as TextInputOptions, a9 as TextInputProps, aa as TextProps, W as TextStyle } from './jsx-runtime-Co9uV6l7.js';
6
6
 
7
7
  interface MessageHistoryConfig {
8
8
  /** Maximum messages to keep per thread (default: 100) */
@@ -288,6 +288,7 @@ declare class ThreadImpl<TState = Record<string, unknown>> implements Thread<TSt
288
288
  */
289
289
  get messages(): AsyncIterable<Message>;
290
290
  get allMessages(): AsyncIterable<Message>;
291
+ getParticipants(): Promise<Author[]>;
291
292
  isSubscribed(): Promise<boolean>;
292
293
  subscribe(): Promise<void>;
293
294
  unsubscribe(): Promise<void>;
@@ -304,6 +305,13 @@ declare class ThreadImpl<TState = Record<string, unknown>> implements Thread<TSt
304
305
  * then uses adapter's native streaming if available, otherwise falls back to post+edit.
305
306
  */
306
307
  private handleStream;
308
+ /**
309
+ * Slack payloads carry the workspace ID in a few different shapes depending on
310
+ * the webhook type:
311
+ * - Message events: `team_id` or `team` as a string
312
+ * - `block_actions` payloads: `team.id` (object), with `user.team_id` as a fallback
313
+ */
314
+ private extractSlackRecipientTeamId;
307
315
  startTyping(status?: string): Promise<void>;
308
316
  /**
309
317
  * Fallback streaming implementation using post + edit.
@@ -410,6 +418,8 @@ interface AddTaskOptions {
410
418
  title: PlanContent;
411
419
  }
412
420
  type UpdateTaskInput = PlanContent | {
421
+ /** Task ID to update. If omitted, updates the last in_progress task. */
422
+ id?: string;
413
423
  /** Task output/results. */
414
424
  output?: PlanContent;
415
425
  /** Optional status override. */
@@ -683,6 +693,14 @@ interface Adapter<TThreadId = unknown, TRawMessage = unknown> {
683
693
  * @returns The channel visibility scope
684
694
  */
685
695
  getChannelVisibility?(threadId: string): ChannelVisibility;
696
+ /**
697
+ * Look up user information by user ID.
698
+ * Optional — not all platforms support this.
699
+ *
700
+ * @param userId - Platform-specific user ID
701
+ * @returns User info, or null if user not found
702
+ */
703
+ getUser?(userId: string): Promise<UserInfo | null>;
686
704
  /** Handle incoming webhook request */
687
705
  handleWebhook(request: Request, options?: WebhookOptions): Promise<Response>;
688
706
  /** Called when Chat instance is created (internal use) */
@@ -772,6 +790,12 @@ interface Adapter<TThreadId = unknown, TRawMessage = unknown> {
772
790
  * @param data - The object data (type depends on kind)
773
791
  */
774
792
  postObject?(threadId: string, kind: string, data: unknown): Promise<RawMessage<TRawMessage>>;
793
+ /**
794
+ * Reconstruct fetchData on an attachment after deserialization.
795
+ * Called during message rehydration for queue/debounce strategies.
796
+ * Uses fetchMetadata and adapter auth context to rebuild the download closure.
797
+ */
798
+ rehydrateAttachment?(attachment: Attachment): Attachment;
775
799
  /** Remove a reaction from a message */
776
800
  removeReaction(threadId: string, messageId: string, emoji: EmojiValue | string): Promise<void>;
777
801
  /** Render formatted content to platform-specific string */
@@ -828,6 +852,7 @@ interface MarkdownTextChunk {
828
852
  type: "markdown_text";
829
853
  }
830
854
  interface TaskUpdateChunk {
855
+ details?: string;
831
856
  id: string;
832
857
  output?: string;
833
858
  status: "pending" | "in_progress" | "complete" | "error";
@@ -908,6 +933,11 @@ interface ChatInstance {
908
933
  * @param options - Webhook options
909
934
  */
910
935
  processModalSubmit(event: Omit<ModalSubmitEvent, "relatedThread" | "relatedMessage" | "relatedChannel">, contextId?: string, options?: WebhookOptions): Promise<ModalResponse | undefined>;
936
+ /**
937
+ * Process an interactive options load event from an adapter.
938
+ * Returns normalized select options for the adapter to render.
939
+ */
940
+ processOptionsLoad(event: OptionsLoadEvent, options?: WebhookOptions): Promise<OptionsLoadResult | undefined>;
911
941
  /**
912
942
  * Process an incoming reaction event from an adapter.
913
943
  * Handles waitUntil registration and error catching internally.
@@ -1190,6 +1220,40 @@ interface Thread<TState = Record<string, unknown>, TRawMessage = unknown> extend
1190
1220
  * Used internally for reconstructing messages from serialized data.
1191
1221
  */
1192
1222
  createSentMessageFromMessage(message: Message<TRawMessage>): SentMessage<TRawMessage>;
1223
+ /**
1224
+ * Get the unique human participants in this thread.
1225
+ *
1226
+ * Scans all messages in the thread and returns deduplicated authors,
1227
+ * excluding the bot itself. Useful for deciding whether to subscribe
1228
+ * based on how many humans are participating — subscribe when it's a
1229
+ * 1:1 conversation, unsubscribe when others join so humans can talk
1230
+ * without the bot replying to every message.
1231
+ *
1232
+ * @returns Array of unique non-bot authors
1233
+ *
1234
+ * @example
1235
+ * ```typescript
1236
+ * // Subscribe only when one person is talking to the bot
1237
+ * bot.onNewMention(async (thread, message) => {
1238
+ * const participants = await thread.getParticipants();
1239
+ * if (participants.length === 1) {
1240
+ * await thread.subscribe();
1241
+ * await thread.post("I'm here to help!");
1242
+ * }
1243
+ * });
1244
+ *
1245
+ * // Unsubscribe when the thread becomes a group conversation
1246
+ * bot.onSubscribedMessage(async (thread, message) => {
1247
+ * const participants = await thread.getParticipants();
1248
+ * if (participants.length > 1) {
1249
+ * await thread.unsubscribe();
1250
+ * return;
1251
+ * }
1252
+ * await thread.post("Still here to help!");
1253
+ * });
1254
+ * ```
1255
+ */
1256
+ getParticipants(): Promise<Author[]>;
1193
1257
  /**
1194
1258
  * Check if this thread is currently subscribed.
1195
1259
  *
@@ -1238,6 +1302,13 @@ interface Thread<TState = Record<string, unknown>, TRawMessage = unknown> extend
1238
1302
  * const result = await agent.stream({ prompt: message.text });
1239
1303
  * await thread.post(result.textStream);
1240
1304
  *
1305
+ * // Stream with options via StreamingPlan PostableObject
1306
+ * const stream = new StreamingPlan(result.fullStream, {
1307
+ * groupTasks: "plan",
1308
+ * endWith: [feedbackBlocks],
1309
+ * });
1310
+ * await thread.post(stream);
1311
+ *
1241
1312
  * // Plan with live updates
1242
1313
  * const plan = new Plan({ initialMessage: "Working..." });
1243
1314
  * await thread.post(plan);
@@ -1425,6 +1496,21 @@ interface Author {
1425
1496
  /** Username/handle for @-mentions */
1426
1497
  userName: string;
1427
1498
  }
1499
+ /** User information returned by adapter.getUser() */
1500
+ interface UserInfo {
1501
+ /** URL to the user's avatar/profile image */
1502
+ avatarUrl?: string;
1503
+ /** User's email address (requires appropriate scopes on some platforms) */
1504
+ email?: string;
1505
+ /** User's display name / full name */
1506
+ fullName: string;
1507
+ /** Whether the user is a bot */
1508
+ isBot: boolean;
1509
+ /** Platform-specific user ID */
1510
+ userId: string;
1511
+ /** Username/handle */
1512
+ userName: string;
1513
+ }
1428
1514
  interface MessageMetadata {
1429
1515
  /** When the message was sent */
1430
1516
  dateSent: Date;
@@ -1562,6 +1648,12 @@ interface Attachment {
1562
1648
  * this method handles the auth automatically.
1563
1649
  */
1564
1650
  fetchData?: () => Promise<Buffer>;
1651
+ /**
1652
+ * Platform-specific metadata needed to reconstruct fetchData after serialization.
1653
+ * Adapters store IDs here (e.g. WhatsApp mediaId, Telegram fileId) so that
1654
+ * fetchData can be rebuilt when a message is rehydrated from the queue.
1655
+ */
1656
+ fetchMetadata?: Record<string, string>;
1565
1657
  /** Image/video height (if applicable) */
1566
1658
  height?: number;
1567
1659
  /** MIME type */
@@ -1857,6 +1949,27 @@ interface ActionEvent<TRawMessage = unknown> {
1857
1949
  * ```
1858
1950
  */
1859
1951
  type ActionHandler = (event: ActionEvent) => void | Promise<void>;
1952
+ /**
1953
+ * Event emitted when an adapter needs dynamic options for an external select.
1954
+ */
1955
+ interface OptionsLoadEvent {
1956
+ /** The action ID of the select requesting options */
1957
+ actionId: string;
1958
+ /** The adapter that received this event */
1959
+ adapter: Adapter;
1960
+ /** The current user-entered query text */
1961
+ query: string;
1962
+ /** Raw platform-specific payload */
1963
+ raw: unknown;
1964
+ /** The user requesting options */
1965
+ user: Author;
1966
+ }
1967
+ interface OptionsLoadGroup {
1968
+ label: string;
1969
+ options: SelectOptionElement[];
1970
+ }
1971
+ type OptionsLoadResult = SelectOptionElement[] | OptionsLoadGroup[];
1972
+ type OptionsLoadHandler = (event: OptionsLoadEvent) => OptionsLoadResult | Promise<OptionsLoadResult | undefined> | undefined;
1860
1973
  /**
1861
1974
  * Event emitted when a user submits a modal form.
1862
1975
  */
@@ -1946,7 +2059,10 @@ interface ModalPushResponse {
1946
2059
  interface ModalCloseResponse {
1947
2060
  action: "close";
1948
2061
  }
1949
- type ModalResponse = ModalCloseResponse | ModalErrorsResponse | ModalUpdateResponse | ModalPushResponse;
2062
+ interface ModalClearResponse {
2063
+ action: "clear";
2064
+ }
2065
+ type ModalResponse = ModalCloseResponse | ModalClearResponse | ModalErrorsResponse | ModalUpdateResponse | ModalPushResponse;
1950
2066
  type ModalSubmitHandler = (event: ModalSubmitEvent) => void | Promise<ModalResponse | void | undefined>;
1951
2067
  type ModalCloseHandler = (event: ModalCloseEvent) => void | Promise<void>;
1952
2068
  /**
@@ -2118,6 +2234,7 @@ interface SerializedMessage {
2118
2234
  size?: number;
2119
2235
  width?: number;
2120
2236
  height?: number;
2237
+ fetchMetadata?: Record<string, string>;
2121
2238
  }>;
2122
2239
  author: {
2123
2240
  userId: string;
@@ -2389,6 +2506,7 @@ declare class Chat<TAdapters extends Record<string, Adapter> = Record<string, Ad
2389
2506
  private readonly _messageHistory;
2390
2507
  private readonly _concurrencyStrategy;
2391
2508
  private readonly _concurrencyConfig;
2509
+ private readonly _concurrentSlots;
2392
2510
  private readonly _lockScope;
2393
2511
  private readonly mentionHandlers;
2394
2512
  private readonly directMessageHandlers;
@@ -2396,6 +2514,7 @@ declare class Chat<TAdapters extends Record<string, Adapter> = Record<string, Ad
2396
2514
  private readonly subscribedMessageHandlers;
2397
2515
  private readonly reactionHandlers;
2398
2516
  private readonly actionHandlers;
2517
+ private readonly optionsLoadHandlers;
2399
2518
  private readonly modalSubmitHandlers;
2400
2519
  private readonly modalCloseHandlers;
2401
2520
  private readonly slashCommandHandlers;
@@ -2571,6 +2690,12 @@ declare class Chat<TAdapters extends Record<string, Adapter> = Record<string, Ad
2571
2690
  */
2572
2691
  onAction(handler: ActionHandler): void;
2573
2692
  onAction(actionIds: string[] | string, handler: ActionHandler): void;
2693
+ /**
2694
+ * Register a handler for loading dynamic options for external selects.
2695
+ * Specific action IDs run before catch-all handlers.
2696
+ */
2697
+ onOptionsLoad(handler: OptionsLoadHandler): void;
2698
+ onOptionsLoad(actionIds: string[] | string, handler: OptionsLoadHandler): void;
2574
2699
  /**
2575
2700
  * Register a handler for modal form submissions.
2576
2701
  *
@@ -2701,6 +2826,7 @@ declare class Chat<TAdapters extends Record<string, Adapter> = Record<string, Ad
2701
2826
  processAction(event: Omit<ActionEvent, "thread" | "openModal"> & {
2702
2827
  adapter: Adapter;
2703
2828
  }, options: WebhookOptions | undefined): Promise<void>;
2829
+ processOptionsLoad(event: OptionsLoadEvent, _options?: WebhookOptions): Promise<OptionsLoadResult | undefined>;
2704
2830
  processModalSubmit(event: Omit<ModalSubmitEvent, "relatedThread" | "relatedMessage" | "relatedChannel">, contextId?: string, _options?: WebhookOptions): Promise<ModalResponse | undefined>;
2705
2831
  processModalClose(event: Omit<ModalCloseEvent, "relatedThread" | "relatedMessage" | "relatedChannel">, contextId?: string, options?: WebhookOptions): void;
2706
2832
  /**
@@ -2768,6 +2894,23 @@ declare class Chat<TAdapters extends Record<string, Adapter> = Record<string, Ad
2768
2894
  * ```
2769
2895
  */
2770
2896
  openDM(user: string | Author): Promise<Thread<TState>>;
2897
+ /**
2898
+ * Look up user information by user ID.
2899
+ *
2900
+ * The adapter is automatically inferred from the user ID format.
2901
+ * Returns user details including email (where available — requires
2902
+ * appropriate scopes on some platforms, e.g. `users:read.email` on Slack).
2903
+ *
2904
+ * @param user - Platform-specific user ID string, or an Author object
2905
+ * @returns User info, or null if user not found
2906
+ *
2907
+ * @example
2908
+ * ```typescript
2909
+ * const user = await chat.getUser("U123456");
2910
+ * console.log(user?.email); // "alice@company.com"
2911
+ * ```
2912
+ */
2913
+ getUser(user: string | Author): Promise<UserInfo | null>;
2771
2914
  /**
2772
2915
  * Get a Channel by its channel ID.
2773
2916
  *
@@ -2795,6 +2938,21 @@ declare class Chat<TAdapters extends Record<string, Adapter> = Record<string, Ad
2795
2938
  * ```
2796
2939
  */
2797
2940
  channel(channelId: string): Channel<TState>;
2941
+ /**
2942
+ * Get a Thread handle by its thread ID.
2943
+ *
2944
+ * The adapter is automatically inferred from the thread ID prefix.
2945
+ *
2946
+ * @param threadId - Full thread ID (e.g., "slack:C123ABC:1234567890.123456")
2947
+ * @returns A Thread that can be used to post messages, subscribe, etc.
2948
+ *
2949
+ * @example
2950
+ * ```typescript
2951
+ * const thread = chat.thread("slack:C123ABC:1234567890.123456");
2952
+ * await thread.post("Hello from outside a webhook!");
2953
+ * ```
2954
+ */
2955
+ thread(threadId: string): Thread<TState>;
2798
2956
  /**
2799
2957
  * Infer which adapter to use based on the userId format.
2800
2958
  */
@@ -2835,9 +2993,12 @@ declare class Chat<TAdapters extends Record<string, Adapter> = Record<string, Ad
2835
2993
  */
2836
2994
  private drainQueue;
2837
2995
  /**
2838
- * Concurrent strategy: no locking, process immediately.
2996
+ * Concurrent strategy: no locking, process immediately — but cap
2997
+ * simultaneous handlers per thread at `maxConcurrent` (default Infinity).
2839
2998
  */
2840
2999
  private handleConcurrent;
3000
+ private acquireConcurrentSlot;
3001
+ private releaseConcurrentSlot;
2841
3002
  /**
2842
3003
  * Dispatch a message to the appropriate handler chain based on
2843
3004
  * subscription status, mention detection, and pattern matching.
@@ -2881,6 +3042,21 @@ declare class Chat<TAdapters extends Record<string, Adapter> = Record<string, Ad
2881
3042
  */
2882
3043
  declare function fromFullStream(stream: AsyncIterable<unknown>): AsyncIterable<string | StreamChunk>;
2883
3044
 
3045
+ /**
3046
+ * Standalone JSON reviver for Chat SDK objects.
3047
+ *
3048
+ * Restores serialized Thread, Channel, and Message instances during
3049
+ * JSON.parse() without requiring a Chat instance. This is useful in
3050
+ * environments like Vercel Workflow functions where importing the full
3051
+ * Chat instance (with its adapter dependencies) is not possible.
3052
+ *
3053
+ * Thread instances created this way use lazy adapter resolution —
3054
+ * the adapter is looked up from the Chat singleton when first accessed,
3055
+ * so `chat.registerSingleton()` must be called before using thread
3056
+ * methods like `post()` (typically inside a "use step" function).
3057
+ */
3058
+ declare function reviver(_key: string, value: unknown): unknown;
3059
+
2884
3060
  interface StreamingMarkdownRendererOptions {
2885
3061
  /**
2886
3062
  * Wrap confirmed table blocks in code fences for append-only consumers that
@@ -2938,6 +3114,58 @@ declare class StreamingMarkdownRenderer {
2938
3114
  private formatAppendOnlyText;
2939
3115
  }
2940
3116
 
3117
+ interface StreamingPlanOptions {
3118
+ /**
3119
+ * Block Kit elements to attach when the stream stops (Slack only).
3120
+ * Useful for adding feedback buttons after a streamed response.
3121
+ */
3122
+ endWith?: unknown[];
3123
+ /**
3124
+ * Controls how task_update chunks are displayed (Slack only).
3125
+ * - `"plan"` - all tasks grouped into a single plan block
3126
+ * - `"timeline"` - individual task cards shown inline with text (default)
3127
+ */
3128
+ groupTasks?: "plan" | "timeline";
3129
+ /**
3130
+ * Minimum interval between updates in ms (default: 500).
3131
+ * Used for fallback mode (post+edit on adapters without native streaming).
3132
+ */
3133
+ updateIntervalMs?: number;
3134
+ }
3135
+ interface StreamingPlanData {
3136
+ options: StreamingPlanOptions;
3137
+ stream: AsyncIterable<string | StreamChunk | StreamEvent>;
3138
+ }
3139
+ /**
3140
+ * A StreamingPlan wraps an async iterable with platform-specific streaming options.
3141
+ *
3142
+ * Use this when you need to pass options like task grouping or stop blocks
3143
+ * to the streaming API. For simple streaming without options, pass the
3144
+ * async iterable directly to `thread.post()`.
3145
+ *
3146
+ * @example
3147
+ * ```typescript
3148
+ * const stream = new StreamingPlan(result.fullStream, {
3149
+ * groupTasks: "plan",
3150
+ * endWith: [feedbackBlock],
3151
+ * });
3152
+ * await thread.post(stream);
3153
+ * ```
3154
+ */
3155
+ declare class StreamingPlan implements PostableObject<StreamingPlanData> {
3156
+ readonly $$typeof: symbol;
3157
+ readonly kind = "stream";
3158
+ private readonly _stream;
3159
+ private readonly _options;
3160
+ constructor(stream: AsyncIterable<string | StreamChunk | StreamEvent>, options?: StreamingPlanOptions);
3161
+ get stream(): AsyncIterable<string | StreamChunk | StreamEvent>;
3162
+ get options(): StreamingPlanOptions;
3163
+ getFallbackText(): string;
3164
+ getPostData(): StreamingPlanData;
3165
+ isSupported(_adapter: Adapter): boolean;
3166
+ onPosted(_context: PostableObjectContext): void;
3167
+ }
3168
+
2941
3169
  /**
2942
3170
  * Get or create an immutable singleton EmojiValue.
2943
3171
  *
@@ -3375,10 +3603,11 @@ declare const toModalElement: typeof toModalElement$1;
3375
3603
 
3376
3604
  declare const fromReactModalElement: typeof fromReactModalElement$1;
3377
3605
  declare const isModalElement: typeof isModalElement$1;
3606
+ declare const ExternalSelect: ExternalSelectComponent;
3378
3607
  declare const Modal: ModalComponent;
3379
3608
  declare const RadioSelect: RadioSelectComponent;
3380
3609
  declare const Select: SelectComponent;
3381
3610
  declare const SelectOption: SelectOptionComponent;
3382
3611
  declare const TextInput: TextInputComponent;
3383
3612
 
3384
- export { type ActionEvent, type ActionHandler, Actions, ActionsComponent, type Adapter, type AdapterPostableMessage, type AddTaskOptions, type AiAssistantMessage, type AiFilePart, type AiImagePart, type AiMessage, type AiMessagePart, type AiTextPart, type AiUserMessage, type AppHomeOpenedEvent, type AppHomeOpenedHandler, type AssistantContextChangedEvent, type AssistantContextChangedHandler, type AssistantThreadStartedEvent, type AssistantThreadStartedHandler, type Attachment, type Author, BaseFormatConverter, Button, ButtonComponent, Card, CardChild, CardComponent, CardElement, CardLink, CardLinkComponent, CardText, type Channel, ChannelImpl, type ChannelInfo, type ChannelVisibility, Chat, type ChatConfig, ChatElement, ChatError, type ChatInstance, type CompletePlanOptions, type ConcurrencyConfig, type ConcurrencyStrategy, ConsoleLogger, type CustomEmojiMap, DEFAULT_EMOJI_MAP, type DirectMessageHandler, Divider, DividerComponent, type Emoji, type EmojiFormats, type EmojiMapConfig, EmojiResolver, type EmojiValue, type EphemeralMessage, type FetchDirection, type FetchOptions, type FetchResult, Field, FieldComponent, Fields, FieldsComponent, type FileUpload, type FormatConverter, type FormattedContent, Image, ImageComponent, LinkButton, LinkButtonComponent, type LinkPreview, type ListThreadsOptions, type ListThreadsResult, type Lock, LockError, type LockScope, type LockScopeContext, type LogLevel, type Logger, type MarkdownConverter, type MarkdownTextChunk, type MemberJoinedChannelEvent, type MemberJoinedChannelHandler, type MentionHandler, Message, type MessageContext, type MessageData, type MessageHandler, MessageHistoryCache, type MessageHistoryConfig, type MessageMetadata, Modal, type ModalCloseEvent, type ModalCloseHandler, type ModalCloseResponse, ModalComponent, ModalElement, type ModalErrorsResponse, type ModalPushResponse, type ModalResponse, type ModalSubmitEvent, type ModalSubmitHandler, type ModalUpdateResponse, NotImplementedError, Plan, type PlanContent, type PlanModel, type PlanModelTask, type PlanTask, type PlanTaskStatus, type PlanUpdateChunk, type PostEphemeralOptions, type Postable, type PostableAst, type PostableCard, type PostableMarkdown, type PostableMessage, type PostableObject, type PostableObjectContext, type PostableRaw, type QueueEntry, RadioSelect, RadioSelectComponent, RateLimitError, type RawMessage, type ReactionEvent, type ReactionHandler, type ScheduledMessage, Section, SectionComponent, Select, SelectComponent, SelectOption, SelectOptionComponent, type SentMessage, type SerializedChannel, type SerializedMessage, type SerializedThread, type SlashCommandEvent, type SlashCommandHandler, type StartPlanOptions, type StateAdapter, type StreamChunk, type StreamEvent, type StreamOptions, StreamingMarkdownRenderer, type SubscribedMessageHandler, THREAD_STATE_TTL_MS, Table, type TaskUpdateChunk, TextComponent, TextInput, TextInputComponent, type Thread, ThreadImpl, type ThreadInfo, type ThreadSummary, type UpdateTaskInput, type WebhookOptions, type WellKnownEmoji, blockquote, cardChildToFallbackText, codeBlock, convertEmojiPlaceholders, createEmoji, defaultEmojiResolver, deriveChannelId, emoji, emphasis, fromFullStream, fromReactElement, fromReactModalElement, getEmoji, getNodeChildren, getNodeValue, inlineCode, isBlockquoteNode, isCardElement, isCodeNode, isDeleteNode, isEmphasisNode, isInlineCodeNode, isJSX, isLinkNode, isListItemNode, isListNode, isModalElement, isParagraphNode, isPostableObject, isStrongNode, isTableCellNode, isTableNode, isTableRowNode, isTextNode, link, markdownToPlainText, paragraph, parseMarkdown, root, strikethrough, stringifyMarkdown, strong, tableElementToAscii, tableToAscii, text, toAiMessages, toCardElement, toModalElement, toPlainText, walkAst };
3613
+ export { type ActionEvent, type ActionHandler, Actions, ActionsComponent, type Adapter, type AdapterPostableMessage, type AddTaskOptions, type AiAssistantMessage, type AiFilePart, type AiImagePart, type AiMessage, type AiMessagePart, type AiTextPart, type AiUserMessage, type AppHomeOpenedEvent, type AppHomeOpenedHandler, type AssistantContextChangedEvent, type AssistantContextChangedHandler, type AssistantThreadStartedEvent, type AssistantThreadStartedHandler, type Attachment, type Author, BaseFormatConverter, Button, ButtonComponent, Card, CardChild, CardComponent, CardElement, CardLink, CardLinkComponent, CardText, type Channel, ChannelImpl, type ChannelInfo, type ChannelVisibility, Chat, type ChatConfig, ChatElement, ChatError, type ChatInstance, type CompletePlanOptions, type ConcurrencyConfig, type ConcurrencyStrategy, ConsoleLogger, type CustomEmojiMap, DEFAULT_EMOJI_MAP, type DirectMessageHandler, Divider, DividerComponent, type Emoji, type EmojiFormats, type EmojiMapConfig, EmojiResolver, type EmojiValue, type EphemeralMessage, ExternalSelect, ExternalSelectComponent, type FetchDirection, type FetchOptions, type FetchResult, Field, FieldComponent, Fields, FieldsComponent, type FileUpload, type FormatConverter, type FormattedContent, Image, ImageComponent, LinkButton, LinkButtonComponent, type LinkPreview, type ListThreadsOptions, type ListThreadsResult, type Lock, LockError, type LockScope, type LockScopeContext, type LogLevel, type Logger, type MarkdownConverter, type MarkdownTextChunk, type MemberJoinedChannelEvent, type MemberJoinedChannelHandler, type MentionHandler, Message, type MessageContext, type MessageData, type MessageHandler, MessageHistoryCache, type MessageHistoryConfig, type MessageMetadata, Modal, type ModalClearResponse, type ModalCloseEvent, type ModalCloseHandler, type ModalCloseResponse, ModalComponent, ModalElement, type ModalErrorsResponse, type ModalPushResponse, type ModalResponse, type ModalSubmitEvent, type ModalSubmitHandler, type ModalUpdateResponse, NotImplementedError, type OptionsLoadEvent, type OptionsLoadGroup, type OptionsLoadHandler, type OptionsLoadResult, Plan, type PlanContent, type PlanModel, type PlanModelTask, type PlanTask, type PlanTaskStatus, type PlanUpdateChunk, type PostEphemeralOptions, type Postable, type PostableAst, type PostableCard, type PostableMarkdown, type PostableMessage, type PostableObject, type PostableObjectContext, type PostableRaw, type QueueEntry, RadioSelect, RadioSelectComponent, RateLimitError, type RawMessage, type ReactionEvent, type ReactionHandler, type ScheduledMessage, Section, SectionComponent, Select, SelectComponent, SelectOption, SelectOptionComponent, SelectOptionElement, type SentMessage, type SerializedChannel, type SerializedMessage, type SerializedThread, type SlashCommandEvent, type SlashCommandHandler, type StartPlanOptions, type StateAdapter, type StreamChunk, type StreamEvent, type StreamOptions, StreamingMarkdownRenderer, StreamingPlan, type StreamingPlanData, type StreamingPlanOptions, type SubscribedMessageHandler, THREAD_STATE_TTL_MS, Table, type TaskUpdateChunk, TextComponent, TextInput, TextInputComponent, type Thread, ThreadImpl, type ThreadInfo, type ThreadSummary, type UpdateTaskInput, type UserInfo, type WebhookOptions, type WellKnownEmoji, blockquote, cardChildToFallbackText, codeBlock, convertEmojiPlaceholders, createEmoji, defaultEmojiResolver, deriveChannelId, emoji, emphasis, fromFullStream, fromReactElement, fromReactModalElement, getEmoji, getNodeChildren, getNodeValue, inlineCode, isBlockquoteNode, isCardElement, isCodeNode, isDeleteNode, isEmphasisNode, isInlineCodeNode, isJSX, isLinkNode, isListItemNode, isListNode, isModalElement, isParagraphNode, isPostableObject, isStrongNode, isTableCellNode, isTableNode, isTableRowNode, isTextNode, link, markdownToPlainText, paragraph, parseMarkdown, reviver, root, strikethrough, stringifyMarkdown, strong, tableElementToAscii, tableToAscii, text, toAiMessages, toCardElement, toModalElement, toPlainText, walkAst };