5 Commits

Author SHA1 Message Date
josh 83ede7eab2 feat(chat): add message reply support (#819)
- add `thread.reply()` for sending messages with native references to
existing messages
- accept either a message object from the same thread or a message id as
the reply target
- support text, markdown, AST, cards, files, and buffered streams
- add WhatsApp contextual replies using the Cloud API
`context.message_id` field
- apply reply context only to the first outgoing message when content is
split across multiple sends
- preserve the target message through sent message edits and thread
history
- throw `NotImplementedError` for adapters without native reply support
- document the API and add message replies to the adapter feature matrix

fixes #786

---------

Signed-off-by: dancer <josh@afterima.ge>
Signed-off-by: Ben Sabic <bensabic@users.noreply.github.com>
Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
Co-authored-by: Aradhya C P <135510032+aradhyacp@users.noreply.github.com>
2026-08-14 14:32:27 +10:00
josh 18d4a230d7 feat(chat): add mark as read support (#820)
- add `thread.markAsRead()` for the current message, an explicit
`Message`, or a message ID
- expose read receipts as an optional adapter capability with explicit
unsupported and thread mismatch errors
- support WhatsApp read acknowledgements, Messenger `mark_seen`, and
XChat read watermarks
- preserve automatic XChat receipts while allowing manual timing and
surfacing explicit failures
- document provider-specific behavior and capability support
- closes #785

---------

Signed-off-by: Ben Sabic <bensabic@users.noreply.github.com>
Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
Co-authored-by: Aradhya C P <135510032+aradhyacp@users.noreply.github.com>
2026-08-14 14:09:23 +10:00
josh 4ac0455134 feat(chat): add message update and delete lifecycle callbacks (#788)
## summary

adds `onMessageUpdated` and `onMessageDeleted`, so a bot can react when
a message is edited or removed. Slack dispatches both today; other
adapters can opt in later

supersedes #549, which was verified there against real Slack webhooks.
reopened from a branch in this repo with the original commits preserved
and signed

```typescript
bot.onMessageUpdated(async (thread, message, previousMessage) => {
  await mirror.update(message.id, message.text);
});

bot.onMessageDeleted(async (event) => {
  await mirror.remove(event.messageId);
});
```

both are lifecycle events: they never route through `onNewMessage`,
`onNewMention`, or `onSubscribedMessage`, and the concurrency strategies
do not apply

### notes

- **the bot's own edits are filtered.** slack sends a `message_changed`
for every `chat.update`, and post-and-edit streaming calls it once per
delta, so without this a single streamed reply would call the handler
back repeatedly on its own message
- **`previousMessage` is forwarded on edits.** slack sends the pre-edit
message and it was being dropped. an edit handler usually needs the
before to know what changed, so it is the optional third argument
- **the two shapes differ deliberately.** an edit carries a full
replacement message, so it gets `(thread, message, previousMessage?)`. a
delete has no message, only the id of what was removed, so it gets an
event. use `chat.thread(event.threadId)` when a delete handler needs one
- **one thread id helper** now serves message, edit, and delete, so an
edit cannot resolve to a different thread than the message it edits

## test plan

core:

- an edit dispatches to `onMessageUpdated` and not to the normal message
handlers
- the handler receives the pre-edit message as its third argument
- the bot's own edits are skipped
- a delete dispatches with normalized event data
- both run inside the active conversation, so read tools built in these
handlers stay scoped

slack:

- `message_changed` dispatches as an update, `message_deleted` as a
delete
- `previous_message` is forwarded, and left undefined when slack omits
it
- hidden unfurl updates stay ignored, hidden real edits still dispatch
- message, edit, and delete resolve to one thread id in a flat DM and in
a threaded `agent_view` DM

verified against a real slack workspace over socket mode: editing and
deleting a DM both routed to the same thread id as the original message

---------

Co-authored-by: Miłosz Lenczewski <m.lenczewski@tidio.net>
2026-08-05 13:22:54 +01: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
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