Skip to content

fix(chat): honor adapter-reported non-mentions - #946

Merged
bensabic merged 5 commits into
mainfrom
fix/chat-authoritative-mention
Sep 17, 2026
Merged

bensabic merged 5 commits into
mainfrom
fix/chat-authoritative-mention

Conversation

@privatenumber

@privatenumber privatenumber commented Sep 16, 2026 •

Copy link
Copy Markdown
Contributor

Problem

Chat SDK decides whether to call onNewMention from message.isMention, and adapters are expected to set that flag from platform-specific content. Core then overrides a negative result:

message.isMention =
  message.isMention || this.detectMention(adapter, message);

An adapter can inspect content that plain text cannot. Slack, for example, distinguishes a user mention from a code-styled text element, even when both contain the same characters.

With ||, that distinction is lost. An adapter that reports isMention: false for a code sample is overridden when text detection finds @username in the flattened message, and the mention handler runs for a message that mentions nobody. #947 depends on this behavior: without it, the Slack adapter cannot keep literal code out of mention routing.

Changes

message.isMention is now a three-state value.

Adapter result Core behavior
true Keep the mention
false Keep the non-mention
undefined Match @username in the message text

An adapter that reads structured content reports true or false; one that needs text matching leaves the flag unset. The same rule applies to messages skipped while draining a queue. Ordinary Linear comments now leave the flag unset so @displayName detection in a comment body keeps working.

Adapter authors that previously returned false to mean "not detected" should return undefined instead. The adapter-building docs now state this contract, which the previous docs left implicit by only showing the true case.

Changed files:

Related PRs

  • #947 builds on this contract to keep literal Slack code references out of mention routing

Signed-off-by: Hiroki Osame <hiroki.osame@gmail.com>
@privatenumber
privatenumber requested a review from a team September 16, 2026 10:01
@vercel

vercel Bot commented Sep 16, 2026 •

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
chat Ready Ready Preview, v0 Sep 17, 2026 8:05am UTC
chat-sdk-nextjs-chat Ready Ready Preview, v0 Sep 17, 2026 8:05am UTC

Signed-off-by: Hiroki Osame <hiroki.osame@gmail.com>
Signed-off-by: Hiroki Osame <hiroki.osame@gmail.com>
Signed-off-by: Hiroki Osame <hiroki.osame@gmail.com>
- X: posts rebuilt from raw or fetched by id, and DMs, no longer report `isMention: false`; only `post.mention.create` events set `true`
- Discord: webhook and gateway paths report `true` for a real ping, role mention, `@everyone`, or allowlisted channel, and leave the field unset otherwise
- Notion: `keyword` mode returns `undefined` when no keyword matches or the keyword list is empty; `isMe` comments still report `false`
- Linear: use an explicit `? true : undefined` ternary for agent session comments
- chat: document that the DM backward-compat rule overrides the adapter's value when no `onDirectMessage` handler is registered
- Docs: describe the three `isMention` states in the API reference, the adapter-building guide, and the Notion mention table; note the DM exception
- Tests: X parseMessage and Notion keyword mode assert the field is undefined; Discord non-mention assertions updated to match
- Changeset: bump `chat` as minor and add notion, x, and discord alongside linear

Signed-off-by: Ben Sabic <bensabic@users.noreply.github.com>
@bensabic
bensabic merged commit fcdc1c9 into main Sep 17, 2026
19 checks passed
@bensabic
bensabic deleted the fix/chat-authoritative-mention branch September 17, 2026 08:08
bensabic added a commit that referenced this pull request Sep 17, 2026
## Summary

