Skip to content

feat(bridges): render agents under their display name on Teams and Telegram - #338

Open
raullopez-sandbox wants to merge 3 commits into
feat/display-name-02-bridge-renderingfrom
feat/display-name-03-teams-telegram
Open

feat(bridges): render agents under their display name on Teams and Telegram#338
raullopez-sandbox wants to merge 3 commits into
feat/display-name-02-bridge-renderingfrom
feat/display-name-03-teams-telegram

Conversation

@raullopez-sandbox

@raullopez-sandbox raullopez-sandbox commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

Fourth PR in the display-name stack. Base is #330 — review that one first.

What changes

The two platforms that still showed the identifier now show the presentation
display_name, where one is set. The escaping they need already landed in #330,
so this is the rendering half alone.

Teams

_message_activity called agent_icon_url and then handed sender_name to the
card — two names for one speaker as soon as a display name exists. It now takes
a single AgentRendering and agent_message_card takes it whole, so a send is
one DB lookup rather than two.

The same name is escaped in one place and raw in another, deliberately:

field form why
card header body_label a TextBlock renders markdown
fallbackText body_label same
altText field_label read out verbatim by a screen reader
activity summary field_label toast/preview text, rendered by no markup engine

A hostile display name of [Switch Support](https://evil.example) therefore
reaches the header defused and the summary raw. Escaping uniformly would have
been the easier call and it degrades both — a screen reader would read the
zero-width space out, and the toast would show it.

mentions on agent_message_card is now required rather than defaulting to
None. Every caller passes it; the default only hid that.

Telegram

Telegram has no per-message identity — one bot posts for every agent, so the
name in the body is the only thing telling two agents apart. All four
attribution sites take body_label, never field_label: message, attachment
caption, album caption, and the live runtime-status card that is edited in place
(which rebuilds the prefix itself rather than going through send_message).

field_label would be wrong here specifically because Telegram links a bare
@handle out of ordinary message text with no markup involved
, so the
unescaped form would notify that account from the prefix alone.

The two escapes on that path do not compound: escape_label_for_body defuses
the markup and html.escape neutralises the tags, and a zero-width space is not
an entity.

The per-agent mark stays keyed on the identifier

display_name and icon_url are both owner-settable, which leaves the coloured
mark as the only per-speaker signal on a Telegram message that an impersonating
agent cannot control. Keyed on the label instead, two agents sharing a display
name would collapse onto one mark, and an agent's mark would move when it is
renamed — losing the recognisability the mark exists for.

Captured from the real adapter:

two agents both called "Switch Dev":
  switchdev  -> 🟤 <b>Switch Dev</b>: hello
  releasebot -> 🔴 <b>Switch Dev</b>: hello

same agent renamed -> 🟤 <b>Something Else Entirely</b>: hello   (mark did not move)

Verification

  • 2190 tests pass, ruff format + ruff check clean, mypy clean on 186 files.
  • Test-merged onto current main in a throwaway worktree: clean, 2195 pass.
  • Payloads captured from the real adapters across all four Telegram sites and all four Teams fields, with before/after diffed.
  • One DB lookup per send confirmed on both, including for a two-file Telegram album.

🤖 Generated with Claude Code

…legram

The two platforms that still showed the identifier now show the presentation
`display_name`, where one is set. The escaping they need already landed with
the base resolver, so this is the rendering half alone.

Teams resolves once per send. `_message_activity` called `agent_icon_url`
and then handed `sender_name` to the card, which is two names for one
speaker as soon as a display name exists; it now takes a single
`AgentRendering` and `agent_message_card` takes it whole. The card header
and `fallbackText` use `body_label` because a TextBlock renders markdown;
`altText` and the activity `summary` use `field_label` because neither is
rendered by any markup engine and the escaped form would show its own
defusal to the reader. `mentions` is required rather than defaulting to
`None` — every caller passes it, and the default only hid that.

Telegram puts the label in the body because it has no per-message identity:
one bot posts for every agent, so the name is the only thing telling two
agents apart. All four attribution sites — message, attachment caption,
album caption, runtime-status edit — take `body_label`, never
`field_label`. Telegram links a bare `@handle` out of ordinary message text
with no markup involved, so the unescaped form would notify that account
from the prefix alone. The two escapes there do not compound:
`escape_label_for_body` defuses the markup and `html.escape` neutralises the
tags, and a zero-width space is not an entity.

The per-agent mark stays keyed on the identifier. `display_name` and
`icon_url` are both owner-settable, which leaves the mark as the only
per-speaker signal on a Telegram message an impersonating agent cannot
control. Keyed on the label instead, two agents sharing a display name would
collapse onto one mark and an agent's mark would move when it is renamed,
losing the recognisability the mark exists for.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
raullopez-sandbox and others added 2 commits September 3, 2026 14:48
The agent list API endpoint was not returning the display_name field,
causing the Switch Console UI to not show agent display names.

Changes:
- Add display_name field to AgentInfo schema
- Update list_agents handler to include display_name in response
- Update list_delegatable_agents to include display_name

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
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.

1 participant