Adapters that exist
All are registered at startup.
Microsoft Teams is the only adapter that has to be reachable from the internet. It runs an HTTP listener, port 3978 by default, that Teams posts activity to. Deploy without Teams and no part of Switch needs a public address.
The adapter contract
CollaborationAdapter is an abstract base class. An implementer provides methods in the following groups: lifecycle, messaging, channels, identity, formatting.
Lifecycle
start and keep it open. The callbacks are the only way work reaches the bridge core — every inbound platform event ends up in one of them. stop tears the connection down.
Messaging
send_message returns the platform’s own id for the post. Return it — the bridge core stores it against the Matrix event id, and threading, edits and deletes all resolve through that pair.
sender_name is the agent whose voice the message goes out in. Render it however the platform allows: a per-agent identity, a display-name override, a prefix.
Channels
get_channel_type is called when Switch adopts an existing channel without a stated type, before it provisions a room around it.
Identity
Formatting
Capability flags
Capability flags are class attributes, not runtime probes. Switch answers questions like “can this bridge create a channel?” while validating a room-creation request, before a connection exists.Defaults you inherit
The base class ships concrete methods with usable defaults. Override one only when the platform does better than the default. They cover attachments, admin messages, runtime-state indicators, direct-message channel creation, directory search, deeplinks, install links, agent icons, mention priming, and channel subscriptions. Direct-message channel creation raises an unsupported error by default, so an adapter that can’t do it fails visibly rather than posting somewhere else. Implement the abstract methods first and nothing more. Get messages flowing, then override defaults one at a time.Inbound models
Everything an adapter hands back through the callbacks is a platform-neutral Pydantic model.
Supporting models:
Attachment, OutboundAttachment, AttachmentFailure, DirectoryUser.
There is no outbound message model. Outbound is a Matrix event handed to the bridge core, which passes primitives to send_message.
Puppeting
A puppet is a Matrix account that stands in for one external person. The bridge core keeps a map from external user id to puppet client id. On an inbound message the core looks up or creates the puppet, waits for it to be ready, invites it to the Matrix room, waits for the join to land, and only then sends. Puppet creation is guarded by a per-user lock with a double-check inside it, because two messages from the same new person can arrive close together. The core refuses to puppet a name belonging to a registered bridged agent, so an external account can’t claim an agent’s identity by picking a display name.Loop prevention
The outbound path skips any Matrix event whose sender is a known puppet. A message that arrived from Slack entered the room as a puppet, so it is never relayed back to Slack. Agents and other Switch participants have non-puppet senders and go out normally.Threads, edits and deletes
A durable table maps Matrix event ids to external post ids. It is written in both directions, with a uniqueness constraint on each side, so either id resolves the other.- Threading. A Matrix reply carries the event id it replies to. The map turns that into the platform’s thread root, and the reply lands in the thread.
- Edits and deletes. The bridge looks up the external post id and calls
update_messageordelete_messageagainst it, however long after the fact.
send_message. Everything in this section depends on it.
Command registry
Room commands live in one registry. ACommand is a frozen dataclass with a name, description, handler, argument spec, targeting, a forward-to-agent flag, a hidden flag and an admin check.
Registered commands include help, reset, compact, interrupt, list-agents, agents-status, roles, list-documents, list-references, list-aliases, set-alias, remove-alias, invite-agent and room-url, most with an all-agents variant.
What differs per platform is only how a person reaches them.
Commands marked admin-owned execute as the admin client. The rest execute as the agents themselves, in their own voice, so
!compact reads in the channel as that agent responding rather than as a system notice.Next steps
Life of a message
One message from a channel to an agent and back, hop by hop
The Matrix substrate
Participants as clients, sync and resume, and the custom events Switch layers on