npm / chat
chat
What changed in chat 4, from its official release notes, upgrade guides and advisories. Sources checked 2026-10-18.
21 breaking7 security5 deprecations116 new APIs and options1 performance48 behavior and defaults2 tooling40 fixes and other
See which of these apply to your code, ranked, in your agent:
npx @unstale/cli initchat 4240 changes
Breaking changes 21
A Teams button opening a modal renders with associatedInputs set to none, so card inputs do not block the dialog or get submitted.
The union gains a member; external adapters that exhaustively handle it with a never check need a text_input case.
Returns undefined for inherited object keys such as "toString" instead of returning Object.prototype members.
Returns an empty array for inherited object keys such as "toString" instead of returning Object.prototype members.
Messages queued under old keys are not migrated, and old and new workers do not exclude each other; upgrade chat and @chat-adapter/slack together and restart all workers.
Invalid arguments now throw ChatError at registration, and global or sticky pattern flags match on every message rather than skipping alternate ones.
AI tool calls inside handlers without a conversation scope are rejected unless given an explicit scope or scope: false; standalone tool factories follow the same rules.
Creating tools with an empty scope id now throws.
Teams card inputs using the IDs actionId, value, or msteams cause a ValidationError when rendered; the cards subpath throws an Error.
The subpath emits the native Adaptive Cards Table element.
A definitive false from an adapter now suppresses SDK text-based mention detection; adapters should return undefined when a mention is undetermined.
Adds cross-platform per-user message persistence, replacing bot.transcripts; migrate the config key to history.user and update call sites.
Applies the conversation scope guard to write tools; out-of-scope thread or channel IDs are rejected before execution. sendDirectMessage remains gated by approval alone.
No longer treats request-body messages as conversation state: it consumes only the latest user message, strips tool parts, rejects messages left without parts with HTTP 400, and gets prior turns from the state adapter when history persistence is enabled.
Direct calls now reject when a receipt fails instead of logging a warning and resolving; callers should await the method or catch its rejection.
Read guards now scope tools in modal, assistant, app-home, app-context, and member-joined handlers; unscoped reads log a warning, and reads from newly scoped handlers may now be rejected.
Teams dialog values submitted as JSON numbers are stringified instead of dropped, so previously absent keys can now be present as strings.
Sets supported Node versions in the package engines.
Adds this method to the Adapter interface and Slack adapter, replacing the previous isExternalChannel boolean.
Makes channelIdFromThreadId required in the Adapter and StateAdapter interfaces.
Improves type safety by changing StreamChunk to a discriminated union.
Security 7
External link metadata is marked as untrusted.
Webhook tenant isolation is hardened.
Adds a Notion adapter for page and block comment discussions, including webhook verification, streaming, history, mentions, and up to 3 native attachments.
Configure the consumer secret to receive XChat webhooks, whose POSTs must carry a valid x-twitter-webhooks-signature.
Supports encrypted send and receive in direct messages and groups through webhook push or polling; signature verification is on by default and undecryptable or unverified events are dropped.
Supports inbound attachments with lazy download and decryption, plus outbound encrypted uploads.
Fixes a security bypass involving url_verification.
Deprecations 5
The subpath is deprecated and re-exports the original API from @chat-adapter/catalog so existing imports keep working.
Passing a guard function remains supported but is deprecated in favor of scope.
Remains available as a deprecated alias for bot.history.user.
Deprecated in favour of HistoryEntry; both types remain exported from chat.
Ships from chat/ai alongside AiMessage, AiMessagePart, and ToAiMessagesOptions; the previous chat re-export remains available but is deprecated.
New APIs and options 116
Adds optional card input values keyed by input ID; Teams populates them from Action.Submit payloads.
Adds an optional hook for adapters to namespace dedupe, lock, queue, and turn keys, isolating Slack message concurrency and cancellation by installation.
Adds an optional field to postable content and streaming options; adapters with a native AI label can display it, and Teams shows its AI generated label.
Adds a zero-dependency catalog of adapters, including metadata and helpers for listing adapters, looking up entries, and checking feature support.
Adds a handler-only overload for messages reaching pattern routing, including attachment-only messages without text.
Standalone tool factories exported from chat/ai now accept strictScope and follow the same scope rules as createChatTools.
Adds an adapter that reads channel chat through EventSub webhooks, replies through the Helix Chat API, deletes messages as a moderator, and sends and receives whispers.
Button and LinkButton accept user IDs allowed to see the button; Slack renders the option as visible_to_user_ids, while other adapters ignore it.
Installation lifecycle events provide a persistable channelId for later proactive messages.
New subpath provides TanStack message conversion for thread history and a Chat SDK toolset; its tools require zod 4.2 or newer.
Adds an installation lifecycle handler; the Teams adapter emits it for installationUpdate activities.
Adds an installation lifecycle handler; the Teams adapter emits it for installationUpdate activities.
Select and RadioSelect inputs in Slack modals can opt in to selection change events.
Adds an option to control grid lines in Teams tables; it defaults to true, and false keeps a table borderless.
Adds a Teams-only table rendering option for grid style.
Adds an option for vertical alignment of cell content in Teams table rendering.
Adds optional positive integer column weights for Teams table rendering.
The legacy setting can be set to false to disable count-based history eviction; the default remains 200 entries per user.
The Teams adapter supports Vercel Connect with lazy app identity resolution and custom webhook verification.
Adds an opt-in webhook option to propagate message, action, and slash command handler errors through waitUntil while preserving default behavior.
Adds a tooltip option to Button; the Teams adapter renders it as an Adaptive Card action tooltip, while other adapters ignore it.
Adds a width hint to Card; the Teams adapter renders it as the msteams full-width card property, while other adapters ignore it.
Adds a tooltip option to LinkButton; the Teams adapter renders it as an Adaptive Card action tooltip, while other adapters ignore it.
Adds promise-based helpers for per-channel message access; reads delegate to the adapter, with SDK-side persisted history served from cache.
Adds promise-based helpers for per-thread message access; reads delegate to the adapter, with SDK-side persisted history served from cache.
Caps lock renewal at a maximum lifetime, defaulting to 10 minutes, so a hung handler cannot block a thread forever.
Introduced as the replacement for TranscriptEntry; both types are exported from chat.
Can include an explicit action ID.
Adds lifecycle support, native stop cancellation through thread.signal, automatic session titles, and session stop/title-change events while preserving the legacy assistant_view compatibility path.
Converts history.user.list() entries into { role, content } messages ready for LLM prompts.
Adds a workflow-safe serialization entrypoint for projects that need to use serialization without Node-only runtime dependencies.
The --connect option scaffolds Discord bots with create-chat-sdk.
The Discord adapter adds support for custom webhook verification.
Adds a shared thread API for marking messages as read across WhatsApp, Messenger, and XChat.
Adds native message replies, including WhatsApp contextual reply support.
The Discord adapter adds Vercel Connect credential resolvers.
Adds strictScope: true to confine a thread scope to that thread, rejecting sibling threads and the parent channel.
Adds a native adapter with signed webhooks, media, quick replies, story context, reactions, and typed Meta API errors.
Adds lifecycle callbacks for message updates and deletions, including Slack message_changed and message_deleted dispatch support.
Adds Notion emoji platform support.
Adds XChat support as a new subpath alongside the existing X adapter; the XChat crypto dependencies are optional peer dependencies.
Adds a modal child rendered by the Slack adapter as a datepicker and by the Teams adapter as Input.Date.
Allows webhook verification to be disabled when an upstream layer already verifies XChat webhooks.
Sets the age gate for the first edit of a fresh message; its default is 5000ms so receiving clients have stored the original.
Adds an optional catalog field for adapters that ship on a subpath.
Adds a modal child rendered by the Slack adapter as a number_input and by the Teams adapter as Input.Number; submitted values arrive in event.values as strings.
Starts or reuses an encrypted one-to-one conversation and performs a full key exchange when needed so the bot can message first.
Controls XChat read receipts and defaults to on.
Provides an environment-variable alternative to consumerSecret for receiving verified XChat webhooks.
Adds a configurable group welcome message.
Adds support for sending and receiving reactions.
Exports a builder for approval cards from the chat/workflow subpath.
Exports a builder for resolved approval cards from the chat/workflow subpath.
Adds an optional Author field to distinguish platform-generated messages from humans and bots; the Slack adapter sets it for messages from its reserved USLACK user.
Adds durable human-in-the-loop approvals through the chat/workflow subpath. The workflow suspends for a decision or optional timeout, validates approvers, finalizes the card, and returns the outcome; it requires the optional workflow peer dependency.
Adds Slack adapter support for Slack's Agent messaging experience, including app_context_changed routing and folded context on DM messages.
Adds the opened Slack tab, allowing handlers to distinguish a Home-tab open from the DM-open signal under agent_view.
Adds a builder for pie, bar, area, and line charts, with JSX support.
Adds a chart element supporting pie, bar, area, and line charts with Slack-style data visualization fields.
Adds a helper for rendering chart data as fallback text on platforms without native chart support.
Adds SlackChartElement types, chart card children, data table rendering, and matching limits to the blocks subpath.
Adds a helper for reading folded app context from DM messages.
Adds a core event carrying normalized AppContextEntity[] data for the user's active view.
Accepts an optional thread reference so prompts can sit at the top of an agent conversation.
Adds an optional accessible table description.
Adds an optional field for specifying rows per page.
Adds the vendor-official Dial adapter to the catalog and CLI scaffold spec.
Adds the vendor-official Photon adapter to the catalog and CLI scaffold spec.
Adds an adapter for replying to public mentions, sending and receiving direct messages, posting and editing from the bot account, and liking posts via the X API v2 with OAuth 2.0 and managed token refresh.
Adds autoCompletePrevious, defaulting to true; pass false to keep multiple tasks in progress for parallel execution.
Adds a zero-dependency catalog subpath with official and vendor-official adapter metadata, environment specs, and setup helpers.
Adds support for stable IDs to link button action handlers.
Adds a burst concurrency strategy.
Adds a subpath for AI utilities, keeping the optional ai and zod peer dependencies out of bundles that do not use them.
Exposes Chat SDK operations as AI SDK tools, with presets, per-tool or global approval settings, and tools that can be cherry-picked.
A standalone reviver is exported for workflow-safe deserialization without adapter dependencies.
Buttons can use actionType: "modal" to support Teams dialogs.
Adds an onOpenModal webhook hook for Teams dialog support.
Adds an enum distinguishing private, workspace, external, and unknown channel scopes.
The Adapter interface gains an optional disconnect() hook, called during chat.shutdown() for resource cleanup.
Introduces the LinkPreview object on Message.
Adds an API for transforming history to AI SDK messages.
Adds forceReleaseLock to StateAdapter for interrupting or steering long-running handlers.
Adds a config option for interrupting or steering long-running handlers when a lock conflict occurs.
Adds a type for scheduled messages, with cancellation support in the Slack adapter.
Adds scheduling of messages to be sent at a future time; the Slack adapter uses chat.scheduleMessage and supports cancellation.
Adds support for native Slack tables.
Button() accepts a disabled prop for Google Chat and Discord.
This config option suppresses the initial "..." placeholder in post+edit fallback streaming.
The Slack adapter adds an onMemberJoinedChannel handler.
Added the CardLink element.
ChatConfig now accepts dedupeTtlMs to configure the deduplication TTL; the default TTL is 5 minutes.
The Slack adapter now supports typing indicators using the Slack assistants API.
Adds handler registration for assistant_thread_context_changed events.
Adds handler registration for assistant_thread_started events.
Adds a Slack adapter method for setting assistant status.
Adds a Slack adapter method for setting assistant title.
Adds a Slack adapter method for setting suggested prompts.
Accepts stopBlocks to support Block Kit on stream finalization.
Adds support for slash commands.
Cards can now include inline Select components.
Cards can now include Radio buttons.
Support was added for creating modals from ephemeral messages.
Adds a field for passing arbitrary metadata through the modal lifecycle.
Adds GitHub and Linear integrations.
Added to modal events to provide related message information.
Added to modal events to provide related thread information.
Adds the LinkButton component for use in applications.
Adds postEphemeral() for ephemeral messages.
Adds a private metadata field to onModalClose events.
Adds support for modal events.
Adds support for modals.
Adds support for selectors.
Adds support for text inputs.
Adds Discord support.
Adds native streaming support.
Performance 1
Resolved user profiles are cached in the state adapter for 1 hour, or 5 minutes after failed lookups, avoiding a Graph call per message.
Behavior and default changes 48
Built-in AI tools set aiGenerated: true on the markdown and raw messages they post or edit.
Button callbackUrl POST requests now include card input values, including when no onAction handler is configured.
TextInput can now be used as a CardChild, allowing users to reply directly on a card instead of opening a dialog.
Teams requires card inputs unless optional is set and validates every input before allowing a button through.
Custom Bot Framework and Graph token factories take precedence over client-secret environment variables.
Accepts the stream returned by TanStack's chat().
Can be set to false to disable count-based history eviction; the default remains 200 entries per user.
The Teams adapter renders Table as the native Adaptive Cards 1.5 Table element, with grid lines, relative column sizing, and an accessible header row.
The Teams adapter uses the service URL encoded in the thread ID for messaging operations, reaching regional or sovereign endpoints; an explicit apiUrl still pins every call.
Accepts the stream returned by TanStack's chat().
Accepts the stream returned by TanStack's chat().
Emitted Adaptive Cards now declare schema version 1.5, which supports action tooltips.
AI tool scoping is tightened.
The Teams cards subpath renders button and link-button tooltips and card width hints as Adaptive Card properties.
Direct-message conversations are isolated.
Google Chat pagination now uses native pagination.
Queued-message scoping is tightened.
Recording storage is bounded.
Slack caches are isolated.
Telegram file downloads are returned as portable ArrayBuffer data while Buffer support remains in the shared attachment contract.
Locks remain alive while message handlers run, keeping queue, burst, and debounce strategies serialized beyond the lock TTL; loops stop if ownership is lost, and debounce continues draining messages that arrive during a handler.
Supports native stop cancellation for Slack Agent Sessions.
Replied-to message context is preserved in normalized messages and populated from Telegram replies.
Built-in agent read tools are confined to the conversation being handled, with an optional scope override.
Cards degrade to text with tappable URL and mention entities plus a URL preview attachment.
Supports editing and deleting the bot's own messages; the first edit of a fresh message is age-gated by editSafetyDelayMs.
Adds typing keep-alive support.
Requests include a chat-sdk-xchat product token so Chat SDK traffic is identifiable in X API request logs; a User-Agent set via apiHeaders takes precedence.
Normalized incoming Teams message authors now expose Microsoft Graph email addresses.
Explicitly configured progress is shown as a native Teams DM status while native streaming is preserved.
The chat package now accepts AI SDK v7 as a peer dependency alongside v6.
Carries folded active-view context so handlers can identify the view associated with a Home-open event.
Accepts AI SDK v7 and newer compatible React, Svelte, and Vue AI SDK peer dependency majors.
Environment credential fallback remains enabled unless an auth or verification field is explicitly passed, so unrelated config such as agentView does not suppress it.
Under agentView, each user message in the Messages tab is a thread root; subscribed conversation-scoped threads from openDM() continue to receive top-level DM messages.
Surfaces Slack's per-block validation details when the API rejects blocks.
The interactive state picker now filters out CLI-incompatible state adapters.
The default message dedupe TTL increases from 5 to 10 minutes to outlast platform redelivery windows; configuring dedupeTtlMs still overrides it.
Adds native draft streaming in Telegram private chats, with fallback streaming elsewhere.
Slack native streaming can send markdown tables without wrapping them in code fences; other consumers retain the append-only table fallback.
New concurrency strategies are implemented for overlapping messages.
Migrates the remaining adapters to shared error types.
Adapters have updated fallback-render behavior.
Markdown can be rendered fully while streaming, including on Slack, which has native streaming.
Markdown rendering now supports tables.
A new ConsoleLogger is created automatically.
Routes the Slack assistant_thread_context_changed event.
Routes the Slack assistant_thread_started event.
Tooling 2
Adds Teams support to the command's Connect scaffolding.
The CLI scaffold now registers the XChat adapter.
Fixes and other changes 40
SlackAdapter and TelegramAdapter can now be assigned to Adapter when exactOptionalPropertyTypes is enabled.
The default thread signal is created on first use, and threads revived inside the Workflow DevKit VM receive an inert signal that never aborts, fixing workflow startup failures.
Modal context is retained until the modal closes, keeping relatedThread and relatedChannel available after validation errors or update and push responses.
Restoring threads and channels now preserves streaming settings and runtime ownership.
After a failed state connection, the next initialize() call or webhook retries initialization; concurrent callers share one attempt, and failures remain cached until shutdown().
The chat JSX runtime now forwards align on Table, matching fromReactElement.
Telegram polling waits for handlers to settle before advancing update offsets.
Callback tokens are consumed once.
Buttons retain their tooltip and other fields when callbackUrl is swapped for a callback token.
Ephemeral follow-ups are preserved.
Explicit Google Chat bot identity is required for reliable mention handling.
Preserves adapter-returned thread ids when editing channel messages.
Queued and debounced messages are isolated by thread when using channel-scoped locks.
Keeps empty table cells to preserve tab-separated column alignment and drops rows with no content, such as placeholder header rows.
Automatic Chat class serializers are isolated from Node-only runtime dependencies.
Email addresses no longer trigger bot mentions because an @ following a word character is not treated as a mention.
Personal and group conversations are routed using their explicit conversation type, so group chats use buffered fallback even when their IDs resemble direct messages.
Detects mentions from structured mention entities, swipe-replies to the bot, and a plain-text @handle fallback.
Keeps messages with empty text when they have supported links or attachments, including images and fetchable text files. Messages with only unsupported attachments remain skipped, and multipart content may contain only attachment parts.
No longer falsely matches @bot when the mention is @bot-dev.
The Telegram adapter no longer falsely matches @bot when the mention is @bot-dev.
Preserves markdown structural whitespace when extracting normalized message text.
Preserves skipped mention routing for debounce and message patterns.
Bot mentions are now detected in skipped queue and burst messages.
Streaming posts without an incoming message context no longer crash when using lightweight thread handles.
Fallback streaming is guarded against empty post and edit calls.
Source maps are disabled in published packages.
An adapter is now used to parse the channel ID for thread.channelId.
Step separator detection for AI SDK v5+ now checks for the finish-step event.
The Thread and Channel interfaces now declare the toJSON() method to match their implementations.
Accumulates text and posts once instead of using the fallbackStream post-and-edit loop that caused 422 errors from empty body edits during TTFT; fallbackStream edit failures are now logged via Logger.
Makes EphemeralMessage generic over TRawMessage.
Fixes compatibility with AI SDK 6.
Fixes non-atomic message deduplication that could silently drop app_mention events.
Improves streaming for AI SDK.
Fixes a crash when openModal is called for an action with no thread context, such as a home tab button.
Streaming markdown healing is added with remend.
Fixes lost metadata that prevented deserialized threads from posting streams to Slack.
allMessages was fixed and tested.
fetchMessages was fixed and tested.