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).
Summary
Two related defects in
@chat-adapter/teams/@chat-adapter/shared(observed on 4.37.0) make bot-authored@Name Surnamementions render as the literal text<at>Name</at> Surnamein Microsoft Teams instead of a mention chip.1. No
mentionentities are attached on sendTeamsFormatConverter.convertMentionsToTeamsrewrites bare@nameto<at>name</at>in outgoing text (dist/index.js ~1247):But Teams only renders
<at>as a mention when the activity also carries a matching entity: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.
replaceBareMentionscaptures a single wordIn
@chat-adapter/shared(dist/index.js ~412), the mention scanner consumes@plus one word-character run:@Hank Fengtherefore 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 textHi <at>Hank</at> Feng — ....Expected behavior
@mentionsagainst the conversation roster and attachmentionentities (matching the full display name greedily), or leave@nameuntouched / strip the@when no entity can be built. Emitting<at>without an entity should never happen.