Commit Graph

112 Commits

Author SHA1 Message Date
dancer 5cead87abf chore: merge main into cli branch 2026-05-29 18:05:52 +01:00
dancer fd6554d9ac feat(cli): rename scaffold package 2026-05-29 18:00:19 +01:00
mazhe-nerd 293356525d feat: docs: add Lark / Feishu adapter (#517)
## Summary

Adds
[`@larksuite/vercel-chat-adapter`](https://www.npmjs.com/package/@larksuite/vercel-chat-adapter),
the Lark / Feishu adapter for Chat SDK, as a **vendor-official community
adapter**.

- **Package**: `@larksuite/vercel-chat-adapter` — published on npm under
the official `larksuite` scope
- **Built on**:
[`@larksuiteoapi/node-sdk`](https://www.npmjs.com/package/@larksuiteoapi/node-sdk)'s
`LarkChannel`, the official Lark Node SDK
- **Docs source**: external README referenced by `adapters.json` lives
in
[`larksuite/node-sdk`](https://github.com/larksuite/node-sdk/tree/cbc4adf13cbcb93b389db01faf428e3b3cef053c/docs/vercel-chat-adapter)
(the official Lark vendor-owned GitHub org, pinned at commit
`cbc4adf1`); the in-tree MDX in this PR is the rendered detail page
(`mdxBody: true`)
- **Capabilities**: native cardkit typewriter streaming, interactive
cards, reactions, edit / delete, message history (via SDK
`normalize()`), DM detection, mention handling, and scan-to-create app
onboarding through `registerLarkApp`

## Changes

| File | Change |
|---|---|
| `apps/docs/adapters.json` | Add Lark / Feishu entry (`community:
true`, `vendorOfficial: true`) |
| `apps/docs/content/adapters/vendor-official/lark.mdx` | New
hand-authored MDX detail page (frontmatter with full features matrix,
install / quick start / configuration / transport / streaming / ID
encoding / history / safety / limitations / FeatureSupport) |
| `apps/docs/content/adapters/vendor-official/meta.json` | Append
`"lark"` to the sidebar `pages` array |
| `packages/integration-tests/src/docs-adapters.test.ts` | Append
`"lark"` to the hardcoded vendor-official slug list asserted by
`Vendor-Official adapter MDX › contains exactly the expected adapters` |

No icon registered in `adapters.json` / `iconMap` / `adapterLogos` —
matches the existing pattern for vendor-official adapters (Beeper,
Resend, Liveblocks, Zernio, Photon).

## Vendor Official tier

Per `docs/contributing/building.mdx` (Qualifications for vendor official
tier):

- ✅ **Commitment for continued maintenance** — owned by the Lark /
Feishu team
- ✅ **GitHub hosting in official vendor-owned org** — adapter README
lives in [`larksuite/node-sdk`](https://github.com/larksuite/node-sdk),
the official Lark org
- ✅ **Documentation in primary vendor docs** — will be cross-linked from
the official Lark Open Platform developer documentation
- ✅ **Announcement** — will be announced through Lark developer
changelog / channels

## A note on source visibility

The adapter source is not currently open-sourced due to internal
release-process requirements. What is public:

- The npm package itself (consumable by any user)
- The README, hosted in `larksuite/node-sdk` (official Lark org)
- The underlying
[`@larksuiteoapi/node-sdk`](https://github.com/larksuite/node-sdk) on
which it is built — this *is* fully open-source

## Test plan

- [x] `pnpm --filter docs build` — docs app builds cleanly;
`/en/adapters/vendor-official/lark` and
`/en/adapters/vendor-official/lark/og` routes are generated
- [x] `pnpm typecheck` — passes (33 tasks)
- [x] `pnpm check` (Ultracite / Biome) — 438 files, no fixes
- [x] `pnpm --filter @chat-adapter/integration-tests test docs-adapters`
— 232 tests pass (frontmatter, vendor-official roster, adapters.json ↔
MDX sync)
- [x] Manual: `/adapters` lists the Lark / Feishu card in the **Vendor
Official** section; `/adapters/vendor-official/lark` renders the MDX
detail page with the FeatureSupport matrix
2026-05-29 12:40:43 +10:00
Meet Modi bd38498b60 docs(adapters): add AgentPhone as vendor-official adapter (#562)
## Summary

Adds [AgentPhone](https://agentphone.to) as a vendor-official adapter in
`adapters.json`.

AgentPhone is the first Chat SDK adapter with unified support for **SMS,
MMS, iMessage, and voice calls** in a single package:

- **SMS/MMS** — send and receive via `agent.message` webhooks
- **iMessage** — full support including tapback reactions (love, like,
laugh, etc.) and expressive send styles
- **Voice** — inbound/outbound call transcripts and summaries via
`agent.call_ended` webhooks
- **HMAC-SHA256** webhook verification with replay protection

| | SMS | MMS | iMessage | Voice |
|---|---|---|---|---|
| Send messages | ✓ | ✓ | ✓ | — |
| Receive messages | ✓ | ✓ | ✓ | — |
| Media attachments | — | ✓ | ✓ | — |
| Reactions | — | — | ✓ | — |
| Call transcripts | — | — | — | ✓ |

- **npm**:
[`@agentphone/chat-sdk-adapter`](https://www.npmjs.com/package/@agentphone/chat-sdk-adapter)
- **GitHub**:
[`AgentPhone-AI/chat-sdk-adapter`](https://github.com/AgentPhone-AI/chat-sdk-adapter)
- **Docs**: [docs.agentphone.ai](https://docs.agentphone.ai)
2026-05-29 12:03:10 +10:00
Ben Sabic 9b8d8c4518 Discoverability lift: link KB guides, broaden npm keywords, mirror to AGENTS.md (#560)
Broad SEO/AEO pass across the docs site, adapter READMEs and AGENTS.md
files, and npm package metadata so Chat SDK content shows up better in
search engines, in LLM-driven package recommendations, and in
IDE/coding-agent context.

**Docs site**

- Adds a `## Resources` section to the Getting Started and AI overview
pages and to the Slack, Discord, GitHub, Liveblocks, and Sendblue
adapter pages, each linking to applicable guides/templates with
descriptions sourced from `resources-edge-config.json` and a cross-link
back to the central `/resources` hub.

**Adapter packages**

- Mirrors the same Resources sections into the Slack, Discord, and
GitHub READMEs (so they surface on npm) and into their AGENTS.md files
(so coding agents see them alongside the API notes).
- Expands `keywords` on every published adapter and state package — adds
`chat-sdk`, `chatbot`, `ai-agent`, `ai-sdk`, `vercel`, plus
platform-specific terms like `slack-bot`, `block-kit`, `slash-commands`,
`github-app`, `whatsapp-business`, `state-adapter`.

**Resources registry**

- Registers four new entries in `resources-edge-config.json`
(Human-in-the-Loop guide, Liveblocks AI agent guide, Slack + Vercel Blob
guide, Durable iMessage Agent template) and runs `pnpm sync-resources`
so the bundled `chat` package guides, `templates.json`, and
`skills/chat/SKILL.md` all pick them up.
- Fixes the synced Slack AI agent guide to import `toAiMessages` from
`chat/ai` instead of the deprecated `chat` re-export path (the upstream
KB source has also been updated, so future syncs will preserve this).

**Drive-by fixes**

- Resend adapter doc quick start: corrects `MemoryStateAdapter` class
import to the `createMemoryState()` factory (matching every other
adapter doc).
- Zalo adapter doc: drops the "community adapter" callout that
duplicated frontmatter.

**Tooling / CI**

- Adds `tsx` as a root devDependency so `pnpm sync-resources` works out
of the box (it previously relied on `npx tsx`, which hung when not
pre-cached).
- Loosens the CI changeset gate to also skip `packages/chat/resources/`
(generated data), matching the existing `*.md` carve-out.

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-05-29 11:41:23 +10:00
luren 5461ea982b feat(telegram): add native DM draft streaming with segmented stream results (#340)
## Summary

Add native Telegram DM streaming via `sendMessageDraft` while preserving
Chat SDK's existing post+edit fallback for non-DM threads.

This PR:
- adds native private-chat draft streaming to the Telegram adapter
- splits long streamed markdown into Telegram-safe persisted segments
- retries without `parse_mode` when Telegram rejects markdown entity
parsing
- exposes segmented native stream results in the chat core
- updates docs and feature matrices to reflect Telegram DM draft
streaming

---------

Co-authored-by: dancer <josh@afterima.ge>
2026-05-28 15:17:10 -07:00
josh 6ed4a43897 feat(slack): add low-level interaction helpers (#559)
## summary

adds lower-level Slack helpers for custom runtimes that already own
routing, state, sessions, or workflow execution

this expands the existing Slack subpaths with typed inbound files,
richer interaction payload fields, thread reply fetching, modal opening,
and generic input request Block Kit helpers

it keeps the helpers on the existing `@chat-adapter/slack` package and
preserves the runtime-light import boundary for `webhook`, `api`, and
`blocks`
2026-05-27 20:21:12 -07:00
josh 25ebc3b925 feat(twilio): add sms, mms, and voice helpers (#558)
## summary

adds a first-class Twilio adapter for SMS and MMS bots, plus low-level
voice helpers for custom Twilio voice routes

this includes webhook parsing and signature verification, outbound
Messages API helpers, phone-number and Messaging Service sending,
inbound MMS attachments with authenticated `fetchData`, plain text card
fallback rendering, markdown conversion, and runtime-light `api`,
`webhook`, `voice`, and `format` subpaths

the adapter intentionally avoids the `twilio` npm runtime dependency so
apps can use the low-level helpers without pulling in the full SDK
2026-05-27 15:39:23 -07:00
josh 0488a6613c docs(slack): document low-level api subpaths (#557)
## summary

documents the low-level Slack subpaths for custom runtimes that own
routing, state, sessions, or workflow execution

covers `@chat-adapter/slack/webhook`, `@chat-adapter/slack/format`,
`@chat-adapter/slack/api`, and `@chat-adapter/slack/blocks`, including
when to use them instead of the full `createSlackAdapter` runtime

also links the new guide from the Slack adapter page
2026-05-27 09:16:47 -07:00
Ben Sabic ac8a20779c feat(chat): add chat/ai subpath for AI SDK utilities (#492)
## Summary

Introduces a dedicated `chat/ai` subpath as the home for every Vercel AI
SDK helper that ships with Chat SDK. Importing from this subpath keeps
the optional `ai` and `zod` peer dependencies out of bundles that don't
use them.

### What's new

- **`createChatTools`** — exposes Chat SDK operations as ready-to-use AI
SDK tools so an agent can read, post, react, edit, delete, and manage
thread subscriptions across every adapter the supplied `Chat` instance
has registered.
- Write operations require user approval by default (`requireApproval:
true`); toggle globally or per-tool.
- Three presets — `reader`, `messenger`, `moderator` — scope the
toolset.
- Individual tools can also be cherry-picked (`import { postMessage,
addReaction } from "chat/ai"`).
- **`toAiMessages`** (and the `Ai*` / `ToAiMessagesOptions` types) now
live alongside the tools at `chat/ai`. The previous `chat` re-exports
continue to work, but are flagged `@deprecated` with an editor hint
pointing to the new home — migration is a one-line import change.
- **Docs** — new `/docs/ai` section between Usage and Adapters in the
sidebar:
  - `/docs/ai` — Overview
  - `/docs/ai/ai-sdk-tools` — `createChatTools` guide
  - `/docs/ai/to-ai-messages` — `toAiMessages` reference
  - `/docs/ai/types` — Reference for every type exported from `chat/ai`
- **Example app** — `examples/nextjs-chat` now demos the new surface via
a "Run Agent Demo" button on the welcome card and a free-form `/agent
<prompt>` slash command (streaming, with a placeholder so users get
immediate feedback in channel contexts where Slack's typing-status API
is a no-op).

### Future plans

`createChatTools` currently exposes the cross-adapter Chat SDK surface
only. A natural follow-up is to also support **platform-specific tools**
— e.g. expose Slack-only `pin`/`unpin`, Discord-only thread archiving,
GitHub-only issue commenting, etc., so users can further extend what
their agent can do without dropping back to raw adapter calls. The shape
would likely be additional opt-in factories under `chat/ai` (or
per-adapter subpaths like `@chat-adapter/slack/ai`) that return tools
layered on top of the platform-specific adapter clients, while keeping
the cross-platform `createChatTools` API as the lowest common
denominator.

### Coverage

- `createChatTools` orchestrator: 100% statements / 94.7% branches.
- Every tool factory's `execute()` is exercised end-to-end (29 tests in
`index.test.ts`).
- `toAiMessages` keeps its existing 35-test suite covering role mapping,
attachment handling, links, transforms, and unsupported-attachment
fallbacks.
- Tools folder overall: 99.0% statements / 86.1% branches / 97.4%
functions / 98.9% lines.

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
Co-authored-by: dancer <josh@afterima.ge>
2026-05-14 20:13:42 -07:00
josh b75eedbb5f feat(chat): add queue-debounce concurrency strategy (#495)
## summary

adds an opt-in `burst` concurrency strategy for #414

when a thread is idle, the first message waits for `debounceMs`,
messages that arrive during that window are queued, and the handler runs
once with the latest message plus earlier burst messages in
`context.skipped`

after the handler finishes, messages that arrived while it was running
are drained like `queue`, so the latest queued message is processed with
earlier queued messages in `context.skipped`

keeps existing `drop`, `queue`, `debounce`, and `concurrent` behavior
unchanged

updates docs to cover `burst`, explain when to choose it over
`debounce`, and document the related `MessageContext` behavior
2026-05-14 19:18:40 -07:00
Hugo 716e934aa2 feat(web-adapter): first class support for Vue and Svelte (#498)
## Summary

<!-- What does this PR do? -->

## Test plan

<!-- How did you verify the changes? -->

## Checklist

- [ ] All commits are signed and verified
- [ ] `pnpm validate` passes
- [ ] Changeset added (or N/A — see
[CONTRIBUTING.md](./CONTRIBUTING.md))
- [ ] Documentation updated (or N/A)

---------

Co-authored-by: dancer <josh@afterima.ge>
2026-05-14 19:18:24 -07:00
Ben Sabic 2f108bd4b3 feat: rename adapter.client to adapter.octokit / adapter.linearClient / adapter.webClient (#478)
## Summary

Rename the public native client getter on the GitHub, Linear, and Slack
adapters so it matches the underlying SDK class instead of the generic
`.client`. The previous `.client` getter is preserved as a `@deprecated`
alias on all three adapters, so existing code keeps working without
changes.

- `bot.getAdapter("github").client` is now
`bot.getAdapter("github").octokit` (returns `Octokit`)
- `bot.getAdapter("linear").client` is now
`bot.getAdapter("linear").linearClient` (returns `LinearClient` from
`@linear/sdk`)
- `bot.getAdapter("slack").client` is now
`bot.getAdapter("slack").webClient` (returns `WebClient` from
`@slack/web-api`)

Each new getter has TSDoc covering single- vs multi-tenant resolution
rules and when calling outside a webhook handler throws. Focused unit
tests assert that the new getter returns the underlying SDK instance,
that single-tenant calls return the same instance, that the deprecated
`.client` alias points at the new getter, that multi-tenant mode without
webhook context throws on both getters, and that inside a webhook
context the getter resolves to the per-tenant client.

Commits are split for review:

1. `feat(adapter-github): rename adapter.client to adapter.octokit`
2. `feat(adapter-linear): rename adapter.client to adapter.linearClient`
3. `docs: use .octokit / .linearClient in chat-sdk.dev examples`
4. `chore: changeset for adapter native client getter rename`
5. `fix: hoist regex literals in new client-getter tests to module
scope`
6. `feat(adapter-slack): rename adapter.client to adapter.webClient`
7. `docs: include slack .webClient in chat-sdk.dev examples`
8. `chore: include adapter-slack in native client getter rename
changeset`
9. `test(adapter-github,adapter-linear): cover with-context resolution
on the new client getters`

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-05-13 08:35:31 +10:00
Ben Sabic 79227ae991 docs: refresh adapter pages with hand-authored MDX (#474)
## Summary

Refreshes the adapter docs end-to-end so every adapter — official,
vendor-official, and community — now ships hand-authored MDX, lives
under a clean URL structure, and renders on a polished
sidebar/right-rail layout dedicated to `/adapters` (the shared `/docs`
chrome is untouched).

```mermaid
flowchart LR
  subgraph Before
    direction TB
    OB[official] --> CB[community<br/>incl. 5 vendor pages]
  end
  subgraph After
    direction TB
    OA[official] --> VA[vendor-official<br/>5 pages] --> CA[community]
  end
  Before -.-> After
```

### Content & routing

- **New `/adapters/vendor-official/<slug>` route** for vendor-maintained
adapters (Beeper Matrix, Photon iMessage, Liveblocks, Resend, Zernio).
Sidebar gets a third labelled group ("Vendor-Official Adapters") between
Official and Community, with a top divider matching the existing
Community treatment.
- **All 13 vendor-official + community adapters migrated** from runtime
README fetching to hand-authored MDX with rich `features:` matrices and
full body content (install, quick start, configuration, auth,
gateway/streaming, troubleshooting). README fetch stays as a fallback
for any future community adapter that hasn't been migrated yet, gated by
a new `mdxBody: true` frontmatter flag.
- **Messenger filter pages removed** (`/adapters/for/<messenger>` + the
"Browse by messenger" chip row on `/adapters`). Existing URLs
308-redirect to `/adapters`.
- **Permanent redirects** from
`/adapters/community/{matrix,imessage,resend,zernio,liveblocks}` to
their new `/adapters/vendor-official/...` paths.
- **Fixed** `/docs/adapters` and `/docs/state` so the bare pages are
accessible again — the previous catch-all redirect (`:slug*`) was
swallowing them. Switched to `:slug+` so subpath URLs still 308 while
the bare pages render.

### Visual polish

- **Adapter-only sidebar variant** (`AdaptersDocsLayout` +
`AdaptersSidebar`) with uppercase eyebrow separators, tighter rows, and
a thin themed scrollbar utility class. The shared `/docs` sidebar is
untouched.
- **Restyled `AdapterHero`**: drops the badges row + packageName, sits
the title inline with the logo, larger 17 px tagline, horizontal divider
beneath the block.
- **Restyled `PackageInstall`** as a tabbed dark single-line snippet
with a `$` prompt prefix and a copy button — replaces the previous
multi-line `CodeBlock` layout.
- **New "Deploy your chat app on Vercel" upsell card** (`<Upsell />`)
replaces the old `EditSource / ScrollTop / Feedback / CopyPage` footer
cluster on every adapter detail page.
- **Listing & messenger pages**: align the H1 to a tighter `text-4xl
sm:text-[44px]`, and the section headers to `text-base font-medium
tracking-tight` with a one-line muted lede.

### Tooling & tests

- Added `mdxBody: true` opt-in to the adapter frontmatter schema
(`source.config.ts`), and updated both detail-page handlers
(`community/[slug]` and the new `vendor-official/[slug]`) to render the
MDX body when present, falling back to README fetch otherwise.
- Refactored both detail-page handlers to flatten the body-render
branches into a `renderBody()` helper, removing the nested ternaries
that were tripping `lint/style/noNestedTernary`.
- New test file
[`packages/integration-tests/src/docs-adapters.test.ts`](https://github.com/vercel/chat/blob/docs/refresh-adapters/packages/integration-tests/src/docs-adapters.test.ts)
— **220 new assertions** covering:
- Adapter MDX frontmatter completeness, slug ↔ filename consistency, and
`type ∈ {platform, state}`.
- Vendor-official invariants: exactly the expected slugs,
`vendorOfficial: true`, `community: true`, `author`, `mdxBody: true`,
`<FeatureSupport />` rendered.
- Community invariants: `community: true` (never vendor-official),
`mdxBody: true`, `<FeatureSupport />`.
- Official invariants: never flagged, `packageName` always under
`@chat-adapter/*`.
- `adapters.json` ↔ MDX sync on `packageName` / `type` / `community` /
`vendorOfficial`.
- Extended `VALID_DOC_PACKAGES` so `docs-content.test.ts` accepts the
new vendor-official + community packages, plus `@chat-adapter/web`,
`@chat-adapter/web/react`, and `@chat-adapter/messenger`.

### Per-package AGENTS.md

- Added `AGENTS.md` to every official adapter and state adapter (14
packages), each tailored to that adapter's surface — overview, directory
layout, build/test commands, public exports, thread ID format, webhook
flow, authentication, format conversion, cards/streaming, platform
quirks, testing approach, coding conventions, and release rules.
- Added a one-line `CLAUDE.md` (`@AGENTS.md`) beside each so Claude Code
picks up the same instructions through its built-in resolver — same
convention as the root.

### Web adapter copy

- Cleaned up the Web adapter tagline (removed inline backticks) and
dropped the now-redundant "v1 scope" section from the body.

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-05-12 16:01:19 +10:00
josh 67c1794a54 docs(chat): clarify direct message routing precedence (#491)
## summary

clarifies that registered `onDirectMessage` handlers take precedence for
incoming DM messages before subscribed-message, mention, and pattern
routing

updates the direct messages, event handling, thread subscription, and
API docs so they match the current runtime behavior

adds `onDirectMessage` to the Chat API docs

fixes #432
2026-05-12 15:14:57 +10:00
josh add27309fb feat(telegram): support typed attachment uploads (#485) 2026-05-11 18:56:42 -07:00
Ben Sabic fdebde7988 Reapply "feat(slack): expose direct WebClient access via adapter.client" (#472) (#476)
This reverts commit 2279f1db70.

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-05-12 08:32:54 +10:00
Salman Shaikh 2ffed48bea feat: make adapter internals protected to enable subclassing (#475)
* chore: changed class function access to protected

* chore: format

* chore: added changeset

* chore(changeset): drop unchanged shared package, expand description

* refactor(adapters): keep internal state private, only protected for extension surface

Narrow scope of #475: caches, polling/runtime state, and one-shot warning
flags stay private. Methods and shared helpers (logger, formatConverter,
chat, config) remain protected as the documented extension surface.

Also fixes a typecheck failure where gchat's oauth2Client (now private)
no longer requires a portable type for the emitted .d.ts.

* test(adapters): add subclass extensibility tests

Each adapter now has a compile-time test that subclasses can access the
documented protected surface. If any of these members revert to private,
the test file fails to type-check.

* style: apply ultracite formatting to subclass tests

* style(slack): mark static cache TTL constants as readonly

These three protected static cache TTLs are configuration constants, not
mutable state. Marking them readonly prevents subclasses (the new extension
surface from this PR) from mutating values shared across every instance
in-process.

* test(adapters): document intent of subclass extensibility tests

Mirrors the inline comment from the Telegram subclass test across the other
nine adapter tests so future readers immediately understand these blocks are
type-only sentinels — they fail at typecheck (not vitest) if a member reverts
to private.

* docs(adapters): document subclassing for adapter customization

Adds a "Customizing an adapter via subclassing" section to the Adapters page
that walks through extending an official adapter to override a protected hook
(using the issue #433 Telegram processUpdate scenario as the canonical
example) and clarifies that private members are intentionally off-limits.

* refactor(linear): expose accessTokenExpiry to subclasses

The surrounding refreshClientCredentialsToken and ensureValidToken methods
are now protected, but accessTokenExpiry was kept private — meaning a
subclass overriding either method couldn't read or update the expiry without
calling super. Flipping it to protected lets subclasses fully reimplement
the token-refresh flow.

* chore(changeset): bump adapters from patch to minor

This PR adds a new, additive capability — subclassing official adapters to
override protected hooks. Per CONTRIBUTING.md, additive backward-compatible
features warrant a minor bump rather than a patch.

* docs(adapters): clarify subclassing surface stability

Correct the parenthetical describing what stays `private` (credentials are
now `protected`) and add a callout warning that the `protected` extension
surface is intentionally broader than the public API but not yet fully
stable, so subclass authors know to pin versions and prefer overriding
the smallest hook.

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-05-10 20:44:41 +10:00
Vishal Yathish 2279f1db70 Revert "feat(slack): expose direct WebClient access via adapter.client" (#472)
* Revert "feat(slack): expose direct WebClient access via adapter.client (#471)"

This reverts commit 8366b8b0fb.

* Fix: The `createSlackAdapter()` helper function silently drops the `apiUrl` config field, so custom Slack API URLs (e.g., for GovSlack) are ignored when using the helper.

This commit fixes the issue reported at packages/adapter-slack/src/index.ts:5055

**Bug explanation:**

The `SlackAdapterConfig` interface defines an `apiUrl` field (line 166) that allows users to override the Slack Web API base URL — useful for GovSlack or self-hosted gateways. The `SlackAdapter` constructor reads this field at line 622:

```typescript
const slackApiUrl = config.apiUrl ?? process.env.SLACK_API_URL;
```

However, the `createSlackAdapter()` helper function (around line 5055) constructs a `resolved` config object that includes many fields from the user's config but omits `apiUrl`. This means when a user writes:

```typescript
createSlackAdapter({ apiUrl: "https://slack-gov.com/api/" })
```

The `apiUrl` is silently dropped and the `WebClient` is created without the custom URL. The `SLACK_API_URL` environment variable fallback still works (since it's checked in the constructor), but explicit config via the helper is lost.

This is clearly a bug — all other config fields are forwarded through the `resolved` object, and `apiUrl` was simply forgotten.

**Fix explanation:**

Added `apiUrl: config?.apiUrl,` to the `resolved` config object in `createSlackAdapter()`. This ensures the `apiUrl` value from user config is properly forwarded to the `SlackAdapter` constructor, matching the pattern used for all other optional config fields.

Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
Co-authored-by: visyat <vishal.yathish@gmail.com>

---------

Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com>
Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
2026-05-09 02:21:33 -07:00
Ben Sabic 8366b8b0fb feat(slack): expose direct WebClient access via adapter.client (#471)
* feat(slack): expose direct WebClient access via adapter.client

Mirror the Linear and GitHub adapter pattern by exposing the underlying
@slack/web-api WebClient as `adapter.client` for any Web API call not
covered by the SDK's high-level methods.

Resolution order:
1. Token from the current request context (multi-workspace webhooks,
   `withBotToken()`).
2. The default `botToken` when configured as a static string or a
   synchronous resolver function.

Throws AuthenticationError outside of any context in multi-workspace
mode, or when `botToken` is configured as an async resolver. For both,
bind the token explicitly with `adapter.withBotToken(token, () => ...)`.

Internally, the existing private `client` field is renamed to `_client`
so the public getter can return per-token cached `WebClient` instances.
All internal API calls continue to route through `_client.foo(await
this.withToken(...))` unchanged. Also fixes `createSlackAdapter()`
silently dropping the `apiUrl` config field, surfaced by the new
apiUrl-propagation test.

* docs(slack): document direct WebClient access

Add Slack to the "Direct client access" section of the chat-sdk.dev
docs (api/chat.mdx, usage.mdx) alongside Linear and GitHub. Update the
multi-tenant Callout to spell out both Slack constraints — request
context required in multi-workspace mode, and `withBotToken()` required
when `botToken` is an async resolver.

Add a parallel "Direct WebClient access" section to the Slack adapter
README with a usage example, the token resolution order, and the
async-resolver workaround.

* feat(example): add Channel Info button using slack.client

Demonstrate the new direct WebClient access pattern in the nextjs-chat
demo with a "Channel Info (Slack)" button. The handler resolves the
Slack adapter from the action event, reaches into
`adapter.client.conversations.info` (channels:read scope, already in
the example manifest), and renders the result as a Card with channel
name, member count, topic, purpose, and the standard flags. Falls back
to a friendly message on non-Slack platforms.

* feat(example): add Pin Message button using slack.client.pins.add

Pin the welcome card itself via `adapter.client.pins.add({ channel,
timestamp: event.messageId })` to demonstrate calling a Slack Web API
endpoint not wrapped by the SDK. Adds the required `pins:write` scope
to the example Slack manifest.

* chore(example): render channel info as a table and include num_members

Replace the Fields/Section layout in the Channel Info card with a
two-column Table for a tidier presentation, and pass
`include_num_members: true` so the Members row is actually populated
(Slack's `conversations.info` omits it by default).

* test(slack): expand coverage for adapter.client

Adds three tests:

- Cache differentiation: distinct tokens produce distinct WebClient
  instances so per-workspace credentials never bleed across calls.
- apiUrl env var resolution: SLACK_API_URL is honored by the WebClient
  the new getter returns (covers GovSlack-style deployments).
- End-to-end multi-workspace token routing: a real block_actions
  webhook drives `processAction`, and the handler-side
  `event.adapter.client.token` matches the installation's bot token —
  proving the request-context-bound client works inside webhook
  dispatch.

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-05-09 18:59:39 +10:00
Ben Sabic 0adf3adef6 feat(tests): add @chat-adapter/tests test kit (#470)
* feat(tests): add @chat-adapter/tests test kit

New package providing Vitest factories, custom matchers, and a setup file for
people building Chat SDK adapters and bots.

Factories: createMockAdapter, createMockChatInstance, createMockState (with
working in-memory subscriptions/locks/KV/queues), createTestMessage,
mockLogger / createMockLogger.

Matchers: toHavePosted(threadId, textPattern?), toHaveDispatched(handler),
toBeSubscribedTo(threadId). Auto-register via the
'@chat-adapter/tests/setup' subpath in vitest setupFiles.

chat and vitest are peer dependencies. Adapter-specific helpers (e.g. signed
Slack webhook builders) belong in each adapter's own /testing subpath, not
in this kit.

* test(integration-tests): allow @chat-adapter/tests imports in README check

* docs: add Testing page covering @chat-adapter/tests

New content/docs/testing.mdx walks bot authors and custom-adapter authors
through the kit's factories, custom matchers, and setup file. Added under
the Usage section in the sidebar, after error-handling.

Cross-link from contributing/testing.mdx clarifying that the hand-rolled
patterns there are for repo contributors building first-party adapters,
while consumers of Chat SDK should use @chat-adapter/tests.

* test(integration-tests): allow @chat-adapter/tests imports in docs check

* fix(tests): match real Adapter.postMessage signature in toHavePosted

Adapter.postMessage is (threadId: string, message: AdapterPostableMessage)
— previously the matcher read args[0] as { id: string } and args[1] as
{ text: string }, neither of which match the actual SDK shape. The matcher's
own tests fed the same wrong shape into the mock so they passed locally
while the matcher silently failed against any real bot or adapter.

Now compares args[0] as a string threadId, and extracts a comparable string
from AdapterPostableMessage's union — strings directly, PostableMarkdown
.markdown, PostableRaw.raw, and PostableCard.fallbackText. PostableAst and
fallback-less cards aren't text-matchable; documented in the JSDoc.

Tests updated to call postMessage with the real signature and to cover
each comparable AdapterPostableMessage shape.

* test(tests): add smoke tests driving matchers against a real Chat

Construct a real `Chat` with a `createMockAdapter` + `createMockState` and
exercise `Chat.thread().post()` and `.subscribe()` end-to-end. The matchers
(toHavePosted, toBeSubscribedTo) then assert against the actual call shape
the SDK uses, so a future signature drift breaks here instead of silently
agreeing with whatever wrong shape lives in the unit tests.

This is the regression guard for the postMessage-shape bug fixed in the
prior commit: each new matcher in subsequent PRs should be paired with a
smoke case here.

* feat(tests): round out adapter mutation matchers

Adds toHaveEdited, toHaveDeleted, toHaveReactedWith, toHaveStartedTyping,
and toHavePostedToChannel — covering the common Adapter mutation surface
that bot authors assert on. Each matcher's signature was checked against
packages/chat/src/types.ts, and each is paired with a smoke case that
drives a real Chat through the corresponding Thread/Channel API so
signature drift breaks the smoke test instead of silently agreeing with
the unit tests.

Emoji matching accepts both plain strings and EmojiValue ({ name }).
Text matching reuses the same extraction rules as toHavePosted —
strings, PostableMarkdown.markdown, PostableRaw.raw, and
PostableCard.fallbackText. Documented in matcher JSDoc, README, and
the Testing docs page.

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-05-09 14:17:36 +10:00
josh 0cc3d06fd4 docs: fix stale API examples, adapter matrix, and broken links (#463)
* 1

* 2

* cs

* 3
2026-05-08 03:58:08 -07:00
josh c1cd9b5da1 feat(chat): add callbackUrl to buttons and modals (#454)
* 1

* wfw

* 4224

* dfe

* wip

* f

* 22

* tsts

* more

* ch

* dc

* t

* tm

* docs

* ex

* k

* cs

* lock

* test(chat): expand callbackUrl coverage

* docs: document callbackUrl handling for adapter authors

* docs: expand changeset for callbackUrl feature

* docs(skill): mention callbackUrl on Button and Modal

* feat(example): add modal callbackUrl workflow demo

* test(integration): add replay tests for callbackUrl flows

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-05-08 17:26:18 +10:00
Vishal Yathish 68025ca965 [messenger] add messenger (meta) platform adapter to chat sdk (#461)
* [messenger] add messenger (meta) platform adapter to chat sdk

- Webhook handling with HMAC-SHA256 signature verification
- Generic and Button template support for cards
- Postback, reaction, delivery/read confirmation handling
- Message caching for fetchMessages (Messenger has no history API)
- Replay tests and ~98% code coverage

Co-authored-by: Dimitar K. Nikolov <mitkodkn@users.noreply.github.com>
Co-authored-by: Ben Sabic <27636870+bensabic@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix: The `@chat-adapter/messenger` package version is `4.15.0` while all other packages in the Changesets fixed version group are at `4.27.0`, breaking the fixed versioning contract.

This commit fixes the issue reported at packages/adapter-messenger/package.json:3

**Bug explanation:**

The repository uses Changesets with a `"fixed"` configuration: `[["chat", "@chat-adapter/*"]]`. This means all packages matching these patterns must always share the same version number. Every package in the group (`chat`, `@chat-adapter/discord`, `@chat-adapter/gchat`, `@chat-adapter/github`, `@chat-adapter/linear`, `@chat-adapter/shared`, `@chat-adapter/slack`, `@chat-adapter/teams`, `@chat-adapter/telegram`, `@chat-adapter/web`, `@chat-adapter/whatsapp`, and the state packages) is at version `4.27.0`, except `@chat-adapter/messenger` which is at `4.15.0`.

This is likely because the messenger adapter was newly added to the monorepo (copied from a template or created fresh) and its version was never aligned with the rest of the fixed group. This mismatch will cause problems with the Changesets release workflow — when Changesets tries to bump versions for the fixed group, it may produce inconsistent or errored releases because one package is 12 minor versions behind the others.

**Fix explanation:**

Changed `"version": "4.15.0"` to `"version": "4.27.0"` in `packages/adapter-messenger/package.json` to align it with all other packages in the fixed version group.

Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
Co-authored-by: visyat <vishal.yathish@gmail.com>

* Fix: Messenger adapter env var guard only checks `FACEBOOK_APP_SECRET` but `createMessengerAdapter` requires all three env vars, causing a `ValidationError` crash at Next.js build time when only `FACEBOOK_APP_SECRET` is set.

This commit fixes the issue reported at examples/nextjs-chat/src/lib/adapters.ts:154

**Bug Analysis:**

The build failure is confirmed in the Vercel build log with:
```
Error [ValidationError]: pageAccessToken is required. Set FACEBOOK_PAGE_ACCESS_TOKEN or provide it in config.
```

The root cause is in `examples/nextjs-chat/src/lib/adapters.ts` at line ~154. The messenger adapter guard only checks for `FACEBOOK_APP_SECRET`:
```typescript
if (process.env.FACEBOOK_APP_SECRET) {
```

However, `createMessengerAdapter` (in `packages/adapter-messenger/src/index.ts`) validates and throws `ValidationError` for each of three required env vars: `FACEBOOK_APP_SECRET`, `FACEBOOK_PAGE_ACCESS_TOKEN`, and `FACEBOOK_VERIFY_TOKEN`. When only `FACEBOOK_APP_SECRET` is set in the Vercel project environment, the guard passes, `createMessengerAdapter` is called, and it throws a `ValidationError` for the missing `FACEBOOK_PAGE_ACCESS_TOKEN`. Since this code runs at module evaluation time during the Next.js build's "Collecting page data" phase, the uncaught error crashes the entire build.

This is inconsistent with other adapters in the same file. For example, the WhatsApp adapter checks both `WHATSAPP_ACCESS_TOKEN` and `WHATSAPP_PHONE_NUMBER_ID`, and the gchat/github/linear/whatsapp adapters all wrap creation in try-catch blocks.

**Fix:**

1. Updated the env var guard to check all three required environment variables (`FACEBOOK_APP_SECRET`, `FACEBOOK_PAGE_ACCESS_TOKEN`, and `FACEBOOK_VERIFY_TOKEN`) before attempting to create the adapter.
2. Wrapped the `createMessengerAdapter` call in a try-catch block (matching the pattern used by gchat, github, linear, and whatsapp adapters) so that any unexpected validation errors are caught and logged as warnings instead of crashing the build.

Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
Co-authored-by: visyat <vishal.yathish@gmail.com>

---------

Co-authored-by: Dimitar K. Nikolov <mitkodkn@users.noreply.github.com>
Co-authored-by: Ben Sabic <27636870+bensabic@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com>
Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
2026-05-08 17:25:54 +10:00
josh eb5f94a8ee feat(chat): add message.subject and adapter client access (#459)
* 1

* w

* 3f

* x

* ln

* gh

* sl

* t1

* u

* t2

* t3

* t4

* t5

* d

* d2

* cs

* fx

* cl

* docs: clean up subject + .client docs and restructure nav

- subject.mdx: simplify prose, drop redundant platform lists, link to MessageSubject API and getAdapter
- api/message.mdx: add MessageSubject TypeTable
- api/chat.mdx: expand getAdapter with Direct client access content
- adapters.mdx: add Parent subject and Native client rows to feature matrix
- usage.mdx: mention .client under Accessing adapters
- adapter-github/-linear READMEs: add Direct API client section
- meta.json: split Features into Messaging + Interactivity, move error-handling to Usage
- title case across messaging-cluster page titles

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-05-08 10:51:53 +10:00
Ben Sabic 3490a8c84c feat: add @chat-adapter/web — browser chat UI for chat-sdk bots (#444)
* feat(chat): expose awaitable Promise from processMessage

Return the inner task as Promise<void> instead of void so streaming
adapters can await full handler completion and surface user-handler
rejections at the wire level. waitUntil semantics for existing webhook
adapters are unchanged — the SDK still tracks the work with errors
swallowed (and logged) so platforms don't retry on handler bugs.

Required by @chat-adapter/web, whose response body is the user
handler's stream.

* feat(adapter-web): add @chat-adapter/web package

A new platform adapter that lets a chat-sdk bot serve a browser chat
UI alongside Slack/Teams/Discord/etc. without writing any client-side
glue. Speaks the AI SDK UI message stream protocol, so @ai-sdk/react's
useChat and the ai-elements component library work out of the box.

- `@chat-adapter/web` — server: createWebAdapter({ userName, getUser })
- `@chat-adapter/web/react` — client: useChat() preconfigured with
  DefaultChatTransport against /api/chat (override via `api`)

Defaults that matter for v1:
- `isDM: true` — every web message routes through onDirectMessage
- `persistMessageHistory: true` — chat-sdk caches each turn in the
  configured state adapter so handlers can read prior context via
  thread.messages / channel.messages (no platform history API exists)
- channelId === threadId — web has no separate channel concept; this
  prevents cross-conversation bleed when a single user has multiple
  useChat sessions
- Native `adapter.stream` implementation pumps text-deltas straight
  onto the SSE response — no post+edit fallback

Out of scope for v1: cards/JSX rendering, reactions, modals, file
uploads, edit/delete, multi-tab proactive push.

* feat(example-nextjs-chat): wire up web adapter and add /chat page

- Register the web adapter in lib/adapters.ts with a demo getUser
  (single shared identity — replace with NextAuth/Clerk/cookie auth
  in production)
- Expose POST /api/chat backed by bot.webhooks.web (using next/after
  for waitUntil)
- Add a minimal /chat page using @chat-adapter/web/react's useChat —
  same bot.onDirectMessage handler that powers Slack now powers the
  browser too

Bumps `ai` to ^6.0.174 to align with @ai-sdk/react@^3 (avoids dual
provider-utils versions in the workspace).

* docs: list @chat-adapter/web in registry

- Add an entry to adapters.json so the package shows up on /adapters
- Add a globe SVG to lib/logos.tsx and wire it into the icon map
- Mention the new adapter in docs/adapters.mdx

* feat(adapter-web): tighten request handling and message construction

- Reject user ids containing ':' with HTTP 400 — the character would
  corrupt the thread-id round-trip through decodeThreadId
- Skip emitting text-start/text-end in postMessage when the resolved
  text is empty so useChat doesn't render blank assistant bubbles
- Derive the parseMessage author from raw.role so rehydrated assistant
  messages report the bot identity instead of "unknown"
- Drop the duplicate handler-error log; chat.processMessage already
  logs at ERROR level
- Document the actual persistMessageHistory default (true) and the
  state-cache rationale; promote the fetchMessages no-op rationale
  into its JSDoc

* test(adapter-web): add direct coverage for stream()

- Aborting request.signal mid-stream short-circuits the iterator and
  still writes text-end via the finally block
- Non-text StreamChunks (task_update, plan_update) are dropped without
  emitting any delta
- The SentMessage returned from thread.post matches the id used in
  text-start / text-end events

* docs(adapter-web): expand README into the full adapter docs page

The docs site renders each adapter's README, so flesh out
@chat-adapter/web to match the depth of @chat-adapter/slack:
authentication boundary, threading semantics, streaming,
persistence, React hook reference, configuration table,
feature matrix, and troubleshooting.

* docs(adapter-web): drop unsupported provider import from streaming example

* fix(adapter-web): validate conversationId for reserved colon character

* fix(example): show error state in web chat demo

* fix(example): add thinking indicator to web chat demo

* feat(example): redesign web chat demo with tailwind

* chore: remove redundant changeset

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
Co-authored-by: dancer <josh@afterima.ge>
2026-05-05 15:55:40 -07:00
Ben Sabic 46d183bdab feat(chat): add Transcripts API + rename per-thread cache to threadHistory (#448)
* feat(chat): add Transcripts API and rename per-thread cache to threadHistory

Introduce `bot.transcripts` for cross-platform per-user message persistence.
When `ChatConfig.transcripts` and `ChatConfig.identity` are configured, every
inbound message has its `userKey` resolved during dispatch and the API exposes
`append` / `list` / `count` / `delete` keyed by that user. Backed by the
existing `StateAdapter.appendToList` primitive — every built-in state adapter
supports it with no contract changes.

Rename the existing per-thread history cache from `messageHistory` to
`threadHistory` (with backwards compat for `ChatConfig.messageHistory` and
`Adapter.persistMessageHistory`) so the two persistence layers don't share a
"messages" name. The state-adapter storage key prefix is unchanged so existing
data isn't orphaned.

`delete()` writes a tombstone via `appendToList(key, _, { maxLength: 1 })`
rather than `state.delete(key)`, because `state.delete` only addresses the
k/v namespace on every non-memory state adapter — `list()` and `count()`
filter the tombstone out so the API contract is preserved.

* docs(chat): cover Transcripts API and Conversation history

Add a Features-style "Conversation history" guide (`/docs/conversation-history`)
walking through identity resolution, the LLM-context append/list pattern,
filtering, and per-user deletion for DSR flows.

Add an API reference page at `/docs/api/transcripts` with `<TypeTable>` blocks
for `ChatConfig.transcripts`, `ChatConfig.identity`, every method on
`bot.transcripts`, and the `TranscriptEntry` shape.

Wire both into the corresponding `meta.json` files.

* example(nextjs-chat): wire Transcripts API into the AI mode handler

Replace the brittle `threadState.history` shim with `bot.transcripts.list({ ..., threadId, limit })` as the fallback context source for platforms without
`fetchMessages` (Telegram, WhatsApp). Drop the `history` field from
`ThreadState` accordingly.

Add a hardcoded `TEST_USER_KEY = "test-user"` so the API can be exercised
without juggling real user identities, plus "Show Transcripts" and
"Clear Transcripts" buttons in the welcome card so the store can be
inspected and reset from chat.

* fix(chat): tighten Transcripts API public surface and wiring

Polish on top of the Transcripts API + threadHistory rename, addressing
review concerns before merge.

Public surface (`types.ts`, `index.ts`):
- Expose `transcripts` on the `ChatInstance` interface so callers typed
  against the public interface can reach `bot.transcripts`.
- Promote the `count` argument to a named `CountQuery` interface,
  matching `DeleteTarget` / `ListQuery`. Exported from `index.ts`.
- Document on `TranscriptsApi.list()` that pagination is intentionally
  out-of-scope — the store keeps at most `maxPerUser` entries per user.
- Reconcile the `TranscriptEntry.id` JSDoc with the implementation:
  UUID assigned at append time, returned in append order, not
  lexicographically sortable; use `timestamp` for cross-store ordering.

Wiring (`chat.ts`):
- Include `threadId` in the identity-resolver failure log context so
  operators can correlate failures with the source thread.

Stale-reference sweep:
- Replace lingering "Messages API" / `chat.messages` /
  `messages.storeFormatted` strings in shipped JSDoc with the new
  `transcripts` names (these ride into `.d.ts` and are user-visible).
- Fix the dead `[Messages API](./messages.ts)` link in the existing
  thread-history-rename changeset.

* test(chat): cover dual-read precedence, resolver edges, concurrent ops

Fill gaps in the Transcripts API + threadHistory rename test suite:

`chat.test.ts` (persistThreadHistory block):
- top-level `config.messageHistory` (deprecated alias) flows through
  to the per-thread cache when `threadHistory` is unset
- `threadHistory` takes precedence over `messageHistory` when both are
  set — pinned by asserting `appendToList` receives the new config's
  `maxLength` / `ttlMs`
- both `persistThreadHistory` and `persistMessageHistory` set on the
  adapter still triggers persistence

`transcripts-wiring.test.ts`:
- sync resolver returning a plain string populates `message.userKey`
- resolver returning `""` is treated as no userKey (truthy check at
  the dispatch hook would silently flip if a future change moved to
  `!== undefined`)

`transcripts.test.ts`:
- concurrent append/delete/append interleave preserves invariants:
  `count()` and `list()` agree (no tombstone leak), no pre-delete
  entry survives, and the post-delete result is bounded by the two
  concurrent appends

* docs(chat): use named types in Transcripts API reference

- `formatted` rows in the AppendInput / TranscriptEntry TypeTables
  now render `FormattedContent | undefined` (the alias actually
  exported from `chat`), instead of `Root | undefined` which would
  force readers to pull the type from `mdast` directly.
- `count` signature uses the new `CountQuery` named type, with a
  one-liner describing its single field.

* example(nextjs-chat): fix stale comment on transcripts demo handler

The action handler was relabelled to `transcripts` when it was wired
to `bot.transcripts.list`, but the leading comment still read
"Demonstrate fetchMessages and allMessages" from the previous
iteration. Update it to describe the transcripts demo.

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-05-05 15:25:37 -07:00
Raimond Lume 3546b3fddb feat(slack): use native markdown_text for outgoing messages (#440)
* feat(slack): use native markdown_text field for outgoing messages

Slack now natively renders markdown via the `markdown_text` parameter on
chat.postMessage / postEphemeral / update / scheduleMessage and via
response_url payloads. The adapter passes markdown through directly instead
of converting to mrkdwn.

- Tables, headings, code fences, blockquotes, and nested lists render
  natively in Slack instead of falling back to ASCII / mrkdwn.
- `string` and `{ raw }` messages still go to `text` (preserves literal `*`).
- `{ markdown }` and `{ ast }` messages go to `markdown_text` (12k char limit).
- `renderWithTableBlocks`, `toBlocksWithTable`, `mdastTableToSlackBlock`,
  and the AST→mrkdwn renderer (`fromAst` / `nodeToMrkdwn`) are removed.
- `SlackMarkdownConverter` alias is removed; use `SlackFormatConverter`.
- `renderFormatted(ast)` now returns standard markdown (was mrkdwn).
- Incoming `message` events still arrive as mrkdwn and are parsed unchanged.

Net -473 lines across markdown.ts and the five sender call sites.

* fix(slack): use mrkdwn fallback for response_url edits
2026-05-05 14:12:22 -07:00
Ben Sabic aa1b08a246 docs: cover gchat verification, linear token encryption, shared crypto helpers (#445)
* docs: cover gchat verification, linear token encryption, shared crypto helpers

The adapter hardening pass in #441 made gchat JWT verification required,
added optional at-rest encryption for Linear OAuth tokens, and promoted
the AES-256-GCM helpers into @chat-adapter/shared. Update the affected
package READMEs and the adapter-authoring guide to match.

* docs(adapter-shared): correct EncryptedTokenData field names

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-05-04 13:56:10 -07:00
josh d9c2fcaf93 docs: update documentation for 4.27.0 release (#439)
* docs: update documentation for 4.27.0 release

* docs: fix broken apiUrl link in adapters feature matrix

* docs: fix error code thrown-by columns and add missing UNKNOWN_USER_ID_FORMAT
2026-05-01 07:12:42 -07:00
Ben Sabic a520797922 feat: add chat.getUser() for cross-platform user lookups (#391)
* feat: add chat.getUser() for cross-platform user lookups

Add UserInfo type and optional getUser() method to the Adapter interface.
Implement on Slack (extends existing lookupUser with email/avatar),
Discord, Google Chat, GitHub, Linear, and Telegram adapters.

Add "Who Am I" button to the example app demonstrating the feature.
Update docs with getUser API reference and usage examples.

* fix(chat): improve getUser across slack and gchat adapters

- slack: return null from lookupUser on failure instead of fallback
  object, removing the isBot === undefined sentinel in getUser
- slack: use image_192 instead of image_72 for better avatar quality
- gchat: cache avatarUrl from webhook sender payload
- gchat: return avatarUrl in getUser response
- gchat: fix tests to use current cache format with isBot field
- docs: document null return, fix example to use message.author

* chore: fix lint

* docs(chat): include Microsoft Teams in getUser supported adapters list

* feat(adapter-teams): add getUser() support (#404)

* feat(adapter-teams): add getUser() via Microsoft Graph API

- Cache aadObjectId from activity.from during webhook handling
- Implement getUser() using Graph GET /users/{user-id} endpoint
- Requires User.Read.All application permission
- Returns null gracefully when user hasn't interacted or Graph call fails

* docs: add getUser() section to Teams adapter README

* chore: apply ultracite formatting to adapter-teams getUser

* fix(chat): cover all 7 adapters in getUser inference and document per-platform constraints

---------

Co-authored-by: dancer <josh@afterima.ge>
2026-04-29 07:58:33 -07:00
josh 70281dc58f feat(chat): add initialOption and option_groups to ExternalSelect (#410)
* feat(chat): add initialOption and option_groups to ExternalSelect

* docs(modals): document ExternalSelect initialOption and option_groups, truncate group label to 75 chars
2026-04-28 09:32:15 +10:00
Ben Sabic b0ab804f18 - Bundle guide markdown and a templates manifest with the chat package at resources/guides/*.md and resources/templates.json so AI agents can discover Chat SDK resources offline (#423)
- Add scripts/sync-resources.ts (run via pnpm sync-resources) that reads apps/docs/resources-edge-config.json, fetches each guide's .md version over https with a timeout and size cap, writes templates.json, and regenerates the Available resources block in skills/chat/SKILL.md
- Migrate the Slack Next.js, Discord Nuxt, and Hono code-review guides from on-site MDX to Vercel KB and register them in the resources edge-config JSON alongside the existing external guides
- Remove /docs/guides MDX content, sidebar entries, top-level Guides nav entry, getting-started cards, and the dead /guides/ branch in the sitemap route now that all guides live externally and are surfaced on /resources
- Replace the homepage Guides/Templates section and the standalone Adapters pill section with a single two-column Resources + Adapters section (icons, headings, descriptions, outline buttons, divider), and drop the URL footer from ResourceCard on the Resources page
- Update skills/chat/SKILL.md to point at resources/guides and resources/templates.json and list the available guides and templates between marker comments that sync-resources rewrites
- Add tsx to knip's ignoreBinaries so npx tsx in the new script does not fail lint

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
Co-authored-by: dancer <josh@afterima.ge>
2026-04-27 10:20:44 +10:00
Ben Sabic e8c4b1a6c4 docs: update adapter docs (#412)
* docs: update adapter docs

* docs: update adapter docs

* docs: update adapter docs

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-04-22 13:28:20 +10:00
Franz Rebaza a179b29141 Implement external_select block kit for Slack (#397)
* feat(slack): external_select's block kit implementation

- block_suggestion handler for slack webgook
- new <ExternalSelect/> to chat modal jsx
- new .onOptionsLoad()

* feat: add tests for new external_selec implementation

* feat(docs): Slack externa_select docs

* chore: changeset

* chore: remove docs from changeset

---------

Co-authored-by: dancer <josh@afterima.ge>
2026-04-21 05:49:51 -07:00
Hayden Bleasel 7e90d9c8fa Add Slack Socket Mode support (#162)
* Add slack/socket mode dependency

* Update config types and SlackAdapter class

* Add socket mode methods, extract interactive dispatch

* Update createSlackAdapter factory function

* Write tests for socket mode

* Create slack-socket-mode.md

* Run fix

* Fix polynomial regex issues

* Fix: Floating promises in `routeSocketEvent` for slash commands and interactive payloads can cause unhandled promise rejections that crash the Node.js process.


This commit fixes the issue reported at packages/adapter-slack/src/index.ts:1152

**Bug Analysis:**

In `routeSocketEvent` (line 1150), which is a synchronous `void` method, two async operations produce floating promises:

1.  `this.handleSlashCommand(params)` (line 1165) - `handleSlashCommand` is `async` and always returns a `Promise<Response>`. It calls `await this.lookupUser(userId)` which internally calls `await this.chat.getState().get()` (before the try/catch around the API call), and `this.chat.processSlashCommand()`. Any of these could throw.
    
2.  `this.dispatchInteractivePayload(payload)` (line 1172) - Returns `Response | Promise<Response>`. When the payload type is `view_submission`, it delegates to `async handleViewSubmission()`, which calls `await this.chat.processModalSubmit()` and accesses `payload.view.state.values` (which could throw on malformed payloads).
    

Since `routeSocketEvent` is synchronous (`void` return type) and called from a sync context within the socket mode event handler (after `await ack()` has already completed), these returned promises are fire-and-forget. If any reject, it triggers an unhandled promise rejection, which in Node.js 15+ terminates the process by default.

In contrast, in the webhook code path (`handleWebhook`), these same methods are always `return`-ed from async functions, so their promises are properly chained to the caller.

**Fix:**

Added `.catch()` handlers to both floating promises:

1.  For `handleSlashCommand`: Added `.catch()` that logs the error via `this.logger.error`.
2.  For `dispatchInteractivePayload`: Since it returns `Response | Promise<Response>` (only a Promise for `view_submission`), used `instanceof Promise` to conditionally attach a `.catch()` handler only when the result is a Promise.

This approach was chosen over making `routeSocketEvent` async because: (a) it doesn't change the method signature, (b) the caller doesn't need to await it (the ack has already been sent), and (c) errors are logged rather than silently swallowed.


Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
Co-authored-by: haydenbleasel <hello@haydenbleasel.com>

* Add socket mode forwarding support to Slack adapter

- Export SlackForwardedSocketEvent type
- Add x-slack-socket-token check at top of handleWebhook() for forwarded events
- Update routeSocketEvent() to accept WebhookOptions and use waitUntil
- Add startSocketModeListener(), runSocketModeListener(), forwardSocketEvent()

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add tests for socket mode forwarding

- Forwarded event accepted/rejected based on appToken
- Bypasses signature verification for forwarded events
- Options passthrough to handlers
- startSocketModeListener returns 200/500 appropriately

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add socket mode cron route and vercel config

- New /api/slack/socket-mode route using createPersistentListener
- Mirrors Discord gateway pattern (CRON_SECRET auth, Redis coordination)
- Cron runs every 9 min, listener duration 10 min

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix signingSecret defaulting to empty string in socket mode

Make signingSecret optional (string | undefined) instead of falling
back to "". verifySignature now returns false when no secret is
configured, preventing HMAC with an empty key from silently passing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Wrap event_callback in try-catch in routeSocketEvent

Sync errors from processEventPayload were silently dropped in
socket mode. Wrap with try-catch for parity with slash_commands
and interactive cases.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Use dedicated socketForwardingSecret for forwarding auth

Stop using the Slack app-level token (xapp-...) as the bearer token
for HTTP forwarding. Adds socketForwardingSecret config option
(auto-detected from SLACK_SOCKET_FORWARDING_SECRET) with fallback
to appToken for backwards compatibility.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Replace double cast with type guard for socket event body

Validate body.event exists and construct a properly typed
SlackWebhookPayload instead of using `as unknown as`.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Internalize SlackForwardedSocketEvent type

Remove export — only used internally by the forwarding mechanism.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix formatting in socketForwardingSecret check

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add socket mode documentation to Slack adapter README

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(slack): use SDK envelope type for socket mode event routing

* fix(slack): pass interactive response through ack in socket mode

* feat(chat): add clear modal response action to close entire view stack

* chore: update changeset for clear modal action

---------

Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: dancer <josh@afterima.ge>
2026-04-20 08:09:26 -07:00
christopherkindl 46fc5bbe9d Sync docs with @vercel/geistdocs 1.2.0–1.2.3 (#399)
* docs: sync geistdocs template 1.2.0–1.2.3 + polish

- Ran @vercel/geistdocs@1.2.2 update --sync against origin/main
  (already contains merged 1.2.3) for:
  - components/geistdocs/*
  - components/geistcn-fallbacks/**/*  (new)
  - components/ui/command-prompt.tsx, navigation-menu.tsx
  - app/styles/geistdocs.css
- Manual overlays (paths skipped by sync due to chat customizations):
  - app/[lang]/layout.tsx: drop scroll-smooth (1.2.2)
  - app/[lang]/docs/[[...slug]]/page.tsx: MobileDocsBar + disable
    default TOC popover (1.2.0)
  - app/[lang]/docs/layout.tsx: wrap in bg-background-200 (1.2.3)
  - components/ui/badge.tsx: secondary variant → bg-gray-300/text-gray-1000 (1.2.3)
- Home hero: replace Get Started + Installer with CommandPrompt
  humans/agents switcher ("npm install chat" / "npx skills add vercel/chat")
- (home) layout: swap bg-sidebar dark:bg-background for bg-background-200
  so /, /adapters, /resources share the navbar surface
- DesktopMenu: active-state detection with longest-prefix match
  (so /docs/api highlights "API", not also "Docs")
- navbar-logo dropdown: drop Chat SDK self-entry
- New geist-fill icons (check-circle-fill, cross-circle-fill,
  warning-fill) ported from @vercel/geistcn-assets; new
  components/custom/status-icons.tsx registers Check/Cross/Warn MDX
  components
- content/docs/adapters.mdx: replace ✅/❌/⚠️ emojis with the new
  icons (emoji.mdx intentionally left alone)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs: use LogoChatSdk from geistcn-fallbacks as the app Logo

Replace the inline Chat SDK wordmark SVG in geistdocs.tsx with
<LogoChatSdk /> so the navbar and other Logo consumers share the
same source of truth.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs: default hero CommandPrompt to humans tab

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 09:23:36 +10:00
Ben Sabic 37dbb4ac8b feat(chat): add thread.getParticipants() (#386)
* feat(chat): add thread.getParticipants() method

Returns unique human participants in a thread by scanning message
history. Excludes the bot itself. Useful for subscribing only to
1:1 conversations and unsubscribing when others join.

* fix: filter all bots in getParticipants(), not just self

Third-party bots (e.g. Jira, GitHub) were included as participants
because only isMe was checked. Now filters on isBot as well.

* docs: add getParticipants() to Thread API reference and guides

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-04-15 10:51:20 +10:00
josh 47a9c04fca fix(docs): replace thread.stream() with adapter.stream() in streaming docs (#387) 2026-04-15 10:32:56 +10:00
Nate McGrady bca47924b7 feat: enhance task update structure with optional details field (#385)
- Added a `details` field to the `task_update` type for providing additional context in task updates.
- Updated relevant documentation and test cases to reflect the new field, improving clarity on task progress reporting.
2026-04-15 09:47:34 +10:00
Ben Sabic 608d5f00b0 feat(chat): add chat.thread() for creating thread handles (#380)
* feat(chat): add chat.thread() method for creating thread handles

Allows constructing a Thread handle from a thread ID outside of webhook
contexts, enabling proactive messaging to existing threads.

Closes #148

* docs: add chat.thread() to API reference

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-04-14 15:12:45 +10:00
josh 1e7c55150e feat: restore attachment fetchData after queue/debounce serialization (#338) 2026-04-13 21:25:21 -07:00
Ben Sabic ccc042a608 docs: misc updates and fixes (#370)
* docs: misc updates and fixes

* docs: update feature matrix to show Teams modal support

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-04-13 20:17:41 +10:00
Hayden Bleasel 2235c160ca fix(chat): export standalone reviver for workflow-safe deserialization (#257)
* fix(chat): export standalone reviver for workflow-safe deserialization

Importing the Chat instance into Vercel Workflow files pulls in adapter
packages that depend on Node.js modules, which aren't available in the
workflow sandbox. This adds a standalone `reviver` function exported from
`chat` that deserializes Thread/Channel/Message objects without needing
a Chat instance or its adapter dependencies.

Also updates the durable chat sessions guide to use the standalone
reviver and dynamic bot imports inside step functions.

Closes #243

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(chat): preserve adapterName on deserialized toJSON and add standalone reviver docs

* fix: sort imports in index.ts

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: dancer <josh@afterima.ge>
Co-authored-by: Ben Sabic <27636870+bensabic@users.noreply.github.com>
Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-04-13 18:34:16 +10:00
Ben Sabic c8b8321576 docs: mention create-bot CLI in vendor official adapter qualifications 2026-04-11 00:14:22 +10:00
Ben Sabic 83cccc3157 feat: add create-bot CLI for scaffolding Chat SDK projects
Introduces `create-bot`, a CLI tool (like create-next-app) that scaffolds
new Chat SDK bot projects. It prompts for a project name, description,
platform adapters, and a state adapter, then generates a complete Next.js
project with webhook routes, environment variables, and bot configuration.

Supports interactive and non-interactive usage via flags (--adapter,
--description, --pm, --quiet, --yes). Includes full test coverage,
README, and documentation integrated into the Chat SDK docs site.

Made-with: Cursor
2026-04-10 19:24:18 +10:00
Ben Sabic 1856198f6d fix(slack): OAuth redirect handling (#307)
* Fix Slack OAuth redirect handling

* fix slack check

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-04-09 00:15:37 +10:00
Malte Ubl 1fcab01302 Move messenger pages to /adapters/for/ and add browse section (#332)
* feat: refine adapter categorization and display logic in AdaptersGrid component

Slack-Thread: https://vercel.slack.com/archives/C08077A6JDB/p1775414281367229?thread_ts=1775414281.367229&cid=C08077A6JDB
Co-authored-by: Malte Ubl <89679+cramforce@users.noreply.github.com>

* feat: rename messenger to for in adapter paths and add messenger links

Slack-Thread: https://vercel.slack.com/archives/C08077A6JDB/p1775414281367229?thread_ts=1775414281.367229&cid=C08077A6JDB
Co-authored-by: Malte Ubl <89679+cramforce@users.noreply.github.com>

---------

Co-authored-by: v0 <v0[bot]@users.noreply.github.com>
Co-authored-by: Malte Ubl <89679+cramforce@users.noreply.github.com>
2026-04-05 12:32:23 -07:00
Malte Ubl acc4a336a0 Minor docs fix (#296) 2026-03-24 14:25:29 -07:00
Malte Ubl f2d8957797 Docs for the new concurrency strategies (#294) 2026-03-24 14:15:44 -07:00