Commit Graph

138 Commits

Author SHA1 Message Date
dependabot[bot] b4a93bdbc5 build(deps-dev): bump @hono/node-server from 2.0.2 to 2.0.10 (#733)
Bumps [@hono/node-server](https://github.com/honojs/node-server) from
2.0.2 to 2.0.10.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/honojs/node-server/releases">@​hono/node-server's
releases</a>.</em></p>
<blockquote>
<h2>v2.0.10</h2>
<h2>Security fixes</h2>
<p>This release includes a fix for the following security issue:</p>
<h3>Unauthenticated memory-leak DoS via aborted WebSocket handshake</h3>
<p>Affects: <code>upgradeWebSocket</code>. A WebSocket upgrade request
with a missing or malformed <code>Sec-WebSocket-Key</code> header leaked
the request's <code>IncomingMessage</code> and left a promise pending,
even though no connection was established. Since the route is reachable
pre-handshake without authentication, an attacker could flood it to
gradually exhaust memory. <a
href="https://github.com/honojs/node-server/security/advisories/GHSA-9mqv-5hh9-4cgg">GHSA-9mqv-5hh9-4cgg</a></p>
<hr />
<p>Users of <code>upgradeWebSocket</code> are encouraged to upgrade to
this version.</p>
<h2>v2.0.9</h2>
<h2>What's Changed</h2>
<ul>
<li>fix(websocket): polyfill missing ErrorEvent global by <a
href="https://github.com/otnc"><code>@​otnc</code></a> in <a
href="https://redirect.github.com/honojs/node-server/pull/371">honojs/node-server#371</a></li>
<li>fix(serve-static): correct Range header parsing edge cases by <a
href="https://github.com/otnc"><code>@​otnc</code></a> in <a
href="https://redirect.github.com/honojs/node-server/pull/372">honojs/node-server#372</a></li>
<li>fix: recover complete request bodies after client disconnect by <a
href="https://github.com/usualoma"><code>@​usualoma</code></a> in <a
href="https://redirect.github.com/honojs/node-server/pull/375">honojs/node-server#375</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/otnc"><code>@​otnc</code></a> made their
first contribution in <a
href="https://redirect.github.com/honojs/node-server/pull/371">honojs/node-server#371</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/honojs/node-server/compare/v2.0.8...v2.0.9">https://github.com/honojs/node-server/compare/v2.0.8...v2.0.9</a></p>
<h2>v2.0.8</h2>
<h2>What's Changed</h2>
<ul>
<li>ci(release): add <code>--no-git-checks</code> option for <code>pnpm
stage publish</code> by <a
href="https://github.com/yusukebe"><code>@​yusukebe</code></a> in <a
href="https://redirect.github.com/honojs/node-server/pull/369">honojs/node-server#369</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/honojs/node-server/compare/v2.0.7...v2.0.8">https://github.com/honojs/node-server/compare/v2.0.7...v2.0.8</a></p>
<h2>v2.0.7</h2>
<h2>What's Changed</h2>
<ul>
<li>chore: migrate to pnpm by <a
href="https://github.com/BlankParticle"><code>@​BlankParticle</code></a>
in <a
href="https://redirect.github.com/honojs/node-server/pull/367">honojs/node-server#367</a></li>
<li>fix(serve-static): serve precompressed files for
application/octet-stream by <a
href="https://github.com/yusukebe"><code>@​yusukebe</code></a> in <a
href="https://redirect.github.com/honojs/node-server/pull/366">honojs/node-server#366</a></li>
<li>chore: bump <code>supertest</code> by <a
href="https://github.com/yusukebe"><code>@​yusukebe</code></a> in <a
href="https://redirect.github.com/honojs/node-server/pull/368">honojs/node-server#368</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/honojs/node-server/compare/v2.0.6...v2.0.7">https://github.com/honojs/node-server/compare/v2.0.6...v2.0.7</a></p>
<h2>v2.0.6</h2>
<h2>What's Changed</h2>
<ul>
<li>ci: publish to npm from CI with OIDC trusted publishing and bump
<code>np</code> by <a
href="https://github.com/yusukebe"><code>@​yusukebe</code></a> in <a
href="https://redirect.github.com/honojs/node-server/pull/361">honojs/node-server#361</a></li>
<li>ci: use npm Staged publishing by <a
href="https://github.com/yusukebe"><code>@​yusukebe</code></a> in <a
href="https://redirect.github.com/honojs/node-server/pull/364">honojs/node-server#364</a></li>
<li>fix: preserve status and statusText when cloning a Response with
liveheaders by <a
href="https://github.com/usualoma"><code>@​usualoma</code></a> in <a
href="https://redirect.github.com/honojs/node-server/pull/363">honojs/node-server#363</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/honojs/node-server/compare/v2.0.5...v2.0.6">https://github.com/honojs/node-server/compare/v2.0.5...v2.0.6</a></p>
<h2>v2.0.5</h2>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/honojs/node-server/commit/7c1457ed5536c02fdd2f001129fae67bcbca54a1"><code>7c1457e</code></a>
2.0.10</li>
<li><a
href="https://github.com/honojs/node-server/commit/3a21938c418340e980cb7ffa88e78369f78392d1"><code>3a21938</code></a>
Merge commit from fork</li>
<li><a
href="https://github.com/honojs/node-server/commit/98420217e53a17a238ef1aa1a6bef0b2b70136c5"><code>9842021</code></a>
2.0.9</li>
<li><a
href="https://github.com/honojs/node-server/commit/51f3bf56f56d9691ec0f7e1562a96f0b485a7dd9"><code>51f3bf5</code></a>
fix: recover complete request bodies after client disconnect (<a
href="https://redirect.github.com/honojs/node-server/issues/375">#375</a>)</li>
<li><a
href="https://github.com/honojs/node-server/commit/fdb87badbe313cfbfe6bb2355e9893dc0698d2bd"><code>fdb87ba</code></a>
fix(serve-static): correct Range header parsing edge cases (<a
href="https://redirect.github.com/honojs/node-server/issues/372">#372</a>)</li>
<li><a
href="https://github.com/honojs/node-server/commit/912e3fd80c4311756f724bd566de1433c8d772d9"><code>912e3fd</code></a>
fix(websocket): polyfill missing ErrorEvent global (<a
href="https://redirect.github.com/honojs/node-server/issues/371">#371</a>)</li>
<li><a
href="https://github.com/honojs/node-server/commit/114c15efb38dabaf81af774ddb764409e3d156d8"><code>114c15e</code></a>
2.0.8</li>
<li><a
href="https://github.com/honojs/node-server/commit/5db2d5df662cd69ff5c4cc23b8ecb3a6f63e4e38"><code>5db2d5d</code></a>
ci(release): add <code>--no-git-checks</code> option for <code>pnpm
stage publish</code> (<a
href="https://redirect.github.com/honojs/node-server/issues/369">#369</a>)</li>
<li><a
href="https://github.com/honojs/node-server/commit/a528a77ed2c28dc12775c849abc6b6df6d4cb44c"><code>a528a77</code></a>
2.0.7</li>
<li><a
href="https://github.com/honojs/node-server/commit/b2d610c1e37a96639fbb2eae662e858800aa8906"><code>b2d610c</code></a>
chore: bump <code>supertest</code> (<a
href="https://redirect.github.com/honojs/node-server/issues/368">#368</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/honojs/node-server/compare/v2.0.2...v2.0.10">compare
view</a></li>
</ul>
</details>
<details>
<summary>Maintainer changes</summary>
<p>This version was pushed to npm by <a
href="https://www.npmjs.com/~GitHub%20Actions">GitHub Actions</a>, a new
releaser for <code>@​hono/node-server</code> since your current
version.</p>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@hono/node-server&package-manager=npm_and_yarn&previous-version=2.0.2&new-version=2.0.10)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts page](https://github.com/vercel/chat/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-22 13:15:31 +10:00
Ben Sabic 4cb7e5d58e feat(chat): durable human-in-the-loop approvals via chat/workflow (#728)
Adds a `chat/workflow` subpath export with `requestApproval()`. This is
the DX from #284, rebuilt on Workflow SDK so the approval survives
deploys, restarts, and arbitrarily long waits. No in-memory promises, no
approvals registry, no restart-recovery machinery: the workflow suspends
on a webhook and resumes when a button is clicked.

`requestApproval()` posts a card with Approve/Deny buttons whose
`callbackUrl` targets a `createWebhook()` URL, suspends the workflow
until a decision (or optional durable-sleep timeout), validates
approvers, finalizes the card in place with the outcome (removing the
buttons, leaving an audit trail), and returns the decision.

```typescript
import { requestApproval } from "chat/workflow";
import type { Thread } from "chat";
export async function deployApproval(opts: { thread: Thread; version: string }) {
  "use workflow";
  const { approved, user, timedOut } = await requestApproval(opts.thread, {
    title: `Deploy ${opts.version}?`,
    fields: { Version: opts.version },
    timeout: "24h",
    approvers: ["U_ALICE", "U_BOB"],
  });
  if (approved) {
    await deploy(opts.version);
  }
}
```

Starting it from a handler is one line. `Thread` instances serialize
across the workflow boundary automatically via the existing
`@workflow/serde` hooks on `ThreadImpl` (requires
`chat.registerSingleton()`):

```typescript
import { start } from "workflow/api";
bot.onNewMention(async (thread, message) => {
  await start(deployApproval, [{ thread, version: parseVersion(message.text) }]);
});
```

**Details**

- `workflow` is a new **optional** peer dependency (same pattern as
`ai`); the subpath is the only code that imports it
- Unauthorized clicks (when `approvers` is set) and unrecognizable
payloads post a notice / are ignored, and the workflow keeps waiting
- On timeout the card is finalized as timed out and the result has
`timedOut: true`
- Card builders (`buildApprovalCard`, `buildResolvedCard`) are exported
for custom flows
- Verified the published `dist` preserves the `"use step"` directives
and down-levels `using` correctly, so the app-side Workflow SDK compiler
handles the library code
- Docs page under Interactivity; changeset (`chat` minor); 8 unit tests
mocking the `workflow` primitives

**Deliberate deviation from #284:** no `thread.requestApproval()`
method. The function must suspend at workflow level, so hanging it off
`ThreadImpl` would make `workflow` a hard dependency of core (or require
prototype patching). The standalone `requestApproval(thread, options)`
keeps the dependency optional.

---------

Signed-off-by: Ben Sabic <bensabic@users.noreply.github.com>
Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-07-22 10:18:38 +10:00
Ben Sabic 4717a38407 feat(slack): support data table and data visualization blocks (#696)
Adds support for Slack's [data
table](https://docs.slack.dev/reference/block-kit/blocks/data-table-block)
and [data
visualization](https://docs.slack.dev/reference/block-kit/blocks/data-visualization-block)
Block Kit blocks.

- **`chat`**: new cross-platform `ChartElement` + `Chart()` builder (JSX
supported) mirroring Slack's model — pie `segments`, or bar/area/line
`series` against shared `categories`. `Table()` gains optional `caption`
and `pageSize`. Charts degrade to a text table on other platforms via
the shared card fallback (`chartElementToFallbackText`).
- **`@chat-adapter/slack`**: card tables now render as paginated,
sortable `data_table` blocks by default (header-only tables keep the
plain `table` block; oversized tables still fall back to ASCII). Charts
render as `data_visualization` blocks; charts violating Slack
constraints — including the undocumented **max 2 charts per message** —
fall back to a text rendering instead of an API rejection. Same
treatment in the `@chat-adapter/slack/blocks` subpath.
- **`postMessage`** now surfaces Slack's per-block validation messages
on `invalid_blocks` errors (this is how the 2-chart limit was found).
- Example app gets a **Show Charts** button and table pagination on
**Show Table**; docs, feature matrices, and changeset updated.

Verified live against Slack: data table pagination/sorting and both
chart types render natively.

<table>
  <tr>
    <th>Data Table</th>
    <th>Data Charts</th>
  </tr>
  <tr>
<td><img width="979" height="896" alt="CleanShot 2026-07-12 at 23 28 32"
src="https://github.com/user-attachments/assets/3307bd90-9322-452f-86fb-07d46446822d"
/></td>
<td><img width="955" height="879" alt="CleanShot 2026-07-12 at 23 29 02"
src="https://github.com/user-attachments/assets/ddb31a1b-e3fd-457c-a2e6-bde4934afebe"
/></td>
  </tr>
</table>

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-07-13 07:57:40 +01:00
damianborowy-nexos 1721fa01e7 feat(slack): add Slack Agent messaging experience (agent_view) support (#684)
## Summary

Add support for Slack's Agent messaging experience (`agent_view`), the
2026 replacement for `assistant_view`.

## Core (`chat`)

- New `onAppContextChanged` event carrying the active-view context as a
normalized `AppContextEntity[]` (`channel` / `canvas` / `list` /
`message` / `unknown`) describing what the user is currently viewing.
- `AppHomeOpenedEvent` now carries:
  - the same folded active-view context as optional `entities`
- the opened `tab` (`"home"` / `"messages"`), so handlers can
distinguish a Home-tab open from the DM-open signal under `agent_view`

## Slack adapter (`@chat-adapter/slack`)

- **`agentView` config flag.** Under `agent_view`:
- `app_home_opened` is the DM-open signal and fires regardless of tab
(branch on `event.tab` if you also publish a Home view)
- DM messages are threaded per Slack's new model — each user message is
a thread root (`thread_ts ?? ts`)
- conversation-scoped threads returned by `openDM()` keep working: when
that thread is subscribed, incoming top-level DM messages route to it,
so `onSubscribedMessage` and per-thread state behave the same as in
legacy mode
- **`app_context_changed` routing** with normalized entities. Malformed
payloads degrade gracefully: a missing `context` yields `entities: []`,
and entities with a null/malformed `value` normalize to `kind:
"unknown"` — never a webhook 500.
- **`getAppContext(message)`** helper to read the folded active-view
context off a DM message.
- **`setSuggestedPrompts`** accepts an optional thread reference
(`agent_view` lets prompts sit at the top of the agent conversation).
- **Env auth fallback now keys off auth fields**: `SLACK_BOT_TOKEN` /
`SLACK_CLIENT_ID` / `SLACK_CLIENT_SECRET` fallback is disabled only when
an auth-related field (`botToken`, `clientId`, `clientSecret`,
`installationProvider`) is passed explicitly, rather than by the
presence of any config object. This lets non-auth options compose with
env auth — e.g. `createSlackAdapter({ agentView: true })` picks up env
credentials — and matches the semantics documented in the adapter's
AGENTS.md. *(Behavior change for callers passing non-auth-only configs
while relying on env vars being ignored.)*
- Bumped `@slack/web-api` to `^7.18.0` (adds the optional `thread_ts`
typing for `setSuggestedPrompts`).

## Docs

- New "Agent messaging experience" section on the Slack adapter page
(config, manifest snippet, threading model, openDM bridge).
- "Handling active-view context" section in handling-events, plus
`tab`/`entities` rows on the app-home event table.
- Callout: under `agent_view`, bot replies are threaded per user
message, so `conversations.history` only returns the user's side of a DM
— build AI conversation history from transcripts instead of channel
history.

## Example app (`examples/nextjs-chat`)

- Plain `SLACK_BOT_TOKEN` adapter branch (previously Slack was only
wired via Vercel Connect).
- DM AI history built from transcripts instead of channel history (see
docs callout above); assistant turns persisted.
- The `dm me` trigger regex now matches mention text, which carries the
`@bot` prefix on Slack.

## Test plan

- `pnpm validate` and `pnpm konsistent` pass.
- Unit tests cover the new events, entity normalization (including
malformed payloads), `agent_view` DM threading, the openDM subscription
bridge, `tab` passthrough, `setSuggestedPrompts` thread handling, and
env-fallback behavior; an integration replay test exercises the full
webhook flow.
- Verified manually against a live `agent_view` workspace:
`onAppContextChanged` entities, folded context on `app_home_opened` and
DM messages, `tab` values for both tabs, per-message DM threading, the
openDM subscription bridge, and signed malformed-payload replays (all
return 200).
- Legacy regression pass with `agentView` off: conversation-scoped DM
threading, Home-tab-only `app_home_opened`, mention flow unchanged.

### Slack references

- Agent messaging experience:
https://docs.slack.dev/changelog/2026/06/30/agent-messages-tab/
- Active-view context:
https://docs.slack.dev/changelog/2026/07/02/app-context/

## Checklist

- [x] All commits are signed and verified
- [x] All commits are signed off for the DCO (`git commit -s`)
- [x] `pnpm validate` passes
- [x] Changeset added
- [x] Documentation updated

---------

Signed-off-by: Damian Borowy <301205838+damianborowy-nexos@users.noreply.github.com>
Signed-off-by: Ben Sabic <bensabic@users.noreply.github.com>
Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-07-12 19:35:28 +10:00
Rich Haines 1dff4515e2 refactor(docs): migrate chat-sdk.dev to @vercel/geistdocs (#686)
## Summary

Migrates `apps/docs` from locally-copied geistdocs runtime code to the
published
[`@vercel/geistdocs`](https://www.npmjs.com/package/@vercel/geistdocs)
package (1.8.2), following the official [migration
guide](https://preview.geistdocs.com/docs/migration). Net **−8,400
lines**.

### Package-backed now

- Docs page + layouts: `createDocsPage`, `GeistdocsDocsLayout`,
`GeistdocsHomeLayout` (JSON-LD + sr-only markdown hints preserved via
`renderTop`)
- Navbar (OSS product switcher via `navbarOssProducts`), footer,
provider, search dialog, page actions (edit source, feedback, copy page,
Ask AI, open-in-chat, scroll top)
- `/api/search` → `createSearchRoute`, `/api/chat` → `createChatRoute`
(AI SDK v6; AI Gateway default, optional `GEISTDOCS_CHAT_PROXY_URL`)
- `llms.mdx` → `createDocsMarkdownRoute`, `sitemap.md` →
`createSitemapMarkdownRoute` (now includes an **Adapters** section)
- **New**: `/agents.md` via `createAgentsRoute`, backed by a new `agent`
readiness config
- `proxy.ts` → `createProxy` with explicit `markdownRoutes` for `/docs`
→ `llms.mdx` and `/adapters` → `adapters.mdx` (adds AI-agent UA
rewrites)
- CSS: `@vercel/geistdocs/styles.css` + slim local overrides (shadcn
tokens for remaining `components/ui`, body tint, prose inline code,
`#nd-*` tweaks); code blocks now use the geist Shiki theme
- Icons/logos from `@vercel/geistdocs/assets/*`; feedback via the
package action (same geistdocs.com endpoint + `siteId`)

### Kept local by design

- Curated `/llms.txt` index + `/llms-full.txt` corpus — the published
`AGENTS.md`/SKILL.md artifacts and integration tests reference this
exact contract
- The adapters section (README fetching, OG images, JSON-LD, feature
matrices, `adapters.mdx` markdown route) — now rendered inside the
package docs layout
- RSS and OG image routes (app-owned per the migration guide)
- Skipped `/.well-known/mcp.json`: no MCP servers configured, and the
proxy matcher must keep excluding `.well-known` for the served
agent-skills files

### Cleanup

- Deleted local copies: `components/geistdocs/*` chrome,
`components/ai-elements/*`, chat hooks/persistence, feedback server
actions, unused shadcn primitives, geistcn logo/icon fallbacks covered
by package assets
- Removed 13 now-unused deps (`ai@5`, `@ai-sdk/react@2`, `dexie`,
`jotai`, `cmdk`, `vaul`, `mermaid`, `nanoid`, `react-player`,
`use-stick-to-bottom`, `@orama/tokenizers`, `dexie-react-hooks`,
`next-themes`)
- Updated `docs-llms.test.ts` proxy assertions to the `createProxy`
markdown-route shape

### Behavior changes to be aware of

- Code blocks use the geist Shiki theme instead of GitHub light/dark
- Ask AI history is no longer persisted in IndexedDB (package owns the
panel)
- Adapters sidebar uses the standard geistdocs tree rendering instead of
the bespoke grouped sidebar
- Per-page markdown output appends the standard geistdocs footer links
(`/sitemap.md`, `/llms.txt`, `/agents.md`)

## Test plan

- `pnpm validate` green (knip + check + typecheck + test + build)
- Smoke-tested against `next build && next start`: `/`, `/docs`,
`/adapters`, `/agents.md`, `/llms.txt`, `/llms-full.txt`, `/sitemap.md`,
page-level `.md` URLs for both docs and adapters, `Accept:
text/markdown` negotiation, search API, JSON-LD, sr-only markdown hints,
edit-source URLs (`apps/docs/content/docs/{path}`), OSS navbar, page
actions
- Verified compiled CSS chunks contain the home grid, Shiki palette, and
geist utilities (note: stale turbopack dev caches from before this
change can serve incomplete CSS — `rm -rf apps/docs/.next` fixes it)

## Checklist

- [x] All commits are signed and verified
- [x] All commits are signed off for the DCO (`git commit -s`)
- [x] `pnpm validate` passes
- [x] Changeset added (or N/A — docs app + tests only, no package
behavior change)
- [x] Documentation updated (or N/A)

---------

Signed-off-by: molebox <rich@vercel.com>
2026-07-09 15:29:47 +02:00
josh ef2542c5fd feat(x): add X (Twitter) adapter (#682)
## summary

new `@chat-adapter/x` adapter for X (Twitter), built on the X API v2 and
the X Activity API. write bot logic once and reply to mentions, hold DM
conversations, post from the account, and like posts, like the other
Chat SDK adapters

what it supports:
- reply to public mentions (`post.mention.create`) and top-level posts
via `channel.post`
- send and receive direct messages (`dm.received` / `dm.sent`)
- edit and delete owned posts, delete own DM events
- likes as the only reaction (`emoji.heart` or `"like"`)
- buffered streaming: accumulates an LLM stream and posts once instead
of post+edit churn on a public timeline
- OAuth 2.0 user context with managed token refresh (rotating refresh
token persisted in the state adapter, optional AES-256-GCM encryption)
- webhook CRC and `x-twitter-webhooks-signature` verification

key design decisions:
- DMs are threaded by the other participant's user id (`x:dm:{userId}`)
because X DM webhooks carry no conversation id, only participants
- OAuth 2.0 only at runtime: DM send and read are verified to work on
OAuth 2.0 user tokens, so no OAuth 1.0a in the adapter (subscription and
webhook setup is one-time and handled in the X developer console)
- parsers were written against real captured payloads: mentions use the
v2 shape (author hydrated in `includes.users`), DMs use the legacy
Account Activity shape (`direct_message_events`,
`message_create.message_data`, a `users` map, and no conversation id)

also includes the `chat/adapters` catalog entry, docs page, CLI scaffold
spec, and `sample-messages.md` with real captured payloads

<details><summary>usage</summary>

```typescript
import { Chat } from "chat";
import { createXAdapter } from "@chat-adapter/x";

const bot = new Chat({
  userName: "mybot",
  adapters: { x: createXAdapter() },
});

bot.onNewMention(async (thread, message) => {
  await thread.post(`hi @${message.author.userName}!`);
});

bot.onDirectMessage(async (thread) => {
  await thread.post("hello from X");
});
```
</details>

## test plan

- adapter unit tests pass against the real captured payload shapes, with
regression tests for author-from-`includes` (mentions) and the legacy
`direct_message_events` shape (DMs)
- real captured `post.mention.create` and `dm.received` payloads
verified end-to-end through `handleWebhook`: signature verification,
routing, author resolution, and participant threading, plus
bad-signature rejection returns 401
- every write and read path fired live against the X API through the
adapter: top-level post, reply to a mention, like and unlike, edit,
delete, DM send, DM read, DM delete
- OAuth 2.0 managed token refresh exercised live (access and refresh
token rotation)

---------

Signed-off-by: dancer <josh@afterima.ge>
2026-07-07 23:07:36 +01:00
zaadevofc eb466e526f docs: add chat-adapter-zaileys community adapter (#677)
## Summary

Adds **chat-adapter-zaileys** to the community adapters catalog — a
WhatsApp adapter powered by
[Zaileys](https://github.com/zeative/zaileys), a batteries-included
TypeScript wrapper around the unofficial WhatsApp Web API.

- npm: https://www.npmjs.com/package/chat-adapter-zaileys
- Repo: https://github.com/zeative/chat-adapter-zaileys
- Docs: https://zeative.github.io/chat-adapter-zaileys/

## What it adds vs the existing Baileys community adapter

- Real `thread.fetchMessages` history backed by a pluggable message
store (memory/SQLite/Postgres/Redis/Convex), with cursor pagination and
`rehydrateAttachment` for queue/debounce strategies
- Cards render as **native WhatsApp buttons** with `chat.onAction`
round-trips
- Poll votes decrypted natively — no `messageSecret` bookkeeping, works
across restarts
- `scheduleMessage` support (persisted scheduler)
- Opt-in slash-command routing to `chat.onSlashCommand`
- QR/pairing auth, reconnection, and session persistence handled by the
underlying client

## Files changed (per `.agents/skills/add-adapter`)

- `apps/docs/content/adapters/community/zaileys.mdx` — docs page with
feature matrix
- `apps/docs/content/adapters/community/meta.json` — slug added to
Platforms
- `apps/docs/adapters.json` — registry entry
- `packages/integration-tests/src/documentation-test-utils.ts` —
`chat-adapter-zaileys` + `zaileys` in `VALID_DOC_PACKAGES`

## Validation

- `pnpm --filter chat build` ✓
- `pnpm --filter @chat-adapter/integration-tests test` → 914/914 ✓
- `pnpm --filter chat typecheck` ✓
- `pnpm check` + `pnpm konsistent` ✓

Signed-off-by: zeative <zaadevofc@gmail.com>
2026-07-06 09:27:43 +10:00
OmriBenShoham 0c761f1bdd docs(adapters): add Dial as vendor-official adapter (#676)
Adds Dial as a vendor-official adapter — SMS, MMS, iMessage, and inbound
voice-call transcripts for Chat SDK.

- `vendor-official/dial.mdx` adapter page (following the Photon / Linq /
Sendblue format)
- catalog entry in `packages/chat/src/adapters/index.ts` with
`DIAL_API_KEY` / `DIAL_FROM_NUMBER_ID` / `DIAL_WEBHOOK_SECRET`
- `create-chat-sdk` scaffold spec entry
- registry entry in `adapters.json` + `dial` added to vendor-official
`meta.json`
- integration-test doc lists + changeset

Repo: https://github.com/GetDial-AI/chat-sdk-adapter · npm:
`@getdial/chat-sdk-adapter` · Dial docs:
https://docs.getdial.ai/integrations/agent-clients/vercel-chat-sdk

The adapter maps a phone conversation to a Chat SDK thread (identified
by the pair of phone numbers — Dial-owned and peer), an SMS/MMS/iMessage
to a message with optional media attachments, and a completed voice
call's transcript to a message on the caller's thread. Outbound sends
and transcript fetches go through the official `@getdial/sdk`; inbound
webhooks are HMAC-SHA256 verified against a per-subscription signing
secret with constant-time compare.

### Validation
- `pnpm --filter chat build` — clean
- `pnpm --filter chat typecheck` — clean
- `pnpm --filter create-chat-sdk typecheck` — clean
- `pnpm --filter @chat-adapter/integration-tests exec vitest run
src/docs-adapters.test.ts` — 361/361 passed
- `pnpm check` (ultracite) — clean
- `pnpm konsistent` — 34 files, no violations
2026-07-06 09:21:34 +10:00
Ben Sabic 3abdc69103 docs(adapters): add Cloudflare Agents as vendor-official state adapter (#669)
Adds Cloudflare Agents as a vendor-official **state** adapter —
`agents/chat-sdk`'s `createChatSdkState()`, a Chat SDK `StateAdapter`
that stores subscriptions, locks, queues, dedupe keys, thread/channel
state, transcripts, and history in Durable Object SQLite via
`ChatSdkStateAgent` sub-agents.

- `vendor-official/cloudflare-agents.mdx` state-adapter page (Agent
setup, wrangler DO migration, sharding, config, storage/cleanup)
- catalog entry in `packages/chat/src/adapters/index.ts` (`group:
vendor-official`, `type: state`)
- registry entry in `adapters.json` + `cloudflare-agents` in
vendor-official `meta.json`
- integration-test doc lists + changeset

Repo: https://github.com/cloudflare/agents · package `agents`
(`agents/chat-sdk`) ·
[docs](https://developers.cloudflare.com/agents/runtime/communication/chat-sdk/)

### Not wired into the create-chat-sdk CLI
This adapter runs inside a Cloudflare Worker with Durable Objects, not
the generated Next.js runtime, so it is intentionally kept out of the
scaffold:
- added to `CLI_INCOMPATIBLE_ADAPTERS` (rejected via `--adapter`, hidden
from the platform picker and e2e run, like `lark`/`matrix`)
- new `listCliStateAdapters()` filters the interactive **state** picker
and the `--help` adapter list (the state picker previously used raw
`listStateAdapters()` and would have offered it, then thrown on
selection)

### Tests
- `catalog/display.test.ts` — `listCliStateAdapters`: returns only state
adapters, includes `memory`/`redis`, and excludes `cloudflare-agents`
while asserting it *is* in the raw catalog
- `catalog/selection.test.ts` —
`resolveAdapterValue("cloudflare-agents")` throws "not supported"
- `cli/program.test.ts` — `buildAdapterList()` help text omits
`cloudflare-agents`
- existing `CLI_SCAFFOLD_SPEC covers every catalog adapter` +
docs-adapters/docs-content suites cover the catalog entry, registry
parity, and MDX imports

### Validation
- create-chat-sdk: **178 passed**, typecheck clean
- integration docs suites pass; Biome + knip clean

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-07-03 01:55:05 +10:00
Ben Sabic 24a04d5653 docs(adapters): add Photon as vendor-official adapter (#668)
Adds Photon as a vendor-official adapter — iMessage for Chat SDK.

- `vendor-official/photon.mdx` adapter page (following the Linq /
Sendblue / Kapso format)
- catalog entry in `packages/chat/src/adapters/index.ts` with
cloud/self-host credential modes
- `create-chat-sdk` scaffold spec entry
- registry entry in `adapters.json` + `photon` added to vendor-official
`meta.json`
- integration-test doc lists + changeset

Repo: https://github.com/photon-hq/vercel-chat-adapter-imessage · npm:
`@photon-ai/chat-adapter-imessage` · built on
[spectrum-ts](https://github.com/photon-hq/spectrum-ts)

The adapter runs in three modes — **Cloud** ([Spectrum
Cloud](https://app.photon.codes)), **self-hosted** (gRPC), and **local**
(on-device, macOS) — auto-detected from environment variables. Cloud
mode delivers inbound messages via HMAC-signed webhooks; DMs can be
replied to cold from a webhook delivery.

### Notes
- Catalog slug is `photon`; docs code examples use `imessage` as the
adapter key to match the upstream README.
- Feature flags encode the README's remote-only caveats (reactions /
editing / typing / modals as `partial`, mentions as DMs-only; no
history, thread info, or reaction removal).

### Validation
- `docs-adapters` integration tests — 1237 passed (catalog↔registry
parity, peerDeps↔PackageInstall alignment)
- `create-chat-sdk` e2e scaffold — 175 passed (scaffolds every catalog
adapter, incl. photon)
- `chat` + `create-chat-sdk` typecheck, Biome check, and konsistent —
clean

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-07-03 01:40:06 +10:00
Ben Sabic 4115c9431e feat(linear): add Vercel Connect support (#649)
Adds Vercel Connect support to the Linear adapter:

- `accessToken` now accepts a `() => string | Promise<string>` resolver
in addition to a string, so tokens can be sourced from Vercel Connect at
runtime.
- A new optional `webhookVerifier` verifies inbound webhooks (Connect
trigger-forwarded requests via a Vercel OIDC token) in place of the
Linear webhook secret.
- Connect-mode outbound calls outside webhook handling are supported via
`withInstallation(organizationId, fn)`.

Pair with `connectLinearAdapter()` from `@vercel/connect/chat`. Includes
a changeset (`@chat-adapter/linear` minor).

Stacked on #647 (base `vercel-connect/base`).

## Companion

`@vercel/connect/chat` subpath: vercel/vercel#16826.

<img width="929" height="664" alt="CleanShot 2026-06-30 at 12 35 30"
src="https://github.com/user-attachments/assets/c5861cb9-d66b-42c6-b838-5b4983f48646"
/>

---------

Signed-off-by: Ben Sabic <bensabic@users.noreply.github.com>
Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-07-03 01:23:21 +10:00
Ben Sabic ab0e1806c8 feat(chat): Vercel Connect (#647)
Adds a Vercel Connect guide to the docs under **Usage**
(`chat-sdk.dev/docs/vercel-connect`), covering connector setup, trigger
forwarding, the per-platform `connect*Adapter` helpers from
`@vercel/connect/chat`, custom OIDC webhook verification and its trust
boundary, and limitations. Also adds a "Vercel Connect Guide" card to
the docs homepage.

This is the base of a stack; the adapter, tests, and CLI PRs below build
on it. Docs-only, so no changeset.

## Stack

- #647 — feat(chat): Vercel Connect (this PR, base → `main`)
- #648 — docs(slack): document Vercel Connect support
- #649 — feat(linear): add Vercel Connect support
- #650 — feat(github): add Vercel Connect support
- #654 — feat(tests): add Vercel Connect webhook contract helper
- #655 — feat(create-chat-sdk): add Vercel Connect mode

All of the above are stacked on this branch (`vercel-connect/base`).

## Companion

The helpers this documents ship in the `@vercel/connect/chat` subpath:
vercel/vercel#16826.

---------

Signed-off-by: Ben Sabic <bensabic@users.noreply.github.com>
Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-07-03 01:01:16 +10:00
Ben Sabic 99c598505f docs: refresh agent docs, README badges, and Chat SDK skill (#646)
- Replace npm version/download badges with Agent Stack and MIT badges on
the root README and all published package READMEs
- Streamline root `AGENTS.md`: fix title, add an accurate monorepo map,
trim duplicated CONTRIBUTING/Ultracite/env-var content, and link to
package-level `AGENTS.md` files
- Slim the Chat SDK agent skill (`skills/chat/SKILL.md` and published
copies) to defer to bundled docs, chat-sdk.dev, Vercel KB, and
`llms.txt` instead of inlining CLI flags, quick-start code, and API
tables
- Polish root README copy (install examples, adapter/build links, Vercel
Plugin URL, Vercel KB link, “Made by Vercel” badge)
- Minor `CONTRIBUTING.md` fixes: simplify DCO wording, correct
preview-branch proxy file references (`proxy.ts` vs middleware)

---------

Signed-off-by: Ben Sabic <bensabic@users.noreply.github.com>
Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-06-27 14:06:13 +10:00
wong2 ef3f0f63bd docs: add Weixin community adapter (#638)
Adds the
[`chat-adapter-weixin`](https://github.com/wong2/weixin-chat-adapter)
community adapter (Weixin / WeChat iLink bot) to the docs.

### What's included
- `apps/docs/content/adapters/community/weixin.mdx` — hand-authored
adapter page following the existing community-adapter structure
(install, quick start, long-polling note, QR login, env vars, config
`TypeTable`, thread-ID format, capabilities/limitations, and
`<FeatureSupport />`).
- `apps/docs/adapters.json` — registry entry (`community: true`, author,
pinned README commit).
- `apps/docs/content/adapters/community/meta.json` — sidebar link under
**Platforms**.

### Notes
The adapter talks to Weixin's iLink bot HTTP JSON APIs directly. It uses
long polling for inbound messages (no webhook) and requires a Chat SDK
`StateAdapter` for cursor / context-token / dedupe / history. It's 1:1
only, so messages route through `onDirectMessage`.

### Verification
`docs-adapters` (322) and `docs-llms` (129) integration tests pass.

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-06-23 20:33:43 +10:00
Ben Sabic 64b66864b1 chore(changesets): ignore all example-* packages and enforce the convention (#626)
Replace the explicit per-example entries in the changesets `ignore` list
with an `example-*` name glob (matched by micromatch). All example apps
are
private and never published, so listing them individually only adds
version
and changelog churn to release PRs, and each new example required
editing
this CODEOWNERS-gated file.

Add an integration test that resolves the changesets config against the
workspace and asserts every examples/* package is in the resolved ignore
list and follows the `example-*` naming convention, so an off-convention
example app fails CI instead of silently leaking into releases.

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-06-22 23:25:43 +10:00
Ben Sabic efa96108bd docs: sync KB resources and harden sync-resources script (#635)
Syncs the bundled Chat SDK KB resources from Edge Config and hardens the
`sync-resources` script that generates them.

- **New guides** (4): Vercel Connect, the Slack Vercel Connect bot, AI
Gateway + AI SDK, and the daily digest bot. Existing guide bodies
refreshed and `templates.json` regenerated.
- **Script hardening** (`scripts/sync-resources.ts`):
- Fetch + validate all guides into memory **before** wiping the
resources dir — a failed fetch now leaves the working tree untouched.
- Validate the `resources-edge-config.json` shape with a clear error
instead of a blind cast.
  - Reject duplicate guide slug collisions.
- Retry transient fetches (5xx / network) with exponential backoff; fail
fast on 4xx, bad content-type, and oversized bodies.
- Mirror `skills/chat/SKILL.md` to **all four** committed copies (docs
site `.well-known` + `AGENTS.md`, and the two `create-chat-sdk` scaffold
templates).
  - TSDoc on every function.
- **Tests**: new offline consistency test in
`packages/integration-tests` — every guide has a non-empty file with no
orphans, `templates.json` mirrors the config, no duplicate slugs, and
all four `SKILL.md` copies are byte-identical to the source.

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-06-22 14:56:17 +10:00
George McCain d034b8b575 docs(adapters): add Linq as vendor-official adapter (#625)
Adds Linq as a vendor-official adapter — iMessage and SMS for Chat SDK.

- `vendor-official/linq.mdx` adapter page (following the Velt /
AgentPhone format)
- catalog entry in `adapters.json`
- `linq` added to the vendor-official `meta.json`

Repo: https://github.com/linq-team/linq-chat-sdk · npm:
`@linqapp/chat-sdk-adapter` (Apache-2.0)

The adapter is built and tested end-to-end against the live Linq API and
the Chat SDK runtime (real iMessage round-trip, webhooks, reactions,
media). Confirmed with Benji that a repo link works and Apache-2.0 is
fine. Happy to adjust the page to match any conventions I missed.

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-06-20 11:47:57 +10:00
Dima Grossman 06af3e12fd docs(adapters): add Novu as vendor-official adapter (#622)
## Summary

Adds Novu as a vendor official adapter to Chat SDK allowing
multi-channel notification delivery and quick channel setup for
multi-tenant apps.

Official change log entry:
https://novu.co/changelog/novu-chat-sdk-adapter/
Official social post: https://x.com/novuhq/status/2067870170320679158 

## Test plan

Manually tested with our team to ensure compatability with the create
chat sdk and template apps, also created an example repo:
https://github.com/novuhq/novu-chat-sdk-example

## Checklist

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

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-06-19 22:11:33 +10:00
OSS Polar Bear 8c7141174a feat(teams): add low-level primitives (#593)
Add Teams subpath exports for custom runtimes, including Bot Connector
API helpers, Graph reads, parse-only webhooks, format helpers, Adaptive
Cards, and Task Module primitives.

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-06-16 18:54:07 +10:00
Ben Sabic 8f3af76565 feat: add create-chat-sdk CLI (#603)
Adds `create-chat-sdk`, a CLI that scaffolds a Next.js Chat SDK bot
project:

```bash
npm create chat-sdk@latest my-bot

# non-interactive
npm create chat-sdk@latest -- my-bot --adapter slack redis -y
```

The user picks platform and state adapters interactively or via
`--adapter`, and the CLI generates a webhook-only project with
`src/lib/bot.ts`, `.env.example`, `next.config.ts`, `package.json`, and
a README, then optionally runs `git init` and installs dependencies.
There are no pages or client UI in the template.

Adapter choices come straight from the `chat/adapters` catalog, so the
CLI has no adapter registry of its own. When a coding agent such as
Cursor or Claude Code runs the CLI, it uses non-interactive defaults and
requires an explicit platform adapter. `--interactive` forces prompts.

## also in this pr

- `google-chat` is renamed to `gchat` everywhere, including docs pages,
the OG image, and adapter catalog. Old URLs redirect permanently,
including language-prefixed and `/og` paths
- a new docs page is available at `chat-sdk.dev/docs/create-chat-sdk`,
and the CLI is promoted on the homepage, package READMEs, and agent
skill
- `create-chat-sdk` releases independently with a minor changeset for
its initial `0.1.0` release

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
Co-authored-by: dancer <josh@afterima.ge>
2026-06-16 08:48:38 +01:00
OSS Polar Bear b14114a714 test(slack): expand emulator coverage for emulate.dev 0.6.0 APIs (#591)
Upgrade @emulators/* to 0.6.0 and add integration tests for DMs,
reactions, fetch history, modals, scheduled messages, file uploads,
member joins, and bookmarks against the in-process Slack emulator.

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-06-09 12:54:30 +10:00
Ben Sabic 778ae69abc add zero-dependency chat/adapters for adapters catalog (#599)
Adapter Catalog:
- Adds a zero-dependency `chat/adapters` subpath for official and
vendor-official adapter metadata.
- Includes typed catalog entries, env specs, peer dependency metadata,
and helper APIs for setup and onboarding flows.
- Wires the subpath into the `chat` package export map and build config.

Code Coverage:
- Adds unit coverage for catalog integrity, registry sync, helper
behavior, official env declarations, and peer dependency derivation.
- Extends docs integration coverage for `chat/adapters` imports and
vendor-official package install metadata.

Documentation:
- Documents the new catalog on the adapter overview page.
- Splits platform-specific adapter guidance into a new
`/docs/platform-adapters` page.
- Renames `/docs/state` to `/docs/state-adapters` and adds a redirect
for the old slug.

Agent Guidance:
- Updates repo-local and public agent guidance so agents know when and
how to use `chat/adapters`.
- Adds focused `AGENTS.md` guidance inside `packages/chat/src/adapters`
for future catalog maintenance.

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-06-09 10:43:00 +10:00
Ben Sabic 7ecb9730e3 chore(docs): add missing Twilio logo and add official platform adapter OG images (#589)
Adds official platform adapter branding across the docs site, npm
READMEs, and social previews.

- **Homepage**: add Twilio to the supported-platforms logo grid
- **OG images**: add custom artwork for all 11 official platform
adapters under content/adapters/official/og/; serve static-first from
the existing /adapters/official/{slug}/og route with dynamic fallback
for state adapters
- **READMEs**: add linked hero banners to every official platform
adapter package README, using the live OG URL as the single image source
- **Tests**: integration-test guardrails for OG image coverage, README
banner discoverability, and knip-clean helpers
- **Changeset**: empty changeset for CI

<img width="2400" height="1256" alt="Chat SDK - Slack"
src="https://github.com/user-attachments/assets/4d186a1c-5651-44b8-8698-091ee23b44da"
/>

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-06-06 14:51:30 +10:00
Ben Sabic 9921dcd1c4 docs(seo): improve npm metadata, README discoverability, and structured data (#587)
Improves Chat SDK discoverability across npm, READMEs, and the docs site
for search engines and AI coding agents.

- **npm metadata**: point every published package `homepage` at
chat-sdk.dev deep links; expand `chat` keywords/description; fix
`repository.directory` (`packages/chat-sdk` → `packages/chat`); align
state adapter keywords
- **READMEs**: add npm callouts, Documentation/Guides links, and AI
Coding Agents sections (skill install, optional Vercel Plugin,
`llms.txt` / `llms-full.txt`) across all published packages and the repo
root
- **docs JSON-LD**: `HowTo` / `TechArticle` on getting-started,
streaming, and cards; `CollectionPage` + official-only `ItemList` on
`/adapters` (with split human vs JSON-LD descriptions)
- **UTMs**: add `chat-sdk_site` / `chat-sdk_repo` tracking params to
Resources links in selected MDX pages and adapter READMEs (discord,
github, slack, liveblocks, getting-started, ai index)
- **contract tests**: integration-tests guardrails for npm metadata and
README discoverability so future package additions don't drift

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-06-05 14:00:18 +10:00
Ben Sabic c290464634 docs(adapters): promote Sendblue to vendor-official adapter (#584)
Moves the Sendblue adapter from community to vendor-official in the docs
site now that Sendblue maintains `chat-adapter-sendblue`.

- package: `chat-adapter-sendblue`
- repo: https://github.com/sendblue-api/chat-adapter-sendblue
- docs: https://docs.sendblue.com/guides/chat-sdk-adapter/

Updates the adapter registry and hand-authored detail page (iMessage,
SMS, RCS, `sb-signing-secret` webhook verification, official Sendblue
guide links). Reorders vendor-official adapters so Liveblocks, Resend,
Sendblue, and Zernio appear first. Adds a permanent redirect from
`/adapters/community/sendblue`.

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-06-05 13:59:46 +10:00
Rodrigo Gaona 2fb8ad29cf docs(adapters): add Kapso WhatsApp adapter (#577)
## Summary

Adds [Kapso WhatsApp](https://docs.kapso.ai/) as a vendor-official
adapter in `adapters.json`.

Kapso is a WhatsApp adapter for Chat SDK that receives signed Kapso
webhooks, replies through Chat SDK threads, and supports WhatsApp-native
messaging features:

- **Text messages** — send and receive WhatsApp messages via Kapso
- **Buttons/cards** — Chat SDK card buttons render as WhatsApp reply
buttons
- **Media** — send and receive images, video, audio, documents, and
stickers
- **Reactions** — add/remove reactions and handle inbound reaction
events
- **History** — fetch Kapso-backed message, contact, and conversation
metadata
- **Webhook verification** — validates Kapso `X-Webhook-Signature`
deliveries

| Feature | Support |
| --- | --- |
| Send/receive text | Yes |
| Reply buttons | Yes, up to 3 |
| CTA URL buttons | Yes |
| Media attachments | Yes |
| Reactions | Yes |
| Message history | Yes |

- **Docs**: [docs.kapso.ai](https://docs.kapso.ai/)
- **npm**:
[`@kapso/chat-adapter`](https://www.npmjs.com/package/@kapso/chat-adapter)
- **GitHub**:
[`gokapso/chat-sdk-adapter`](https://github.com/gokapso/chat-sdk-adapter)
- **Blog**: https://kapso.ai/blog/chat-sdk-adapter
2026-06-04 22:12:04 +10:00
josh 9b99ee2d2d fix(docs): allow Velt adapter examples (#575)
## summary

fixes the release-blocking docs validation failures from the Velt
vendor-official adapter page

this updates the docs integration-test allowlists so `velt` is an
expected vendor-official adapter slug and `@veltdev/chat-sdk-adapter` is
accepted in docs code examples

no changeset needed because this only updates test expectations for
existing docs content
2026-06-01 18:27:32 -07:00
Ben Sabic 5a72259fbc feat(docs): improve LLM/search discoverability of docs (#566)
SEO/AEO improvements for the docs site:

- Append `| Chat SDK` to every page's `<title>` and `og:title` (home
stays `Chat SDK`).
- Expose a plain-markdown version of every docs and adapter page via an
`sr-only` AI/LLM hint link and a `text/markdown` alternate; adapter
pages get a new `/adapters/<group>/<slug>.md` endpoint.
- Turn `llms.txt` into a sitemap-style index linking to each page's
markdown, and move the full-text concatenation to `llms-full.txt`. A
`<link rel="llms-txt">` is added to every page.

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-06-01 12:35:34 -07: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
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
Ben Sabic cd7b6af1c8 test(integration-tests): add Emulate.dev-backed tests for the GitHub adapter (#479)
## Summary

Add an Emulate.dev-backed integration-test suite for the GitHub adapter,
mirroring the structure of the Slack work in #477. Tests drive the
adapter against an in-process
[`@emulators/github`](https://emulate.dev/docs/github) server and assert
on its stateful store (comments, reviews) rather than `mock.calls`,
catching wire-format and contract issues that pure Octokit mocks miss.

- New private devDeps in `packages/integration-tests`:
`@emulators/github`, `@emulators/core`, `@hono/node-server`, plus a
workspace dep on `@chat-adapter/github`. (`@emulators/core` and
`@hono/node-server` are also declared by #477; this PR is independent of
merge order.)
- New harness `packages/integration-tests/src/github-emulator-utils.ts`
boots the emulator on an ephemeral `127.0.0.1` port, seeds a
deterministic user / repo / issue / PR / starter review comment, and
exposes a near-passthrough HTTP forwarder. **No re-signing needed**
here. `@emulators/core`'s `WebhookDispatcher` already signs deliveries
with `X-Hub-Signature-256: sha256=<hex>` exactly as the GitHub adapter
expects. The harness also adds a small URL rewriter for Octokit's
`pulls.createReplyForReviewComment` shortcut endpoint, translating it
into the canonical review-comment POST that the emulator implements.
- Four new test files (12 tests), wired to the adapter via its existing
`apiUrl` + `webhookSecret` config **zero source changes** to
`packages/adapter-github`:
- `emulator-github-auth.test.ts` (2) `GET /user` populates `botUserId`
during `initialize()`.
- `emulator-github-comments.test.ts` (4) `thread.post` / `edit` /
`delete` on issue and PR-conversation threads.
- `emulator-github-reviews.test.ts` (3) review-comment replies routed
through `pulls.createReplyForReviewComment` with the right
\`in_reply_to_id\`, plus edit / delete.
- `emulator-github-events.test.ts` (3) full inbound `issue_comment` /
`pull_request_review_comment` round-trip, including bot self-message
filtering.

```mermaid
flowchart LR
  subgraph Test["Vitest test process"]
    SDK[GitHubAdapter + Chat]
    Forwarder["HTTP forwarder<br/>passthrough"]
    Emu["@emulators/github<br/>(in-process Hono)"]
  end

  SDK -->|"issues.createComment / pulls.* / GET /user<br/>(apiUrl override)"| Emu
  Emu -->|"X-Hub-Signature-256 + x-github-event"| Forwarder
  Forwarder -->|"chat.webhooks.github(request)"| SDK
```

### Out of scope (deliberate)

- **Reactions** `@emulators/github` does not implement the `/reactions`
endpoints used by the adapter. Reaction logic is still covered by the
existing mock-based tests in
`packages/adapter-github/src/index.test.ts`.
- **GitHub App auth** (JWT \u2192 installation token via `POST
/app/installations/:id/access_tokens`) the adapter and emulator both
support it, but PAT-mode was the agreed scope here.
- **Multi-tenant install flows** via `installation` webhook events.
- Branches/refs, releases, search, actions, checks not used by the
adapter.

## Test plan

- [x] `pnpm --filter @chat-adapter/integration-tests test` 407 tests
pass across 34 files (including the 12 new emulator-github tests, ~480
ms total).
- [x] `pnpm check` and `pnpm knip` clean.
- [x] CI safety verified: ephemeral ports (`port: 0`), loopback-only
binds (`127.0.0.1`), deterministic teardown via `httpServer.close()`, no
env vars, no external network egress.

## Checklist

- [x] All commits are signed and verified
- [x] \`pnpm validate\` passes
- [x] Changeset added (or N/A see [CONTRIBUTING.md](./CONTRIBUTING.md))
N/A: \`@chat-adapter/integration-tests\` is \`private: true\` and the
change is test-only.
- [x] Documentation updated (or N/A)
\`packages/integration-tests/README.md\` describes the new
emulator-github test category.

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-05-13 07:12:55 +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
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
Ben Sabic 14b1434998 test(integration-tests): add Emulate.dev-backed tests for the Slack adapter (#477)
* chore(integration-tests): add Emulate.dev Slack emulator devDeps

Add @emulators/slack, @emulators/core, and @hono/node-server as
devDependencies of the private integration-tests package. These power
the upcoming in-process Slack emulator harness used to drive the Slack
adapter against a stateful, Slack-shaped HTTP server instead of mocks.

* test(integration-tests): add Slack emulator test harness

Introduce slack-emulator-utils.ts, a test-only helper that boots the
@emulators/slack Hono app on an ephemeral 127.0.0.1 port via
@hono/node-server and pre-seeds a deterministic team / channel / bot /
human user / OAuth app. The Slack adapter is wired to it via its
existing apiUrl config; no source changes required.

Also exposes startSlackWebhookForwarder, a tiny in-process Node http
forwarder that re-signs the emulator's outbound event_callback
deliveries with x-slack-signature / x-slack-request-timestamp before
handing them to chat.webhooks.slack(...). The emulator's core
WebhookDispatcher only emits GitHub-style X-Hub-Signature-256 headers,
so this bridge is what makes inbound flows exercise the SDK's real
HMAC verification path.

The handle returns direct access to the emulator's Store and
WebhookDispatcher so tests can assert on persisted state instead of
mock call records.

* test(integration-tests): cover Slack auth, postMessage, and reactions via emulator

Add three test files that drive the SlackAdapter's outbound WebClient
calls through the in-process emulator and assert on its stateful store
rather than on mock call records:

- emulator-slack-auth.test.ts (3 tests): auth.test populates botUserId
  during initialize(); explicit botUserId is respected; multi-workspace
  mode skips the call entirely.
- emulator-slack-post-message.test.ts (5 tests): plain text and threaded
  thread.post round-trip into the messages collection and are visible
  via conversations.replies; editMessage updates via chat.update;
  deleteMessage removes via chat.delete; markdown posts succeed.
- emulator-slack-reactions.test.ts (3 tests): addReaction /
  removeReaction round-trip via reactions.add / reactions.remove and
  show up via reactions.get; multi-user reactions accumulate correctly.

These exercise the full HTTP path against a Slack-shaped server,
catching wire-format and contract issues that pure mocks miss.

* test(integration-tests): cover Slack inbound events and OAuth v2 install via emulator

Add two test files that drive end-to-end flows previously only
verifiable against real Slack:

- emulator-slack-events.test.ts (4 tests): a human posts to the
  emulator, which dispatches an event_callback to the local forwarder,
  which signs the body and hands it to chat.webhooks.slack(...). The
  SDK's onNewMention and onNewMessage handlers run with a live Thread
  and the bot's reply lands back in the emulator. Bot self-messages
  are correctly filtered. This is the only Slack adapter test in the
  repo that covers the full inbound-then-outbound round-trip without
  hand-crafted webhook payloads.
- emulator-slack-oauth.test.ts (4 tests): handleOAuthCallback
  exchanges a real authorization code via oauth.v2.access against the
  emulator's authorize/callback flow; the resulting installation is
  persisted in the state adapter; invalid codes and mismatched
  client_secrets are rejected; the freshly issued bot token works for
  subsequent chat.postMessage calls via withBotToken.

* docs(integration-tests): document emulator-* test category

Add an "Emulator tests" entry to the package README so newcomers can
distinguish the new emulator-backed suite from the existing unit and
replay tests, and find the harness in slack-emulator-utils.ts.

* fix(integration-tests): keep full token scopes after emulator.reset()

`applyTokenSeed` (used during `reset()`) was granting only
["chat:write", "channels:read"] to seeded tokens, while the initial
`createCoreServer({ tokens })` call granted the full bot/human scope
sets. After the first `reset()` the bot token silently lost
`channels:history`, `users:read`, `reactions:read`, and
`reactions:write`, which would surface as flaky behaviour for any test
that relied on those scopes after a reset.

Unify both call sites on a single `buildTokenSeedEntries` helper so
fresh-boot and post-reset state always grant the same scopes. Add a
regression test in emulator-slack-auth.test.ts that triggers a manual
`emulator.reset()` and re-asserts that the bot token still resolves
via auth.test.

* refactor(integration-tests): reorganize Slack emulator tests under emulator/slack/

Address review feedback (visyat) by moving the flat
`emulator-slack-*.test.ts` files into a per-adapter directory:

  packages/integration-tests/src/emulator/slack/
      utils.ts
      auth.test.ts
      events.test.ts
      oauth.test.ts
      post-message.test.ts
      reactions.test.ts

This scales cleanly as more adapter emulator suites land (e.g.
`emulator/github/`), instead of cluttering the top-level src tree with
adapter-prefixed file names.

Also hoist the duplicated `silentLogger` definition from each test
file into the shared `utils.ts`, removing five identical copies.

No behavior changes. All 20 emulator tests still pass.

* test(integration-tests): cover Slack multi-workspace token resolution via emulator

Add `emulator/slack/multi-workspace.test.ts` exercising the path that
was previously only covered by replay/mock-based tests: the adapter
runs without a hardcoded `botToken`, multiple workspaces are persisted
in the state adapter via `adapter.setInstallation(teamId, ...)`, and
inbound `event_callback`s for either team are routed to the correct
per-tenant bot token end-to-end against the shared emulator.

To make this work, the helper grew two small additions:

- `addEmulatorWorkspace(emulator, seed)` — register an additional
  team + bot + channel + token on an already-booted emulator, so two
  tenants live side-by-side without needing two emulator instances.
- The inbound forwarder's `augmentEventEnvelope` now accepts an
  optional `resolveTeamId(envelope)` callback, defaulting to a constant
  teamId for the existing single-workspace tests. The multi-workspace
  test passes a resolver that looks up the dispatched event's channel
  in the emulator store and returns the owning team's id, so the SDK's
  per-team token resolver sees the right `team_id` on the envelope.

Three new tests: team-A routing, team-B routing, and `getInstallation`
returning null for unknown team ids. Follow-up from review feedback on
PR #477.

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-05-12 08:29:02 +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 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
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
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
Malte Ubl 9824d3335e Adapter hardening pass (#441)
* Adapter hardening pass

* chore: remove redundant changeset

---------

Co-authored-by: dancer <josh@afterima.ge>
2026-05-02 08:37:45 -07:00
mdnanocom 8a0c7b308d [chat] fix Slack streaming team ID for interactive payloads (#330)
* [chat] fix Slack streaming team ID for interactive payloads

* chore: downgrade changeset to patch

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-04-29 08:17:05 -07:00
Aamir Jawaid ed46bae52e feat(adapter-teams): native streaming for DMs via emit (#416)
* feat(adapter-teams): use native Teams SDK streaming for DMs

Use ctx.stream.emit() from the Teams SDK for DM streaming instead of
manual post+edit. This sends proper typing activities with streamType
channelData, giving the native streaming UI in Teams.

- Capture IStreamer from activity context in handleMessageActivity
- Block handler with deferred promise so stream stays alive during processing
- streamViaEmit() for DMs: uses stream.emit() with incremental text deltas
- Group chats: accumulate full response and post as single message (no flicker)
- Handle StreamCancelledError and stream.canceled for graceful cancellation

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

* fix(tests): update Teams streaming assertions for accumulate-and-post

Group chats now accumulate streamed chunks and post as a single message
instead of post+edit, so assertions should check sentActivities not
updatedActivities.

* style: format replay-streaming test

* chore: add changeset for teams native streaming

---------
2026-04-29 08:08:29 -07:00
Dima Voytenko 2531e9cff5 feat(slack): dynamic botToken resolver and custom webhookVerifier (#421)
* feat(slack): dynamic botToken resolver and custom webhookVerifier

Allow `botToken` to be a function returning `string | Promise<string>`
so apps can rotate or lazily fetch tokens; the resolver is invoked per
API call. Add `webhookVerifier: (request) => string | Promise<string>`
as an alternative to `signingSecret` for custom request verification —
returns the verified body text or throws to produce a 401.

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

* change verifier signature to make it compatible with  function

* make scheduleMessage cancel() rotation-safe and honor verifier body

- scheduleMessage cancel(): re-resolve token in single-workspace mode so
  rotation works. Slack rotated tokens have a 12h TTL and scheduled messages
  can outlive their schedule-time token, leaving cancel() with stale auth.
  Multi-workspace still snapshots ctx.token since cancel() runs outside
  the AsyncLocalStorage frame.
- webhookVerifier: when it returns a string, use it as the verified body
  for downstream parsing. JSDoc previously implied this contract; the code
  only checked truthiness.
- webhookVerifier JSDoc: explicit SECURITY note that timestamp/replay
  protection is the implementer's responsibility when bypassing signingSecret.
- Tests: cover Attachment.fetchData snapshot semantics — multi-workspace
  uses the ctx token captured at attachment creation; single-workspace
  re-resolves the default provider per fetch (rotation-safe).

* docs(slack): document botToken resolver and webhookVerifier in README

* opt out of SLACK_SIGNING_SECRET env fallback when webhookVerifier is set

A webhookVerifier passed in config was being silently shadowed by
SLACK_SIGNING_SECRET in the env (read by both createSlackAdapter and the
SlackAdapter constructor). An explicit verifier now opts out of that
fallback in both code paths. Added a regression test that stubs the env
var via vi.stubEnv.

* register handleReactionEvent's outer promise via waitUntil

handleReactionEvent does async work (conversations.replies, users.info)
before delegating to chat.processReaction, which is the only point that
registers a waitUntil task. The outer prep work was untracked, so callers
that drained waitUntil tasks could complete before the reaction handler
finished — flaky in CI under tight microtask scheduling. Track the outer
promise too so the full handler is awaited.

* fix(integration-tests): drain waitUntil cascade in test tracker

---------

Co-authored-by: dancer <josh@afterima.ge>
2026-04-26 16:00:37 -07: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
mdnanocom 0f8b2b150b [slack] [bug] preserve self mentions in multi-workspace installs (#355)
* [slack] preserve self mentions in multi-workspace installs

* add changeset for slack multi-workspace self-mention fix

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-04-14 13:27:22 +10:00