ProtectedavailableAll active agents, loaded at init for multi-word name matching. Sorted longest-name-first.
ProtecteddefaultDefault agent loaded from config DefaultAgentName.
ProtectedfallbackFallback context user (service account) loaded from config email.
ProtectedsettingsExtension settings from mj.config.cjs.
Get the list of available agent names, sorted longest-first. Useful for subclasses implementing multi-word agent name matching.
ProtectedPlatformHuman-readable platform name (e.g., "Slack", "Teams") used in conversation names and log messages. Subclasses must return their platform identifier.
ProtectedbuildBuild an AgentIdentity from an agent entity.
Only includes IconURL if it's a valid HTTPS URL.
ProtectedcollectCollect the files an agent produced, from the run's own fileOutputs.
This is the canonical source — fileOutputs is what MJ turns into file artifacts — and it
carries the real filename and MIME type rather than leaving them to be guessed. Entries
already saved to storage (fileId, no fileData) are skipped: those have a durable
location, and re-uploading their bytes to chat is not this method's job.
Preferred over collectInlineFileAttachments, which depends on the model choosing to
emit a data: URI — it does so only sometimes, so relying on it meant a generated document
reached chat on one run and not the next.
ProtectedcollectHarvest files that an agent inlined as data: URIs on its actionable commands.
MJ's document actions fall back to embedding the generated file itself when no file storage account is configured. That URI cannot be opened from a chat client, so the file is decoded here and handed to the platform's uploader as an ordinary attachment.
ProtecteddetectProtectedfetchFetch thread history from Teams.
NOTE: Not yet implemented. Requires Microsoft Graph API access with
ChannelMessage.Read.All permission. Currently returns empty array,
meaning conversations are single-turn.
Future implementation would use:
GET /teams/{id}/channels/{id}/messages/{id}/replies
ProtectedformatFormat agent response as a rich Teams Adaptive Card.
When a full ExecuteAgentResult is available, builds a rich card with
agent header, action buttons, explorer links, and metadata footer.
Falls back to basic markdown-to-TextBlock conversion otherwise.
Optionalmetadata: AgentResponseMetadataProtectedgetGet the bot's own user ID on this platform (to identify bot messages in thread history).
Handle an Adaptive Card Action.Submit from a form rendered in Teams.
Extracts field values from activity.value, builds the same
@{_mode:"form",...} payload that Slack uses for modal submissions,
and routes through HandleMessage() as a synthetic IncomingMessage.
Does NOT send a bot acknowledgement message — the agent's own response serves as the acknowledgement, and a bot-authored echo of the form values would pollute the conversation history the agent sees.
Main entry point: handle an incoming message from the platform.
Orchestrates the full flow from message receipt to response delivery. Errors are caught and reported as user-facing error messages — this method never throws.
Normalized incoming message from the platform adapter.
Initialize the adapter: resolve the fallback context user, load the default MJ agent (using the fallback user as contextUser), load all available agents, then run platform init. Must be called before handling any messages.
ProtectedisWas this history message written by ANY bot (not just this one)?
Thread affinity and conversation context must both ignore other bots' messages: an agent's reply names itself in prose, which the mention matcher would read as a user request, and as a 'user' turn it feeds one agent's self-description into another's context.
The default is false because the marker is platform-specific — see SlackAdapter, which
reads the Events API's bot_id/bot_profile/bot_message fields.
ProtectedisWas this message posted by THIS bot?
Not a bare === getBotUserId(). A platform may publish more than one identifier for the
same bot and return a different one depending on how the message was posted — on Slack a
post carrying a username/icon_url override comes back with a bot_id and NO user,
and this adapter sets that override on every agent reply so the agent answers under its own
name. Platforms with that split override this; the default is a single-identifier compare.
ProtectedisIs this artifact meant for the user, rather than internal system state?
Fails OPEN: an unreadable artifact keeps its link, since losing a working link on a transient read failure is worse than the rare case of surfacing an internal one.
ProtectedlookupLook up the email for a Teams user.
In Teams, the email is often available directly on the activity's from property.
Teams user ID.
Email address, or null if not available.
Convert a Bot Framework TurnContext into a normalized IncomingMessage.
Called by TeamsMessagingExtension when a message activity is received.
Bot Framework turn context for the current activity.
Normalized incoming message.
ProtectedmatchMatch agent names mentioned in the message text against known agents.
Strips the bot's platform mention, then checks for known agent names
preceded by @ (case-insensitive). Names are checked longest-first
to avoid prefix collisions (e.g., "Research Agent" before "Research").
The raw message text.
Array of matched agent names.
ProtectedonInitialize: set the bot ID from config or environment.
ProtectedresolveResolve which agent to use for this message.
Priority:
ProtectedresolveResolve the MJ user for the platform sender.
Flow:
lookupUserEmail() (platform API) then look upProtectedrespondsTeams delivers a channel message to a bot only when that bot is @mentioned, so replies never fan out across installed apps the way they do on Slack. The multi-bot thread gate is therefore unnecessary here — and would wrongly suppress replies, since a Teams thread reply that reaches this bot at all was already addressed to it.
ProtectedsendSend the final formatted response as an Adaptive Card.
ProtectedsendPost a new streaming message or update an existing one. Falls back to sending a new message if the channel doesn't support updates.
Instead of caching update support on the singleton, we detect per-turn
by checking channelId. Web Chat ('webchat') doesn't support updates;
real Teams ('msteams') does. For unknown channels we try/catch.
Optional_agent: MJAIAgentEntityExtendedProtectedshouldDetermine whether the bot should respond to this message.
Responds to:
ProtectedshowSend a typing indicator to the Teams conversation. Teams natively supports typing indicators via the Bot Framework.
Optional_agent: MJAIAgentEntityExtendedProtectedstripProtectedupdateUpdate the streaming progress message with the final Adaptive Card. Falls back to sending a new message if the channel doesn't support updates.
Protected OptionaluploadOptional per-platform file upload. Implemented by adapters whose platform can accept file
uploads (see SlackAdapter.uploadMediaOutputs); absent elsewhere, in which case binary
output is simply not delivered as files.
Microsoft Teams-specific adapter that implements all platform operations using the Bot Framework SDK (
botbuilder).Features
ActivityTypes.Typing<at>BotName</at>) for clean agent inputAuthentication
Requires a Microsoft App ID and Password registered in Azure Bot Service. The Bot Framework SDK handles JWT token validation automatically.
Thread History
Teams thread history fetching via Microsoft Graph API is not yet implemented. This means conversations are currently single-turn. Multi-turn support requires Graph API access with
ChannelMessage.Read.Allpermission.