Skip to content

feat(teams): show that an agent is working — eyes on the message, card in its thread (CHOO-2315) - #275

Open
amaudruz wants to merge 3 commits into
mainfrom
feature-request/teams-addressing-indicators
Open

feat(teams): show that an agent is working — eyes on the message, card in its thread (CHOO-2315)#275
amaudruz wants to merge 3 commits into
mainfrom
feature-request/teams-addressing-indicators

Conversation

@amaudruz

Copy link
Copy Markdown
Collaborator

Teams gets the acknowledgement Slack already has.

  • 👀 on the message that asked, for the length of the turn — added when the agent starts, removed when it goes idle. Plain Bot Connector call (PUT/DELETE /v3/conversations/{conversation}/activities/{activity}/reactions/1f440_eyes) on the same regional host and app-only token the bridge already uses to send, so no Graph permission and no delegated user. It renders in a channel, a group chat and a 1:1 alike — the only signal that does.
  • The status card lands in that message's thread rather than at the channel root (runtime_state_follows_anchor), because Teams renders a thread inline under its root post.

Which message gets marked is recovered on the way in: a runtime-state report names the thread, not the message inside it, so inbound messages record what asked, per thread and per conversation. Inside a thread that is a reply, not the root; a chat has no thread at all.

The honest negatives

Written down in the bridge README rather than faked:

  • No per-agent @handle. A Teams app manifest admits one bot (bots is maxItems: 1) and a tag — the nearest analogue to a Slack user group — takes user ids only, so a bot cannot be in one. The exception is an Entra agent user account, which is @-mentionable like a person but preview-gated and costs a directory object and a licence per agent.
  • Native streaming not used. The streaminfo "informative update" is documented as one-on-one chats only, one request per second, with a hard two-minute cap — a response-latency surface, not a signal for work that runs for minutes.
  • Typing indicator not relied on. Transient, undocumented as to scope, and in practice absent in channels.
  • The reaction endpoint has no Bot Connector REST reference page — Microsoft documents the capability through the Teams SDK. A tenant whose Teams service has not shipped it answers an error; the mark is skipped with a warning and the turn carries on with the card alone.

Tests

test_teams_adapter.py — the mark goes on the message that asked and not the thread root, comes off when the turn ends, is added once per turn, survives awaiting-input, covers every message an agent marked, works in a chat with no thread, and leaves the card intact when the tenant refuses the reaction. test_teams_clients.py — the reaction URL shape, empty body, id escaping, and error propagation.

Not yet verified against a live tenant: which conversation types actually render the reaction. Worth a check before this is announced.

🤖 Generated with Claude Code

…d in its thread

Teams gets the acknowledgement Slack already has. The message that asked
carries 👀 for the length of the turn, added when the agent starts and taken
off when it goes idle, and the status card lands in that message's thread
rather than at the bottom of the channel.

The reaction is a plain Bot Connector call —
PUT/DELETE /v3/conversations/{conversation}/activities/{activity}/reactions/{id}
— on the same regional host and app-only token the bridge already uses to send,
so it needs no Graph permission and no delegated user, and it renders in a
channel, a group chat and a 1:1 alike. Microsoft documents the capability
through the Teams SDK rather than the Bot Connector REST reference, so a tenant
whose Teams service has not shipped it answers an error: the mark is skipped
with a warning and the turn carries on with the card alone.

Which message gets marked is recovered on the way in. A runtime-state report
names the thread, not the message inside it, so inbound messages record what
asked, per thread and per conversation — inside a thread that is a reply, not
the root, and a chat has no thread at all.

Two Teams affordances were considered and left out, written down in the
bridge README rather than faked: native streaming is one-on-one only with a
hard two-minute cap, and the typing indicator does not render in channels.
Teams also has no per-agent @-handle to match Slack's user groups — one app
admits one bot, and a tag cannot contain one.

CHOO-2315

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…-addressing-indicators

# Conflicts:
#	CHANGELOG.md
#	core/switch_core/bridges/collaboration/teams/README.md
#	core/switch_core/bridges/collaboration/teams/adapter.py
#	core/tests/switch_core/bridges/collaboration/test_teams_clients.py
@abeldantas

abeldantas commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

Resolved the merge conflicts with main (branch predated the 0.23.0 release cut and the Teams README/adapter rewrites)

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