- Code samples that contain the bot's id no longer start a conversation
- Real mentions keep working, including beside a code sample
- Depends on [#946](#946), which lets
the adapter's non-mention decision stick

## Problem

Chat SDK routes a message to `onNewMention` when the bot is mentioned,
and the Slack adapter decided that from the raw text: it scanned for the
bot's id and marked every `app_mention` event as a mention.

Slack models those two cases differently. A [user
mention](https://docs.slack.dev/reference/block-kit/block-elements/user-element/)
"renders as a mention of a user", while a [text
element](https://docs.slack.dev/reference/block-kit/block-elements/text-element/)
with `style.code` displays literal code. Both can carry the same id
characters:

```json
{ "type": "text", "text": "<@UBOT123>", "style": { "code": true } }
```

```json
{ "type": "user", "user_id": "UBOT123" }
```

Only the second is a mention. Because the adapter also searched the
text, a message that only documents the bot was treated as an
invocation:

1. A user posts a code sample that contains the bot's id.
2. Slack delivers the event as `app_mention`.
3. The adapter set `isMention`, and the mention handler ran.

## Changes

The adapter now classifies the content the way Slack renders it, instead
of scanning every string for the bot's id.

- A `user` element for the bot outside code is a mention.
- A `<@U…>` token in a text object explicitly typed `mrkdwn` is a
mention.
- Inline code (`style.code`) and `preformatted` elements are not.
- A rich-text `text` element, a link label, and a `raw_text` table cell
are display text. A token there stays literal, so it is not a mention.

When a message carries blocks, those blocks model its body and the
flattened `text` field is not consulted, because it loses the
code/literal distinction. Attachment blocks are authoritative for their
attachment, so an attachment's legacy fallback cannot add mention
evidence. Blocks and mrkdwn content are both traversed, so a `section`
block mention is still found.

The unconditional `app_mention` override is gone. A fully inspected
message with no mention reports `isMention: false`, even when the bot's
display name appears as plain text, which
[#946](#946) preserves. If the
adapter cannot identify the bot (`botUserId` unresolved), an
`app_mention` event is still trusted and other messages stay
undetermined.

The routing test in
[packages/adapter-slack/src/index.test.ts](https://github.com/vercel/chat/blob/b2d05aa2/packages/adapter-slack/src/index.test.ts)
sends a signed webhook through the real adapter and a real `Chat`
instance. Against the pre-fix adapter it fails because the mention
handler runs; with the fix it passes. Seven cases were confirmed to fail
before the element-type fix, covering `raw_text` cells, rich-text text
elements, link labels, `plain_text` blocks, and attachment fallbacks.

The Slack integration suite also delivered events as `text: "@botName
..."`. Slack mentions are `<@U…>` tokens, so those fixtures were
corrected to the documented syntax; the operations those tests own
(subscriptions, edits, reactions, uploads) are unchanged.

Changed files:

-
[packages/adapter-slack/src/index.ts](https://github.com/vercel/chat/blob/b2d05aa2/packages/adapter-slack/src/index.ts):
element-typed mention classification across rich text, tables, and
attachments
-
[packages/adapter-slack/src/index.test.ts](https://github.com/vercel/chat/blob/b2d05aa2/packages/adapter-slack/src/index.test.ts):
adapter-level classification and webhook-to-handler routing
-
[packages/integration-tests/src/slack.test.ts](https://github.com/vercel/chat/blob/b2d05aa2/packages/integration-tests/src/slack.test.ts):
mention fixtures corrected to `<@U…>` syntax

## Related PRs

- Depends on [#946](#946): without
it, `Chat` re-detects the mention from the flattened text and the false
positive returns

---------

Signed-off-by: Hiroki Osame <hiroki.osame@gmail.com>
Signed-off-by: Ben Sabic <bensabic@users.noreply.github.com>
Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
amitvijapur added a commit to amitvijapur/chat that referenced this pull request Sep 20, 2026
…acket-snapshot

Forwarding moved behind enqueueOrderedForward on main (vercel#946, vercel#927), which
defers the task a microtask even on an empty queue, so the snapshot now
happens in the raw handler before the enqueue and the clone is what gets
forwarded.
patrick-chinchill added a commit to Chinchill-AI/chat-sdk-python that referenced this pull request Oct 1, 2026
…termined comment mentions (#232)

Linear agent sessions now route every event to one stable thread, linear:{issueId}:s:{agentSessionId}, instead of one :c:{comment}:s:{session} thread per source comment.
Ports upstream 3d2cb22a (vercel/chat#885, chat@4.40.0): rootless sessions are dispatched with a synthetic root comment, creator-less sessions are authored by linear-automation, prompted events fall back to the activity id, and fetch pages agentSession.activities for sessions with no root comment.
Ports the Linear half of fcdc1c9e (vercel/chat#946, chat@4.41.0): agent-session comments are is_mention=True, and ordinary comments leave is_mention unset so core @mention detection runs.
Divergence: the activities query is raw GraphQL, checked against Linear's published schema (no Python @linear/sdk). Documented in docs/UPSTREAM_SYNC.md.
Consumer impact: new agent-session events use the stable session thread id. Stored old :c:...:s:... ids still decode to the same session, so posting to them still works.
Closes #232

This branch was successfully deployed

2 active deployments
Preview – chat — aeeaf4e6 Deployed Sep 17, 2026 by vercel[bot]
Preview – chat-sdk-nextjs-chat — aeeaf4e6 Deployed Sep 17, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants