mirror of
https://github.com/vercel/chat.git
synced 2026-09-14 18:32:29 +08:00
@chat-adapter/telegram@4.40.0
597 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
85a37c896f |
chore(release): version packages (#878)
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/gchat@4.40.0 ### Minor Changes - |
||
|
|
31bce0a7a0 |
feat(whatsapp): expose typed API errors (#896)
- export `WhatsAppApiError` so consumers can handle Meta error codes without parsing error messages - expose HTTP status, provider details, optional subcode and trace ID, and the raw response - cover message requests, media uploads, and media metadata failures while preserving existing error messages and `AdapterError` compatibility - add regression coverage and document error handling closes #712 --------- Signed-off-by: Ben Sabic <bensabic@users.noreply.github.com> Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com> |
||
|
|
7062c395d0 |
fix(teams): preserve outgoing mention text (#898)
- keep outgoing `@names` as plain text instead of generating `<at>` markup without the mention entities Teams requires - preserve multi-word names across plain text, raw, markdown, and AST messages - keep incoming mention decoding and explicit raw markup unchanged - add formatter and send/edit regression tests and document that plain-text names do not notify users closes #853 |
||
|
|
aaeede70be |
feat(teams): dispatch bot join events (#899)
- dispatch `onMemberJoinedChannel` when the bot joins a Teams channel or group chat - expose `botUserId` from the configured app identity - preserve channel routing, inviter identity, and webhook `waitUntil` tracking - add regression tests and document the bot-only scope addresses the bot-join portion of #847; personal install/uninstall hooks remain separate --------- Signed-off-by: Ben Sabic <bensabic@users.noreply.github.com> Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com> |
||
|
|
43dba3de1a |
perf(telegram): skip unused plain draft rendering (#900)
## Summary Native Telegram drafts rendered the accumulated Markdown as plain text before replacing it with the rich or Markdown result. This computes only the selected draft format, while keeping the rich-to-Markdown-to-plain fallback and final delivery behavior unchanged. For an 82-chunk rich response, the pinned reproduction reduces discarded plain conversion from 83 calls over 139,400 characters to 0. It sends the same 83 Bot API requests with identical bodies and outcomes. Copy and run: ```sh git clone --depth 1 --filter=blob:none --sparse --branch repro/telegram-lazy-plain https://github.com/onmax/repros.git telegram-lazy-plain-repro cd telegram-lazy-plain-repro git sparse-checkout set telegram-lazy-plain telegram-lazy-plain-fix cd telegram-lazy-plain corepack pnpm install --frozen-lockfile --ignore-scripts && corepack pnpm verify cd ../telegram-lazy-plain-fix corepack pnpm install --frozen-lockfile --ignore-scripts && corepack pnpm verify ``` ## Test plan - `pnpm --filter @chat-adapter/telegram test`, 335 tests - `pnpm --filter @chat-adapter/telegram typecheck` - `pnpm --filter @chat-adapter/telegram build` - `pnpm validate` - Eight reproduction controls against the source build ## 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 N/A, public behavior and configuration are unchanged Model: GPT-5.6 Sol. Harness: Codex. Signed-off-by: Max <maximogarciamtnez@gmail.com> |
||
|
|
8fdaf4a9d7 |
fix(slack): render markdown in post-and-edit stream fallback (#901)
## Summary
`SlackAdapter.stream()`'s post-and-edit fallback passes its accumulated
text to `postMessage`/`editMessage` as a **bare string**, which
`SlackFormatConverter.toSlackPayload` resolves to Slack's `text` field:
```ts
if (fallback.message) {
await this.editMessage(threadId, fallback.message.id, committable);
} else {
fallback.message = await this.postMessage(threadId, committable);
}
```
`text` renders classic mrkdwn (`*bold*`) only, not the GFM the renderer
emits — so **every intermediate edit during a fallback stream shows the
user raw `**`/`#`/backtick syntax**. Only a caller-side final
replacement recovers the formatting, and only for the last frame; every
frame before it was wrong while it was on screen.
It's also a materially smaller ceiling: `text` on `chat.update` caps at
4,000 characters vs. `markdown_text`'s 12,000, so fallback mode fails on
long answers sooner than it needs to — `msg_too_long` mid-stream,
independent of the formatting bug.
`StreamingMarkdownRenderer.getCommittableText()` is already documented
as text "safe for append-only streaming" — it holds back unclosed inline
markers (`**`, `*`, `~~`, `` ` ``, `[`) and unconfirmed table headers.
And the renderer's own class doc is explicit about which side owns
conversion: "Outputs markdown (not platform text). Format conversion
still happens in the adapter's editMessage → renderPostable → fromAst
pipeline." Native mode honors that; fallback mode ships the same output
through the wrong field. Wrapping it as `{ markdown: committable }`
routes it into the same `markdown_text` field native mode streams into,
via `toSlackPayload`'s existing `markdown` branch — nothing new to
build.
Per Slack's reference for
[`chat.postMessage`](https://docs.slack.dev/reference/methods/chat.postMessage/)
and
[`chat.update`](https://docs.slack.dev/reference/methods/chat.update/),
`markdown_text` needs no scope beyond the `chat:write` the adapter
already holds and carries no app-feature gate. Its one constraint is
mutual exclusivity with `text`/`blocks` (`markdown_text_conflict`), and
`toSlackPayload` emits exactly one field per branch, so that conflict
can't arise here.
One behavior change worth naming: Slack documents that mobile
notifications use `message.text` for block-based messages, and doesn't
document how push previews are derived for `markdown_text`. In practice
we see no notification regression — our app already posts
`markdown_text` in native mode and for final message replacements — but
flagging it rather than leaving it to be discovered.
### How we hit this
A Slack Workflow Builder–authored message (posted as a bot, no real
`event.user`) leaves `recipient_user_id` invalid for native streaming,
so the adapter drops into fallback from the first send and stays there
for the whole answer. That root cause is separate and app-side — not
part of this PR — but it's what made this reproducible for us. Note it
isn't the only route in: once `switchToFallback()` latches
`nativeStreamingBroken` on a `feature_not_enabled` / `method_deprecated`
/ `unknown_method` error, *every* subsequent stream on that adapter
instance takes this path.
## Test plan
- `pnpm validate` — 43/43 tasks pass (knip, lint, typecheck, test,
build).
- `pnpm test:workspace` — 3,714 passed / 6 skipped, 102 files.
- New test in `packages/adapter-slack/src/index.test.ts`: `streams
fallback updates through markdown, not plain text` — asserts every
`postMessage`/`editMessage` call in fallback mode receives `{ markdown:
<string> }` rather than a bare string, and that markdown syntax survives
to the last frame.
- Three existing `native streaming fallback` tests read the posted
payload to assert content; updated to read `.markdown` via a small
`markdownOf` helper. These are the only places in the suite that assumed
the bare-string shape — worth knowing for anyone auditing the blast
radius.
## 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 (patch, `@chat-adapter/slack`)
- [x] Documentation updated (N/A — no public API change)
Signed-off-by: CamdenA21 <camden@sandstone.com>
|
||
|
|
2cc8cc3f80 |
fix(slack): surface custom status text in the Agent messaging experience (#897)
## summary - restore custom loading labels for `startTyping` and `setAssistantStatus` under `agentView`, which stopped displaying after #862 moved status updates to the native sessions API - send custom labels through `assistant.threads.setStatus` with `loading_messages`; `setAssistantStatus` preserves explicit arrays, then configured defaults, then falls back to the custom status - keep native `processing` and initiator attribution when `startTyping()` has no custom status, and use native `active` when clearing - add regression coverage for message precedence, native routing, clearing, and native API failures - verify custom labels in DMs and channels, streamed completion, and real native stop-button cancellation against locally built packages ## limitations custom labels and native session state are not equivalent: in the test workspace, the custom-label path displayed the requested text but did not create a native processing session or stop button, even with `agent_session_stopped` enabled use `startTyping()` without custom text when native processing and stop behavior are required; an existing native processing indicator can also take precedence over a custom label Slack's [native sessions API](https://docs.slack.dev/ai/agent-sessions/) does not accept custom loading text, so this restores labels through the [legacy status endpoint](https://docs.slack.dev/reference/methods/assistant.threads.setStatus/) without promising identical lifecycle behavior --------- Co-authored-by: dancer <josh@afterima.ge> |
||
|
|
4a0b5c0c3f |
feat(cards): add button tooltips and a card width hint (#895)
Buttons can now show hover text, and a card can ask to be rendered wider
than usual. Both are small hints: Teams renders them, and every other
adapter leaves the card exactly as it was before.
### Button tooltips
`Button` and `LinkButton` take an optional `tooltip`. On Teams it
appears when someone hovers over the button.
```tsx
<Card title="Deploy request">
<Actions>
<Button id="approve" style="primary" tooltip="Ships this build to production">
Approve
</Button>
<LinkButton url="https://example.com/build/1234" tooltip="Opens the build log in your browser">
View build
</LinkButton>
</Actions>
</Card>
```
The same option is available on the plain builder functions:
```ts
Button({ id: "approve", label: "Approve", tooltip: "Ships this build to production" })
```
Tooltips survive the `callbackUrl` flow too. When a button's callback
URL is swapped for a token before the card is sent, every other field on
the button is kept, so a tooltip on a callback button shows up just like
one on a regular button.
### Full-width cards
`Card` takes an optional `width`, either `"default"` or `"full"`. Teams
draws a `"full"` card wider than its usual size, which suits tables and
digests. It does not stretch the card across the whole chat pane, that
is how Teams defines full width.
```tsx
<Card title="Weekly digest" width="full">
<Table headers={["Service", "Uptime"]} rows={[["api", "99.98%"], ["web", "99.95%"]]} />
</Card>
```
### What Teams receives
- `tooltip` becomes the `tooltip` on the Adaptive Card action, for both
submit and open-URL buttons.
- `width="full"` becomes `msteams: { width: "full" }` on the card.
- The card now declares Adaptive Card version 1.5, which is the version
that introduced action tooltips. Teams accepts cards up to 1.6 for bots,
so nothing changes for existing cards beyond the version number.
- The runtime-free `@chat-adapter/teams/cards` helpers understand both
new fields as well, so apps that build Teams cards without the full
adapter get the same result.
### Why only Teams
Slack and Google Chat have no hover text for buttons. They do have
screen-reader labels, but those replace the button text for assistive
technology rather than adding to it, so mapping a tooltip onto them
would change what a screen reader announces. The fields are documented
as Teams-only for that reason.
### Small cleanup along the way
The JSX runtime used to decide whether a set of props belonged to a
`Card` by checking for a fixed list of prop names. Any new `Card` prop
that was not on that list was silently dropped. Since `Card` is the only
component left once every other one has been matched, the props are now
used directly and the list is gone.
Docs for both props are on the cards page and in the API reference.
---------
Signed-off-by: Mohammed Mansoor Ahmed <mansoorahmed.mohammed@gmail.com>
Signed-off-by: Ben Sabic <bensabic@users.noreply.github.com>
Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
|
||
|
|
043386b52c |
feat(telegram): add Business mode support (#888)
- Adds Telegram Business mode support to `@chat-adapter/telegram` - Handles `business_connection`, `business_message`, and `edited_business_message` updates - Passes `business_connection_id` on outbound sends, edits, typing, and file uploads - Opt-in via `businessMode: true` (default off, backward compatible) Closes #887 --------- Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com> |
||
|
|
d4a1f03afc |
fix(slack): rotate long native streams before expiry (#884)
Slack expires native streams after roughly five minutes. Finalize long-running streams after four minutes by default and continue in a fresh segment so late appends do not fail with message_not_in_streaming_state. Preserve open fenced code blocks by closing and reopening them across the segment boundary. The threshold is configurable with streamSegmentMaxAgeMs. --------- Signed-off-by: dcbuild3r <dcbuilder@pm.me> Signed-off-by: Ben Sabic <bensabic@users.noreply.github.com> Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com> |
||
|
|
51322dde8f |
fix(slack): decode the bot self mention in incoming message text (#891)
## Problem When a Slack message mentions the bot, the adapter resolves every mention in the text to a display name — except the bot's own. `<@U_BOT>` stays in `message.text` as raw user-ID markup while other mentions become `@<DisplayName>`. Downstream consumers therefore see inconsistent text. For LLM-based consumers this is actively harmful: small models classify differently depending on whether the mention arrived as self-describing `@<DisplayName>` text or as cryptic `<@U_BOT>` markup, and there is no way for a consumer to tell a real mention from a lookalike string without re-implementing Slack's mrkdwn rules. The reason the bot's own mention was left raw is detection coupling: `Chat.detectMention` matched `@botUserId` / `<@botUserId>` in the text, so resolving the markup would have hidden the mention from detection. ## Changes - `resolveInlineMentions` now decodes the bot's own mention like any other: `<@U_BOT>` resolves to `@<DisplayName>` (via the same `users.info` lookup and cache as all other mentions). - Because resolution renders the ID markup away, detection moves to where the ID is still known: `parseSlackMessage` tests the raw event text for the bot's mention (labeled, unlabeled, and bare `@ID` forms) and sets `isMention` on the parsed message. `Chat.detectMention` remains as the fallback for username-style mentions on the rendered text. - The `skipSelfMention` option is removed; the history/thread fetch paths that passed `skipSelfMention: false` now behave identically to live events, which also fixes an inconsistency where edited messages (`parseSlackMessageSync`) never resolved mentions at all. ## Behavior change `message.text` for a message that mentions the bot changes from `<@U_BOT> hello` to `@Vercel Bot hello` (the bot's resolved display name). Mention detection is preserved: `isMention` is set from the raw event text, and the labeled form is matched by the new detection patterns. Apps matching the raw `<@U_BOT>` markup in `message.text` should match the resolved display name or read `message.raw` instead. ## Relation to #355 #355 intentionally introduced the self-mention skip: in multi-workspace installs, the request-scoped bot ID was being resolved before mention detection ran, which broke `onNewMention` for those workspaces. This PR preserves that guarantee without the markup coupling — the multi-workspace replay test from that scenario is updated and still asserts that a plain `message` event containing the bot's mention sets `isMention: true` and routes to `onNewMention`. Detection additionally covers the labeled form (`<@U_BOT|Name>`) that resolution now produces. --------- Signed-off-by: dancer <josh@afterima.ge> Co-authored-by: dancer <josh@afterima.ge> |
||
|
|
f485255bcf |
fix(adapters): harden webhook tenant isolation (#877)
Multi-workspace Slack now ignores commands and interactions when their installation cannot be found, and channel names stay isolated per workspace. Google Chat no longer learns its identity from incoming mentions, and forward history reads use bounded native pagination. Webhook logs avoid message content. The example app protects preview routing, records only successful verified deliveries, and caps recording size and retention. --------- Signed-off-by: Ben Sabic <bensabic@users.noreply.github.com> Signed-off-by: dancer <josh@afterima.ge> Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com> Co-authored-by: dancer <josh@afterima.ge> |
||
|
|
b7c9316bfd |
fix(chat): tighten conversation boundaries (#875)
Agent tools now keep user profile lookups behind approval and apply conversation scope to typing indicators. Discord thread targets are checked against their parent channel, and private slash-command follow-ups stay private. Twilio direct messages no longer share history or scope across recipients. Queued messages restore their own conversation context before handlers run. Callback tokens are bound to their action and conversation, expire sooner, and can only be used once. Link preview metadata is clearly marked as untrusted and bounded before it reaches an AI prompt. --------- Signed-off-by: Ben Sabic <bensabic@users.noreply.github.com> Signed-off-by: dancer <josh@afterima.ge> Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com> Co-authored-by: dancer <josh@afterima.ge> |
||
|
|
a8de95bcc4 |
fix(teams): infer missing conversation types (#879)
## Summary Teams can omit `conversationType` while still sending `conversation.isGroup`. Use `isGroup` and team context as a fallback so `a:`-prefixed group chats are not treated as DMs. An explicit `conversationType` still takes precedence. ## Test plan - `pnpm validate` - `pnpm --filter @chat-adapter/teams test`, 276 tests passed ## Checklist - [x] All commits are signed and verified - [x] All commits are signed off for the DCO with `git commit -s` - [x] `pnpm validate` passes - [x] Changeset added - [x] Documentation updated Signed-off-by: onmax <maximogarciamtnez@gmail.com> |
||
|
|
78021c09d0 |
fix(slack): pass team id to agent view prompt resolvers (#889)
## summary - pass the workspace `team_id` from `app_home_opened` events to dynamic `suggestedPrompts` resolvers - prefer `authorizations[0].team_id` with the top-level `team_id` as a fallback - add regression coverage for multi-workspace agent view apps - fixes #874 ## test plan - reproduce the missing workspace context with Slack's documented event envelope - verify the resolver receives the workspace id - `pnpm build` - `pnpm --filter @chat-adapter/slack test` - `pnpm --filter @chat-adapter/integration-tests test` - `pnpm typecheck` - `pnpm exec ultracite check packages/adapter-slack/src/index.ts packages/adapter-slack/src/index.test.ts packages/adapter-slack/src/types.ts` Signed-off-by: dancer <josh@afterima.ge> |
||
|
|
8b6d7f3ab3 |
fix(slack): clear Agent Session typing status (#882)
## Summary - map non-empty `startTyping` statuses to the Slack Agent Session `processing` state - map an empty `startTyping` status to `active`, allowing generic Chat SDK callers to end typing without posting a message - preserve the legacy `assistant.threads.setStatus` path and `initiator_user_id` propagation This fixes the regression reported in mastra-ai/mastra#22670. Mastra already clears typing through the generic `startTyping(threadId, "")` contract, so the platform-specific lifecycle translation belongs in the Slack adapter. |
||
|
|
3d2cb22a41 |
fix(linear): stabilize agent session event threads (#885)
## Summary Route every event in a Linear agent session to the stable issue/session thread instead of deriving a new thread from each source comment. Also accept sessions created without a root comment and attribute creator-less sessions to a distinct Linear automation identity instead of the bot itself, which previously caused automation-created sessions to be dropped as self-messages. ## Test plan - bunx vitest run packages/adapter-linear/src/index.test.ts (164 passed) - bunx tsc -p packages/adapter-linear/tsconfig.json --noEmit - bunx biome check on changed files ## Checklist - [x] All commits are signed and verified - [x] All commits are signed off for the DCO (git commit -s) - [ ] pnpm validate passes (targeted package validation run) - [x] Changeset added - [x] Documentation updated (N/A; documented stable session thread format is now used consistently) --------- Signed-off-by: dcbuild3r <dcbuilder@pm.me> Signed-off-by: dancer <josh@afterima.ge> Co-authored-by: dancer <josh@afterima.ge> |
||
|
|
c2b6bff07c |
fix(slack): use bot user ID for message authors (#883)
## Summary Slack bot messages can include both an app-scoped bot_id and a user-scoped bot_profile.user_id. Use the user ID for normalized message authors when available so identity, self-message checks, and downstream user lookups operate on the same ID Slack uses for users. ## Test plan - bunx vitest run packages/adapter-slack/src/index.test.ts packages/adapter-slack/src/markdown.test.ts (490 passed) - bunx tsc -p packages/adapter-slack/tsconfig.json --noEmit - bunx biome check on changed files --------- Signed-off-by: dcbuild3r <dcbuilder@pm.me> Signed-off-by: dancer <josh@afterima.ge> Co-authored-by: dancer <josh@afterima.ge> |
||
|
|
7609d8f60e |
fix(adapters): validate external request targets (#876)
Adapters now reject untrusted destinations before sending credentials, message content, or attachment requests. Teams Connector and Graph calls stay on known Microsoft hosts, Instagram downloads stay on trusted Meta hosts, and Slack response URLs are checked before use. XChat now handles CRC challenges itself and rejects tokens that could be reused to forge webhook signatures. Signed-off-by: Ben Sabic <bensabic@users.noreply.github.com> Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com> |
||
|
|
f691ad5848 |
docs: add LINE community adapter (#873)
## Summary - Add `chat-adapter-line` to the community adapter catalog. - Add a hand-authored LINE adapter docs page with configuration, webhook, messaging, and feature-matrix details. - Register `chat-adapter-line` as a valid docs code-example import. ## Validation - `pnpm --filter @chat-adapter/integration-tests test -- src/docs-adapters.test.ts --coverage=false` — 467 tests passed - `pnpm --filter @chat-adapter/integration-tests test -- src/docs-content.test.ts --coverage=false` — 91 tests passed - `pnpm --filter @chat-adapter/integration-tests test -- src/docs-llms.test.ts --coverage=false` — 145 tests passed - `pnpm exec biome check apps/docs/content/adapters/community/line.mdx apps/docs/content/adapters/community/meta.json apps/docs/adapters.json packages/integration-tests/src/documentation-test-utils.ts` — passed Signed-off-by: PunGrumpy <108584943+PunGrumpy@users.noreply.github.com> |
||
|
|
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 - |
||
|
|
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>
|
||
|
|
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>
|
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
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>
|
||
|
|
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 [ |
||
|
|
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 - |