Skip to content

adapter-teams: outgoing @mentions render as literal <at> tags (no mention entities; multi-word names split) #853

Description

@vody

Summary

Two related defects in @chat-adapter/teams / @chat-adapter/shared (observed on 4.37.0) make bot-authored @Name Surname mentions render as the literal text <at>Name</at> Surname in Microsoft Teams instead of a mention chip.

1. No mention entities are attached on send

TeamsFormatConverter.convertMentionsToTeams rewrites bare @name to <at>name</at> in outgoing text (dist/index.js ~1247):

convertMentionsToTeams(text) {
  return replaceBareMentions(text, (_mention, name) => `<at>${name}</at>`);
}

But Teams only renders <at> as a mention when the activity also carries a matching entity:

"entities": [{ "type": "mention",
               "text": "<at>Name Surname</at>",
               "mentioned": { "id": "29:...", "name": "Name Surname" } }]

The adapter never constructs outgoing mention entities (it reads them on inbound — the e.type === "mention" check around dist/index.js ~1837 — but no send path writes them). Without the entity, Teams displays the raw tag as literal text, which is worse than not converting at all.

2. replaceBareMentions captures a single word

In @chat-adapter/shared (dist/index.js ~412), the mention scanner consumes @ plus one word-character run:

if (text[index] === "@" && ... && isWord(text[index + 1])) {
  let cursor = index + 2;
  while (cursor < end && isWord(text[cursor])) cursor += 1;
  const mention = text.slice(index, cursor);
  ...
}

@Hank Feng therefore becomes <at>Hank</at> Feng — even with entities fixed, the chip would carry half the display name and fail to match the roster name.

Observed impact (production)

A bot reply beginning Hi @Hank Feng — ... was delivered to a Teams channel as the literal text Hi <at>Hank</at> Feng — ....

Expected behavior

  • Either resolve outgoing @mentions against the conversation roster and attach mention entities (matching the full display name greedily), or leave @name untouched / strip the @ when no entity can be built. Emitting <at> without an entity should never happen.
  • Mention capture should support multi-word display names (e.g. greedy longest-match against known conversation members).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions