mirror of
https://github.com/vercel/chat.git
synced 2026-09-14 18:32:29 +08:00
@chat-adapter/instagram@4.39.0
900 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
7a1798bdfd |
chore(release): version packages (#841)
This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated. # Releases ## @chat-adapter/shared@4.39.0 ### Minor Changes -@chat-adapter/state-pg@4.39.0 @chat-adapter/web@4.39.0 @chat-adapter/teams@4.39.0 @chat-adapter/discord@4.39.0 @chat-adapter/telegram@4.39.0 @chat-adapter/state-redis@4.39.0 chat@4.39.0 @chat-adapter/gchat@4.39.0 @chat-adapter/github@4.39.0 @chat-adapter/x@4.39.0 @chat-adapter/tests@4.39.0 @chat-adapter/state-memory@4.39.0 @chat-adapter/instagram@4.39.0 @chat-adapter/linear@4.39.0 @chat-adapter/state-ioredis@4.39.0 @chat-adapter/whatsapp@4.39.0 @chat-adapter/twilio@4.39.0 @chat-adapter/slack@4.39.0 @chat-adapter/messenger@4.39.0 @chat-adapter/notion@4.39.0 @chat-adapter/shared@4.39.0 |
||
|
|
75cadbf9aa |
feat(twilio): add RCS support for interactive inbound and rich outbound (#590)
Extend the Twilio adapter with RCS webhook parsing, Content API integration, and card-to-template mapping with SMS fallback. --------- Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com> |
||
|
|
169788b65a |
feat(chat): introduce unified History API with user, thread, and chan… (#592)
Adds `bot.history` as the canonical entry point for message history,
with three scopes: `user`, `thread`, and `channel`. `bot.transcripts`
stays as a deprecated alias, so nothing breaks.
## Why
History access was spread across `bot.transcripts`, `thread.messages` /
`thread.allMessages`, and per-adapter calls. `bot.history` puts the
promise-based read paths in one place, and the AI tools
(`fetchMessages`, `fetchChannelMessages`, `listThreads`) now route
through it.
## User scope
Cross-platform per-user persistence, identical in surface to
`bot.transcripts`:
```typescript
const bot = new Chat({
adapters: { slack, telegram },
state,
history: {
user: {
identity: ({ author }) => author.email ?? null,
retention: "30d",
maxPerUser: 200,
},
},
});
await bot.history.user.append(thread, message);
const entries = await bot.history.user.list({ userKey, limit: 20 });
await bot.history.user.delete({ userKey });
```
The new `toPromptEntries` helper turns those entries into `{ role,
content }` messages for an LLM call:
```typescript
import { toPromptEntries } from "chat";
const entries = await bot.history.user.list({ userKey });
const { text } = await generateText({
model,
messages: toPromptEntries(entries),
});
```
## Thread scope
Single-page reads and an auto-paginating generator:
```typescript
// One page, newest messages by default
const { messages, nextCursor } = await bot.history.thread.list(thread.id, {
limit: 20,
});
// Everything, oldest first, pagination handled for you
for await (const msg of bot.history.thread.collect(thread.id, { limit: 50 })) {
console.log(msg.text);
}
```
## Channel scope
```typescript
// Top-level channel messages (not thread replies)
const { messages } = await bot.history.channel.listMessages("slack:C123", {
limit: 20,
});
// Thread listings
const { threads } = await bot.history.channel.listThreads("slack:C123");
// Threads together with a page of messages each
const result = await bot.history.channel.listThreadsWithMessages("slack:C123", {
maxThreads: 5,
messagesPerThread: 10,
});
```
## Semantics
The read paths are strict about where data comes from:
- The adapter named in the ID prefix must be registered. A typo'd or
unknown prefix throws instead of reading as an empty conversation.
- The SDK-side `ThreadHistoryCache` only serves adapters that persist
history there (`persistThreadHistory: true`, e.g. Telegram, WhatsApp).
For every other adapter the platform response is authoritative, so an
empty page is a real empty page, and a `cursor` always returns the
adapter's response as-is.
- Cache reads honor the same windows as adapter reads: backward
(default) gives the newest N, forward the oldest N, and `collect()`
yields the oldest N on both paths.
- `channel.listMessages` throws a capability error on adapters without
`fetchChannelMessages` (persisting adapters are served from the
channel-keyed cache instead), and `listThreadsWithMessages` fetches
per-thread pages through `history.thread.list` a few threads at a time
to stay inside platform rate limits.
## Migration
```typescript
// Before
const bot = new Chat({
identity: ({ author }) => author.email ?? null,
transcripts: { retention: "30d", maxPerUser: 200 },
});
await bot.transcripts.append(thread, msg);
// After
const bot = new Chat({
history: {
user: {
identity: ({ author }) => author.email ?? null,
retention: "30d",
maxPerUser: 200,
},
},
});
await bot.history.user.append(thread, msg);
```
You can migrate one field at a time: when both `history.user` and the
legacy `transcripts` block are set they merge, with `history.user`
winning field by field, so settings left on `transcripts` keep applying
until you move them. `TranscriptEntry` is deprecated in favour of
`HistoryEntry` (also exported as `UserHistoryEntry`); all deprecated
names keep working in the current major version.
## Included
- New `packages/chat/src/history/` module with unit tests for every
scope
- AI tools rewired to `bot.history`, keeping their scope guards
- The nextjs example uses the new APIs throughout, with Thread History
and Channel History test buttons that exercise every scope
- Docs: `/docs/history` guide, `/docs/api/history` reference,
deprecation callouts on the transcripts pages
- Changeset (`minor` for `chat`)
---------
Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
|
||
|
|
4fa1c2bcf9 |
build(deps-dev): bump postcss from 8.5.25 to 8.5.26 (#795)
Bumps [postcss](https://github.com/postcss/postcss) from 8.5.25 to 8.5.26. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/postcss/postcss/releases">postcss's releases</a>.</em></p> <blockquote> <h2>8.5.26</h2> <ul> <li>Fixed <code>list.split()</code> regression (by <a href="https://github.com/lazerg"><code>@lazerg</code></a>).</li> <li>Track symlinks in path protection in source map loading (by <a href="https://github.com/drengir1"><code>@drengir1</code></a>).</li> </ul> </blockquote> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/postcss/postcss/blob/main/CHANGELOG.md">postcss's changelog</a>.</em></p> <blockquote> <h2>8.5.26</h2> <ul> <li>Fixed <code>list.split()</code> regression (by <a href="https://github.com/lazerg"><code>@lazerg</code></a>).</li> <li>Track symlinks in path protection in source map loading (by <a href="https://github.com/drengir1"><code>@drengir1</code></a>).</li> </ul> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/postcss/postcss/commit/07b25773f38f77919f2af02ae3e8896b0deb5988"><code>07b2577</code></a> Release 8.5.26 version</li> <li><a href="https://github.com/postcss/postcss/commit/47de6b9d7c55674cb326c5de7a734a740916defc"><code>47de6b9</code></a> Update CI</li> <li><a href="https://github.com/postcss/postcss/commit/1493a83db7830912316512f55ab6064e7b7dd68e"><code>1493a83</code></a> Fix Rule#selectors losing the empty selector (<a href="https://redirect.github.com/postcss/postcss/issues/2129">#2129</a>)</li> <li><a href="https://github.com/postcss/postcss/commit/180db166e250d20e6761b224ae8d8134c9ba3e40"><code>180db16</code></a> Typo</li> <li><a href="https://github.com/postcss/postcss/commit/29e9e00f132c96e46e1de295b816fe88a05354e7"><code>29e9e00</code></a> Resolve symlinks before the previous-source-map containment check (<a href="https://redirect.github.com/postcss/postcss/issues/2125">#2125</a>)</li> <li><a href="https://github.com/postcss/postcss/commit/3ba8f84703a884329b58abea579c3615684e0b7e"><code>3ba8f84</code></a> Update dependencies</li> <li><a href="https://github.com/postcss/postcss/commit/87e72f671fd0d401c52822b5226c656632d92ec0"><code>87e72f6</code></a> Update lock file</li> <li><a href="https://github.com/postcss/postcss/commit/caaeeb907e4a816c44a23b00b151882bd02325a1"><code>caaeeb9</code></a> Upgrade nanoid to fix infinite loop on zero size (<a href="https://redirect.github.com/postcss/postcss/issues/2124">#2124</a>)</li> <li><a href="https://github.com/postcss/postcss/commit/3609b6f4296952d0b5b9ddae42c8d73ee460c041"><code>3609b6f</code></a> Explain how to type plugin options</li> <li><a href="https://github.com/postcss/postcss/commit/fbad419cbd01cd7a9a1a46413447f2cd9b3fce4a"><code>fbad419</code></a> docs: show ESM and TypeScript plugin declaration (<a href="https://redirect.github.com/postcss/postcss/issues/2118">#2118</a>)</li> <li>See full diff in <a href="https://github.com/postcss/postcss/compare/8.5.25...8.5.26">compare view</a></li> </ul> </details> <br /> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com> |
||
|
|
5b538f6f21 |
fix(chat): keep thread locks alive during long handlers (#821)
- renew a held thread or channel lock every 10 seconds while a locking concurrency strategy is running - stop the heartbeat before releasing the lock, and handle extension failures without unhandled rejections - add regression coverage proving `queue`, `burst`, and `debounce` remain serialized when a handler exceeds the 30-second lock TTL - keep the existing short TTL, so a crashed process still releases its lock automatically Mosoo Agents hit this with Chat SDK's Telegram adapter while waiting on long-running Codex Agent handlers. Once a handler crossed 30 seconds, a later Telegram message could acquire an expired channel lock and run concurrently on the same conversation. Fixes #685. --------- Signed-off-by: Yevanchen <cyefan2@gmail.com> Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com> |
||
|
|
864d922204 |
fix(slack): keep alert attachment content on normalized messages (#846)
Fixes #608.
Slack integrations such as Sentry, PagerDuty and GitHub carry their real
payload in an attachment's `title`, `text` and `fields`. The adapter
only read attachments to build link-unfurl previews, so that content
reached nowhere on the normalized `Message`: `msg.text` held only the
top-level one-line summary, and every consumer inherited the gap,
including `thread.messages`, `toAiMessages` and the `chat/ai`
`fetchMessages` tool.
Non-unfurl attachment content is now folded into the text before the AST
is assembled, so both `formatted` and the derived plain text carry it.
This follows the same approach as #817, which preserved pasted tables.
Three decisions worth calling out for review:
- Link unfurls stay excluded, on the same grounds their blocks already
are via `isForeignAttachment`: the content is not the message author's.
- `fallback` is used only when the attachment has neither its own fields
nor blocks. It is otherwise a plain-text stand-in that duplicates
content rendered elsewhere, and including it unconditionally would
inject strings like `[no preview available]` into messages that already
carry table blocks.
- Mentions inside attachment content are resolved on the async path
only, matching how table cells are already handled in `resolvedContent`.
`fields` was missing from the `SlackEvent["attachments"]` type and has
been added.
## Test plan
Three tests added in `packages/adapter-slack/src/index.test.ts`,
covering alert content (title, text and fields), the `fallback`-only
case, and exclusion of unfurl attachments. The alert test asserts both
the sync `parseMessage` and async `parseSlackMessage` paths, following
the existing table-attachment test.
- `vitest run src/index.test.ts` in `packages/adapter-slack`: 419
passed.
- Reverting only the source change and keeping the new tests makes two
of the three fail with the reported symptom (`expected 'New alert' to be
'New alert\n\nTypeError: cannot read p…'`), confirming they exercise the
bug rather than the implementation.
- `tsc --noEmit` clean, `ultracite check` clean on the changed files.
- `pnpm validate` passes except `create-chat-sdk#test`, which fails
identically on `main` at
|
||
|
|
e71bfead52 |
fix(slack): preserve first line of incoming code blocks (#843)
## Summary fixes #842 normalizes incoming Slack triple-backtick code fences before parsing them as CommonMark Slack treats text immediately after an opening fence as code content, while CommonMark treats it as the fence's info string; putting Slack fences on their own lines preserves the first code line in both `message.text` and `message.formatted` the normalization also separates fences from surrounding text so inline Slack code blocks are parsed as fenced code blocks instead of regular Markdown text ## Test plan - added format-level regression coverage for code starting immediately after an opening fence and for fences adjacent to surrounding text - added converter-level assertions for the parsed code node and extracted plain text - ran the focused Slack format and Markdown test suites - ran `pnpm validate` ## 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 — see [CONTRIBUTING.md](./CONTRIBUTING.md)) - [x] Documentation updated (or N/A) --------- Signed-off-by: akkadaska <akkadaska@gmail.com> Signed-off-by: Ben Sabic <bensabic@users.noreply.github.com> Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com> |
||
|
|
a18e79224e |
feat(telegram): describe locations, contacts, polls and dice (#836)
Telegram sends several message kinds with neither text nor a file. They reached the handler as empty messages: the content was in the payload, but anything reading `text` saw nothing and could not tell an empty delivery from a shared location. A location, venue, contact, poll, dice, game, invoice and story now each produce a short literal description, in the same place a sticker produces its emoji: ``` 📍 55.75, 37.61 📍 Central Library, 12 Main St 👤 Ada Lovelace +15551234567 📊 Lunch or dinner? 🎲 4 🎮 Corsairs 🧾 Yearly plan — 49.99 USD 📖 Story ``` The wording stays minimal and the structured payload is untouched on the raw message, so a handler that wants the coordinates or the poll options still has them. Two Bot API quirks shape the implementation: - A venue message also carries a top-level `location` field for backward compatibility, so the venue check runs first. Otherwise every venue would render as bare coordinates. - An invoice's `total_amount` is in the currency's smallest unit, and the exponent varies per currency ([currencies.json](https://core.telegram.org/bots/payments/currencies.json)): JPY and Telegram Stars count whole units, BHD, IQD and JOD use three decimals, everything else two. `sample-messages.md` gains fixtures for the new kinds, including the venue with its co-set location and a contact without a `last_name`. Signed-off-by: grootbro <vadim@ravefox.dev> Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com> |
||
|
|
a0ba986827 |
feat(telegram): parse stickers and animations (#835)
Based on #834. A sticker carries no text, so it reached the handler as an empty message and looked like a delivery that had lost its body. An animation — the MP4 Telegram sends for a GIF — was not declared on the message type and was dropped on the floor. A sticker now reports the emoji it stands for as the message text, plus an image attachment typed by its real format: WebP for a still one, WebM for a video sticker, TGS for a Lottie one. An animation arrives as a video attachment alongside the other media types. --------- Signed-off-by: grootbro <vadim@ravefox.dev> Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com> |
||
|
|
26a06ca51d |
feat(telegram): treat a reply to the bot as a mention (#834)
Based on #833. In a group a bot only sees messages that address it, and people address a bot by replying to it as often as by typing its handle. The adapter reported `isMention` for the handle but not for the reply, so a bot went quiet the moment the conversation moved to replies. `mentionOnReply` turns that on. **Off by default** — the flag changes which messages report `isMention`, and a bot that deliberately answers only explicit mentions should keep the stricter behaviour. It also reads `TELEGRAM_MENTION_ON_REPLY`, so a deployment can set it without code, and the key is declared in the adapters catalog. The check runs before the empty-text guard, so a reply carrying only a photo or a document counts too. --------- Signed-off-by: grootbro <vadim@ravefox.dev> Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com> |
||
|
|
d5ebec127b |
feat(telegram): implement native message replies (#833)
`Thread.reply()` throws `NotImplementedError` on Telegram: the adapter has no `reply` method, even though the Bot API threads an answer to its question with `reply_parameters`. `postMessage` takes an optional reply target and passes it to every send path — text, rich messages, documents, attachments and both media group variants — and `reply()` delegates to it, the same shape the WhatsApp adapter uses for this contract. The target is decoded through the existing `decodeCompositeMessageId`, so a target from another chat is rejected exactly as an edit would be. `allow_sending_without_reply` is set: a deleted target degrades to an unthreaded message instead of failing the send. Three tests cover it: the reference lands on a reply, a plain `postMessage` stays unthreaded, and a target from another chat is refused. --------- Signed-off-by: grootbro <vadim@ravefox.dev> Signed-off-by: Ben Sabic <bensabic@users.noreply.github.com> Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com> |
||
|
|
500b7e6d2c |
fix(web): prevent tool approval bypass via client-supplied messages array (#857)
Hardens two trust boundaries reported against the framework: the web adapter derived conversation state from the client-supplied `body.messages` array, and the AI SDK write tools skipped the conversation scope check that read tools already enforced. ## Web adapter: client-supplied messages `handleWebhook` previously accepted the full `useChat` `messages` array from the browser. A client could forge tool-call and approval parts in it, and handlers reading `message.raw` would see that forged state as if the server had produced it. The adapter now: - consumes only the latest user message and ignores the rest of the array - strips tool parts from that message, so forged tool-call or approval state never reaches handlers; text, file, and custom `data-*` parts pass through to `message.raw` unchanged - returns 400 when nothing usable remains after stripping - no longer passes `originalMessages` to `createUIMessageStream` (nothing registers `onFinish`, so it was never consumed; prior turns come from the state adapter via `persistMessageHistory`, never from the request body) ## AI SDK tools: scope on writes `createChatTools` now runs the same scope guard on write tools that read tools already used. A thread or channel id the model supplies that resolves outside the scoped conversation is rejected before the write executes. The guard is threaded through each tool factory (`ToolOptions.guard`) rather than wrapped around `execute`, so it is typed against each tool's input schema and a future tool can't ship unguarded. `sendDirectMessage` targets a user id rather than a conversation, so the guard has nothing to check it against; it stays gated by approval, and the docs now say so explicitly. --------- Signed-off-by: Ben Sabic <bensabic@users.noreply.github.com> Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com> |
||
|
|
b6fa24c68f |
fix(adapters): guard attachment downloads across slack, discord, telegram, and whatsapp (#865)
Follows up on #850, #856, and #859 by adopting the shared guarded downloader (`downloadAttachment` in `@chat-adapter/shared`) in the remaining adapters that fetch attachment bytes from event-supplied URLs. - Slack, Discord, and WhatsApp attachment downloads now refuse private and internal addresses (as URL literals, through DNS resolution, and after redirects), cap responses at 25 MB, and time out after 30 seconds. - Slack sends the bot token only on hops to trusted Slack origins, so a redirect can never carry it to another host, and keeps the HTML-login-page detection. A protected `createFileTransport()` override routes downloads through a proxy. - WhatsApp keeps its access token on Meta's media hosts, and the configured Graph origin via the hosts allowlist; `downloadMedia()` accepts a custom transport. - Telegram keeps downloads on the Web Fetch API because a downstream Cloudflare Workers consumer depends on portability (#828), enforcing the same 25 MB cap and 30-second timeout with web streams. - `downloadAttachment` now resolves `headers` per hop (function form decides what each redirect target receives), forwards the resolved headers to custom transports, and accepts an `onResponse` hook that can reject a final response before its body is read. - Adds "Inbound attachments" docs sections for all four adapters. --------- Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com> |
||
|
|
2ce2be008f |
feat(slack): add Agent Sessions lifecycle and native stop (#862)
Migrates Slack's `agent_view` integration to the Agent Sessions
lifecycle while preserving the legacy `assistant_view` compatibility
path.
- Adds `agents.sessions.setStatus` and `agents.sessions.rename` support
for processing, active, suspended, and closed sessions.
- Handles `agent_session_stopped` without taking the message lock,
clears Slack's processing state, and dispatches `onAgentSessionStopped`.
- Adds cross-process turn cancellation through the configured state
adapter and exposes the active turn as `thread.signal`.
- Handles `agent_session_title_changed` and automatically titles new
agent conversations from their root message, with a configurable
resolver.
- Propagates `session_status` through native stream completion and
supports suspended human-in-the-loop turns.
- Updates Slack manifests, examples, API docs, fixtures, and migration
guidance for the February 2027 `assistant_view` retirement.
Configure the Agent messaging experience and optional title resolver:
```ts
const slack = createSlackAdapter({
agentView: true,
sessionTitle: ({ text }) => text.split("\n", 1)[0]?.slice(0, 80) ?? null,
});
```
Pass the thread signal into model generation so Slack's native stop
button cancels upstream work as well as message delivery:
```ts
bot.onDirectMessage(async (thread, message) => {
await thread.startTyping();
const result = await agent.stream({
prompt: message.text,
abortSignal: thread.signal,
});
await thread.post(result.fullStream);
});
```
React to session lifecycle events:
```ts
bot.onAgentSessionStopped(async (event) => {
await releaseExternalResources(event.threadId);
});
bot.onAgentSessionTitleChanged(async (event) => {
await syncTitle(event.threadId, event.title);
});
```
Leave a stream suspended when the agent needs user input or approval:
```ts
await thread.post(
new StreamingPlan(result.fullStream, {
sessionStatus: "suspended",
})
);
```
---------
Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
|
||
|
|
50af1605d5 |
chore(docs): use geistdocs 1.23.1 (#864)
Uses `@vercel/geistdocs@1.23.1`, which includes the desktop navbar fix: clicking an open navigation trigger closes its menu. Release: https://github.com/vercel/geistdocs/releases/tag/%40vercel%2Fgeistdocs%401.23.1 ## Validation - `pnpm install --lockfile-only --ignore-scripts` - `git diff --check` |
||
|
|
153bd9640d |
fix(messenger): guard attachment downloads (#856)
## summary - restrict Messenger attachment downloads to Meta's `fbsbx.com` and `fbcdn.net` hosts while preserving external URLs on `attachment.url` - reject untrusted URLs before connecting using HTTPS validation, connection-bound DNS checks, manual redirect validation, timeouts, and streamed size limits - move the guarded downloader into `@chat-adapter/shared` and keep the Teams implementation behaviorally equivalent - normalize malformed redirect locations and other download failures as typed `NetworkError` values - document the inbound attachment policy for Messenger - stacked on #850 and should merge after it ## test plan - verified valid Meta image, audio, video, and file CDN hosts remain downloadable - verified external hosts, private addresses, malformed URLs, unsafe ports, trailing dots, and suffix attacks are rejected - verified mixed private and public DNS results fail closed - verified redirects are revalidated and malformed or external destinations are rejected - verified declared and streamed size limits and stalled body timeouts - ran workspace build, affected package tests and typechecks, integration checks, Knip, Ultracite, and diff validation Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com> |
||
|
|
bb926884a2 |
fix(teams): secure attachment downloads (#850)
## summary - restrict anonymous attachment downloads to current Microsoft 365 SharePoint and OneDrive for Business hosts - reject internal addresses using connection-bound DNS validation - revalidate every redirect and disable connection reuse outside the guarded transport - enforce a 25 MB streaming response limit and a 15 second request timeout - preserve connector-origin bot authentication and the protected custom fetch override - document the default anonymous download policy ## test plan - verify trusted Microsoft 365 attachment hosts remain supported - verify HTTP, custom ports, lookalike domains, trailing-dot hosts, and generic off-origin URLs are rejected - verify private IPv4, encoded IPv4, bracketed IPv6, and mixed DNS results are rejected - verify redirects are revalidated before another request - verify oversized streamed responses are stopped - verify activity parsing and attachment rehydration use the guarded transport - run Teams tests, typecheck, formatting, and production builds --------- Signed-off-by: dancer <josh@afterima.ge> Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com> |
||
|
|
eddcd7e46b |
fix(telegram): return portable file data (#828)
## Summary Telegram file downloads already receive their bytes from the Web Fetch API as an `ArrayBuffer`, but the adapter immediately converts them with `Buffer.from(...)` before returning. That conversion is unnecessary for consumers that accept web-standard binary data, and it throws when the Node `Buffer` global is unavailable. The [Fetch standard](https://fetch.spec.whatwg.org/#dom-body-arraybuffer) defines `Response.arrayBuffer()` as returning an `ArrayBuffer`; Cloudflare Workers exposes the [Fetch API natively](https://developers.cloudflare.com/workers/runtime-apis/fetch/), while `Buffer` belongs to its [Node.js compatibility surface](https://developers.cloudflare.com/workers/runtime-apis/nodejs/buffer/). This change returns the fetched `ArrayBuffer` directly from Telegram. The shared `Attachment.fetchData` and protected Telegram method use `Buffer | ArrayBuffer` so existing adapters and subclasses that return `Buffer` remain source-compatible. The two consumers of that contract now accept the portable value: `chat/ai` passes `ArrayBuffer` directly to the AI SDK, and the X adapter normalizes either type at its Buffer-based upload boundary. The public file documentation and patch changesets are updated with the same contract. The downstream evidence is a pnpm patch in the private Calories Cloudflare Workers consumer at `patches/@chat-adapter__telegram@4.36.0.patch`. Its portability hunk changes `downloadFile` from `Promise<Buffer>` to `Promise<ArrayBuffer>` and changes `Buffer.from(await response.arrayBuffer())` to `response.arrayBuffer()`; the other Telegram hunks in that patch are already upstream and are intentionally excluded here. ## Test plan - `pnpm validate` - `pnpm --filter @chat-adapter/telegram test` (269 tests) - `pnpm --filter @chat-adapter/telegram typecheck` - `pnpm --filter chat test` (1,131 tests) - `pnpm --filter chat typecheck` - `pnpm --filter @chat-adapter/x test` (222 tests) - `pnpm --filter @chat-adapter/x typecheck` - Added a regression test that removes the global `Buffer`, exercises Telegram's mocked `getFile` and file-fetch path, and asserts the returned bytes are an `ArrayBuffer`. The runtime proof is limited to the isolated download seam under Node with `Buffer` removed. This PR does not claim a deployed no-compatibility Cloudflare Worker or a live Telegram end-to-end request. ## 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 — see [CONTRIBUTING.md](./CONTRIBUTING.md)) - [x] Documentation updated (or N/A) --------- Signed-off-by: onmax <maximogarciamtnez@gmail.com> |
||
|
|
63997acaa8 |
fix(teams): hydrate incoming users without Graph (#860)
## Summary Changes live incoming Teams author hydration to `ctx.api.conversations.getMemberById`, so the normal path no longer requires Microsoft Graph's `User.Read.All` permission or tenant admin consent. Explicit `getUser()` lookups remain Graph-backed. ## Test Plan - `pnpm --filter @chat-adapter/teams test` (264 passed) - `pnpm --filter @chat-adapter/teams exec vitest run src/index.test.ts -t 'incoming sender email'` (8 passed) - `pnpm --filter @chat-adapter/teams typecheck` - `pnpm --filter @chat-adapter/teams... build` - `pnpm check` - `git diff --check` - built and packed `@chat-adapter/teams`; inspected the artifact for both the Connector lookup and preserved Graph lookup The regression tests assert the exact activity conversation and sender IDs, Graph isolation on Connector success and failure, cache behavior, the missing-AAD fallback, and the DM path. A live Microsoft Teams tenant was not available for runtime verification. ## Checklist - [x] All commits are signed and verified - [x] All commits are signed off for the DCO (`git commit -s`) - [ ] `pnpm validate` passes - [x] Changeset added (or N/A — see [CONTRIBUTING.md](./CONTRIBUTING.md)) - [x] Documentation updated (or N/A) --------- Signed-off-by: onmax <maximogarciamtnez@gmail.com> |
||
|
|
ea716568fa |
chore(docs): update geistdocs to 1.24.0 (#863)
Updates the docs app to `@vercel/geistdocs@1.24.0` and refreshes the pnpm lockfile. Includes improved agent recovery and discovery from https://github.com/vercel/geistdocs/pull/255. ## Validation - `pnpm install --lockfile-only --ignore-scripts` - `git diff --check` |
||
|
|
28bc776858 |
fix(twilio): isolate message locks by conversation (#849)
## summary - use thread-scoped locking so separate Twilio conversations no longer contend for the same sender lock - preserve the existing Twilio channel ID format and channel behavior - add regression coverage proving concurrent recipients process independently Signed-off-by: dancer <josh@afterima.ge> |
||
|
|
16ea171e68 |
fix(chat): preserve thread id when editing channel messages (#848)
## summary - preserve adapter-returned thread ids on messages returned by channel message edits - prevent edited messages from falling back to the parent channel id - add regression coverage for adapters that return a thread id when posting a channel message Signed-off-by: dancer <josh@afterima.ge> |
||
|
|
a0084cb6e2 |
chore(docs): update geistdocs to 1.23.1 (#861)
Updates the docs app to `@vercel/geistdocs@1.23.1` and refreshes the pnpm lockfile. ## Validation - `pnpm install --lockfile-only --ignore-scripts` - `git diff --check` |
||
|
|
c4a359e7e9 |
fix(telegram): require webhook verification by default (#858)
## summary - require `secretToken` when Telegram resolves to webhook mode - reject unverified messages and callback queries before dispatch - add `allowUnverifiedWebhooks` as an explicit escape hatch for local fixtures or trusted upstream verification - preserve polling without requiring webhook credentials - deduplicate every accepted webhook update - update adapter docs, configuration metadata, and integration fixtures --------- Signed-off-by: dancer <josh@afterima.ge> |
||
|
|
7c269653ef |
fix(adapters): restrict attachment credentials to trusted hosts (#859)
## summary - only send Slack bearer tokens to trusted Slack file origins or the configured API origin - fetch external Slack file URLs without authentication, including rehydrated attachments - reject WhatsApp media URLs outside trusted Meta CDN hosts or the configured Graph origin before sending credentials - apply the Slack policy to both the adapter and lower-level API primitive ## test plan - verify trusted Slack file URLs receive bearer authentication - verify external and malformed Slack URLs receive no authentication - verify rehydrated cross-tenant Slack attachments do not resolve or send installation tokens - verify trusted Meta CDN and configured Graph URLs remain downloadable - verify untrusted, malformed, HTTP, and host-confusion WhatsApp URLs are rejected Signed-off-by: dancer <josh@afterima.ge> |
||
|
|
b7e4bbfbb0 |
chore(docs): upgrade Geistdocs to 1.22.0 (#855)
## Summary
Upgrades the chat-sdk.dev docs app from `@vercel/geistdocs` **1.20.4 →
1.22.0** (published 2026-08-21, Apache-2.0) and `next` **16.2.11 →
16.3.1**, following the bundled 1.22.0 template as the source of truth.
The target release includes all of the behavior-changing PRs for this
round:
- vercel/geistdocs#245 — require Next.js 16.3, scaffold 16.3.1, drop the
dev filesystem-cache flag (shipped in 1.21.1)
- vercel/geistdocs#246 — Cache Components across Geistdocs
- vercel/geistdocs#249 — Partial Prefetching + instant docs navigation
- vercel/geistdocs#250 — stable Next 16.3 APIs, retryable page/Ask AI
error boundaries, full prefetch of package links, no generic page shell
- vercel/geistdocs#251 — tree sidebar preserves scroll position on
folder toggles
## Adapter and configuration changes
- `next.config.ts`: `cacheComponents: true`, `partialPrefetching: true`;
removed `experimental.turbopackFileSystemCacheForDev` (default in 16.3).
Redirects, `/sitemap.xml` rewrite, and image config unchanged.
- New `lib/geistdocs/root-params.ts`; all layouts read `[lang]` via
`next/root-params` instead of `params`. Route handlers keep
route-context `params`.
- Root layout gains `generateStaticParams` returning every configured
language (`en`) — home (`/`), `/adapters`, and `/resources` now
prerender statically (previously dynamic).
- Route adapters no longer re-export `revalidate`/`dynamic` from package
factories (`agents.md`, `sitemap.md`, `llms.mdx`), and custom routes
drop their own `revalidate` exports (`llms.txt`, `llms-full.txt`,
`adapters.mdx`, `rss.xml`, `resources`).
- `llms.mdx` adopts the template form: `sources: [geistdocsSource]` +
`notFound: {}`, enabling smart agent-readable 404/410 responses with
real HTTP statuses.
- `rss.xml` migrated to the template's `"use cache"` +
`cacheLife("max")` form with `getPublicPath` base-path handling.
- `resources` page: `revalidate = 86400` → `"use cache"` +
`cacheLife("days")` (same 1-day lifetime).
- App-owned data fetching moved off `next: { revalidate }` (unsupported
under Cache Components): GitHub README fetches and homepage OSS stats
now use `"use cache"` + `cacheLife("hours")`.
- Homepage Shiki highlighting (`Demo`, `CodePreview`, `highlightCode`)
runs inside `"use cache"` scopes — Shiki reads `Date.now()` internally,
which otherwise fails prerendering.
- App-owned links to statically generated docs pages get
`prefetch={true}` (platform grid, feature matrix, adapter slug list,
"Visit Documentation") per the template's agent guidance; package-owned
sidebar/prev-next links already prefetch fully in 1.22.0.
- `Analytics`/`SpeedInsights` moved into
`components/geistdocs/provider.tsx` per the template.
- CSS: `styles/geistdocs.css` now imports `@vercel/geistdocs/theme.css`
(self-sourcing package dist/streamdown) instead of layering on
`styles.css` from `global.css`; updated the mobile breadcrumb selector
for the new package DOM; kept the site-specific shadcn tokens, dark
background-scale override, prose, TOC, and streamdown fixes.
- Added `apps/docs/AGENTS.md` capturing the packaged-architecture
conventions (cache-components rules, root-params, markdown contract,
proxy mappings).
## PR #251 (tree sidebar scroll) verification
The fix is package-internal (`manualToggleRef` in `SidebarTree`); no
consumer change is needed. This site's sidebars render no collapsible
folder rows (content uses spread folders, `...api` etc.), so I verified
the shipped behavior against the bundled 1.22.0 template with
`sidebarMode="tree"` enabled locally: expanding/collapsing a folder
preserves the exact sidebar scroll position (772 → 772), and a route
change into a collapsed folder still scrolls the active item into view.
8/8 checks pass.
## Static-generation coverage
Production build: 290/290 static pages generated. Every intended
parameter tuple is prerendered with complete content (verified H1/body
in emitted HTML):
- `/en` home, `/en/adapters` listing, `/en/resources` — now fully static
(were `ƒ` on main)
- `/en/docs/*` — 45 pages, complete static HTML + one generic
`[[...slug]]` fallback entry (allowed)
- `/en/adapters/{official,community,vendor-official}/*` — 44 detail
pages + `/en/adapters.mdx/*` markdown for all 44
- `/en/sitemap.md` — SSG
Intentional contract differences (match the 1.22.0 template's own build
output):
- OG image routes (`/og/[...slug]`, adapter `*/og`) render on demand
under Cache Components instead of build-time SSG; Next 16.3 caches the
rendered image per route. URLs and content types verified unchanged.
- `llms.txt`, `llms-full.txt`, `llms.mdx`, `rss.xml`, `agents.md` remain
on-demand route handlers (same as main); `agents.md` reads the request
origin by package design.
- Unknown HTML routes: browsers receive the docs shell with 200 before
not-found UI resolves; crawlers get a real 404. Machine-readable unknown
routes return the new smart 404 body with real 404 status and
`X-Robots-Tag: noindex`.
## Lockfile
`pnpm-lock.yaml` delta: the `docs` importer's `@vercel/geistdocs`
(1.20.4 → 1.22.0) and `next` (16.2.11 → 16.3.1) bumps, their
peer-context re-resolutions, and one mechanical re-keying of `@swc/core`
peer contexts to include `@swc/helpers` across existing entries (no
version changes outside the docs app). Verified with `pnpm install
--frozen-lockfile`.
## Test results
- `pnpm install --frozen-lockfile` ✓
- `pnpm check` ✓ (1 pre-existing warning in untouched
`lib/read-more.ts`)
- `pnpm typecheck` — 43/43 ✓
- `pnpm knip` ✓, `pnpm konsistent` ✓
- `pnpm test` — 47/47 turbo tasks ✓
- Clean production build (removed `.next`/`.source`) ✓ 290/290
- Production-server (`next start`) contract checks: HTML docs,
`.md`/`.mdx`, `Accept: text/markdown`, agent-UA negotiation, `llms.txt`,
`llms-full.txt`, `sitemap.md`, `agents.md`, `/.well-known/mcp.json`
(intentional 404), `rss.xml`, `robots.txt`, `/sitemap.xml` rewrite, OG
images, `AGENTS.md`, all redirects (308s), `/api/search` JSON and not
rewritten as markdown, `/docs.md` section root ✓
- Browser checks (Playwright, Chrome, `next start`) — 16/16: instant
sidebar + prev/next client navigation with complete content and no
loading shell, single visible H1 (Activity-preserved routes stay
hidden), Copy Page, page actions menu, search → result navigation, Ask
AI panel with suggestions, theme switch (dark applies the site's
background scale), mobile navbar menu and docs sheet navigation, unknown
page shows not-found UI, zero console/page errors and failed requests
- Ask AI scoped failure: with no local AI Gateway credentials the chat
surfaces the error, the surrounding page stays intact, and resubmission
retries cleanly
- Visual parity screenshots vs production (home/docs/adapters, light +
dark) match
## Preview
- Preview:
https://chat-git-richardhaines-geistdocs-122-upgrade.vercel.sh —
deployment **Ready**, build passed on Vercel.
- The preview sits behind Vercel SSO (Fork Protection), so automated
route checks aren't possible without a bypass token; please spot-check
through SSO: `/docs/getting-started`, `/docs/getting-started.md`,
`/adapters`, `/llms.txt`, and a client navigation between docs pages.
Signed-off-by: molebox <rich@vercel.com>
|
||
|
|
c4f709fe93 |
fix(discord): route thread starter message operations correctly (#815)
## summary - route edits, deletes, and reactions on text channel thread starters through the parent channel - retry against the thread channel only when Discord returns unknown message, preserving forum and media post behavior - preserve Discord API error codes so fallback behavior is limited to error `10008` - add regression coverage for parent routing, forum fallback, and unrelated Discord errors - fixes #809 --------- Signed-off-by: dancer <josh@afterima.ge> Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com> |
||
|
|
4c18ec98e8 |
chore(docs): update Geistdocs to 1.20.4 (#845)
Updates Geistdocs so link-preview bots receive HTML and preserve rich unfurls instead of being served Markdown. |
||
|
|
294b595da1 |
docs: require non-static credential support for vendor-official adapters (#844)
Adds a qualification to the vendor-official listing guide: credential fields must accept resolver functions alongside static strings, resolved per outbound call, so short-lived tokens from tools like Vercel Connect work. Also adds the matching reviewer check and links the Vercel Connect docs. Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com> |
||
|
|
3e6e866a0c |
fix(whatsapp): support business-scoped user ids (#818)
- support phone-based IDs, BSUIDs, parent BSUIDs, and username-only webhook payloads - preserve existing thread IDs by storing identity aliases and outbound routing details in the configured state adapter - send replies using `to`, `recipient`, or both according to the identifiers available - preserve thread continuity across `user_changed_number` and `user_changed_user_id` system messages - update WhatsApp types and documentation for the new identity fields and authentication-template limitation - closes #794 --------- Signed-off-by: dancer <josh@afterima.ge> Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com> Co-authored-by: Pablo Botta <886512+p4bl1t0@users.noreply.github.com> |
||
|
|
929878b56f |
fix(chat): allow link button IDs in JSX (#838)
- Accept the documented optional `id` prop in the `LinkButton` JSX
runtime guard.
- Preserve the action ID in the resulting `LinkButtonElement`.
## Discovery
We found this when Omniagent’s Slack sign-in card used the documented
`<LinkButton id="…" url="…">` API and Chat SDK’s preview renderer threw
`LinkButton requires a 'url' prop` despite receiving one.
## Root cause
The `!('id' in props)` check was introduced when `LinkButton` did not
support IDs, as a structural distinction from `Button`. Stable
link-button IDs were later added in #598 across `LinkButtonProps`,
`LinkButtonOptions`, `LinkButtonElement`, JSX resolution, adapters, and
the public documentation, but the old JSX guard was not updated.
Component identity is already established by `type === LinkButton`
before this guard runs. Requiring a string `url` is therefore sufficient
and restores the intended API without changing button dispatch or
adapter behavior.
Signed-off-by: bryan-hunter <bryan.hunter@vercel.com>
|
||
|
|
2f7aca1915 |
chore: add .vercel.approvers with vercel/ai-sdk team (#837)
Adds a `.vercel.approvers` file designating the **@vercel/ai-sdk** team as approvers for this repository. Format follows the convention used in other Vercel repos (e.g. `vercel/vercel`): ``` @vercel/ai-sdk:team ``` --------- Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com> Co-authored-by: Caleb An <291946795+caleb-vercel@users.noreply.github.com> |
||
|
|
aea4d753de |
chore(release): version packages (#829)
This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated. # Releases ## @chat-adapter/discord@4.38.1 ### Patch Changes - Updated dependencies [@chat-adapter/shared@4.38.1 @chat-adapter/state-memory@4.38.1 @chat-adapter/web@4.38.1 @chat-adapter/notion@4.38.1 @chat-adapter/whatsapp@4.38.1 @chat-adapter/slack@4.38.1 @chat-adapter/messenger@4.38.1 @chat-adapter/state-ioredis@4.38.1 @chat-adapter/twilio@4.38.1 @chat-adapter/linear@4.38.1 @chat-adapter/x@4.38.1 @chat-adapter/telegram@4.38.1 @chat-adapter/instagram@4.38.1 @chat-adapter/state-pg@4.38.1 @chat-adapter/discord@4.38.1 @chat-adapter/github@4.38.1 chat@4.38.1 @chat-adapter/tests@4.38.1 @chat-adapter/gchat@4.38.1 @chat-adapter/state-redis@4.38.1 @chat-adapter/teams@4.38.1 |
||
|
|
6cb933ebe2 |
fix(chat): isolate channel-scoped queue dispatch by thread (#832)
## summary - dispatch queued, debounced, and burst messages using the dequeued message's thread id instead of the lock holder's thread id - restrict skipped message context and queue logs to the dispatched message's thread - prevent subscriptions, mentions, state, and replies from crossing thread boundaries - add regressions for queue and debounce with channel-scoped locks --------- Signed-off-by: dancer <josh@afterima.ge> |
||
|
|
d8103a103c |
fix(twilio): restrict authenticated media downloads (#831)
## summary - validate media URLs against the configured Twilio API origin before resolving credentials - reject protocol, hostname, and port mismatches without making a network request - preserve support for configured regional Twilio API origins - document that `apiUrl` defines the trusted origin for media downloads ## test plan - added API-level coverage for trusted regional origins and untrusted URL variants - added adapter-level coverage for rehydrated attachments from untrusted origins - ran the Twilio build, tests, typecheck, integration tests, and formatting checks Signed-off-by: dancer <josh@afterima.ge> |
||
|
|
3268703894 |
fix(gchat): use media api for attachment downloads (#830)
## summary - use Google Chat `media.download` with `attachmentDataRef.resourceName` as the only attachment byte download path - remove the unsupported `downloadUri` fallback and URL-only `fetchData` rehydration - preserve `downloadUri` as attachment metadata for human access - add regression coverage for media download failures and URL-only attachments |
||
|
|
764e4759bd |
fix(slack): preserve pasted tables in message content (#817)
- parse Slack table blocks from both top-level blocks and attachment blocks - preserve pasted spreadsheet data in formatted mdast and plain message text - support rich text, raw text, and numeric table cells - ignore malformed table blocks without dropping valid content - fixes #803 --------- Signed-off-by: dancer <josh@afterima.ge> Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com> |
||
|
|
caab5c3843 |
chore(release): version packages (#805)
This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated. # Releases ## @chat-adapter/discord@4.38.0 ### Minor Changes -@chat-adapter/state-redis@4.38.0 @chat-adapter/x@4.38.0 @chat-adapter/twilio@4.38.0 @chat-adapter/telegram@4.38.0 @chat-adapter/state-memory@4.38.0 @chat-adapter/teams@4.38.0 create-chat-sdk@0.4.0 @chat-adapter/state-ioredis@4.38.0 @chat-adapter/web@4.38.0 @chat-adapter/discord@4.38.0 chat@4.38.0 @chat-adapter/gchat@4.38.0 @chat-adapter/github@4.38.0 @chat-adapter/slack@4.38.0 @chat-adapter/instagram@4.38.0 @chat-adapter/whatsapp@4.38.0 @chat-adapter/shared@4.38.0 @chat-adapter/tests@4.38.0 @chat-adapter/state-pg@4.38.0 @chat-adapter/notion@4.38.0 @chat-adapter/messenger@4.38.0 @chat-adapter/linear@4.38.0 |
||
|
|
4bdf7213b6 |
fix(discord): preserve forwarded message snapshots (#825)
Discord forwards place the original content and attachments under `message_snapshots`, while the outer message fields are empty. The Discord adapter currently reads only the outer fields in both direct Gateway and forwarded-webhook modes, so forwarded voice notes and files arrive as blank messages. This flattens the snapshot content and attachments into the normalized Chat SDK message while preserving any outer content and attachments. Verified with focused regressions for both Gateway modes, the full Discord adapter test suite (285 tests), typecheck, build, and repository formatting checks. --------- Signed-off-by: onmax <maximogarciamtnez@gmail.com> Signed-off-by: dancer <josh@afterima.ge> Co-authored-by: dancer <josh@afterima.ge> |
||
|
|
745fdf5a97 |
fix(adapters): harden Telegram streaming and XChat read receipts (#826)
## summary - pace Telegram post-and-edit streams for private and non-private chat limits, including the final edit - respect Telegram `retry_after` cooldowns and reject when the complete response cannot be delivered - prevent explicit XChat read receipts from advancing past an unresolved message - preserve latest-event fallback for delivered XChat messages without a sequence id - update adapter documentation and regression coverage --------- Signed-off-by: dancer <josh@afterima.ge> |
||
|
|
3bbf3ff542 |
fix(telegram): make native draft streaming opt-in (#822)
- use post-and-edit streaming by default to avoid leaked draft previews in Telegram clients - add `nativeStreaming: true` for explicitly enabling native draft previews in private chats - preserve existing native streaming behavior when enabled - document the client compatibility tradeoff - closes #782 before: private chat streams used native Telegram drafts by default, which could remain visible over the final message on Telegram macOS after: streams use post-and-edit by default across Telegram clients, while native drafts remain available as an opt-in --------- 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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
7a1150ce23 |
Add Vercel Connect support to Telegram (#813)
Adds function-backed Telegram bot-token resolution so the adapter can
use short-lived Vercel Connect credentials for every Bot API and
file-download request. Static tokens retain their existing synchronous
behavior, while native Telegram webhook verification or polling remains
unchanged.
```ts
import { createTelegramAdapter } from "@chat-adapter/telegram";
import { connectTelegramAdapter } from "@vercel/connect/chat";
createTelegramAdapter({
...connectTelegramAdapter("telegram/acme-telegram"),
secretToken: process.env.TELEGRAM_WEBHOOK_SECRET_TOKEN,
});
```
`create-chat-sdk` now recognizes Telegram as Connect-capable, preserves
`TELEGRAM_WEBHOOK_SECRET_TOKEN`, and emits native-webhook guidance:
```bash
npm create chat-sdk@latest -- my-bot --adapter telegram memory --connect -y
```
This PR is stacked on the Notion Connect work in #812. Validated with
the Telegram adapter suite (251 tests), create-chat-sdk suite (211
tests), package type checks/builds, and repository lint/format checks.
---------
Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
|
||
|
|
06b04ac4d9 |
Add Vercel Connect support to Notion (#812)
Adds function-backed Notion access-token resolution so the adapter can
use short-lived Vercel Connect credentials for every API request, retry,
and multipart upload. Direct Notion webhooks continue to use
`NOTION_VERIFICATION_TOKEN` and native HMAC verification because Connect
does not forward Notion triggers.
```ts
import { createNotionAdapter } from "@chat-adapter/notion";
import { connectNotionAdapter } from "@vercel/connect/chat";
createNotionAdapter({
...connectNotionAdapter("notion/acme-notion"),
verificationToken: process.env.NOTION_VERIFICATION_TOKEN,
});
```
`create-chat-sdk` now recognizes Notion as Connect-capable, preserves
the native webhook verification token, and emits direct-webhook
guidance:
```bash
npm create chat-sdk@latest -- my-bot --adapter notion memory --connect -y
```
Validated with the Notion adapter suite (71 tests), create-chat-sdk
suite (209 tests), package type checks/builds, and repository
lint/format checks.
Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
|
||
|
|
0f24cc3062 |
feat(chat): preserve replied-to message context (#802)
## Summary - add optional, normalized `Message.replyTo` context that survives JSON and workflow serialization, queue rehydration, thread history, and `SentMessage` reconstruction - populate it from Telegram's `reply_to_message`, including combined media groups, so handlers don't need raw Telegram payloads - keep the core contract adapter-neutral while Telegram owns only its platform mapping, allowing other adapters to populate it when they receive full replied-to messages Signed-off-by: onmax <maximogarciamtnez@gmail.com> |
||
|
|
a94995e5b4 |
fix(discord): restore forwarded attachment downloads (#800)
## Summary Discord inbound messages created attachment objects without `fetchData`, so consumers could not download audio or other attachments. The omission affected forwarded Gateway webhooks, REST and history parsing, and direct Discord.js Gateway messages. Route attachments from all three inbound paths through the adapter's existing `rehydrateAttachment()` implementation. Existing parser and Gateway tests now assert that the resulting attachments are downloadable. --------- Signed-off-by: onmax <maximogarciamtnez@gmail.com> |
||
|
|
1d2b78d933 |
Deduplicate repeated Telegram webhook updates (#799)
## Summary Telegram retries webhook deliveries after non-2xx responses, and its `update_id` field is explicitly intended for ignoring repeated updates. The Telegram adapter previously routed every webhook delivery independently. This change atomically claims each integer `update_id` through the configured `StateAdapter` before routing the update. Repeated deliveries return 200 without reaching bot handlers, while state failures return 503 without dispatching so Telegram can retry. Updates without an integer `update_id` keep their existing behavior, and polling remains unchanged. Claims expire after 24 hours because Telegram retains incoming updates for no longer than 24 hours. This is a bounded retention choice, not a documented retry timeout. Cross-instance deduplication requires shared durable state; in-memory state only protects one process. The change provides webhook-delivery idempotency, not end-to-end exactly-once handler completion. Telegram contract: [Update](https://core.telegram.org/bots/api#update) and [setWebhook](https://core.telegram.org/bots/api#setwebhook). ## Test plan - `pnpm --filter @chat-adapter/telegram test` - `pnpm --filter @chat-adapter/telegram typecheck` - `pnpm check` - `pnpm konsistent` - `TURBO_CONCURRENCY=1 pnpm validate` Regression coverage verifies sequential and concurrent repeated deliveries, distinct update IDs, missing update IDs, duplicate 200 responses, and state-failure retry behavior. GitHub CI also passes on Node 22 and Node 24. ## 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 — see [CONTRIBUTING.md](./CONTRIBUTING.md)) - [x] Documentation updated (or N/A) --------- Signed-off-by: onmax <maximogarciamtnez@gmail.com> Signed-off-by: dancer <josh@afterima.ge> Co-authored-by: dancer <josh@afterima.ge> |
||
|
|
927d0dbd7d |
docs: add cross-link card sections and page-level SEO metadata (#804)
Many docs pages are orphaned: nothing links to them apart from the sidebar, so readers and crawlers rarely find them. This PR gives every docs page a Read more section with four cards at the bottom of the article, above the prev/next footer. Cards are picked deterministically in lib/read-more.ts: the page's related frontmatter first, then prerequisites, then siblings from the same sidebar section, then the rest of the page tree, so every page always fills all four slots. Card titles and descriptions come from the target page's own frontmatter, nothing is duplicated. The section is injected through the MDX wrapper slot in the docs route, so it applies to all pages without touching content. To make the links topical rather than positional, 26 pages get related frontmatter additions. The 20 pages that no other page referenced (all ten api/ pages among them) now each have at least one inbound link, generally pairing guides with their API reference and back. The bundled copy of create-chat-sdk.mdx is synced to keep the byte-match test green. Official adapter pages get the same treatment with a More adapters section: same-type adapters first (platform or state, using the catalog order), topped up from the other official group. Vendor-official and community adapters are never shown, and their pages don't render the section. It reuses AdapterCard, so logos and package names match the listing page. Two small SEO fixes ride along. JSON-LD was allowlisted to three docs pages; the allowlist is gone, so all 45 now emit HowTo or TechArticle plus a BreadcrumbList. Docs and adapter detail pages also emit canonical URLs now, resolved against the existing metadataBase. Verified against the production build: all 45 docs pages and all 19 official adapter pages render exactly four cards, no page is left unreferenced, canonicals and JSON-LD are present everywhere, and pnpm validate passes. Docs-only, so no changeset. --------- Signed-off-by: Ben Sabic <bensabic@users.noreply.github.com> Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com> |
||
|
|
a0cba0288a |
Add Vercel Connect support to Discord (#808)
Adds function-backed Discord bot token and application ID resolvers,
plus custom webhook verification for Vercel Connect trigger-forwarded
interactions. Native Discord Ed25519 verification remains the default
when no custom verifier is configured.
```ts
import { createDiscordAdapter } from "@chat-adapter/discord";
import { connectDiscordAdapter } from "@vercel/connect/chat";
createDiscordAdapter({
...connectDiscordAdapter("discord/acme-discord"),
});
```
`create-chat-sdk` now recognizes Discord as Connect-capable, generates
`DISCORD_CONNECTOR` instead of native credential variables, and
preserves `CRON_SECRET` for Gateway forwarding:
```bash
npm create chat-sdk@latest -- my-bot --adapter discord memory --connect -y
```
Validated with the Discord adapter suite (284 tests), create-chat-sdk
suite (206 tests), package type checks/builds, and repository
lint/format checks.
Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
|