@chat-adapter/teams 4.3.0 → 4.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -24,6 +24,7 @@ import {
24
24
  import {
25
25
  convertEmojiPlaceholders,
26
26
  defaultEmojiResolver,
27
+ Message,
27
28
  NotImplementedError
28
29
  } from "chat";
29
30
 
@@ -707,7 +708,7 @@ var TeamsAdapter = class {
707
708
  const text = activity.text || "";
708
709
  const normalizedText = this.normalizeMentions(text, activity);
709
710
  const isMe = this.isMessageFromSelf(activity);
710
- return {
711
+ return new Message({
711
712
  id: activity.id || "",
712
713
  threadId,
713
714
  text: this.formatConverter.extractPlainText(normalizedText),
@@ -733,7 +734,7 @@ var TeamsAdapter = class {
733
734
  !(att.contentType === "text/html" && !att.contentUrl)
734
735
  )
735
736
  ).map((att) => this.createAttachment(att))
736
- };
737
+ });
737
738
  }
738
739
  /**
739
740
  * Create an Attachment object from a Teams attachment.
@@ -1149,7 +1150,7 @@ var TeamsAdapter = class {
1149
1150
  });
1150
1151
  const messages = graphMessages.map((msg) => {
1151
1152
  const isFromBot = msg.from?.application?.id === this.config.appId || msg.from?.user?.id === this.config.appId;
1152
- return {
1153
+ return new Message({
1153
1154
  id: msg.id,
1154
1155
  threadId,
1155
1156
  text: this.extractTextFromGraphMessage(msg),
@@ -1169,7 +1170,7 @@ var TeamsAdapter = class {
1169
1170
  edited: !!msg.lastModifiedDateTime
1170
1171
  },
1171
1172
  attachments: this.extractAttachmentsFromGraphMessage(msg)
1172
- };
1173
+ });
1173
1174
  });
1174
1175
  let nextCursor;
1175
1176
  if (hasMoreMessages && graphMessages.length > 0) {
@@ -1289,7 +1290,7 @@ var TeamsAdapter = class {
1289
1290
  });
1290
1291
  const messages = graphMessages.map((msg) => {
1291
1292
  const isFromBot = msg.from?.application?.id === this.config.appId || msg.from?.user?.id === this.config.appId;
1292
- return {
1293
+ return new Message({
1293
1294
  id: msg.id,
1294
1295
  threadId,
1295
1296
  text: this.extractTextFromGraphMessage(msg),
@@ -1309,7 +1310,7 @@ var TeamsAdapter = class {
1309
1310
  edited: !!msg.lastModifiedDateTime
1310
1311
  },
1311
1312
  attachments: this.extractAttachmentsFromGraphMessage(msg)
1312
- };
1313
+ });
1313
1314
  });
1314
1315
  let nextCursor;
1315
1316
  if (hasMoreMessages && graphMessages.length > 0) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/cards.ts","../src/markdown.ts"],"sourcesContent":["import { ClientSecretCredential } from \"@azure/identity\";\nimport { Client } from \"@microsoft/microsoft-graph-client\";\nimport {\n TokenCredentialAuthenticationProvider,\n type TokenCredentialAuthenticationProviderOptions,\n} from \"@microsoft/microsoft-graph-client/authProviders/azureTokenCredentials\";\nimport type { Activity, ConversationReference } from \"botbuilder\";\nimport {\n ActivityTypes,\n CloudAdapter,\n ConfigurationBotFrameworkAuthentication,\n TeamsInfo,\n type TurnContext,\n} from \"botbuilder\";\n\n/** Extended CloudAdapter that exposes processActivity for serverless environments */\nclass ServerlessCloudAdapter extends CloudAdapter {\n handleActivity(\n authHeader: string,\n activity: Activity,\n logic: (context: TurnContext) => Promise<void>,\n ) {\n return this.processActivity(authHeader, activity, logic);\n }\n}\n\nimport {\n AdapterRateLimitError,\n AuthenticationError,\n bufferToDataUri,\n extractCard,\n extractFiles,\n NetworkError,\n PermissionError,\n toBuffer,\n ValidationError,\n} from \"@chat-adapter/shared\";\nimport type {\n ActionEvent,\n Adapter,\n AdapterPostableMessage,\n Attachment,\n ChatInstance,\n EmojiValue,\n FetchOptions,\n FetchResult,\n FileUpload,\n FormattedContent,\n Logger,\n Message,\n RawMessage,\n ReactionEvent,\n ThreadInfo,\n WebhookOptions,\n} from \"chat\";\nimport {\n convertEmojiPlaceholders,\n defaultEmojiResolver,\n NotImplementedError,\n} from \"chat\";\nimport { cardToAdaptiveCard } from \"./cards\";\nimport { TeamsFormatConverter } from \"./markdown\";\n\n/** Microsoft Graph API chat message type */\ninterface GraphChatMessage {\n id: string;\n createdDateTime?: string;\n lastModifiedDateTime?: string;\n replyToId?: string; // ID of parent message for channel threads\n body?: {\n content?: string;\n contentType?: \"text\" | \"html\";\n };\n from?: {\n user?: {\n id?: string;\n displayName?: string;\n };\n application?: {\n id?: string;\n displayName?: string;\n };\n };\n attachments?: Array<{\n id?: string;\n contentType?: string;\n contentUrl?: string;\n content?: string; // JSON string for adaptive cards\n name?: string;\n }>;\n}\n\nexport interface TeamsAdapterConfig {\n /** Microsoft App ID */\n appId: string;\n /** Microsoft App Password */\n appPassword: string;\n /** Logger instance for error reporting */\n logger: Logger;\n /** Microsoft App Type */\n appType?: \"MultiTenant\" | \"SingleTenant\";\n /** Microsoft App Tenant ID */\n appTenantId?: string;\n /** Override bot username (optional) */\n userName?: string;\n}\n\n/** Teams-specific thread ID data */\nexport interface TeamsThreadId {\n conversationId: string;\n serviceUrl: string;\n replyToId?: string;\n}\n\n/** Teams channel context extracted from activity.channelData */\ninterface TeamsChannelContext {\n teamId: string;\n channelId: string;\n tenantId: string;\n}\n\nexport class TeamsAdapter implements Adapter<TeamsThreadId, unknown> {\n readonly name = \"teams\";\n readonly userName: string;\n readonly botUserId?: string;\n\n private botAdapter: ServerlessCloudAdapter;\n private graphClient: Client | null = null;\n private chat: ChatInstance | null = null;\n private logger: Logger;\n private formatConverter = new TeamsFormatConverter();\n private config: TeamsAdapterConfig;\n\n constructor(config: TeamsAdapterConfig) {\n this.config = config;\n this.logger = config.logger;\n this.userName = config.userName || \"bot\";\n\n if (config.appType === \"SingleTenant\" && !config.appTenantId) {\n throw new ValidationError(\n \"teams\",\n \"appTenantId is required for SingleTenant app type\",\n );\n }\n\n // Pass empty config object, credentials go via factory\n const auth = new ConfigurationBotFrameworkAuthentication({\n MicrosoftAppId: config.appId,\n MicrosoftAppPassword: config.appPassword,\n MicrosoftAppType: config.appType || \"MultiTenant\",\n MicrosoftAppTenantId:\n config.appType === \"SingleTenant\" ? config.appTenantId : undefined,\n });\n\n this.botAdapter = new ServerlessCloudAdapter(auth);\n\n // Initialize Microsoft Graph client for message history (requires tenant ID)\n if (config.appTenantId) {\n const credential = new ClientSecretCredential(\n config.appTenantId,\n config.appId,\n config.appPassword,\n );\n\n const authProvider = new TokenCredentialAuthenticationProvider(\n credential,\n {\n scopes: [\"https://graph.microsoft.com/.default\"],\n } as TokenCredentialAuthenticationProviderOptions,\n );\n\n this.graphClient = Client.initWithMiddleware({ authProvider });\n }\n }\n\n async initialize(chat: ChatInstance): Promise<void> {\n this.chat = chat;\n }\n\n async handleWebhook(\n request: Request,\n options?: WebhookOptions,\n ): Promise<Response> {\n const body = await request.text();\n this.logger.debug(\"Teams webhook raw body\", { body });\n\n let activity: Activity;\n try {\n activity = JSON.parse(body);\n } catch (e) {\n this.logger.error(\"Failed to parse request body\", { error: e });\n return new Response(\"Invalid JSON\", { status: 400 });\n }\n\n // Get the auth header for token validation\n const authHeader = request.headers.get(\"authorization\") || \"\";\n\n try {\n // Use handleActivity which takes the activity directly\n // instead of mocking Node.js req/res objects\n await this.botAdapter.handleActivity(\n authHeader,\n activity,\n async (context) => {\n await this.handleTurn(context, options);\n },\n );\n\n return new Response(JSON.stringify({}), {\n status: 200,\n headers: { \"Content-Type\": \"application/json\" },\n });\n } catch (error) {\n this.logger.error(\"Bot adapter process error\", { error });\n return new Response(JSON.stringify({ error: \"Internal error\" }), {\n status: 500,\n headers: { \"Content-Type\": \"application/json\" },\n });\n }\n }\n\n private async handleTurn(\n context: TurnContext,\n options?: WebhookOptions,\n ): Promise<void> {\n if (!this.chat) {\n this.logger.warn(\"Chat instance not initialized, ignoring event\");\n return;\n }\n\n const activity = context.activity;\n\n // Cache serviceUrl and tenantId for the user - needed for opening DMs later\n if (activity.from?.id && activity.serviceUrl) {\n const userId = activity.from.id;\n const channelData = activity.channelData as {\n tenant?: { id?: string };\n team?: { id?: string };\n channel?: { id?: string };\n };\n const tenantId = channelData?.tenant?.id;\n const ttl = 30 * 24 * 60 * 60 * 1000; // 30 days\n\n // Store serviceUrl and tenantId for DM creation\n this.chat\n .getState()\n .set(`teams:serviceUrl:${userId}`, activity.serviceUrl, ttl)\n .catch((err) => {\n this.logger.error(\"Failed to cache serviceUrl\", {\n userId,\n error: err,\n });\n });\n if (tenantId) {\n this.chat\n .getState()\n .set(`teams:tenantId:${userId}`, tenantId, ttl)\n .catch((err) => {\n this.logger.error(\"Failed to cache tenantId\", {\n userId,\n error: err,\n });\n });\n }\n\n // Cache team/channel context for proper message fetching in channel threads\n // This allows fetchMessages to use the channel-specific endpoint for thread filtering\n // The Graph API requires aadGroupId (GUID format), not the Teams thread-style ID\n // Note: The botbuilder types don't include aadGroupId, but it's present at runtime\n // aadGroupId is only available in installationUpdate/conversationUpdate events\n const team = channelData?.team as\n | { id?: string; aadGroupId?: string }\n | undefined;\n const teamAadGroupId = team?.aadGroupId;\n const teamThreadId = team?.id; // Thread-style ID like \"19:xxx@thread.tacv2\"\n const conversationId = activity.conversation?.id || \"\";\n const baseChannelId = conversationId.replace(/;messageid=\\d+/, \"\");\n\n if (teamAadGroupId && channelData?.channel?.id && tenantId) {\n // We have aadGroupId (from installationUpdate/conversationUpdate) - cache it\n const context: TeamsChannelContext = {\n teamId: teamAadGroupId, // Use aadGroupId (GUID) for Graph API\n channelId: channelData.channel.id,\n tenantId,\n };\n const contextJson = JSON.stringify(context);\n\n // Cache by conversation ID (channel)\n this.chat\n .getState()\n .set(`teams:channelContext:${baseChannelId}`, contextJson, ttl)\n .catch((err) => {\n this.logger.error(\"Failed to cache channel context\", {\n conversationId: baseChannelId,\n error: err,\n });\n });\n\n // Also cache by team thread-style ID for lookup from regular messages\n // (which don't have aadGroupId but do have team.id)\n if (teamThreadId) {\n this.chat\n .getState()\n .set(`teams:teamContext:${teamThreadId}`, contextJson, ttl)\n .catch((err) => {\n this.logger.error(\"Failed to cache team context\", {\n teamThreadId,\n error: err,\n });\n });\n }\n\n this.logger.info(\n \"Cached Teams team GUID from installation/update event\",\n {\n activityType: activity.type,\n conversationId: baseChannelId,\n teamThreadId,\n teamGuid: context.teamId,\n channelId: context.channelId,\n },\n );\n } else if (teamThreadId && channelData?.channel?.id && tenantId) {\n // Regular message event - no aadGroupId, but try to look up from previous cache\n const cachedTeamContext = await this.chat\n .getState()\n .get<string>(`teams:teamContext:${teamThreadId}`);\n\n if (cachedTeamContext) {\n // Found cached context from installation event - also cache by channel ID\n this.chat\n .getState()\n .set(\n `teams:channelContext:${baseChannelId}`,\n cachedTeamContext,\n ttl,\n )\n .catch((err) => {\n this.logger.error(\"Failed to cache channel context from team\", {\n conversationId: baseChannelId,\n error: err,\n });\n });\n this.logger.info(\"Using cached Teams team GUID for channel\", {\n conversationId: baseChannelId,\n teamThreadId,\n });\n } else {\n // No cached context - try to fetch team details via Bot Framework API\n // TeamsInfo.getTeamDetails() calls /v3/teams/{teamId} and returns aadGroupId\n try {\n const teamDetails = await TeamsInfo.getTeamDetails(context);\n if (teamDetails?.aadGroupId) {\n const fetchedContext: TeamsChannelContext = {\n teamId: teamDetails.aadGroupId,\n channelId: channelData.channel.id,\n tenantId,\n };\n const contextJson = JSON.stringify(fetchedContext);\n\n // Cache by conversation ID\n this.chat\n .getState()\n .set(`teams:channelContext:${baseChannelId}`, contextJson, ttl)\n .catch((err) => {\n this.logger.error(\"Failed to cache fetched channel context\", {\n conversationId: baseChannelId,\n error: err,\n });\n });\n\n // Also cache by team thread-style ID\n this.chat\n .getState()\n .set(`teams:teamContext:${teamThreadId}`, contextJson, ttl)\n .catch((err) => {\n this.logger.error(\"Failed to cache fetched team context\", {\n teamThreadId,\n error: err,\n });\n });\n\n this.logger.info(\n \"Fetched and cached Teams team GUID via TeamsInfo API\",\n {\n conversationId: baseChannelId,\n teamThreadId,\n teamGuid: teamDetails.aadGroupId,\n teamName: teamDetails.name,\n },\n );\n }\n } catch (error) {\n // TeamsInfo.getTeamDetails() only works in team scope\n this.logger.debug(\n \"Could not fetch team details (may not be a team scope)\",\n { teamThreadId, error },\n );\n }\n }\n }\n }\n\n // Handle message reactions\n if (activity.type === ActivityTypes.MessageReaction) {\n this.handleReactionActivity(activity, options);\n return;\n }\n\n // Handle adaptive card actions (button clicks)\n if (activity.type === ActivityTypes.Invoke) {\n await this.handleInvokeActivity(context, options);\n return;\n }\n\n // Only handle message activities\n if (activity.type !== ActivityTypes.Message) {\n this.logger.debug(\"Ignoring non-message activity\", {\n type: activity.type,\n });\n return;\n }\n\n // Check if this message activity is actually a button click (Action.Submit)\n // Teams sends Action.Submit as a message with value.actionId\n const actionValue = activity.value as\n | { actionId?: string; value?: string }\n | undefined;\n if (actionValue?.actionId) {\n this.handleMessageAction(activity, actionValue, options);\n return;\n }\n\n const threadId = this.encodeThreadId({\n conversationId: activity.conversation?.id || \"\",\n serviceUrl: activity.serviceUrl || \"\",\n replyToId: activity.replyToId,\n });\n\n // Let Chat class handle async processing and waitUntil\n this.chat.processMessage(\n this,\n threadId,\n this.parseTeamsMessage(activity, threadId),\n options,\n );\n }\n\n /**\n * Handle Action.Submit button clicks sent as message activities.\n * Teams sends these with type \"message\" and value.actionId.\n */\n private handleMessageAction(\n activity: Activity,\n actionValue: { actionId?: string; value?: string },\n options?: WebhookOptions,\n ): void {\n if (!this.chat || !actionValue.actionId) return;\n\n const threadId = this.encodeThreadId({\n conversationId: activity.conversation?.id || \"\",\n serviceUrl: activity.serviceUrl || \"\",\n });\n\n const actionEvent: Omit<ActionEvent, \"thread\"> & { adapter: TeamsAdapter } =\n {\n actionId: actionValue.actionId,\n value: actionValue.value,\n user: {\n userId: activity.from?.id || \"unknown\",\n userName: activity.from?.name || \"unknown\",\n fullName: activity.from?.name || \"unknown\",\n isBot: false,\n isMe: false,\n },\n messageId: activity.replyToId || activity.id || \"\",\n threadId,\n adapter: this,\n raw: activity,\n };\n\n this.logger.debug(\"Processing Teams message action (Action.Submit)\", {\n actionId: actionValue.actionId,\n value: actionValue.value,\n messageId: actionEvent.messageId,\n threadId,\n });\n\n this.chat.processAction(actionEvent, options);\n }\n\n /**\n * Handle invoke activities (adaptive card actions, etc.).\n */\n private async handleInvokeActivity(\n context: TurnContext,\n options?: WebhookOptions,\n ): Promise<void> {\n const activity = context.activity;\n\n // Handle adaptive card action invokes\n if (activity.name === \"adaptiveCard/action\") {\n await this.handleAdaptiveCardAction(context, activity, options);\n return;\n }\n\n this.logger.debug(\"Ignoring unsupported invoke\", {\n name: activity.name,\n });\n }\n\n /**\n * Handle adaptive card button clicks.\n * The action data is in activity.value with our { actionId, value } structure.\n */\n private async handleAdaptiveCardAction(\n context: TurnContext,\n activity: Activity,\n options?: WebhookOptions,\n ): Promise<void> {\n if (!this.chat) return;\n\n // Activity.value contains our action data\n const actionData = activity.value?.action?.data as\n | { actionId?: string; value?: string }\n | undefined;\n\n if (!actionData?.actionId) {\n this.logger.debug(\"Adaptive card action missing actionId\", {\n value: activity.value,\n });\n // Send acknowledgment response\n await context.sendActivity({\n type: ActivityTypes.InvokeResponse,\n value: { status: 200 },\n });\n return;\n }\n\n const threadId = this.encodeThreadId({\n conversationId: activity.conversation?.id || \"\",\n serviceUrl: activity.serviceUrl || \"\",\n });\n\n const actionEvent: Omit<ActionEvent, \"thread\"> & { adapter: TeamsAdapter } =\n {\n actionId: actionData.actionId,\n value: actionData.value,\n user: {\n userId: activity.from?.id || \"unknown\",\n userName: activity.from?.name || \"unknown\",\n fullName: activity.from?.name || \"unknown\",\n isBot: false,\n isMe: false,\n },\n messageId: activity.replyToId || activity.id || \"\",\n threadId,\n adapter: this,\n raw: activity,\n };\n\n this.logger.debug(\"Processing Teams adaptive card action\", {\n actionId: actionData.actionId,\n value: actionData.value,\n messageId: actionEvent.messageId,\n threadId,\n });\n\n this.chat.processAction(actionEvent, options);\n\n // Send acknowledgment response to prevent timeout\n await context.sendActivity({\n type: ActivityTypes.InvokeResponse,\n value: { status: 200 },\n });\n }\n\n /**\n * Handle Teams reaction events (reactionsAdded/reactionsRemoved).\n */\n private handleReactionActivity(\n activity: Activity,\n options?: WebhookOptions,\n ): void {\n if (!this.chat) return;\n\n // Extract the message ID from conversation ID\n // Format: \"19:xxx@thread.tacv2;messageid=1767297849909\"\n const conversationId = activity.conversation?.id || \"\";\n const messageIdMatch = conversationId.match(/messageid=(\\d+)/);\n const messageId = messageIdMatch?.[1] || activity.replyToId || \"\";\n\n // Build thread ID - KEEP the full conversation ID including ;messageid=XXX\n // This is required for Teams to reply in the correct thread\n const threadId = this.encodeThreadId({\n conversationId: conversationId,\n serviceUrl: activity.serviceUrl || \"\",\n });\n\n const user = {\n userId: activity.from?.id || \"unknown\",\n userName: activity.from?.name || \"unknown\",\n fullName: activity.from?.name,\n isBot: false,\n isMe: this.isMessageFromSelf(activity),\n };\n\n // Process added reactions\n const reactionsAdded = activity.reactionsAdded || [];\n for (const reaction of reactionsAdded) {\n const rawEmoji = reaction.type || \"\";\n const emojiValue = defaultEmojiResolver.fromTeams(rawEmoji);\n\n const event: Omit<ReactionEvent, \"adapter\" | \"thread\"> = {\n emoji: emojiValue,\n rawEmoji,\n added: true,\n user,\n messageId,\n threadId,\n raw: activity,\n };\n\n this.logger.debug(\"Processing Teams reaction added\", {\n emoji: emojiValue.name,\n rawEmoji,\n messageId,\n });\n\n this.chat.processReaction({ ...event, adapter: this }, options);\n }\n\n // Process removed reactions\n const reactionsRemoved = activity.reactionsRemoved || [];\n for (const reaction of reactionsRemoved) {\n const rawEmoji = reaction.type || \"\";\n const emojiValue = defaultEmojiResolver.fromTeams(rawEmoji);\n\n const event: Omit<ReactionEvent, \"adapter\" | \"thread\"> = {\n emoji: emojiValue,\n rawEmoji,\n added: false,\n user,\n messageId,\n threadId,\n raw: activity,\n };\n\n this.logger.debug(\"Processing Teams reaction removed\", {\n emoji: emojiValue.name,\n rawEmoji,\n messageId,\n });\n\n this.chat.processReaction({ ...event, adapter: this }, options);\n }\n }\n\n private parseTeamsMessage(\n activity: Activity,\n threadId: string,\n ): Message<unknown> {\n const text = activity.text || \"\";\n // Normalize mentions - format converter will convert <at>name</at> to @name\n const normalizedText = this.normalizeMentions(text, activity);\n\n const isMe = this.isMessageFromSelf(activity);\n\n return {\n id: activity.id || \"\",\n threadId,\n text: this.formatConverter.extractPlainText(normalizedText),\n formatted: this.formatConverter.toAst(normalizedText),\n raw: activity,\n author: {\n userId: activity.from?.id || \"unknown\",\n userName: activity.from?.name || \"unknown\",\n fullName: activity.from?.name || \"unknown\",\n isBot: activity.from?.role === \"bot\",\n isMe,\n },\n metadata: {\n dateSent: activity.timestamp\n ? new Date(activity.timestamp)\n : new Date(),\n edited: false,\n },\n attachments: (activity.attachments || [])\n .filter(\n (att) =>\n // Filter out adaptive cards (handled separately as cards, not attachments)\n att.contentType !== \"application/vnd.microsoft.card.adaptive\" &&\n // Filter out text/html without contentUrl - this is just the formatted\n // version of the message text, not an actual file attachment.\n // Real HTML file attachments would have a contentUrl.\n !(att.contentType === \"text/html\" && !att.contentUrl),\n )\n .map((att) => this.createAttachment(att)),\n };\n }\n\n /**\n * Create an Attachment object from a Teams attachment.\n */\n private createAttachment(att: {\n contentType?: string;\n contentUrl?: string;\n name?: string;\n }): Attachment {\n const url = att.contentUrl;\n\n // Determine type based on contentType\n let type: Attachment[\"type\"] = \"file\";\n if (att.contentType?.startsWith(\"image/\")) {\n type = \"image\";\n } else if (att.contentType?.startsWith(\"video/\")) {\n type = \"video\";\n } else if (att.contentType?.startsWith(\"audio/\")) {\n type = \"audio\";\n }\n\n return {\n type,\n url,\n name: att.name,\n mimeType: att.contentType,\n fetchData: url\n ? async () => {\n const response = await fetch(url);\n if (!response.ok) {\n throw new NetworkError(\n \"teams\",\n `Failed to fetch file: ${response.status} ${response.statusText}`,\n );\n }\n const arrayBuffer = await response.arrayBuffer();\n return Buffer.from(arrayBuffer);\n }\n : undefined,\n };\n }\n\n private normalizeMentions(text: string, _activity: Activity): string {\n // Don't strip mentions - the format converter will convert <at>name</at> to @name\n // Just trim any leading/trailing whitespace that might result from mention placement\n return text.trim();\n }\n\n async postMessage(\n threadId: string,\n message: AdapterPostableMessage,\n ): Promise<RawMessage<unknown>> {\n const { conversationId, serviceUrl } = this.decodeThreadId(threadId);\n\n // Check for files to upload\n const files = extractFiles(message);\n const fileAttachments =\n files.length > 0 ? await this.filesToAttachments(files) : [];\n\n // Check if message contains a card\n const card = extractCard(message);\n let activity: Partial<Activity>;\n\n if (card) {\n // Render card as Adaptive Card\n const adaptiveCard = cardToAdaptiveCard(card);\n\n activity = {\n type: ActivityTypes.Message,\n // Don't include text - Teams shows both text and card if text is present\n attachments: [\n {\n contentType: \"application/vnd.microsoft.card.adaptive\",\n content: adaptiveCard,\n },\n ...fileAttachments,\n ],\n };\n\n this.logger.debug(\"Teams API: sendActivity (adaptive card)\", {\n conversationId,\n serviceUrl,\n fileCount: fileAttachments.length,\n });\n } else {\n // Regular text message\n const text = convertEmojiPlaceholders(\n this.formatConverter.renderPostable(message),\n \"teams\",\n );\n\n activity = {\n type: ActivityTypes.Message,\n text,\n textFormat: \"markdown\",\n attachments: fileAttachments.length > 0 ? fileAttachments : undefined,\n };\n\n this.logger.debug(\"Teams API: sendActivity (message)\", {\n conversationId,\n serviceUrl,\n textLength: text.length,\n fileCount: fileAttachments.length,\n });\n }\n\n // Use the adapter to send the message\n const conversationReference = {\n channelId: \"msteams\",\n serviceUrl,\n conversation: { id: conversationId },\n };\n\n let messageId = \"\";\n\n try {\n await this.botAdapter.continueConversationAsync(\n this.config.appId,\n conversationReference as Partial<ConversationReference>,\n async (context) => {\n const response = await context.sendActivity(activity);\n messageId = response?.id || \"\";\n },\n );\n } catch (error) {\n this.logger.error(\"Teams API: sendActivity failed\", {\n conversationId,\n error,\n });\n this.handleTeamsError(error, \"postMessage\");\n }\n\n this.logger.debug(\"Teams API: sendActivity response\", { messageId });\n\n return {\n id: messageId,\n threadId,\n raw: activity,\n };\n }\n\n /**\n * Convert files to Teams attachments.\n * Uses inline data URIs for small files.\n */\n private async filesToAttachments(\n files: FileUpload[],\n ): Promise<Array<{ contentType: string; contentUrl: string; name: string }>> {\n const attachments: Array<{\n contentType: string;\n contentUrl: string;\n name: string;\n }> = [];\n\n for (const file of files) {\n // Convert data to Buffer using shared utility\n const buffer = await toBuffer(file.data, {\n platform: \"teams\",\n throwOnUnsupported: false,\n });\n if (!buffer) {\n continue;\n }\n\n // Create data URI using shared utility\n const mimeType = file.mimeType || \"application/octet-stream\";\n const dataUri = bufferToDataUri(buffer, mimeType);\n\n attachments.push({\n contentType: mimeType,\n contentUrl: dataUri,\n name: file.filename,\n });\n }\n\n return attachments;\n }\n\n async editMessage(\n threadId: string,\n messageId: string,\n message: AdapterPostableMessage,\n ): Promise<RawMessage<unknown>> {\n const { conversationId, serviceUrl } = this.decodeThreadId(threadId);\n\n // Check if message contains a card\n const card = extractCard(message);\n let activity: Partial<Activity>;\n\n if (card) {\n // Render card as Adaptive Card\n const adaptiveCard = cardToAdaptiveCard(card);\n\n activity = {\n id: messageId,\n type: ActivityTypes.Message,\n // Don't include text - Teams shows both text and card if text is present\n attachments: [\n {\n contentType: \"application/vnd.microsoft.card.adaptive\",\n content: adaptiveCard,\n },\n ],\n };\n\n this.logger.debug(\"Teams API: updateActivity (adaptive card)\", {\n conversationId,\n messageId,\n });\n } else {\n // Regular text message\n const text = convertEmojiPlaceholders(\n this.formatConverter.renderPostable(message),\n \"teams\",\n );\n\n activity = {\n id: messageId,\n type: ActivityTypes.Message,\n text,\n textFormat: \"markdown\",\n };\n\n this.logger.debug(\"Teams API: updateActivity\", {\n conversationId,\n messageId,\n textLength: text.length,\n });\n }\n\n const conversationReference = {\n channelId: \"msteams\",\n serviceUrl,\n conversation: { id: conversationId },\n };\n\n try {\n await this.botAdapter.continueConversationAsync(\n this.config.appId,\n conversationReference as Partial<ConversationReference>,\n async (context) => {\n await context.updateActivity(activity);\n },\n );\n } catch (error) {\n this.logger.error(\"Teams API: updateActivity failed\", {\n conversationId,\n messageId,\n error,\n });\n this.handleTeamsError(error, \"editMessage\");\n }\n\n this.logger.debug(\"Teams API: updateActivity response\", { ok: true });\n\n return {\n id: messageId,\n threadId,\n raw: activity,\n };\n }\n\n async deleteMessage(threadId: string, messageId: string): Promise<void> {\n const { conversationId, serviceUrl } = this.decodeThreadId(threadId);\n\n const conversationReference = {\n channelId: \"msteams\",\n serviceUrl,\n conversation: { id: conversationId },\n };\n\n this.logger.debug(\"Teams API: deleteActivity\", {\n conversationId,\n messageId,\n });\n\n try {\n await this.botAdapter.continueConversationAsync(\n this.config.appId,\n conversationReference as Partial<ConversationReference>,\n async (context) => {\n await context.deleteActivity(messageId);\n },\n );\n } catch (error) {\n this.logger.error(\"Teams API: deleteActivity failed\", {\n conversationId,\n messageId,\n error,\n });\n this.handleTeamsError(error, \"deleteMessage\");\n }\n\n this.logger.debug(\"Teams API: deleteActivity response\", { ok: true });\n }\n\n async addReaction(\n _threadId: string,\n _messageId: string,\n _emoji: EmojiValue | string,\n ): Promise<void> {\n throw new NotImplementedError(\n \"Teams Bot Framework does not expose reaction APIs\",\n \"addReaction\",\n );\n }\n\n async removeReaction(\n _threadId: string,\n _messageId: string,\n _emoji: EmojiValue | string,\n ): Promise<void> {\n throw new NotImplementedError(\n \"Teams Bot Framework does not expose reaction APIs\",\n \"removeReaction\",\n );\n }\n\n async startTyping(threadId: string): Promise<void> {\n const { conversationId, serviceUrl } = this.decodeThreadId(threadId);\n\n const conversationReference = {\n channelId: \"msteams\",\n serviceUrl,\n conversation: { id: conversationId },\n };\n\n this.logger.debug(\"Teams API: sendActivity (typing)\", { conversationId });\n\n try {\n await this.botAdapter.continueConversationAsync(\n this.config.appId,\n conversationReference as Partial<ConversationReference>,\n async (context) => {\n await context.sendActivity({ type: ActivityTypes.Typing });\n },\n );\n } catch (error) {\n this.logger.error(\"Teams API: sendActivity (typing) failed\", {\n conversationId,\n error,\n });\n this.handleTeamsError(error, \"startTyping\");\n }\n\n this.logger.debug(\"Teams API: sendActivity (typing) response\", {\n ok: true,\n });\n }\n\n /**\n * Open a direct message conversation with a user.\n * Returns a thread ID that can be used to post messages.\n *\n * The serviceUrl and tenantId are automatically resolved from cached user interactions.\n * If no cached values are found, defaults are used (which may not work for all tenants).\n */\n async openDM(userId: string): Promise<string> {\n // Look up cached serviceUrl and tenantId for this user from state\n const cachedServiceUrl = await this.chat\n ?.getState()\n .get<string>(`teams:serviceUrl:${userId}`);\n const cachedTenantId = await this.chat\n ?.getState()\n .get<string>(`teams:tenantId:${userId}`);\n\n const serviceUrl =\n cachedServiceUrl || \"https://smba.trafficmanager.net/teams/\";\n // Use cached tenant ID, config tenant ID, or undefined (will fail for multi-tenant)\n const tenantId = cachedTenantId || this.config.appTenantId;\n\n this.logger.debug(\"Teams: creating 1:1 conversation\", {\n userId,\n serviceUrl,\n tenantId,\n cachedServiceUrl: !!cachedServiceUrl,\n cachedTenantId: !!cachedTenantId,\n });\n\n if (!tenantId) {\n throw new ValidationError(\n \"teams\",\n \"Cannot open DM: tenant ID not found. User must interact with the bot first (via @mention) to cache their tenant ID.\",\n );\n }\n\n let conversationId = \"\";\n\n // Create the 1:1 conversation using createConversationAsync\n // The conversation ID is captured from within the callback, not from the return value\n // biome-ignore lint/suspicious/noExplicitAny: BotBuilder types are incomplete\n await (this.botAdapter as any).createConversationAsync(\n this.config.appId,\n \"msteams\",\n serviceUrl,\n \"\", // empty audience\n {\n isGroup: false,\n bot: { id: this.config.appId, name: this.userName },\n members: [{ id: userId }],\n tenantId,\n channelData: {\n tenant: { id: tenantId },\n },\n },\n async (turnContext: TurnContext) => {\n // Capture the conversation ID from the new context\n conversationId = turnContext?.activity?.conversation?.id || \"\";\n this.logger.debug(\"Teams: conversation created in callback\", {\n conversationId,\n activityId: turnContext?.activity?.id,\n });\n },\n );\n\n if (!conversationId) {\n throw new NetworkError(\n \"teams\",\n \"Failed to create 1:1 conversation - no ID returned\",\n );\n }\n\n this.logger.debug(\"Teams: 1:1 conversation created\", { conversationId });\n\n return this.encodeThreadId({\n conversationId,\n serviceUrl,\n });\n }\n\n async fetchMessages(\n threadId: string,\n options: FetchOptions = {},\n ): Promise<FetchResult<unknown>> {\n if (!this.graphClient) {\n throw new NotImplementedError(\n \"Teams fetchMessages requires appTenantId to be configured for Microsoft Graph API access.\",\n \"fetchMessages\",\n );\n }\n\n const { conversationId } = this.decodeThreadId(threadId);\n const limit = options.limit || 50;\n const cursor = options.cursor;\n const direction = options.direction ?? \"backward\";\n\n // Extract message ID for thread filtering (format: \"19:xxx@thread.tacv2;messageid=123456\")\n const messageIdMatch = conversationId.match(/;messageid=(\\d+)/);\n const threadMessageId = messageIdMatch?.[1];\n\n // Strip ;messageid= from conversation ID\n const baseConversationId = conversationId.replace(/;messageid=\\d+/, \"\");\n\n // Try to get cached channel context for proper thread-level message fetching\n let channelContext: TeamsChannelContext | null = null;\n if (threadMessageId && this.chat) {\n const cachedContext = await this.chat\n .getState()\n .get<string>(`teams:channelContext:${baseConversationId}`);\n if (cachedContext) {\n try {\n channelContext = JSON.parse(cachedContext) as TeamsChannelContext;\n } catch {\n // Invalid cached data, ignore\n }\n }\n\n // Note: Team GUID is cached during webhook handling via TeamsInfo.getTeamDetails()\n // If no cached context, we'll fall back to the chat endpoint (less accurate for channels)\n }\n\n try {\n this.logger.debug(\"Teams Graph API: fetching messages\", {\n conversationId: baseConversationId,\n threadMessageId,\n hasChannelContext: !!channelContext,\n limit,\n cursor,\n direction,\n });\n\n // If we have channel context and a thread message ID, use the channel replies endpoint\n // This gives us proper thread-level filtering instead of all messages in the channel\n if (channelContext && threadMessageId) {\n return this.fetchChannelThreadMessages(\n channelContext,\n threadMessageId,\n threadId,\n options,\n );\n }\n\n // Teams conversation IDs:\n // - Channels: \"19:xxx@thread.tacv2\"\n // - Group chats: \"19:xxx@thread.v2\"\n // - 1:1 chats: other formats (e.g., \"a]xxx\", \"8:orgid:xxx\")\n // For Graph API, we use /chats/{chat-id}/messages for all chat types\n\n // Note: Teams Graph API only supports orderby(\"createdDateTime desc\")\n // Ascending order is not supported, so we work around this limitation.\n // Also, max page size is 50 messages per request.\n\n let graphMessages: GraphChatMessage[];\n let hasMoreMessages = false;\n\n if (direction === \"forward\") {\n // Forward direction: need to fetch ALL messages to find the oldest ones\n // since API only supports descending order. Paginate with max 50 per request.\n const allMessages: GraphChatMessage[] = [];\n let nextLink: string | undefined;\n const apiUrl = `/chats/${encodeURIComponent(baseConversationId)}/messages`;\n\n do {\n const request = nextLink\n ? this.graphClient.api(nextLink)\n : this.graphClient\n .api(apiUrl)\n .top(50) // Max allowed by Teams API\n .orderby(\"createdDateTime desc\");\n\n const response = await request.get();\n const pageMessages = (response.value || []) as GraphChatMessage[];\n allMessages.push(...pageMessages);\n nextLink = response[\"@odata.nextLink\"];\n } while (nextLink);\n\n // Reverse to get chronological order (oldest first)\n allMessages.reverse();\n\n // Find starting position based on cursor (cursor is a timestamp)\n let startIndex = 0;\n if (cursor) {\n startIndex = allMessages.findIndex(\n (msg) => msg.createdDateTime && msg.createdDateTime > cursor,\n );\n if (startIndex === -1) startIndex = allMessages.length;\n }\n\n // Check if there are more messages beyond our slice\n hasMoreMessages = startIndex + limit < allMessages.length;\n // Take only the requested limit\n graphMessages = allMessages.slice(startIndex, startIndex + limit);\n } else {\n // Backward direction: simple pagination\n let request = this.graphClient\n .api(`/chats/${encodeURIComponent(baseConversationId)}/messages`)\n .top(limit)\n .orderby(\"createdDateTime desc\");\n\n if (cursor) {\n // Get messages older than cursor\n request = request.filter(`createdDateTime lt ${cursor}`);\n }\n\n const response = await request.get();\n graphMessages = (response.value || []) as GraphChatMessage[];\n\n // API returns newest first, reverse to get chronological order\n graphMessages.reverse();\n\n // We have more if we got a full page\n hasMoreMessages = graphMessages.length >= limit;\n }\n\n // For group chats (non-channel), filter to only messages from the \"thread\" onwards.\n // Teams group chats don't have real threading - the messageid in the conversation ID\n // is just UI context. We filter by message ID (which is a timestamp) to simulate threading.\n if (threadMessageId && !channelContext) {\n graphMessages = graphMessages.filter((msg) => {\n // Include messages with ID >= thread message ID (IDs are timestamps)\n return msg.id && msg.id >= threadMessageId;\n });\n this.logger.debug(\"Filtered group chat messages to thread\", {\n threadMessageId,\n filteredCount: graphMessages.length,\n });\n }\n\n this.logger.debug(\"Teams Graph API: fetched messages\", {\n count: graphMessages.length,\n direction,\n hasMoreMessages,\n });\n\n const messages = graphMessages.map((msg: GraphChatMessage) => {\n const isFromBot =\n msg.from?.application?.id === this.config.appId ||\n msg.from?.user?.id === this.config.appId;\n\n return {\n id: msg.id,\n threadId,\n text: this.extractTextFromGraphMessage(msg),\n formatted: this.formatConverter.toAst(\n this.extractTextFromGraphMessage(msg),\n ),\n raw: msg,\n author: {\n userId:\n msg.from?.user?.id || msg.from?.application?.id || \"unknown\",\n userName:\n msg.from?.user?.displayName ||\n msg.from?.application?.displayName ||\n \"unknown\",\n fullName:\n msg.from?.user?.displayName ||\n msg.from?.application?.displayName ||\n \"unknown\",\n isBot: !!msg.from?.application,\n isMe: isFromBot,\n },\n metadata: {\n dateSent: msg.createdDateTime\n ? new Date(msg.createdDateTime)\n : new Date(),\n edited: !!msg.lastModifiedDateTime,\n },\n attachments: this.extractAttachmentsFromGraphMessage(msg),\n };\n });\n\n // Determine nextCursor based on direction\n let nextCursor: string | undefined;\n if (hasMoreMessages && graphMessages.length > 0) {\n if (direction === \"forward\") {\n // Forward: use the newest message's timestamp (last in returned slice)\n const lastMsg = graphMessages[graphMessages.length - 1];\n if (lastMsg?.createdDateTime) {\n nextCursor = lastMsg.createdDateTime;\n }\n } else {\n // Backward: use the oldest message's timestamp (first in returned array)\n const oldestMsg = graphMessages[0];\n if (oldestMsg?.createdDateTime) {\n nextCursor = oldestMsg.createdDateTime;\n }\n }\n }\n\n return { messages, nextCursor };\n } catch (error) {\n this.logger.error(\"Teams Graph API: fetchMessages error\", { error });\n\n // Check if it's a permission error\n if (error instanceof Error && error.message?.includes(\"403\")) {\n throw new NotImplementedError(\n \"Teams fetchMessages requires one of these Azure AD app permissions: ChatMessage.Read.Chat, Chat.Read.All, or Chat.Read.WhereInstalled\",\n \"fetchMessages\",\n );\n }\n\n throw error;\n }\n }\n\n /**\n * Fetch messages from a Teams channel thread using the channel-specific Graph API endpoint.\n * This provides proper thread-level filtering by fetching only replies to a specific message.\n *\n * Endpoint: GET /teams/{team-id}/channels/{channel-id}/messages/{message-id}/replies\n */\n private async fetchChannelThreadMessages(\n context: TeamsChannelContext,\n threadMessageId: string,\n threadId: string,\n options: FetchOptions,\n ): Promise<FetchResult<unknown>> {\n const limit = options.limit || 50;\n const cursor = options.cursor;\n const direction = options.direction ?? \"backward\";\n\n this.logger.debug(\"Teams Graph API: fetching channel thread messages\", {\n teamId: context.teamId,\n channelId: context.channelId,\n threadMessageId,\n limit,\n cursor,\n direction,\n });\n\n // Build the endpoint URLs:\n // Parent message: /teams/{team-id}/channels/{channel-id}/messages/{message-id}\n // Replies: /teams/{team-id}/channels/{channel-id}/messages/{message-id}/replies\n const parentUrl = `/teams/${encodeURIComponent(context.teamId)}/channels/${encodeURIComponent(context.channelId)}/messages/${encodeURIComponent(threadMessageId)}`;\n const repliesUrl = `${parentUrl}/replies`;\n\n const graphClient = this.graphClient;\n if (!graphClient) {\n throw new AuthenticationError(\"teams\", \"Graph client not initialized\");\n }\n\n // Fetch the parent message (the original message that started the thread)\n let parentMessage: GraphChatMessage | null = null;\n try {\n parentMessage = (await graphClient\n .api(parentUrl)\n .get()) as GraphChatMessage;\n } catch (err) {\n this.logger.warn(\"Failed to fetch parent message\", {\n threadMessageId,\n err,\n });\n }\n\n let graphMessages: GraphChatMessage[];\n let hasMoreMessages = false;\n\n if (direction === \"forward\") {\n // Forward direction: fetch all replies and paginate in chronological order (oldest first)\n // Graph API returns messages in descending order (newest first), so we must reverse\n const allReplies: GraphChatMessage[] = [];\n let nextLink: string | undefined;\n\n do {\n const request = nextLink\n ? graphClient.api(nextLink)\n : graphClient.api(repliesUrl).top(50);\n\n const response = await request.get();\n const pageMessages = (response.value || []) as GraphChatMessage[];\n allReplies.push(...pageMessages);\n nextLink = response[\"@odata.nextLink\"];\n } while (nextLink);\n\n // Reverse replies to get chronological order (oldest first)\n allReplies.reverse();\n\n // Prepend parent message (it's the oldest - started the thread)\n const allMessages = parentMessage\n ? [parentMessage, ...allReplies]\n : allReplies;\n\n // Find starting position based on cursor\n let startIndex = 0;\n if (cursor) {\n startIndex = allMessages.findIndex(\n (msg) => msg.createdDateTime && msg.createdDateTime > cursor,\n );\n if (startIndex === -1) startIndex = allMessages.length;\n }\n\n hasMoreMessages = startIndex + limit < allMessages.length;\n graphMessages = allMessages.slice(startIndex, startIndex + limit);\n } else {\n // Backward direction: return most recent messages in chronological order\n // Graph API returns messages in descending order (newest first)\n const allReplies: GraphChatMessage[] = [];\n let nextLink: string | undefined;\n\n do {\n const request = nextLink\n ? graphClient.api(nextLink)\n : graphClient.api(repliesUrl).top(50);\n\n const response = await request.get();\n const pageMessages = (response.value || []) as GraphChatMessage[];\n allReplies.push(...pageMessages);\n nextLink = response[\"@odata.nextLink\"];\n } while (nextLink);\n\n // Reverse replies to get chronological order (oldest first)\n allReplies.reverse();\n\n // Prepend parent message (it's the oldest - started the thread)\n const allMessages = parentMessage\n ? [parentMessage, ...allReplies]\n : allReplies;\n\n if (cursor) {\n // Find position of cursor (cursor is timestamp of the oldest message in previous batch)\n // We want messages OLDER than cursor (earlier in chronological order)\n const cursorIndex = allMessages.findIndex(\n (msg) => msg.createdDateTime && msg.createdDateTime >= cursor,\n );\n if (cursorIndex > 0) {\n // Take messages before the cursor position\n const sliceStart = Math.max(0, cursorIndex - limit);\n graphMessages = allMessages.slice(sliceStart, cursorIndex);\n hasMoreMessages = sliceStart > 0;\n } else {\n // Cursor not found or at start - take the most recent (end of array)\n graphMessages = allMessages.slice(-limit);\n hasMoreMessages = allMessages.length > limit;\n }\n } else {\n // No cursor - get the most recent messages (end of chronological array)\n graphMessages = allMessages.slice(-limit);\n hasMoreMessages = allMessages.length > limit;\n }\n }\n\n this.logger.debug(\"Teams Graph API: fetched channel thread messages\", {\n count: graphMessages.length,\n direction,\n hasMoreMessages,\n });\n\n const messages = graphMessages.map((msg: GraphChatMessage) => {\n const isFromBot =\n msg.from?.application?.id === this.config.appId ||\n msg.from?.user?.id === this.config.appId;\n\n return {\n id: msg.id,\n threadId,\n text: this.extractTextFromGraphMessage(msg),\n formatted: this.formatConverter.toAst(\n this.extractTextFromGraphMessage(msg),\n ),\n raw: msg,\n author: {\n userId: msg.from?.user?.id || msg.from?.application?.id || \"unknown\",\n userName:\n msg.from?.user?.displayName ||\n msg.from?.application?.displayName ||\n \"unknown\",\n fullName:\n msg.from?.user?.displayName ||\n msg.from?.application?.displayName ||\n \"unknown\",\n isBot: !!msg.from?.application,\n isMe: isFromBot,\n },\n metadata: {\n dateSent: msg.createdDateTime\n ? new Date(msg.createdDateTime)\n : new Date(),\n edited: !!msg.lastModifiedDateTime,\n },\n attachments: this.extractAttachmentsFromGraphMessage(msg),\n };\n });\n\n // Determine nextCursor\n let nextCursor: string | undefined;\n if (hasMoreMessages && graphMessages.length > 0) {\n if (direction === \"forward\") {\n const lastMsg = graphMessages[graphMessages.length - 1];\n if (lastMsg?.createdDateTime) {\n nextCursor = lastMsg.createdDateTime;\n }\n } else {\n const oldestMsg = graphMessages[0];\n if (oldestMsg?.createdDateTime) {\n nextCursor = oldestMsg.createdDateTime;\n }\n }\n }\n\n return { messages, nextCursor };\n }\n\n /**\n * Extract plain text from a Graph API message.\n */\n private extractTextFromGraphMessage(msg: GraphChatMessage): string {\n // body.content contains the message text (HTML or text depending on contentType)\n if (msg.body?.contentType === \"text\") {\n return msg.body.content || \"\";\n }\n\n // For HTML content, strip tags (basic implementation)\n let text = \"\";\n if (msg.body?.content) {\n text = msg.body.content.replace(/<[^>]*>/g, \"\").trim();\n }\n\n // If text is empty but message has adaptive card attachments, try to extract card title\n if (!text && msg.attachments?.length) {\n for (const att of msg.attachments) {\n if (att.contentType === \"application/vnd.microsoft.card.adaptive\") {\n try {\n const card = JSON.parse(att.content || \"{}\");\n // Look for title in common locations\n const title = this.extractCardTitle(card);\n if (title) {\n return title;\n }\n return \"[Card]\";\n } catch {\n return \"[Card]\";\n }\n }\n }\n }\n\n return text;\n }\n\n /**\n * Extract a title/summary from an Adaptive Card structure.\n */\n private extractCardTitle(card: unknown): string | null {\n if (!card || typeof card !== \"object\") return null;\n\n const cardObj = card as Record<string, unknown>;\n\n // Check for body array and find first TextBlock with large/bolder style (likely title)\n if (Array.isArray(cardObj.body)) {\n for (const element of cardObj.body) {\n if (\n element &&\n typeof element === \"object\" &&\n (element as Record<string, unknown>).type === \"TextBlock\"\n ) {\n const textBlock = element as Record<string, unknown>;\n // Title blocks often have weight: \"bolder\" or size: \"large\"\n if (\n textBlock.weight === \"bolder\" ||\n textBlock.size === \"large\" ||\n textBlock.size === \"extraLarge\"\n ) {\n const text = textBlock.text;\n if (typeof text === \"string\") {\n return text;\n }\n }\n }\n }\n // Fallback: just get first TextBlock's text\n for (const element of cardObj.body) {\n if (\n element &&\n typeof element === \"object\" &&\n (element as Record<string, unknown>).type === \"TextBlock\"\n ) {\n const text = (element as Record<string, unknown>).text;\n if (typeof text === \"string\") {\n return text;\n }\n }\n }\n }\n\n return null;\n }\n\n /**\n * Extract attachments from a Graph API message.\n */\n private extractAttachmentsFromGraphMessage(\n msg: GraphChatMessage,\n ): Attachment[] {\n if (!msg.attachments?.length) {\n return [];\n }\n\n return msg.attachments.map((att) => ({\n type: att.contentType?.includes(\"image\") ? \"image\" : \"file\",\n name: att.name || undefined,\n url: att.contentUrl || undefined,\n mimeType: att.contentType || undefined,\n }));\n }\n\n async fetchThread(threadId: string): Promise<ThreadInfo> {\n const { conversationId } = this.decodeThreadId(threadId);\n\n return {\n id: threadId,\n channelId: conversationId,\n metadata: {},\n };\n }\n\n encodeThreadId(platformData: TeamsThreadId): string {\n // Base64 encode both since conversationId and serviceUrl can contain special characters\n const encodedConversationId = Buffer.from(\n platformData.conversationId,\n ).toString(\"base64url\");\n const encodedServiceUrl = Buffer.from(platformData.serviceUrl).toString(\n \"base64url\",\n );\n return `teams:${encodedConversationId}:${encodedServiceUrl}`;\n }\n\n /**\n * Check if a thread is a direct message conversation.\n * Teams DMs have conversation IDs that don't start with \"19:\" (which is for groups/channels).\n */\n isDM(threadId: string): boolean {\n const { conversationId } = this.decodeThreadId(threadId);\n // Group chats and channels start with \"19:\", DMs don't\n return !conversationId.startsWith(\"19:\");\n }\n\n decodeThreadId(threadId: string): TeamsThreadId {\n const parts = threadId.split(\":\");\n if (parts.length !== 3 || parts[0] !== \"teams\") {\n throw new ValidationError(\n \"teams\",\n `Invalid Teams thread ID: ${threadId}`,\n );\n }\n const conversationId = Buffer.from(\n parts[1] as string,\n \"base64url\",\n ).toString(\"utf-8\");\n const serviceUrl = Buffer.from(parts[2] as string, \"base64url\").toString(\n \"utf-8\",\n );\n return { conversationId, serviceUrl };\n }\n\n parseMessage(raw: unknown): Message<unknown> {\n const activity = raw as Activity;\n const threadId = this.encodeThreadId({\n conversationId: activity.conversation?.id || \"\",\n serviceUrl: activity.serviceUrl || \"\",\n });\n return this.parseTeamsMessage(activity, threadId);\n }\n\n /**\n * Check if a Teams activity is from this bot.\n *\n * Teams bot IDs can appear in different formats:\n * - Just the app ID: \"abc123-def456-...\"\n * - With prefix: \"28:abc123-def456-...\"\n *\n * We check both exact match and suffix match (after colon delimiter)\n * to handle all formats safely.\n */\n private isMessageFromSelf(activity: Activity): boolean {\n const fromId = activity.from?.id;\n if (!fromId || !this.config.appId) {\n return false;\n }\n\n // Exact match (bot ID is just the app ID)\n if (fromId === this.config.appId) {\n return true;\n }\n\n // Teams format: \"28:{appId}\" or similar prefix patterns\n // Check if it ends with our appId after a colon delimiter\n if (fromId.endsWith(`:${this.config.appId}`)) {\n return true;\n }\n\n return false;\n }\n\n renderFormatted(content: FormattedContent): string {\n return this.formatConverter.fromAst(content);\n }\n\n /**\n * Convert Teams/BotBuilder errors to standardized AdapterError types.\n */\n private handleTeamsError(error: unknown, operation: string): never {\n // Handle BotBuilder errors with status codes\n if (error && typeof error === \"object\") {\n const err = error as Record<string, unknown>;\n\n // Check for HTTP status code\n const statusCode =\n (err.statusCode as number) ||\n (err.status as number) ||\n (err.code as number);\n\n if (statusCode === 401 || statusCode === 403) {\n throw new AuthenticationError(\n \"teams\",\n `Authentication failed for ${operation}: ${err.message || \"unauthorized\"}`,\n );\n }\n\n if (statusCode === 404) {\n throw new NetworkError(\n \"teams\",\n `Resource not found during ${operation}: conversation or message may no longer exist`,\n error instanceof Error ? error : undefined,\n );\n }\n\n if (statusCode === 429) {\n const retryAfter =\n typeof err.retryAfter === \"number\" ? err.retryAfter : undefined;\n throw new AdapterRateLimitError(\"teams\", retryAfter);\n }\n\n // Permission errors\n if (\n statusCode === 403 ||\n (err.message &&\n typeof err.message === \"string\" &&\n err.message.toLowerCase().includes(\"permission\"))\n ) {\n throw new PermissionError(\"teams\", operation);\n }\n\n // Generic error with message\n if (err.message && typeof err.message === \"string\") {\n throw new NetworkError(\n \"teams\",\n `Teams API error during ${operation}: ${err.message}`,\n error instanceof Error ? error : undefined,\n );\n }\n }\n\n // Fallback for unknown error types\n throw new NetworkError(\n \"teams\",\n `Teams API error during ${operation}: ${String(error)}`,\n error instanceof Error ? error : undefined,\n );\n }\n}\n\nexport function createTeamsAdapter(config: TeamsAdapterConfig): TeamsAdapter {\n return new TeamsAdapter(config);\n}\n\n// Re-export card converter for advanced use\nexport { cardToAdaptiveCard, cardToFallbackText } from \"./cards\";\nexport { TeamsFormatConverter } from \"./markdown\";\n","/**\n * Teams Adaptive Card converter for cross-platform cards.\n *\n * Converts CardElement to Microsoft Adaptive Cards format.\n * @see https://adaptivecards.io/\n */\n\nimport {\n createEmojiConverter,\n mapButtonStyle,\n cardToFallbackText as sharedCardToFallbackText,\n} from \"@chat-adapter/shared\";\nimport type {\n ActionsElement,\n ButtonElement,\n CardChild,\n CardElement,\n DividerElement,\n FieldsElement,\n ImageElement,\n SectionElement,\n TextElement,\n} from \"chat\";\n\n/**\n * Convert emoji placeholders in text to Teams format.\n */\nconst convertEmoji = createEmojiConverter(\"teams\");\n\n// Adaptive Card types (simplified)\nexport interface AdaptiveCard {\n type: \"AdaptiveCard\";\n $schema: string;\n version: string;\n body: AdaptiveCardElement[];\n actions?: AdaptiveCardAction[];\n}\n\nexport interface AdaptiveCardElement {\n type: string;\n [key: string]: unknown;\n}\n\nexport interface AdaptiveCardAction {\n type: string;\n title: string;\n data?: Record<string, unknown>;\n style?: string;\n}\n\nconst ADAPTIVE_CARD_SCHEMA =\n \"http://adaptivecards.io/schemas/adaptive-card.json\";\nconst ADAPTIVE_CARD_VERSION = \"1.4\";\n\n/**\n * Convert a CardElement to a Teams Adaptive Card.\n */\nexport function cardToAdaptiveCard(card: CardElement): AdaptiveCard {\n const body: AdaptiveCardElement[] = [];\n const actions: AdaptiveCardAction[] = [];\n\n // Add title as TextBlock\n if (card.title) {\n body.push({\n type: \"TextBlock\",\n text: convertEmoji(card.title),\n weight: \"bolder\",\n size: \"large\",\n wrap: true,\n });\n }\n\n // Add subtitle as TextBlock\n if (card.subtitle) {\n body.push({\n type: \"TextBlock\",\n text: convertEmoji(card.subtitle),\n isSubtle: true,\n wrap: true,\n });\n }\n\n // Add header image if present\n if (card.imageUrl) {\n body.push({\n type: \"Image\",\n url: card.imageUrl,\n size: \"stretch\",\n });\n }\n\n // Convert children\n for (const child of card.children) {\n const result = convertChildToAdaptive(child);\n body.push(...result.elements);\n actions.push(...result.actions);\n }\n\n const adaptiveCard: AdaptiveCard = {\n type: \"AdaptiveCard\",\n $schema: ADAPTIVE_CARD_SCHEMA,\n version: ADAPTIVE_CARD_VERSION,\n body,\n };\n\n if (actions.length > 0) {\n adaptiveCard.actions = actions;\n }\n\n return adaptiveCard;\n}\n\ninterface ConvertResult {\n elements: AdaptiveCardElement[];\n actions: AdaptiveCardAction[];\n}\n\n/**\n * Convert a card child element to Adaptive Card elements.\n */\nfunction convertChildToAdaptive(child: CardChild): ConvertResult {\n switch (child.type) {\n case \"text\":\n return { elements: [convertTextToElement(child)], actions: [] };\n case \"image\":\n return { elements: [convertImageToElement(child)], actions: [] };\n case \"divider\":\n return { elements: [convertDividerToElement(child)], actions: [] };\n case \"actions\":\n return convertActionsToElements(child);\n case \"section\":\n return convertSectionToElements(child);\n case \"fields\":\n return { elements: [convertFieldsToElement(child)], actions: [] };\n default:\n return { elements: [], actions: [] };\n }\n}\n\nfunction convertTextToElement(element: TextElement): AdaptiveCardElement {\n const textBlock: AdaptiveCardElement = {\n type: \"TextBlock\",\n text: convertEmoji(element.content),\n wrap: true,\n };\n\n if (element.style === \"bold\") {\n textBlock.weight = \"bolder\";\n } else if (element.style === \"muted\") {\n textBlock.isSubtle = true;\n }\n\n return textBlock;\n}\n\nfunction convertImageToElement(element: ImageElement): AdaptiveCardElement {\n return {\n type: \"Image\",\n url: element.url,\n altText: element.alt || \"Image\",\n size: \"auto\",\n };\n}\n\nfunction convertDividerToElement(\n _element: DividerElement,\n): AdaptiveCardElement {\n // Adaptive Cards don't have a native divider, use a separator container\n return {\n type: \"Container\",\n separator: true,\n items: [],\n };\n}\n\nfunction convertActionsToElements(element: ActionsElement): ConvertResult {\n // In Adaptive Cards, actions go at the card level, not inline\n const actions: AdaptiveCardAction[] = element.children.map((button) =>\n convertButtonToAction(button),\n );\n\n return { elements: [], actions };\n}\n\nfunction convertButtonToAction(button: ButtonElement): AdaptiveCardAction {\n const action: AdaptiveCardAction = {\n type: \"Action.Submit\",\n title: convertEmoji(button.label),\n data: {\n actionId: button.id,\n value: button.value,\n },\n };\n\n const style = mapButtonStyle(button.style, \"teams\");\n if (style) {\n action.style = style;\n }\n\n return action;\n}\n\nfunction convertSectionToElements(element: SectionElement): ConvertResult {\n const elements: AdaptiveCardElement[] = [];\n const actions: AdaptiveCardAction[] = [];\n\n // Wrap section in a container\n const containerItems: AdaptiveCardElement[] = [];\n\n for (const child of element.children) {\n const result = convertChildToAdaptive(child);\n containerItems.push(...result.elements);\n actions.push(...result.actions);\n }\n\n if (containerItems.length > 0) {\n elements.push({\n type: \"Container\",\n items: containerItems,\n });\n }\n\n return { elements, actions };\n}\n\nfunction convertFieldsToElement(element: FieldsElement): AdaptiveCardElement {\n // Use FactSet for key-value pairs\n const facts = element.children.map((field) => ({\n title: convertEmoji(field.label),\n value: convertEmoji(field.value),\n }));\n\n return {\n type: \"FactSet\",\n facts,\n };\n}\n\n/**\n * Generate fallback text from a card element.\n * Used when adaptive cards aren't supported.\n */\nexport function cardToFallbackText(card: CardElement): string {\n return sharedCardToFallbackText(card, {\n boldFormat: \"**\",\n lineBreak: \"\\n\\n\",\n platform: \"teams\",\n });\n}\n","/**\n * Teams-specific format conversion using AST-based parsing.\n *\n * Teams supports a subset of HTML for formatting:\n * - Bold: <b> or <strong>\n * - Italic: <i> or <em>\n * - Strikethrough: <s> or <strike>\n * - Links: <a href=\"url\">text</a>\n * - Code: <pre> and <code>\n *\n * Teams also accepts standard markdown in most cases.\n */\n\nimport {\n type AdapterPostableMessage,\n BaseFormatConverter,\n type Content,\n getNodeChildren,\n getNodeValue,\n isBlockquoteNode,\n isCodeNode,\n isDeleteNode,\n isEmphasisNode,\n isInlineCodeNode,\n isLinkNode,\n isListItemNode,\n isListNode,\n isParagraphNode,\n isStrongNode,\n isTextNode,\n parseMarkdown,\n type Root,\n} from \"chat\";\n\nexport class TeamsFormatConverter extends BaseFormatConverter {\n /**\n * Convert @mentions to Teams format in plain text.\n * @name → <at>name</at>\n */\n private convertMentionsToTeams(text: string): string {\n return text.replace(/@(\\w+)/g, \"<at>$1</at>\");\n }\n\n /**\n * Override renderPostable to convert @mentions in plain strings.\n */\n override renderPostable(message: AdapterPostableMessage): string {\n if (typeof message === \"string\") {\n return this.convertMentionsToTeams(message);\n }\n if (\"raw\" in message) {\n return this.convertMentionsToTeams(message.raw);\n }\n if (\"markdown\" in message) {\n return this.fromAst(parseMarkdown(message.markdown));\n }\n if (\"ast\" in message) {\n return this.fromAst(message.ast);\n }\n return \"\";\n }\n\n /**\n * Render an AST to Teams format.\n * Teams accepts standard markdown, so we just stringify cleanly.\n */\n fromAst(ast: Root): string {\n return this.fromAstWithNodeConverter(ast, (node) => this.nodeToTeams(node));\n }\n\n /**\n * Parse Teams message into an AST.\n * Converts Teams HTML/mentions to standard markdown format.\n */\n toAst(teamsText: string): Root {\n // Convert Teams HTML to markdown, then parse\n let markdown = teamsText;\n\n // Convert @mentions from Teams format: <at>Name</at> -> @Name\n markdown = markdown.replace(/<at>([^<]+)<\\/at>/gi, \"@$1\");\n\n // Convert HTML tags to markdown\n // Bold: <b>, <strong> -> **text**\n markdown = markdown.replace(\n /<(b|strong)>([^<]+)<\\/(b|strong)>/gi,\n \"**$2**\",\n );\n\n // Italic: <i>, <em> -> _text_\n markdown = markdown.replace(/<(i|em)>([^<]+)<\\/(i|em)>/gi, \"_$2_\");\n\n // Strikethrough: <s>, <strike> -> ~~text~~\n markdown = markdown.replace(\n /<(s|strike)>([^<]+)<\\/(s|strike)>/gi,\n \"~~$2~~\",\n );\n\n // Links: <a href=\"url\">text</a> -> [text](url)\n markdown = markdown.replace(\n /<a[^>]+href=\"([^\"]+)\"[^>]*>([^<]+)<\\/a>/gi,\n \"[$2]($1)\",\n );\n\n // Code: <code>text</code> -> `text`\n markdown = markdown.replace(/<code>([^<]+)<\\/code>/gi, \"`$1`\");\n\n // Pre: <pre>text</pre> -> ```text```\n markdown = markdown.replace(/<pre>([^<]+)<\\/pre>/gi, \"```\\n$1\\n```\");\n\n // Strip remaining HTML tags\n markdown = markdown.replace(/<[^>]+>/g, \"\");\n\n // Decode HTML entities\n markdown = markdown\n .replace(/&lt;/g, \"<\")\n .replace(/&gt;/g, \">\")\n .replace(/&amp;/g, \"&\")\n .replace(/&quot;/g, '\"')\n .replace(/&#39;/g, \"'\");\n\n return parseMarkdown(markdown);\n }\n\n private nodeToTeams(node: Content): string {\n // Use type guards for type-safe node handling\n if (isParagraphNode(node)) {\n return getNodeChildren(node)\n .map((child) => this.nodeToTeams(child))\n .join(\"\");\n }\n\n if (isTextNode(node)) {\n // Convert @mentions to Teams format <at>mention</at>\n return node.value.replace(/@(\\w+)/g, \"<at>$1</at>\");\n }\n\n if (isStrongNode(node)) {\n // Teams supports **text** markdown\n const content = getNodeChildren(node)\n .map((child) => this.nodeToTeams(child))\n .join(\"\");\n return `**${content}**`;\n }\n\n if (isEmphasisNode(node)) {\n // Teams supports _text_ markdown\n const content = getNodeChildren(node)\n .map((child) => this.nodeToTeams(child))\n .join(\"\");\n return `_${content}_`;\n }\n\n if (isDeleteNode(node)) {\n // Teams supports ~~text~~ markdown\n const content = getNodeChildren(node)\n .map((child) => this.nodeToTeams(child))\n .join(\"\");\n return `~~${content}~~`;\n }\n\n if (isInlineCodeNode(node)) {\n return `\\`${node.value}\\``;\n }\n\n if (isCodeNode(node)) {\n return `\\`\\`\\`${node.lang || \"\"}\\n${node.value}\\n\\`\\`\\``;\n }\n\n if (isLinkNode(node)) {\n const linkText = getNodeChildren(node)\n .map((child) => this.nodeToTeams(child))\n .join(\"\");\n // Standard markdown link format\n return `[${linkText}](${node.url})`;\n }\n\n if (isBlockquoteNode(node)) {\n return getNodeChildren(node)\n .map((child) => `> ${this.nodeToTeams(child)}`)\n .join(\"\\n\");\n }\n\n if (isListNode(node)) {\n return getNodeChildren(node)\n .map((item, i) => {\n const prefix = node.ordered ? `${i + 1}.` : \"-\";\n const content = getNodeChildren(item)\n .map((child) => this.nodeToTeams(child))\n .join(\"\");\n return `${prefix} ${content}`;\n })\n .join(\"\\n\");\n }\n\n if (isListItemNode(node)) {\n return getNodeChildren(node)\n .map((child) => this.nodeToTeams(child))\n .join(\"\");\n }\n\n if (node.type === \"break\") {\n return \"\\n\";\n }\n\n if (node.type === \"thematicBreak\") {\n return \"---\";\n }\n\n // For unsupported nodes, try to extract text\n const children = getNodeChildren(node);\n if (children.length > 0) {\n return children.map((child) => this.nodeToTeams(child)).join(\"\");\n }\n return getNodeValue(node);\n }\n}\n"],"mappings":"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","names":["context"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/cards.ts","../src/markdown.ts"],"sourcesContent":["import { ClientSecretCredential } from \"@azure/identity\";\nimport { Client } from \"@microsoft/microsoft-graph-client\";\nimport {\n TokenCredentialAuthenticationProvider,\n type TokenCredentialAuthenticationProviderOptions,\n} from \"@microsoft/microsoft-graph-client/authProviders/azureTokenCredentials\";\nimport type { Activity, ConversationReference } from \"botbuilder\";\nimport {\n ActivityTypes,\n CloudAdapter,\n ConfigurationBotFrameworkAuthentication,\n TeamsInfo,\n type TurnContext,\n} from \"botbuilder\";\n\n/** Extended CloudAdapter that exposes processActivity for serverless environments */\nclass ServerlessCloudAdapter extends CloudAdapter {\n handleActivity(\n authHeader: string,\n activity: Activity,\n logic: (context: TurnContext) => Promise<void>,\n ) {\n return this.processActivity(authHeader, activity, logic);\n }\n}\n\nimport {\n AdapterRateLimitError,\n AuthenticationError,\n bufferToDataUri,\n extractCard,\n extractFiles,\n NetworkError,\n PermissionError,\n toBuffer,\n ValidationError,\n} from \"@chat-adapter/shared\";\nimport type {\n ActionEvent,\n Adapter,\n AdapterPostableMessage,\n Attachment,\n ChatInstance,\n EmojiValue,\n FetchOptions,\n FetchResult,\n FileUpload,\n FormattedContent,\n Logger,\n RawMessage,\n ReactionEvent,\n ThreadInfo,\n WebhookOptions,\n} from \"chat\";\nimport {\n convertEmojiPlaceholders,\n defaultEmojiResolver,\n Message,\n NotImplementedError,\n} from \"chat\";\nimport { cardToAdaptiveCard } from \"./cards\";\nimport { TeamsFormatConverter } from \"./markdown\";\n\n/** Microsoft Graph API chat message type */\ninterface GraphChatMessage {\n id: string;\n createdDateTime?: string;\n lastModifiedDateTime?: string;\n replyToId?: string; // ID of parent message for channel threads\n body?: {\n content?: string;\n contentType?: \"text\" | \"html\";\n };\n from?: {\n user?: {\n id?: string;\n displayName?: string;\n };\n application?: {\n id?: string;\n displayName?: string;\n };\n };\n attachments?: Array<{\n id?: string;\n contentType?: string;\n contentUrl?: string;\n content?: string; // JSON string for adaptive cards\n name?: string;\n }>;\n}\n\nexport interface TeamsAdapterConfig {\n /** Microsoft App ID */\n appId: string;\n /** Microsoft App Password */\n appPassword: string;\n /** Logger instance for error reporting */\n logger: Logger;\n /** Microsoft App Type */\n appType?: \"MultiTenant\" | \"SingleTenant\";\n /** Microsoft App Tenant ID */\n appTenantId?: string;\n /** Override bot username (optional) */\n userName?: string;\n}\n\n/** Teams-specific thread ID data */\nexport interface TeamsThreadId {\n conversationId: string;\n serviceUrl: string;\n replyToId?: string;\n}\n\n/** Teams channel context extracted from activity.channelData */\ninterface TeamsChannelContext {\n teamId: string;\n channelId: string;\n tenantId: string;\n}\n\nexport class TeamsAdapter implements Adapter<TeamsThreadId, unknown> {\n readonly name = \"teams\";\n readonly userName: string;\n readonly botUserId?: string;\n\n private botAdapter: ServerlessCloudAdapter;\n private graphClient: Client | null = null;\n private chat: ChatInstance | null = null;\n private logger: Logger;\n private formatConverter = new TeamsFormatConverter();\n private config: TeamsAdapterConfig;\n\n constructor(config: TeamsAdapterConfig) {\n this.config = config;\n this.logger = config.logger;\n this.userName = config.userName || \"bot\";\n\n if (config.appType === \"SingleTenant\" && !config.appTenantId) {\n throw new ValidationError(\n \"teams\",\n \"appTenantId is required for SingleTenant app type\",\n );\n }\n\n // Pass empty config object, credentials go via factory\n const auth = new ConfigurationBotFrameworkAuthentication({\n MicrosoftAppId: config.appId,\n MicrosoftAppPassword: config.appPassword,\n MicrosoftAppType: config.appType || \"MultiTenant\",\n MicrosoftAppTenantId:\n config.appType === \"SingleTenant\" ? config.appTenantId : undefined,\n });\n\n this.botAdapter = new ServerlessCloudAdapter(auth);\n\n // Initialize Microsoft Graph client for message history (requires tenant ID)\n if (config.appTenantId) {\n const credential = new ClientSecretCredential(\n config.appTenantId,\n config.appId,\n config.appPassword,\n );\n\n const authProvider = new TokenCredentialAuthenticationProvider(\n credential,\n {\n scopes: [\"https://graph.microsoft.com/.default\"],\n } as TokenCredentialAuthenticationProviderOptions,\n );\n\n this.graphClient = Client.initWithMiddleware({ authProvider });\n }\n }\n\n async initialize(chat: ChatInstance): Promise<void> {\n this.chat = chat;\n }\n\n async handleWebhook(\n request: Request,\n options?: WebhookOptions,\n ): Promise<Response> {\n const body = await request.text();\n this.logger.debug(\"Teams webhook raw body\", { body });\n\n let activity: Activity;\n try {\n activity = JSON.parse(body);\n } catch (e) {\n this.logger.error(\"Failed to parse request body\", { error: e });\n return new Response(\"Invalid JSON\", { status: 400 });\n }\n\n // Get the auth header for token validation\n const authHeader = request.headers.get(\"authorization\") || \"\";\n\n try {\n // Use handleActivity which takes the activity directly\n // instead of mocking Node.js req/res objects\n await this.botAdapter.handleActivity(\n authHeader,\n activity,\n async (context) => {\n await this.handleTurn(context, options);\n },\n );\n\n return new Response(JSON.stringify({}), {\n status: 200,\n headers: { \"Content-Type\": \"application/json\" },\n });\n } catch (error) {\n this.logger.error(\"Bot adapter process error\", { error });\n return new Response(JSON.stringify({ error: \"Internal error\" }), {\n status: 500,\n headers: { \"Content-Type\": \"application/json\" },\n });\n }\n }\n\n private async handleTurn(\n context: TurnContext,\n options?: WebhookOptions,\n ): Promise<void> {\n if (!this.chat) {\n this.logger.warn(\"Chat instance not initialized, ignoring event\");\n return;\n }\n\n const activity = context.activity;\n\n // Cache serviceUrl and tenantId for the user - needed for opening DMs later\n if (activity.from?.id && activity.serviceUrl) {\n const userId = activity.from.id;\n const channelData = activity.channelData as {\n tenant?: { id?: string };\n team?: { id?: string };\n channel?: { id?: string };\n };\n const tenantId = channelData?.tenant?.id;\n const ttl = 30 * 24 * 60 * 60 * 1000; // 30 days\n\n // Store serviceUrl and tenantId for DM creation\n this.chat\n .getState()\n .set(`teams:serviceUrl:${userId}`, activity.serviceUrl, ttl)\n .catch((err) => {\n this.logger.error(\"Failed to cache serviceUrl\", {\n userId,\n error: err,\n });\n });\n if (tenantId) {\n this.chat\n .getState()\n .set(`teams:tenantId:${userId}`, tenantId, ttl)\n .catch((err) => {\n this.logger.error(\"Failed to cache tenantId\", {\n userId,\n error: err,\n });\n });\n }\n\n // Cache team/channel context for proper message fetching in channel threads\n // This allows fetchMessages to use the channel-specific endpoint for thread filtering\n // The Graph API requires aadGroupId (GUID format), not the Teams thread-style ID\n // Note: The botbuilder types don't include aadGroupId, but it's present at runtime\n // aadGroupId is only available in installationUpdate/conversationUpdate events\n const team = channelData?.team as\n | { id?: string; aadGroupId?: string }\n | undefined;\n const teamAadGroupId = team?.aadGroupId;\n const teamThreadId = team?.id; // Thread-style ID like \"19:xxx@thread.tacv2\"\n const conversationId = activity.conversation?.id || \"\";\n const baseChannelId = conversationId.replace(/;messageid=\\d+/, \"\");\n\n if (teamAadGroupId && channelData?.channel?.id && tenantId) {\n // We have aadGroupId (from installationUpdate/conversationUpdate) - cache it\n const context: TeamsChannelContext = {\n teamId: teamAadGroupId, // Use aadGroupId (GUID) for Graph API\n channelId: channelData.channel.id,\n tenantId,\n };\n const contextJson = JSON.stringify(context);\n\n // Cache by conversation ID (channel)\n this.chat\n .getState()\n .set(`teams:channelContext:${baseChannelId}`, contextJson, ttl)\n .catch((err) => {\n this.logger.error(\"Failed to cache channel context\", {\n conversationId: baseChannelId,\n error: err,\n });\n });\n\n // Also cache by team thread-style ID for lookup from regular messages\n // (which don't have aadGroupId but do have team.id)\n if (teamThreadId) {\n this.chat\n .getState()\n .set(`teams:teamContext:${teamThreadId}`, contextJson, ttl)\n .catch((err) => {\n this.logger.error(\"Failed to cache team context\", {\n teamThreadId,\n error: err,\n });\n });\n }\n\n this.logger.info(\n \"Cached Teams team GUID from installation/update event\",\n {\n activityType: activity.type,\n conversationId: baseChannelId,\n teamThreadId,\n teamGuid: context.teamId,\n channelId: context.channelId,\n },\n );\n } else if (teamThreadId && channelData?.channel?.id && tenantId) {\n // Regular message event - no aadGroupId, but try to look up from previous cache\n const cachedTeamContext = await this.chat\n .getState()\n .get<string>(`teams:teamContext:${teamThreadId}`);\n\n if (cachedTeamContext) {\n // Found cached context from installation event - also cache by channel ID\n this.chat\n .getState()\n .set(\n `teams:channelContext:${baseChannelId}`,\n cachedTeamContext,\n ttl,\n )\n .catch((err) => {\n this.logger.error(\"Failed to cache channel context from team\", {\n conversationId: baseChannelId,\n error: err,\n });\n });\n this.logger.info(\"Using cached Teams team GUID for channel\", {\n conversationId: baseChannelId,\n teamThreadId,\n });\n } else {\n // No cached context - try to fetch team details via Bot Framework API\n // TeamsInfo.getTeamDetails() calls /v3/teams/{teamId} and returns aadGroupId\n try {\n const teamDetails = await TeamsInfo.getTeamDetails(context);\n if (teamDetails?.aadGroupId) {\n const fetchedContext: TeamsChannelContext = {\n teamId: teamDetails.aadGroupId,\n channelId: channelData.channel.id,\n tenantId,\n };\n const contextJson = JSON.stringify(fetchedContext);\n\n // Cache by conversation ID\n this.chat\n .getState()\n .set(`teams:channelContext:${baseChannelId}`, contextJson, ttl)\n .catch((err) => {\n this.logger.error(\"Failed to cache fetched channel context\", {\n conversationId: baseChannelId,\n error: err,\n });\n });\n\n // Also cache by team thread-style ID\n this.chat\n .getState()\n .set(`teams:teamContext:${teamThreadId}`, contextJson, ttl)\n .catch((err) => {\n this.logger.error(\"Failed to cache fetched team context\", {\n teamThreadId,\n error: err,\n });\n });\n\n this.logger.info(\n \"Fetched and cached Teams team GUID via TeamsInfo API\",\n {\n conversationId: baseChannelId,\n teamThreadId,\n teamGuid: teamDetails.aadGroupId,\n teamName: teamDetails.name,\n },\n );\n }\n } catch (error) {\n // TeamsInfo.getTeamDetails() only works in team scope\n this.logger.debug(\n \"Could not fetch team details (may not be a team scope)\",\n { teamThreadId, error },\n );\n }\n }\n }\n }\n\n // Handle message reactions\n if (activity.type === ActivityTypes.MessageReaction) {\n this.handleReactionActivity(activity, options);\n return;\n }\n\n // Handle adaptive card actions (button clicks)\n if (activity.type === ActivityTypes.Invoke) {\n await this.handleInvokeActivity(context, options);\n return;\n }\n\n // Only handle message activities\n if (activity.type !== ActivityTypes.Message) {\n this.logger.debug(\"Ignoring non-message activity\", {\n type: activity.type,\n });\n return;\n }\n\n // Check if this message activity is actually a button click (Action.Submit)\n // Teams sends Action.Submit as a message with value.actionId\n const actionValue = activity.value as\n | { actionId?: string; value?: string }\n | undefined;\n if (actionValue?.actionId) {\n this.handleMessageAction(activity, actionValue, options);\n return;\n }\n\n const threadId = this.encodeThreadId({\n conversationId: activity.conversation?.id || \"\",\n serviceUrl: activity.serviceUrl || \"\",\n replyToId: activity.replyToId,\n });\n\n // Let Chat class handle async processing and waitUntil\n this.chat.processMessage(\n this,\n threadId,\n this.parseTeamsMessage(activity, threadId),\n options,\n );\n }\n\n /**\n * Handle Action.Submit button clicks sent as message activities.\n * Teams sends these with type \"message\" and value.actionId.\n */\n private handleMessageAction(\n activity: Activity,\n actionValue: { actionId?: string; value?: string },\n options?: WebhookOptions,\n ): void {\n if (!this.chat || !actionValue.actionId) return;\n\n const threadId = this.encodeThreadId({\n conversationId: activity.conversation?.id || \"\",\n serviceUrl: activity.serviceUrl || \"\",\n });\n\n const actionEvent: Omit<ActionEvent, \"thread\" | \"openModal\"> & {\n adapter: TeamsAdapter;\n } = {\n actionId: actionValue.actionId,\n value: actionValue.value,\n user: {\n userId: activity.from?.id || \"unknown\",\n userName: activity.from?.name || \"unknown\",\n fullName: activity.from?.name || \"unknown\",\n isBot: false,\n isMe: false,\n },\n messageId: activity.replyToId || activity.id || \"\",\n threadId,\n adapter: this,\n raw: activity,\n };\n\n this.logger.debug(\"Processing Teams message action (Action.Submit)\", {\n actionId: actionValue.actionId,\n value: actionValue.value,\n messageId: actionEvent.messageId,\n threadId,\n });\n\n this.chat.processAction(actionEvent, options);\n }\n\n /**\n * Handle invoke activities (adaptive card actions, etc.).\n */\n private async handleInvokeActivity(\n context: TurnContext,\n options?: WebhookOptions,\n ): Promise<void> {\n const activity = context.activity;\n\n // Handle adaptive card action invokes\n if (activity.name === \"adaptiveCard/action\") {\n await this.handleAdaptiveCardAction(context, activity, options);\n return;\n }\n\n this.logger.debug(\"Ignoring unsupported invoke\", {\n name: activity.name,\n });\n }\n\n /**\n * Handle adaptive card button clicks.\n * The action data is in activity.value with our { actionId, value } structure.\n */\n private async handleAdaptiveCardAction(\n context: TurnContext,\n activity: Activity,\n options?: WebhookOptions,\n ): Promise<void> {\n if (!this.chat) return;\n\n // Activity.value contains our action data\n const actionData = activity.value?.action?.data as\n | { actionId?: string; value?: string }\n | undefined;\n\n if (!actionData?.actionId) {\n this.logger.debug(\"Adaptive card action missing actionId\", {\n value: activity.value,\n });\n // Send acknowledgment response\n await context.sendActivity({\n type: ActivityTypes.InvokeResponse,\n value: { status: 200 },\n });\n return;\n }\n\n const threadId = this.encodeThreadId({\n conversationId: activity.conversation?.id || \"\",\n serviceUrl: activity.serviceUrl || \"\",\n });\n\n const actionEvent: Omit<ActionEvent, \"thread\" | \"openModal\"> & {\n adapter: TeamsAdapter;\n } = {\n actionId: actionData.actionId,\n value: actionData.value,\n user: {\n userId: activity.from?.id || \"unknown\",\n userName: activity.from?.name || \"unknown\",\n fullName: activity.from?.name || \"unknown\",\n isBot: false,\n isMe: false,\n },\n messageId: activity.replyToId || activity.id || \"\",\n threadId,\n adapter: this,\n raw: activity,\n };\n\n this.logger.debug(\"Processing Teams adaptive card action\", {\n actionId: actionData.actionId,\n value: actionData.value,\n messageId: actionEvent.messageId,\n threadId,\n });\n\n this.chat.processAction(actionEvent, options);\n\n // Send acknowledgment response to prevent timeout\n await context.sendActivity({\n type: ActivityTypes.InvokeResponse,\n value: { status: 200 },\n });\n }\n\n /**\n * Handle Teams reaction events (reactionsAdded/reactionsRemoved).\n */\n private handleReactionActivity(\n activity: Activity,\n options?: WebhookOptions,\n ): void {\n if (!this.chat) return;\n\n // Extract the message ID from conversation ID\n // Format: \"19:xxx@thread.tacv2;messageid=1767297849909\"\n const conversationId = activity.conversation?.id || \"\";\n const messageIdMatch = conversationId.match(/messageid=(\\d+)/);\n const messageId = messageIdMatch?.[1] || activity.replyToId || \"\";\n\n // Build thread ID - KEEP the full conversation ID including ;messageid=XXX\n // This is required for Teams to reply in the correct thread\n const threadId = this.encodeThreadId({\n conversationId: conversationId,\n serviceUrl: activity.serviceUrl || \"\",\n });\n\n const user = {\n userId: activity.from?.id || \"unknown\",\n userName: activity.from?.name || \"unknown\",\n fullName: activity.from?.name,\n isBot: false,\n isMe: this.isMessageFromSelf(activity),\n };\n\n // Process added reactions\n const reactionsAdded = activity.reactionsAdded || [];\n for (const reaction of reactionsAdded) {\n const rawEmoji = reaction.type || \"\";\n const emojiValue = defaultEmojiResolver.fromTeams(rawEmoji);\n\n const event: Omit<ReactionEvent, \"adapter\" | \"thread\"> = {\n emoji: emojiValue,\n rawEmoji,\n added: true,\n user,\n messageId,\n threadId,\n raw: activity,\n };\n\n this.logger.debug(\"Processing Teams reaction added\", {\n emoji: emojiValue.name,\n rawEmoji,\n messageId,\n });\n\n this.chat.processReaction({ ...event, adapter: this }, options);\n }\n\n // Process removed reactions\n const reactionsRemoved = activity.reactionsRemoved || [];\n for (const reaction of reactionsRemoved) {\n const rawEmoji = reaction.type || \"\";\n const emojiValue = defaultEmojiResolver.fromTeams(rawEmoji);\n\n const event: Omit<ReactionEvent, \"adapter\" | \"thread\"> = {\n emoji: emojiValue,\n rawEmoji,\n added: false,\n user,\n messageId,\n threadId,\n raw: activity,\n };\n\n this.logger.debug(\"Processing Teams reaction removed\", {\n emoji: emojiValue.name,\n rawEmoji,\n messageId,\n });\n\n this.chat.processReaction({ ...event, adapter: this }, options);\n }\n }\n\n private parseTeamsMessage(\n activity: Activity,\n threadId: string,\n ): Message<unknown> {\n const text = activity.text || \"\";\n // Normalize mentions - format converter will convert <at>name</at> to @name\n const normalizedText = this.normalizeMentions(text, activity);\n\n const isMe = this.isMessageFromSelf(activity);\n\n return new Message({\n id: activity.id || \"\",\n threadId,\n text: this.formatConverter.extractPlainText(normalizedText),\n formatted: this.formatConverter.toAst(normalizedText),\n raw: activity,\n author: {\n userId: activity.from?.id || \"unknown\",\n userName: activity.from?.name || \"unknown\",\n fullName: activity.from?.name || \"unknown\",\n isBot: activity.from?.role === \"bot\",\n isMe,\n },\n metadata: {\n dateSent: activity.timestamp\n ? new Date(activity.timestamp)\n : new Date(),\n edited: false,\n },\n attachments: (activity.attachments || [])\n .filter(\n (att) =>\n // Filter out adaptive cards (handled separately as cards, not attachments)\n att.contentType !== \"application/vnd.microsoft.card.adaptive\" &&\n // Filter out text/html without contentUrl - this is just the formatted\n // version of the message text, not an actual file attachment.\n // Real HTML file attachments would have a contentUrl.\n !(att.contentType === \"text/html\" && !att.contentUrl),\n )\n .map((att) => this.createAttachment(att)),\n });\n }\n\n /**\n * Create an Attachment object from a Teams attachment.\n */\n private createAttachment(att: {\n contentType?: string;\n contentUrl?: string;\n name?: string;\n }): Attachment {\n const url = att.contentUrl;\n\n // Determine type based on contentType\n let type: Attachment[\"type\"] = \"file\";\n if (att.contentType?.startsWith(\"image/\")) {\n type = \"image\";\n } else if (att.contentType?.startsWith(\"video/\")) {\n type = \"video\";\n } else if (att.contentType?.startsWith(\"audio/\")) {\n type = \"audio\";\n }\n\n return {\n type,\n url,\n name: att.name,\n mimeType: att.contentType,\n fetchData: url\n ? async () => {\n const response = await fetch(url);\n if (!response.ok) {\n throw new NetworkError(\n \"teams\",\n `Failed to fetch file: ${response.status} ${response.statusText}`,\n );\n }\n const arrayBuffer = await response.arrayBuffer();\n return Buffer.from(arrayBuffer);\n }\n : undefined,\n };\n }\n\n private normalizeMentions(text: string, _activity: Activity): string {\n // Don't strip mentions - the format converter will convert <at>name</at> to @name\n // Just trim any leading/trailing whitespace that might result from mention placement\n return text.trim();\n }\n\n async postMessage(\n threadId: string,\n message: AdapterPostableMessage,\n ): Promise<RawMessage<unknown>> {\n const { conversationId, serviceUrl } = this.decodeThreadId(threadId);\n\n // Check for files to upload\n const files = extractFiles(message);\n const fileAttachments =\n files.length > 0 ? await this.filesToAttachments(files) : [];\n\n // Check if message contains a card\n const card = extractCard(message);\n let activity: Partial<Activity>;\n\n if (card) {\n // Render card as Adaptive Card\n const adaptiveCard = cardToAdaptiveCard(card);\n\n activity = {\n type: ActivityTypes.Message,\n // Don't include text - Teams shows both text and card if text is present\n attachments: [\n {\n contentType: \"application/vnd.microsoft.card.adaptive\",\n content: adaptiveCard,\n },\n ...fileAttachments,\n ],\n };\n\n this.logger.debug(\"Teams API: sendActivity (adaptive card)\", {\n conversationId,\n serviceUrl,\n fileCount: fileAttachments.length,\n });\n } else {\n // Regular text message\n const text = convertEmojiPlaceholders(\n this.formatConverter.renderPostable(message),\n \"teams\",\n );\n\n activity = {\n type: ActivityTypes.Message,\n text,\n textFormat: \"markdown\",\n attachments: fileAttachments.length > 0 ? fileAttachments : undefined,\n };\n\n this.logger.debug(\"Teams API: sendActivity (message)\", {\n conversationId,\n serviceUrl,\n textLength: text.length,\n fileCount: fileAttachments.length,\n });\n }\n\n // Use the adapter to send the message\n const conversationReference = {\n channelId: \"msteams\",\n serviceUrl,\n conversation: { id: conversationId },\n };\n\n let messageId = \"\";\n\n try {\n await this.botAdapter.continueConversationAsync(\n this.config.appId,\n conversationReference as Partial<ConversationReference>,\n async (context) => {\n const response = await context.sendActivity(activity);\n messageId = response?.id || \"\";\n },\n );\n } catch (error) {\n this.logger.error(\"Teams API: sendActivity failed\", {\n conversationId,\n error,\n });\n this.handleTeamsError(error, \"postMessage\");\n }\n\n this.logger.debug(\"Teams API: sendActivity response\", { messageId });\n\n return {\n id: messageId,\n threadId,\n raw: activity,\n };\n }\n\n /**\n * Convert files to Teams attachments.\n * Uses inline data URIs for small files.\n */\n private async filesToAttachments(\n files: FileUpload[],\n ): Promise<Array<{ contentType: string; contentUrl: string; name: string }>> {\n const attachments: Array<{\n contentType: string;\n contentUrl: string;\n name: string;\n }> = [];\n\n for (const file of files) {\n // Convert data to Buffer using shared utility\n const buffer = await toBuffer(file.data, {\n platform: \"teams\",\n throwOnUnsupported: false,\n });\n if (!buffer) {\n continue;\n }\n\n // Create data URI using shared utility\n const mimeType = file.mimeType || \"application/octet-stream\";\n const dataUri = bufferToDataUri(buffer, mimeType);\n\n attachments.push({\n contentType: mimeType,\n contentUrl: dataUri,\n name: file.filename,\n });\n }\n\n return attachments;\n }\n\n async editMessage(\n threadId: string,\n messageId: string,\n message: AdapterPostableMessage,\n ): Promise<RawMessage<unknown>> {\n const { conversationId, serviceUrl } = this.decodeThreadId(threadId);\n\n // Check if message contains a card\n const card = extractCard(message);\n let activity: Partial<Activity>;\n\n if (card) {\n // Render card as Adaptive Card\n const adaptiveCard = cardToAdaptiveCard(card);\n\n activity = {\n id: messageId,\n type: ActivityTypes.Message,\n // Don't include text - Teams shows both text and card if text is present\n attachments: [\n {\n contentType: \"application/vnd.microsoft.card.adaptive\",\n content: adaptiveCard,\n },\n ],\n };\n\n this.logger.debug(\"Teams API: updateActivity (adaptive card)\", {\n conversationId,\n messageId,\n });\n } else {\n // Regular text message\n const text = convertEmojiPlaceholders(\n this.formatConverter.renderPostable(message),\n \"teams\",\n );\n\n activity = {\n id: messageId,\n type: ActivityTypes.Message,\n text,\n textFormat: \"markdown\",\n };\n\n this.logger.debug(\"Teams API: updateActivity\", {\n conversationId,\n messageId,\n textLength: text.length,\n });\n }\n\n const conversationReference = {\n channelId: \"msteams\",\n serviceUrl,\n conversation: { id: conversationId },\n };\n\n try {\n await this.botAdapter.continueConversationAsync(\n this.config.appId,\n conversationReference as Partial<ConversationReference>,\n async (context) => {\n await context.updateActivity(activity);\n },\n );\n } catch (error) {\n this.logger.error(\"Teams API: updateActivity failed\", {\n conversationId,\n messageId,\n error,\n });\n this.handleTeamsError(error, \"editMessage\");\n }\n\n this.logger.debug(\"Teams API: updateActivity response\", { ok: true });\n\n return {\n id: messageId,\n threadId,\n raw: activity,\n };\n }\n\n async deleteMessage(threadId: string, messageId: string): Promise<void> {\n const { conversationId, serviceUrl } = this.decodeThreadId(threadId);\n\n const conversationReference = {\n channelId: \"msteams\",\n serviceUrl,\n conversation: { id: conversationId },\n };\n\n this.logger.debug(\"Teams API: deleteActivity\", {\n conversationId,\n messageId,\n });\n\n try {\n await this.botAdapter.continueConversationAsync(\n this.config.appId,\n conversationReference as Partial<ConversationReference>,\n async (context) => {\n await context.deleteActivity(messageId);\n },\n );\n } catch (error) {\n this.logger.error(\"Teams API: deleteActivity failed\", {\n conversationId,\n messageId,\n error,\n });\n this.handleTeamsError(error, \"deleteMessage\");\n }\n\n this.logger.debug(\"Teams API: deleteActivity response\", { ok: true });\n }\n\n async addReaction(\n _threadId: string,\n _messageId: string,\n _emoji: EmojiValue | string,\n ): Promise<void> {\n throw new NotImplementedError(\n \"Teams Bot Framework does not expose reaction APIs\",\n \"addReaction\",\n );\n }\n\n async removeReaction(\n _threadId: string,\n _messageId: string,\n _emoji: EmojiValue | string,\n ): Promise<void> {\n throw new NotImplementedError(\n \"Teams Bot Framework does not expose reaction APIs\",\n \"removeReaction\",\n );\n }\n\n async startTyping(threadId: string): Promise<void> {\n const { conversationId, serviceUrl } = this.decodeThreadId(threadId);\n\n const conversationReference = {\n channelId: \"msteams\",\n serviceUrl,\n conversation: { id: conversationId },\n };\n\n this.logger.debug(\"Teams API: sendActivity (typing)\", { conversationId });\n\n try {\n await this.botAdapter.continueConversationAsync(\n this.config.appId,\n conversationReference as Partial<ConversationReference>,\n async (context) => {\n await context.sendActivity({ type: ActivityTypes.Typing });\n },\n );\n } catch (error) {\n this.logger.error(\"Teams API: sendActivity (typing) failed\", {\n conversationId,\n error,\n });\n this.handleTeamsError(error, \"startTyping\");\n }\n\n this.logger.debug(\"Teams API: sendActivity (typing) response\", {\n ok: true,\n });\n }\n\n /**\n * Open a direct message conversation with a user.\n * Returns a thread ID that can be used to post messages.\n *\n * The serviceUrl and tenantId are automatically resolved from cached user interactions.\n * If no cached values are found, defaults are used (which may not work for all tenants).\n */\n async openDM(userId: string): Promise<string> {\n // Look up cached serviceUrl and tenantId for this user from state\n const cachedServiceUrl = await this.chat\n ?.getState()\n .get<string>(`teams:serviceUrl:${userId}`);\n const cachedTenantId = await this.chat\n ?.getState()\n .get<string>(`teams:tenantId:${userId}`);\n\n const serviceUrl =\n cachedServiceUrl || \"https://smba.trafficmanager.net/teams/\";\n // Use cached tenant ID, config tenant ID, or undefined (will fail for multi-tenant)\n const tenantId = cachedTenantId || this.config.appTenantId;\n\n this.logger.debug(\"Teams: creating 1:1 conversation\", {\n userId,\n serviceUrl,\n tenantId,\n cachedServiceUrl: !!cachedServiceUrl,\n cachedTenantId: !!cachedTenantId,\n });\n\n if (!tenantId) {\n throw new ValidationError(\n \"teams\",\n \"Cannot open DM: tenant ID not found. User must interact with the bot first (via @mention) to cache their tenant ID.\",\n );\n }\n\n let conversationId = \"\";\n\n // Create the 1:1 conversation using createConversationAsync\n // The conversation ID is captured from within the callback, not from the return value\n // biome-ignore lint/suspicious/noExplicitAny: BotBuilder types are incomplete\n await (this.botAdapter as any).createConversationAsync(\n this.config.appId,\n \"msteams\",\n serviceUrl,\n \"\", // empty audience\n {\n isGroup: false,\n bot: { id: this.config.appId, name: this.userName },\n members: [{ id: userId }],\n tenantId,\n channelData: {\n tenant: { id: tenantId },\n },\n },\n async (turnContext: TurnContext) => {\n // Capture the conversation ID from the new context\n conversationId = turnContext?.activity?.conversation?.id || \"\";\n this.logger.debug(\"Teams: conversation created in callback\", {\n conversationId,\n activityId: turnContext?.activity?.id,\n });\n },\n );\n\n if (!conversationId) {\n throw new NetworkError(\n \"teams\",\n \"Failed to create 1:1 conversation - no ID returned\",\n );\n }\n\n this.logger.debug(\"Teams: 1:1 conversation created\", { conversationId });\n\n return this.encodeThreadId({\n conversationId,\n serviceUrl,\n });\n }\n\n async fetchMessages(\n threadId: string,\n options: FetchOptions = {},\n ): Promise<FetchResult<unknown>> {\n if (!this.graphClient) {\n throw new NotImplementedError(\n \"Teams fetchMessages requires appTenantId to be configured for Microsoft Graph API access.\",\n \"fetchMessages\",\n );\n }\n\n const { conversationId } = this.decodeThreadId(threadId);\n const limit = options.limit || 50;\n const cursor = options.cursor;\n const direction = options.direction ?? \"backward\";\n\n // Extract message ID for thread filtering (format: \"19:xxx@thread.tacv2;messageid=123456\")\n const messageIdMatch = conversationId.match(/;messageid=(\\d+)/);\n const threadMessageId = messageIdMatch?.[1];\n\n // Strip ;messageid= from conversation ID\n const baseConversationId = conversationId.replace(/;messageid=\\d+/, \"\");\n\n // Try to get cached channel context for proper thread-level message fetching\n let channelContext: TeamsChannelContext | null = null;\n if (threadMessageId && this.chat) {\n const cachedContext = await this.chat\n .getState()\n .get<string>(`teams:channelContext:${baseConversationId}`);\n if (cachedContext) {\n try {\n channelContext = JSON.parse(cachedContext) as TeamsChannelContext;\n } catch {\n // Invalid cached data, ignore\n }\n }\n\n // Note: Team GUID is cached during webhook handling via TeamsInfo.getTeamDetails()\n // If no cached context, we'll fall back to the chat endpoint (less accurate for channels)\n }\n\n try {\n this.logger.debug(\"Teams Graph API: fetching messages\", {\n conversationId: baseConversationId,\n threadMessageId,\n hasChannelContext: !!channelContext,\n limit,\n cursor,\n direction,\n });\n\n // If we have channel context and a thread message ID, use the channel replies endpoint\n // This gives us proper thread-level filtering instead of all messages in the channel\n if (channelContext && threadMessageId) {\n return this.fetchChannelThreadMessages(\n channelContext,\n threadMessageId,\n threadId,\n options,\n );\n }\n\n // Teams conversation IDs:\n // - Channels: \"19:xxx@thread.tacv2\"\n // - Group chats: \"19:xxx@thread.v2\"\n // - 1:1 chats: other formats (e.g., \"a]xxx\", \"8:orgid:xxx\")\n // For Graph API, we use /chats/{chat-id}/messages for all chat types\n\n // Note: Teams Graph API only supports orderby(\"createdDateTime desc\")\n // Ascending order is not supported, so we work around this limitation.\n // Also, max page size is 50 messages per request.\n\n let graphMessages: GraphChatMessage[];\n let hasMoreMessages = false;\n\n if (direction === \"forward\") {\n // Forward direction: need to fetch ALL messages to find the oldest ones\n // since API only supports descending order. Paginate with max 50 per request.\n const allMessages: GraphChatMessage[] = [];\n let nextLink: string | undefined;\n const apiUrl = `/chats/${encodeURIComponent(baseConversationId)}/messages`;\n\n do {\n const request = nextLink\n ? this.graphClient.api(nextLink)\n : this.graphClient\n .api(apiUrl)\n .top(50) // Max allowed by Teams API\n .orderby(\"createdDateTime desc\");\n\n const response = await request.get();\n const pageMessages = (response.value || []) as GraphChatMessage[];\n allMessages.push(...pageMessages);\n nextLink = response[\"@odata.nextLink\"];\n } while (nextLink);\n\n // Reverse to get chronological order (oldest first)\n allMessages.reverse();\n\n // Find starting position based on cursor (cursor is a timestamp)\n let startIndex = 0;\n if (cursor) {\n startIndex = allMessages.findIndex(\n (msg) => msg.createdDateTime && msg.createdDateTime > cursor,\n );\n if (startIndex === -1) startIndex = allMessages.length;\n }\n\n // Check if there are more messages beyond our slice\n hasMoreMessages = startIndex + limit < allMessages.length;\n // Take only the requested limit\n graphMessages = allMessages.slice(startIndex, startIndex + limit);\n } else {\n // Backward direction: simple pagination\n let request = this.graphClient\n .api(`/chats/${encodeURIComponent(baseConversationId)}/messages`)\n .top(limit)\n .orderby(\"createdDateTime desc\");\n\n if (cursor) {\n // Get messages older than cursor\n request = request.filter(`createdDateTime lt ${cursor}`);\n }\n\n const response = await request.get();\n graphMessages = (response.value || []) as GraphChatMessage[];\n\n // API returns newest first, reverse to get chronological order\n graphMessages.reverse();\n\n // We have more if we got a full page\n hasMoreMessages = graphMessages.length >= limit;\n }\n\n // For group chats (non-channel), filter to only messages from the \"thread\" onwards.\n // Teams group chats don't have real threading - the messageid in the conversation ID\n // is just UI context. We filter by message ID (which is a timestamp) to simulate threading.\n if (threadMessageId && !channelContext) {\n graphMessages = graphMessages.filter((msg) => {\n // Include messages with ID >= thread message ID (IDs are timestamps)\n return msg.id && msg.id >= threadMessageId;\n });\n this.logger.debug(\"Filtered group chat messages to thread\", {\n threadMessageId,\n filteredCount: graphMessages.length,\n });\n }\n\n this.logger.debug(\"Teams Graph API: fetched messages\", {\n count: graphMessages.length,\n direction,\n hasMoreMessages,\n });\n\n const messages = graphMessages.map((msg: GraphChatMessage) => {\n const isFromBot =\n msg.from?.application?.id === this.config.appId ||\n msg.from?.user?.id === this.config.appId;\n\n return new Message({\n id: msg.id,\n threadId,\n text: this.extractTextFromGraphMessage(msg),\n formatted: this.formatConverter.toAst(\n this.extractTextFromGraphMessage(msg),\n ),\n raw: msg,\n author: {\n userId:\n msg.from?.user?.id || msg.from?.application?.id || \"unknown\",\n userName:\n msg.from?.user?.displayName ||\n msg.from?.application?.displayName ||\n \"unknown\",\n fullName:\n msg.from?.user?.displayName ||\n msg.from?.application?.displayName ||\n \"unknown\",\n isBot: !!msg.from?.application,\n isMe: isFromBot,\n },\n metadata: {\n dateSent: msg.createdDateTime\n ? new Date(msg.createdDateTime)\n : new Date(),\n edited: !!msg.lastModifiedDateTime,\n },\n attachments: this.extractAttachmentsFromGraphMessage(msg),\n });\n });\n\n // Determine nextCursor based on direction\n let nextCursor: string | undefined;\n if (hasMoreMessages && graphMessages.length > 0) {\n if (direction === \"forward\") {\n // Forward: use the newest message's timestamp (last in returned slice)\n const lastMsg = graphMessages[graphMessages.length - 1];\n if (lastMsg?.createdDateTime) {\n nextCursor = lastMsg.createdDateTime;\n }\n } else {\n // Backward: use the oldest message's timestamp (first in returned array)\n const oldestMsg = graphMessages[0];\n if (oldestMsg?.createdDateTime) {\n nextCursor = oldestMsg.createdDateTime;\n }\n }\n }\n\n return { messages, nextCursor };\n } catch (error) {\n this.logger.error(\"Teams Graph API: fetchMessages error\", { error });\n\n // Check if it's a permission error\n if (error instanceof Error && error.message?.includes(\"403\")) {\n throw new NotImplementedError(\n \"Teams fetchMessages requires one of these Azure AD app permissions: ChatMessage.Read.Chat, Chat.Read.All, or Chat.Read.WhereInstalled\",\n \"fetchMessages\",\n );\n }\n\n throw error;\n }\n }\n\n /**\n * Fetch messages from a Teams channel thread using the channel-specific Graph API endpoint.\n * This provides proper thread-level filtering by fetching only replies to a specific message.\n *\n * Endpoint: GET /teams/{team-id}/channels/{channel-id}/messages/{message-id}/replies\n */\n private async fetchChannelThreadMessages(\n context: TeamsChannelContext,\n threadMessageId: string,\n threadId: string,\n options: FetchOptions,\n ): Promise<FetchResult<unknown>> {\n const limit = options.limit || 50;\n const cursor = options.cursor;\n const direction = options.direction ?? \"backward\";\n\n this.logger.debug(\"Teams Graph API: fetching channel thread messages\", {\n teamId: context.teamId,\n channelId: context.channelId,\n threadMessageId,\n limit,\n cursor,\n direction,\n });\n\n // Build the endpoint URLs:\n // Parent message: /teams/{team-id}/channels/{channel-id}/messages/{message-id}\n // Replies: /teams/{team-id}/channels/{channel-id}/messages/{message-id}/replies\n const parentUrl = `/teams/${encodeURIComponent(context.teamId)}/channels/${encodeURIComponent(context.channelId)}/messages/${encodeURIComponent(threadMessageId)}`;\n const repliesUrl = `${parentUrl}/replies`;\n\n const graphClient = this.graphClient;\n if (!graphClient) {\n throw new AuthenticationError(\"teams\", \"Graph client not initialized\");\n }\n\n // Fetch the parent message (the original message that started the thread)\n let parentMessage: GraphChatMessage | null = null;\n try {\n parentMessage = (await graphClient\n .api(parentUrl)\n .get()) as GraphChatMessage;\n } catch (err) {\n this.logger.warn(\"Failed to fetch parent message\", {\n threadMessageId,\n err,\n });\n }\n\n let graphMessages: GraphChatMessage[];\n let hasMoreMessages = false;\n\n if (direction === \"forward\") {\n // Forward direction: fetch all replies and paginate in chronological order (oldest first)\n // Graph API returns messages in descending order (newest first), so we must reverse\n const allReplies: GraphChatMessage[] = [];\n let nextLink: string | undefined;\n\n do {\n const request = nextLink\n ? graphClient.api(nextLink)\n : graphClient.api(repliesUrl).top(50);\n\n const response = await request.get();\n const pageMessages = (response.value || []) as GraphChatMessage[];\n allReplies.push(...pageMessages);\n nextLink = response[\"@odata.nextLink\"];\n } while (nextLink);\n\n // Reverse replies to get chronological order (oldest first)\n allReplies.reverse();\n\n // Prepend parent message (it's the oldest - started the thread)\n const allMessages = parentMessage\n ? [parentMessage, ...allReplies]\n : allReplies;\n\n // Find starting position based on cursor\n let startIndex = 0;\n if (cursor) {\n startIndex = allMessages.findIndex(\n (msg) => msg.createdDateTime && msg.createdDateTime > cursor,\n );\n if (startIndex === -1) startIndex = allMessages.length;\n }\n\n hasMoreMessages = startIndex + limit < allMessages.length;\n graphMessages = allMessages.slice(startIndex, startIndex + limit);\n } else {\n // Backward direction: return most recent messages in chronological order\n // Graph API returns messages in descending order (newest first)\n const allReplies: GraphChatMessage[] = [];\n let nextLink: string | undefined;\n\n do {\n const request = nextLink\n ? graphClient.api(nextLink)\n : graphClient.api(repliesUrl).top(50);\n\n const response = await request.get();\n const pageMessages = (response.value || []) as GraphChatMessage[];\n allReplies.push(...pageMessages);\n nextLink = response[\"@odata.nextLink\"];\n } while (nextLink);\n\n // Reverse replies to get chronological order (oldest first)\n allReplies.reverse();\n\n // Prepend parent message (it's the oldest - started the thread)\n const allMessages = parentMessage\n ? [parentMessage, ...allReplies]\n : allReplies;\n\n if (cursor) {\n // Find position of cursor (cursor is timestamp of the oldest message in previous batch)\n // We want messages OLDER than cursor (earlier in chronological order)\n const cursorIndex = allMessages.findIndex(\n (msg) => msg.createdDateTime && msg.createdDateTime >= cursor,\n );\n if (cursorIndex > 0) {\n // Take messages before the cursor position\n const sliceStart = Math.max(0, cursorIndex - limit);\n graphMessages = allMessages.slice(sliceStart, cursorIndex);\n hasMoreMessages = sliceStart > 0;\n } else {\n // Cursor not found or at start - take the most recent (end of array)\n graphMessages = allMessages.slice(-limit);\n hasMoreMessages = allMessages.length > limit;\n }\n } else {\n // No cursor - get the most recent messages (end of chronological array)\n graphMessages = allMessages.slice(-limit);\n hasMoreMessages = allMessages.length > limit;\n }\n }\n\n this.logger.debug(\"Teams Graph API: fetched channel thread messages\", {\n count: graphMessages.length,\n direction,\n hasMoreMessages,\n });\n\n const messages = graphMessages.map((msg: GraphChatMessage) => {\n const isFromBot =\n msg.from?.application?.id === this.config.appId ||\n msg.from?.user?.id === this.config.appId;\n\n return new Message({\n id: msg.id,\n threadId,\n text: this.extractTextFromGraphMessage(msg),\n formatted: this.formatConverter.toAst(\n this.extractTextFromGraphMessage(msg),\n ),\n raw: msg,\n author: {\n userId: msg.from?.user?.id || msg.from?.application?.id || \"unknown\",\n userName:\n msg.from?.user?.displayName ||\n msg.from?.application?.displayName ||\n \"unknown\",\n fullName:\n msg.from?.user?.displayName ||\n msg.from?.application?.displayName ||\n \"unknown\",\n isBot: !!msg.from?.application,\n isMe: isFromBot,\n },\n metadata: {\n dateSent: msg.createdDateTime\n ? new Date(msg.createdDateTime)\n : new Date(),\n edited: !!msg.lastModifiedDateTime,\n },\n attachments: this.extractAttachmentsFromGraphMessage(msg),\n });\n });\n\n // Determine nextCursor\n let nextCursor: string | undefined;\n if (hasMoreMessages && graphMessages.length > 0) {\n if (direction === \"forward\") {\n const lastMsg = graphMessages[graphMessages.length - 1];\n if (lastMsg?.createdDateTime) {\n nextCursor = lastMsg.createdDateTime;\n }\n } else {\n const oldestMsg = graphMessages[0];\n if (oldestMsg?.createdDateTime) {\n nextCursor = oldestMsg.createdDateTime;\n }\n }\n }\n\n return { messages, nextCursor };\n }\n\n /**\n * Extract plain text from a Graph API message.\n */\n private extractTextFromGraphMessage(msg: GraphChatMessage): string {\n // body.content contains the message text (HTML or text depending on contentType)\n if (msg.body?.contentType === \"text\") {\n return msg.body.content || \"\";\n }\n\n // For HTML content, strip tags (basic implementation)\n let text = \"\";\n if (msg.body?.content) {\n text = msg.body.content.replace(/<[^>]*>/g, \"\").trim();\n }\n\n // If text is empty but message has adaptive card attachments, try to extract card title\n if (!text && msg.attachments?.length) {\n for (const att of msg.attachments) {\n if (att.contentType === \"application/vnd.microsoft.card.adaptive\") {\n try {\n const card = JSON.parse(att.content || \"{}\");\n // Look for title in common locations\n const title = this.extractCardTitle(card);\n if (title) {\n return title;\n }\n return \"[Card]\";\n } catch {\n return \"[Card]\";\n }\n }\n }\n }\n\n return text;\n }\n\n /**\n * Extract a title/summary from an Adaptive Card structure.\n */\n private extractCardTitle(card: unknown): string | null {\n if (!card || typeof card !== \"object\") return null;\n\n const cardObj = card as Record<string, unknown>;\n\n // Check for body array and find first TextBlock with large/bolder style (likely title)\n if (Array.isArray(cardObj.body)) {\n for (const element of cardObj.body) {\n if (\n element &&\n typeof element === \"object\" &&\n (element as Record<string, unknown>).type === \"TextBlock\"\n ) {\n const textBlock = element as Record<string, unknown>;\n // Title blocks often have weight: \"bolder\" or size: \"large\"\n if (\n textBlock.weight === \"bolder\" ||\n textBlock.size === \"large\" ||\n textBlock.size === \"extraLarge\"\n ) {\n const text = textBlock.text;\n if (typeof text === \"string\") {\n return text;\n }\n }\n }\n }\n // Fallback: just get first TextBlock's text\n for (const element of cardObj.body) {\n if (\n element &&\n typeof element === \"object\" &&\n (element as Record<string, unknown>).type === \"TextBlock\"\n ) {\n const text = (element as Record<string, unknown>).text;\n if (typeof text === \"string\") {\n return text;\n }\n }\n }\n }\n\n return null;\n }\n\n /**\n * Extract attachments from a Graph API message.\n */\n private extractAttachmentsFromGraphMessage(\n msg: GraphChatMessage,\n ): Attachment[] {\n if (!msg.attachments?.length) {\n return [];\n }\n\n return msg.attachments.map((att) => ({\n type: att.contentType?.includes(\"image\") ? \"image\" : \"file\",\n name: att.name || undefined,\n url: att.contentUrl || undefined,\n mimeType: att.contentType || undefined,\n }));\n }\n\n async fetchThread(threadId: string): Promise<ThreadInfo> {\n const { conversationId } = this.decodeThreadId(threadId);\n\n return {\n id: threadId,\n channelId: conversationId,\n metadata: {},\n };\n }\n\n encodeThreadId(platformData: TeamsThreadId): string {\n // Base64 encode both since conversationId and serviceUrl can contain special characters\n const encodedConversationId = Buffer.from(\n platformData.conversationId,\n ).toString(\"base64url\");\n const encodedServiceUrl = Buffer.from(platformData.serviceUrl).toString(\n \"base64url\",\n );\n return `teams:${encodedConversationId}:${encodedServiceUrl}`;\n }\n\n /**\n * Check if a thread is a direct message conversation.\n * Teams DMs have conversation IDs that don't start with \"19:\" (which is for groups/channels).\n */\n isDM(threadId: string): boolean {\n const { conversationId } = this.decodeThreadId(threadId);\n // Group chats and channels start with \"19:\", DMs don't\n return !conversationId.startsWith(\"19:\");\n }\n\n decodeThreadId(threadId: string): TeamsThreadId {\n const parts = threadId.split(\":\");\n if (parts.length !== 3 || parts[0] !== \"teams\") {\n throw new ValidationError(\n \"teams\",\n `Invalid Teams thread ID: ${threadId}`,\n );\n }\n const conversationId = Buffer.from(\n parts[1] as string,\n \"base64url\",\n ).toString(\"utf-8\");\n const serviceUrl = Buffer.from(parts[2] as string, \"base64url\").toString(\n \"utf-8\",\n );\n return { conversationId, serviceUrl };\n }\n\n parseMessage(raw: unknown): Message<unknown> {\n const activity = raw as Activity;\n const threadId = this.encodeThreadId({\n conversationId: activity.conversation?.id || \"\",\n serviceUrl: activity.serviceUrl || \"\",\n });\n return this.parseTeamsMessage(activity, threadId);\n }\n\n /**\n * Check if a Teams activity is from this bot.\n *\n * Teams bot IDs can appear in different formats:\n * - Just the app ID: \"abc123-def456-...\"\n * - With prefix: \"28:abc123-def456-...\"\n *\n * We check both exact match and suffix match (after colon delimiter)\n * to handle all formats safely.\n */\n private isMessageFromSelf(activity: Activity): boolean {\n const fromId = activity.from?.id;\n if (!fromId || !this.config.appId) {\n return false;\n }\n\n // Exact match (bot ID is just the app ID)\n if (fromId === this.config.appId) {\n return true;\n }\n\n // Teams format: \"28:{appId}\" or similar prefix patterns\n // Check if it ends with our appId after a colon delimiter\n if (fromId.endsWith(`:${this.config.appId}`)) {\n return true;\n }\n\n return false;\n }\n\n renderFormatted(content: FormattedContent): string {\n return this.formatConverter.fromAst(content);\n }\n\n /**\n * Convert Teams/BotBuilder errors to standardized AdapterError types.\n */\n private handleTeamsError(error: unknown, operation: string): never {\n // Handle BotBuilder errors with status codes\n if (error && typeof error === \"object\") {\n const err = error as Record<string, unknown>;\n\n // Check for HTTP status code\n const statusCode =\n (err.statusCode as number) ||\n (err.status as number) ||\n (err.code as number);\n\n if (statusCode === 401 || statusCode === 403) {\n throw new AuthenticationError(\n \"teams\",\n `Authentication failed for ${operation}: ${err.message || \"unauthorized\"}`,\n );\n }\n\n if (statusCode === 404) {\n throw new NetworkError(\n \"teams\",\n `Resource not found during ${operation}: conversation or message may no longer exist`,\n error instanceof Error ? error : undefined,\n );\n }\n\n if (statusCode === 429) {\n const retryAfter =\n typeof err.retryAfter === \"number\" ? err.retryAfter : undefined;\n throw new AdapterRateLimitError(\"teams\", retryAfter);\n }\n\n // Permission errors\n if (\n statusCode === 403 ||\n (err.message &&\n typeof err.message === \"string\" &&\n err.message.toLowerCase().includes(\"permission\"))\n ) {\n throw new PermissionError(\"teams\", operation);\n }\n\n // Generic error with message\n if (err.message && typeof err.message === \"string\") {\n throw new NetworkError(\n \"teams\",\n `Teams API error during ${operation}: ${err.message}`,\n error instanceof Error ? error : undefined,\n );\n }\n }\n\n // Fallback for unknown error types\n throw new NetworkError(\n \"teams\",\n `Teams API error during ${operation}: ${String(error)}`,\n error instanceof Error ? error : undefined,\n );\n }\n}\n\nexport function createTeamsAdapter(config: TeamsAdapterConfig): TeamsAdapter {\n return new TeamsAdapter(config);\n}\n\n// Re-export card converter for advanced use\nexport { cardToAdaptiveCard, cardToFallbackText } from \"./cards\";\nexport { TeamsFormatConverter } from \"./markdown\";\n","/**\n * Teams Adaptive Card converter for cross-platform cards.\n *\n * Converts CardElement to Microsoft Adaptive Cards format.\n * @see https://adaptivecards.io/\n */\n\nimport {\n createEmojiConverter,\n mapButtonStyle,\n cardToFallbackText as sharedCardToFallbackText,\n} from \"@chat-adapter/shared\";\nimport type {\n ActionsElement,\n ButtonElement,\n CardChild,\n CardElement,\n DividerElement,\n FieldsElement,\n ImageElement,\n SectionElement,\n TextElement,\n} from \"chat\";\n\n/**\n * Convert emoji placeholders in text to Teams format.\n */\nconst convertEmoji = createEmojiConverter(\"teams\");\n\n// Adaptive Card types (simplified)\nexport interface AdaptiveCard {\n type: \"AdaptiveCard\";\n $schema: string;\n version: string;\n body: AdaptiveCardElement[];\n actions?: AdaptiveCardAction[];\n}\n\nexport interface AdaptiveCardElement {\n type: string;\n [key: string]: unknown;\n}\n\nexport interface AdaptiveCardAction {\n type: string;\n title: string;\n data?: Record<string, unknown>;\n style?: string;\n}\n\nconst ADAPTIVE_CARD_SCHEMA =\n \"http://adaptivecards.io/schemas/adaptive-card.json\";\nconst ADAPTIVE_CARD_VERSION = \"1.4\";\n\n/**\n * Convert a CardElement to a Teams Adaptive Card.\n */\nexport function cardToAdaptiveCard(card: CardElement): AdaptiveCard {\n const body: AdaptiveCardElement[] = [];\n const actions: AdaptiveCardAction[] = [];\n\n // Add title as TextBlock\n if (card.title) {\n body.push({\n type: \"TextBlock\",\n text: convertEmoji(card.title),\n weight: \"bolder\",\n size: \"large\",\n wrap: true,\n });\n }\n\n // Add subtitle as TextBlock\n if (card.subtitle) {\n body.push({\n type: \"TextBlock\",\n text: convertEmoji(card.subtitle),\n isSubtle: true,\n wrap: true,\n });\n }\n\n // Add header image if present\n if (card.imageUrl) {\n body.push({\n type: \"Image\",\n url: card.imageUrl,\n size: \"stretch\",\n });\n }\n\n // Convert children\n for (const child of card.children) {\n const result = convertChildToAdaptive(child);\n body.push(...result.elements);\n actions.push(...result.actions);\n }\n\n const adaptiveCard: AdaptiveCard = {\n type: \"AdaptiveCard\",\n $schema: ADAPTIVE_CARD_SCHEMA,\n version: ADAPTIVE_CARD_VERSION,\n body,\n };\n\n if (actions.length > 0) {\n adaptiveCard.actions = actions;\n }\n\n return adaptiveCard;\n}\n\ninterface ConvertResult {\n elements: AdaptiveCardElement[];\n actions: AdaptiveCardAction[];\n}\n\n/**\n * Convert a card child element to Adaptive Card elements.\n */\nfunction convertChildToAdaptive(child: CardChild): ConvertResult {\n switch (child.type) {\n case \"text\":\n return { elements: [convertTextToElement(child)], actions: [] };\n case \"image\":\n return { elements: [convertImageToElement(child)], actions: [] };\n case \"divider\":\n return { elements: [convertDividerToElement(child)], actions: [] };\n case \"actions\":\n return convertActionsToElements(child);\n case \"section\":\n return convertSectionToElements(child);\n case \"fields\":\n return { elements: [convertFieldsToElement(child)], actions: [] };\n default:\n return { elements: [], actions: [] };\n }\n}\n\nfunction convertTextToElement(element: TextElement): AdaptiveCardElement {\n const textBlock: AdaptiveCardElement = {\n type: \"TextBlock\",\n text: convertEmoji(element.content),\n wrap: true,\n };\n\n if (element.style === \"bold\") {\n textBlock.weight = \"bolder\";\n } else if (element.style === \"muted\") {\n textBlock.isSubtle = true;\n }\n\n return textBlock;\n}\n\nfunction convertImageToElement(element: ImageElement): AdaptiveCardElement {\n return {\n type: \"Image\",\n url: element.url,\n altText: element.alt || \"Image\",\n size: \"auto\",\n };\n}\n\nfunction convertDividerToElement(\n _element: DividerElement,\n): AdaptiveCardElement {\n // Adaptive Cards don't have a native divider, use a separator container\n return {\n type: \"Container\",\n separator: true,\n items: [],\n };\n}\n\nfunction convertActionsToElements(element: ActionsElement): ConvertResult {\n // In Adaptive Cards, actions go at the card level, not inline\n const actions: AdaptiveCardAction[] = element.children.map((button) =>\n convertButtonToAction(button),\n );\n\n return { elements: [], actions };\n}\n\nfunction convertButtonToAction(button: ButtonElement): AdaptiveCardAction {\n const action: AdaptiveCardAction = {\n type: \"Action.Submit\",\n title: convertEmoji(button.label),\n data: {\n actionId: button.id,\n value: button.value,\n },\n };\n\n const style = mapButtonStyle(button.style, \"teams\");\n if (style) {\n action.style = style;\n }\n\n return action;\n}\n\nfunction convertSectionToElements(element: SectionElement): ConvertResult {\n const elements: AdaptiveCardElement[] = [];\n const actions: AdaptiveCardAction[] = [];\n\n // Wrap section in a container\n const containerItems: AdaptiveCardElement[] = [];\n\n for (const child of element.children) {\n const result = convertChildToAdaptive(child);\n containerItems.push(...result.elements);\n actions.push(...result.actions);\n }\n\n if (containerItems.length > 0) {\n elements.push({\n type: \"Container\",\n items: containerItems,\n });\n }\n\n return { elements, actions };\n}\n\nfunction convertFieldsToElement(element: FieldsElement): AdaptiveCardElement {\n // Use FactSet for key-value pairs\n const facts = element.children.map((field) => ({\n title: convertEmoji(field.label),\n value: convertEmoji(field.value),\n }));\n\n return {\n type: \"FactSet\",\n facts,\n };\n}\n\n/**\n * Generate fallback text from a card element.\n * Used when adaptive cards aren't supported.\n */\nexport function cardToFallbackText(card: CardElement): string {\n return sharedCardToFallbackText(card, {\n boldFormat: \"**\",\n lineBreak: \"\\n\\n\",\n platform: \"teams\",\n });\n}\n","/**\n * Teams-specific format conversion using AST-based parsing.\n *\n * Teams supports a subset of HTML for formatting:\n * - Bold: <b> or <strong>\n * - Italic: <i> or <em>\n * - Strikethrough: <s> or <strike>\n * - Links: <a href=\"url\">text</a>\n * - Code: <pre> and <code>\n *\n * Teams also accepts standard markdown in most cases.\n */\n\nimport {\n type AdapterPostableMessage,\n BaseFormatConverter,\n type Content,\n getNodeChildren,\n getNodeValue,\n isBlockquoteNode,\n isCodeNode,\n isDeleteNode,\n isEmphasisNode,\n isInlineCodeNode,\n isLinkNode,\n isListItemNode,\n isListNode,\n isParagraphNode,\n isStrongNode,\n isTextNode,\n parseMarkdown,\n type Root,\n} from \"chat\";\n\nexport class TeamsFormatConverter extends BaseFormatConverter {\n /**\n * Convert @mentions to Teams format in plain text.\n * @name → <at>name</at>\n */\n private convertMentionsToTeams(text: string): string {\n return text.replace(/@(\\w+)/g, \"<at>$1</at>\");\n }\n\n /**\n * Override renderPostable to convert @mentions in plain strings.\n */\n override renderPostable(message: AdapterPostableMessage): string {\n if (typeof message === \"string\") {\n return this.convertMentionsToTeams(message);\n }\n if (\"raw\" in message) {\n return this.convertMentionsToTeams(message.raw);\n }\n if (\"markdown\" in message) {\n return this.fromAst(parseMarkdown(message.markdown));\n }\n if (\"ast\" in message) {\n return this.fromAst(message.ast);\n }\n return \"\";\n }\n\n /**\n * Render an AST to Teams format.\n * Teams accepts standard markdown, so we just stringify cleanly.\n */\n fromAst(ast: Root): string {\n return this.fromAstWithNodeConverter(ast, (node) => this.nodeToTeams(node));\n }\n\n /**\n * Parse Teams message into an AST.\n * Converts Teams HTML/mentions to standard markdown format.\n */\n toAst(teamsText: string): Root {\n // Convert Teams HTML to markdown, then parse\n let markdown = teamsText;\n\n // Convert @mentions from Teams format: <at>Name</at> -> @Name\n markdown = markdown.replace(/<at>([^<]+)<\\/at>/gi, \"@$1\");\n\n // Convert HTML tags to markdown\n // Bold: <b>, <strong> -> **text**\n markdown = markdown.replace(\n /<(b|strong)>([^<]+)<\\/(b|strong)>/gi,\n \"**$2**\",\n );\n\n // Italic: <i>, <em> -> _text_\n markdown = markdown.replace(/<(i|em)>([^<]+)<\\/(i|em)>/gi, \"_$2_\");\n\n // Strikethrough: <s>, <strike> -> ~~text~~\n markdown = markdown.replace(\n /<(s|strike)>([^<]+)<\\/(s|strike)>/gi,\n \"~~$2~~\",\n );\n\n // Links: <a href=\"url\">text</a> -> [text](url)\n markdown = markdown.replace(\n /<a[^>]+href=\"([^\"]+)\"[^>]*>([^<]+)<\\/a>/gi,\n \"[$2]($1)\",\n );\n\n // Code: <code>text</code> -> `text`\n markdown = markdown.replace(/<code>([^<]+)<\\/code>/gi, \"`$1`\");\n\n // Pre: <pre>text</pre> -> ```text```\n markdown = markdown.replace(/<pre>([^<]+)<\\/pre>/gi, \"```\\n$1\\n```\");\n\n // Strip remaining HTML tags\n markdown = markdown.replace(/<[^>]+>/g, \"\");\n\n // Decode HTML entities\n markdown = markdown\n .replace(/&lt;/g, \"<\")\n .replace(/&gt;/g, \">\")\n .replace(/&amp;/g, \"&\")\n .replace(/&quot;/g, '\"')\n .replace(/&#39;/g, \"'\");\n\n return parseMarkdown(markdown);\n }\n\n private nodeToTeams(node: Content): string {\n // Use type guards for type-safe node handling\n if (isParagraphNode(node)) {\n return getNodeChildren(node)\n .map((child) => this.nodeToTeams(child))\n .join(\"\");\n }\n\n if (isTextNode(node)) {\n // Convert @mentions to Teams format <at>mention</at>\n return node.value.replace(/@(\\w+)/g, \"<at>$1</at>\");\n }\n\n if (isStrongNode(node)) {\n // Teams supports **text** markdown\n const content = getNodeChildren(node)\n .map((child) => this.nodeToTeams(child))\n .join(\"\");\n return `**${content}**`;\n }\n\n if (isEmphasisNode(node)) {\n // Teams supports _text_ markdown\n const content = getNodeChildren(node)\n .map((child) => this.nodeToTeams(child))\n .join(\"\");\n return `_${content}_`;\n }\n\n if (isDeleteNode(node)) {\n // Teams supports ~~text~~ markdown\n const content = getNodeChildren(node)\n .map((child) => this.nodeToTeams(child))\n .join(\"\");\n return `~~${content}~~`;\n }\n\n if (isInlineCodeNode(node)) {\n return `\\`${node.value}\\``;\n }\n\n if (isCodeNode(node)) {\n return `\\`\\`\\`${node.lang || \"\"}\\n${node.value}\\n\\`\\`\\``;\n }\n\n if (isLinkNode(node)) {\n const linkText = getNodeChildren(node)\n .map((child) => this.nodeToTeams(child))\n .join(\"\");\n // Standard markdown link format\n return `[${linkText}](${node.url})`;\n }\n\n if (isBlockquoteNode(node)) {\n return getNodeChildren(node)\n .map((child) => `> ${this.nodeToTeams(child)}`)\n .join(\"\\n\");\n }\n\n if (isListNode(node)) {\n return getNodeChildren(node)\n .map((item, i) => {\n const prefix = node.ordered ? `${i + 1}.` : \"-\";\n const content = getNodeChildren(item)\n .map((child) => this.nodeToTeams(child))\n .join(\"\");\n return `${prefix} ${content}`;\n })\n .join(\"\\n\");\n }\n\n if (isListItemNode(node)) {\n return getNodeChildren(node)\n .map((child) => this.nodeToTeams(child))\n .join(\"\");\n }\n\n if (node.type === \"break\") {\n return \"\\n\";\n }\n\n if (node.type === \"thematicBreak\") {\n return \"---\";\n }\n\n // For unsupported nodes, try to extract text\n const children = getNodeChildren(node);\n if (children.length > 0) {\n return children.map((child) => this.nodeToTeams(child)).join(\"\");\n }\n return getNodeValue(node);\n }\n}\n"],"mappings":";AAAA,SAAS,8BAA8B;AACvC,SAAS,cAAc;AACvB;AAAA,EACE;AAAA,OAEK;AAEP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AAaP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAkBP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;;;ACpDP;AAAA,EACE;AAAA,EACA;AAAA,EACA,sBAAsB;AAAA,OACjB;AAgBP,IAAM,eAAe,qBAAqB,OAAO;AAuBjD,IAAM,uBACJ;AACF,IAAM,wBAAwB;AAKvB,SAAS,mBAAmB,MAAiC;AAClE,QAAM,OAA8B,CAAC;AACrC,QAAM,UAAgC,CAAC;AAGvC,MAAI,KAAK,OAAO;AACd,SAAK,KAAK;AAAA,MACR,MAAM;AAAA,MACN,MAAM,aAAa,KAAK,KAAK;AAAA,MAC7B,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AAGA,MAAI,KAAK,UAAU;AACjB,SAAK,KAAK;AAAA,MACR,MAAM;AAAA,MACN,MAAM,aAAa,KAAK,QAAQ;AAAA,MAChC,UAAU;AAAA,MACV,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AAGA,MAAI,KAAK,UAAU;AACjB,SAAK,KAAK;AAAA,MACR,MAAM;AAAA,MACN,KAAK,KAAK;AAAA,MACV,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AAGA,aAAW,SAAS,KAAK,UAAU;AACjC,UAAM,SAAS,uBAAuB,KAAK;AAC3C,SAAK,KAAK,GAAG,OAAO,QAAQ;AAC5B,YAAQ,KAAK,GAAG,OAAO,OAAO;AAAA,EAChC;AAEA,QAAM,eAA6B;AAAA,IACjC,MAAM;AAAA,IACN,SAAS;AAAA,IACT,SAAS;AAAA,IACT;AAAA,EACF;AAEA,MAAI,QAAQ,SAAS,GAAG;AACtB,iBAAa,UAAU;AAAA,EACzB;AAEA,SAAO;AACT;AAUA,SAAS,uBAAuB,OAAiC;AAC/D,UAAQ,MAAM,MAAM;AAAA,IAClB,KAAK;AACH,aAAO,EAAE,UAAU,CAAC,qBAAqB,KAAK,CAAC,GAAG,SAAS,CAAC,EAAE;AAAA,IAChE,KAAK;AACH,aAAO,EAAE,UAAU,CAAC,sBAAsB,KAAK,CAAC,GAAG,SAAS,CAAC,EAAE;AAAA,IACjE,KAAK;AACH,aAAO,EAAE,UAAU,CAAC,wBAAwB,KAAK,CAAC,GAAG,SAAS,CAAC,EAAE;AAAA,IACnE,KAAK;AACH,aAAO,yBAAyB,KAAK;AAAA,IACvC,KAAK;AACH,aAAO,yBAAyB,KAAK;AAAA,IACvC,KAAK;AACH,aAAO,EAAE,UAAU,CAAC,uBAAuB,KAAK,CAAC,GAAG,SAAS,CAAC,EAAE;AAAA,IAClE;AACE,aAAO,EAAE,UAAU,CAAC,GAAG,SAAS,CAAC,EAAE;AAAA,EACvC;AACF;AAEA,SAAS,qBAAqB,SAA2C;AACvE,QAAM,YAAiC;AAAA,IACrC,MAAM;AAAA,IACN,MAAM,aAAa,QAAQ,OAAO;AAAA,IAClC,MAAM;AAAA,EACR;AAEA,MAAI,QAAQ,UAAU,QAAQ;AAC5B,cAAU,SAAS;AAAA,EACrB,WAAW,QAAQ,UAAU,SAAS;AACpC,cAAU,WAAW;AAAA,EACvB;AAEA,SAAO;AACT;AAEA,SAAS,sBAAsB,SAA4C;AACzE,SAAO;AAAA,IACL,MAAM;AAAA,IACN,KAAK,QAAQ;AAAA,IACb,SAAS,QAAQ,OAAO;AAAA,IACxB,MAAM;AAAA,EACR;AACF;AAEA,SAAS,wBACP,UACqB;AAErB,SAAO;AAAA,IACL,MAAM;AAAA,IACN,WAAW;AAAA,IACX,OAAO,CAAC;AAAA,EACV;AACF;AAEA,SAAS,yBAAyB,SAAwC;AAExE,QAAM,UAAgC,QAAQ,SAAS;AAAA,IAAI,CAAC,WAC1D,sBAAsB,MAAM;AAAA,EAC9B;AAEA,SAAO,EAAE,UAAU,CAAC,GAAG,QAAQ;AACjC;AAEA,SAAS,sBAAsB,QAA2C;AACxE,QAAM,SAA6B;AAAA,IACjC,MAAM;AAAA,IACN,OAAO,aAAa,OAAO,KAAK;AAAA,IAChC,MAAM;AAAA,MACJ,UAAU,OAAO;AAAA,MACjB,OAAO,OAAO;AAAA,IAChB;AAAA,EACF;AAEA,QAAM,QAAQ,eAAe,OAAO,OAAO,OAAO;AAClD,MAAI,OAAO;AACT,WAAO,QAAQ;AAAA,EACjB;AAEA,SAAO;AACT;AAEA,SAAS,yBAAyB,SAAwC;AACxE,QAAM,WAAkC,CAAC;AACzC,QAAM,UAAgC,CAAC;AAGvC,QAAM,iBAAwC,CAAC;AAE/C,aAAW,SAAS,QAAQ,UAAU;AACpC,UAAM,SAAS,uBAAuB,KAAK;AAC3C,mBAAe,KAAK,GAAG,OAAO,QAAQ;AACtC,YAAQ,KAAK,GAAG,OAAO,OAAO;AAAA,EAChC;AAEA,MAAI,eAAe,SAAS,GAAG;AAC7B,aAAS,KAAK;AAAA,MACZ,MAAM;AAAA,MACN,OAAO;AAAA,IACT,CAAC;AAAA,EACH;AAEA,SAAO,EAAE,UAAU,QAAQ;AAC7B;AAEA,SAAS,uBAAuB,SAA6C;AAE3E,QAAM,QAAQ,QAAQ,SAAS,IAAI,CAAC,WAAW;AAAA,IAC7C,OAAO,aAAa,MAAM,KAAK;AAAA,IAC/B,OAAO,aAAa,MAAM,KAAK;AAAA,EACjC,EAAE;AAEF,SAAO;AAAA,IACL,MAAM;AAAA,IACN;AAAA,EACF;AACF;AAMO,SAAS,mBAAmB,MAA2B;AAC5D,SAAO,yBAAyB,MAAM;AAAA,IACpC,YAAY;AAAA,IACZ,WAAW;AAAA,IACX,UAAU;AAAA,EACZ,CAAC;AACH;;;AC3OA;AAAA,EAEE;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AAEA,IAAM,uBAAN,cAAmC,oBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA,EAKpD,uBAAuB,MAAsB;AACnD,WAAO,KAAK,QAAQ,WAAW,aAAa;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA,EAKS,eAAe,SAAyC;AAC/D,QAAI,OAAO,YAAY,UAAU;AAC/B,aAAO,KAAK,uBAAuB,OAAO;AAAA,IAC5C;AACA,QAAI,SAAS,SAAS;AACpB,aAAO,KAAK,uBAAuB,QAAQ,GAAG;AAAA,IAChD;AACA,QAAI,cAAc,SAAS;AACzB,aAAO,KAAK,QAAQ,cAAc,QAAQ,QAAQ,CAAC;AAAA,IACrD;AACA,QAAI,SAAS,SAAS;AACpB,aAAO,KAAK,QAAQ,QAAQ,GAAG;AAAA,IACjC;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,QAAQ,KAAmB;AACzB,WAAO,KAAK,yBAAyB,KAAK,CAAC,SAAS,KAAK,YAAY,IAAI,CAAC;AAAA,EAC5E;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,WAAyB;AAE7B,QAAI,WAAW;AAGf,eAAW,SAAS,QAAQ,uBAAuB,KAAK;AAIxD,eAAW,SAAS;AAAA,MAClB;AAAA,MACA;AAAA,IACF;AAGA,eAAW,SAAS,QAAQ,+BAA+B,MAAM;AAGjE,eAAW,SAAS;AAAA,MAClB;AAAA,MACA;AAAA,IACF;AAGA,eAAW,SAAS;AAAA,MAClB;AAAA,MACA;AAAA,IACF;AAGA,eAAW,SAAS,QAAQ,2BAA2B,MAAM;AAG7D,eAAW,SAAS,QAAQ,yBAAyB,cAAc;AAGnE,eAAW,SAAS,QAAQ,YAAY,EAAE;AAG1C,eAAW,SACR,QAAQ,SAAS,GAAG,EACpB,QAAQ,SAAS,GAAG,EACpB,QAAQ,UAAU,GAAG,EACrB,QAAQ,WAAW,GAAG,EACtB,QAAQ,UAAU,GAAG;AAExB,WAAO,cAAc,QAAQ;AAAA,EAC/B;AAAA,EAEQ,YAAY,MAAuB;AAEzC,QAAI,gBAAgB,IAAI,GAAG;AACzB,aAAO,gBAAgB,IAAI,EACxB,IAAI,CAAC,UAAU,KAAK,YAAY,KAAK,CAAC,EACtC,KAAK,EAAE;AAAA,IACZ;AAEA,QAAI,WAAW,IAAI,GAAG;AAEpB,aAAO,KAAK,MAAM,QAAQ,WAAW,aAAa;AAAA,IACpD;AAEA,QAAI,aAAa,IAAI,GAAG;AAEtB,YAAM,UAAU,gBAAgB,IAAI,EACjC,IAAI,CAAC,UAAU,KAAK,YAAY,KAAK,CAAC,EACtC,KAAK,EAAE;AACV,aAAO,KAAK,OAAO;AAAA,IACrB;AAEA,QAAI,eAAe,IAAI,GAAG;AAExB,YAAM,UAAU,gBAAgB,IAAI,EACjC,IAAI,CAAC,UAAU,KAAK,YAAY,KAAK,CAAC,EACtC,KAAK,EAAE;AACV,aAAO,IAAI,OAAO;AAAA,IACpB;AAEA,QAAI,aAAa,IAAI,GAAG;AAEtB,YAAM,UAAU,gBAAgB,IAAI,EACjC,IAAI,CAAC,UAAU,KAAK,YAAY,KAAK,CAAC,EACtC,KAAK,EAAE;AACV,aAAO,KAAK,OAAO;AAAA,IACrB;AAEA,QAAI,iBAAiB,IAAI,GAAG;AAC1B,aAAO,KAAK,KAAK,KAAK;AAAA,IACxB;AAEA,QAAI,WAAW,IAAI,GAAG;AACpB,aAAO,SAAS,KAAK,QAAQ,EAAE;AAAA,EAAK,KAAK,KAAK;AAAA;AAAA,IAChD;AAEA,QAAI,WAAW,IAAI,GAAG;AACpB,YAAM,WAAW,gBAAgB,IAAI,EAClC,IAAI,CAAC,UAAU,KAAK,YAAY,KAAK,CAAC,EACtC,KAAK,EAAE;AAEV,aAAO,IAAI,QAAQ,KAAK,KAAK,GAAG;AAAA,IAClC;AAEA,QAAI,iBAAiB,IAAI,GAAG;AAC1B,aAAO,gBAAgB,IAAI,EACxB,IAAI,CAAC,UAAU,KAAK,KAAK,YAAY,KAAK,CAAC,EAAE,EAC7C,KAAK,IAAI;AAAA,IACd;AAEA,QAAI,WAAW,IAAI,GAAG;AACpB,aAAO,gBAAgB,IAAI,EACxB,IAAI,CAAC,MAAM,MAAM;AAChB,cAAM,SAAS,KAAK,UAAU,GAAG,IAAI,CAAC,MAAM;AAC5C,cAAM,UAAU,gBAAgB,IAAI,EACjC,IAAI,CAAC,UAAU,KAAK,YAAY,KAAK,CAAC,EACtC,KAAK,EAAE;AACV,eAAO,GAAG,MAAM,IAAI,OAAO;AAAA,MAC7B,CAAC,EACA,KAAK,IAAI;AAAA,IACd;AAEA,QAAI,eAAe,IAAI,GAAG;AACxB,aAAO,gBAAgB,IAAI,EACxB,IAAI,CAAC,UAAU,KAAK,YAAY,KAAK,CAAC,EACtC,KAAK,EAAE;AAAA,IACZ;AAEA,QAAI,KAAK,SAAS,SAAS;AACzB,aAAO;AAAA,IACT;AAEA,QAAI,KAAK,SAAS,iBAAiB;AACjC,aAAO;AAAA,IACT;AAGA,UAAM,WAAW,gBAAgB,IAAI;AACrC,QAAI,SAAS,SAAS,GAAG;AACvB,aAAO,SAAS,IAAI,CAAC,UAAU,KAAK,YAAY,KAAK,CAAC,EAAE,KAAK,EAAE;AAAA,IACjE;AACA,WAAO,aAAa,IAAI;AAAA,EAC1B;AACF;;;AFvMA,IAAM,yBAAN,cAAqC,aAAa;AAAA,EAChD,eACE,YACA,UACA,OACA;AACA,WAAO,KAAK,gBAAgB,YAAY,UAAU,KAAK;AAAA,EACzD;AACF;AAiGO,IAAM,eAAN,MAA8D;AAAA,EAC1D,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EAED;AAAA,EACA,cAA6B;AAAA,EAC7B,OAA4B;AAAA,EAC5B;AAAA,EACA,kBAAkB,IAAI,qBAAqB;AAAA,EAC3C;AAAA,EAER,YAAY,QAA4B;AACtC,SAAK,SAAS;AACd,SAAK,SAAS,OAAO;AACrB,SAAK,WAAW,OAAO,YAAY;AAEnC,QAAI,OAAO,YAAY,kBAAkB,CAAC,OAAO,aAAa;AAC5D,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAGA,UAAM,OAAO,IAAI,wCAAwC;AAAA,MACvD,gBAAgB,OAAO;AAAA,MACvB,sBAAsB,OAAO;AAAA,MAC7B,kBAAkB,OAAO,WAAW;AAAA,MACpC,sBACE,OAAO,YAAY,iBAAiB,OAAO,cAAc;AAAA,IAC7D,CAAC;AAED,SAAK,aAAa,IAAI,uBAAuB,IAAI;AAGjD,QAAI,OAAO,aAAa;AACtB,YAAM,aAAa,IAAI;AAAA,QACrB,OAAO;AAAA,QACP,OAAO;AAAA,QACP,OAAO;AAAA,MACT;AAEA,YAAM,eAAe,IAAI;AAAA,QACvB;AAAA,QACA;AAAA,UACE,QAAQ,CAAC,sCAAsC;AAAA,QACjD;AAAA,MACF;AAEA,WAAK,cAAc,OAAO,mBAAmB,EAAE,aAAa,CAAC;AAAA,IAC/D;AAAA,EACF;AAAA,EAEA,MAAM,WAAW,MAAmC;AAClD,SAAK,OAAO;AAAA,EACd;AAAA,EAEA,MAAM,cACJ,SACA,SACmB;AACnB,UAAM,OAAO,MAAM,QAAQ,KAAK;AAChC,SAAK,OAAO,MAAM,0BAA0B,EAAE,KAAK,CAAC;AAEpD,QAAI;AACJ,QAAI;AACF,iBAAW,KAAK,MAAM,IAAI;AAAA,IAC5B,SAAS,GAAG;AACV,WAAK,OAAO,MAAM,gCAAgC,EAAE,OAAO,EAAE,CAAC;AAC9D,aAAO,IAAI,SAAS,gBAAgB,EAAE,QAAQ,IAAI,CAAC;AAAA,IACrD;AAGA,UAAM,aAAa,QAAQ,QAAQ,IAAI,eAAe,KAAK;AAE3D,QAAI;AAGF,YAAM,KAAK,WAAW;AAAA,QACpB;AAAA,QACA;AAAA,QACA,OAAO,YAAY;AACjB,gBAAM,KAAK,WAAW,SAAS,OAAO;AAAA,QACxC;AAAA,MACF;AAEA,aAAO,IAAI,SAAS,KAAK,UAAU,CAAC,CAAC,GAAG;AAAA,QACtC,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAChD,CAAC;AAAA,IACH,SAAS,OAAO;AACd,WAAK,OAAO,MAAM,6BAA6B,EAAE,MAAM,CAAC;AACxD,aAAO,IAAI,SAAS,KAAK,UAAU,EAAE,OAAO,iBAAiB,CAAC,GAAG;AAAA,QAC/D,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAChD,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,MAAc,WACZ,SACA,SACe;AACf,QAAI,CAAC,KAAK,MAAM;AACd,WAAK,OAAO,KAAK,+CAA+C;AAChE;AAAA,IACF;AAEA,UAAM,WAAW,QAAQ;AAGzB,QAAI,SAAS,MAAM,MAAM,SAAS,YAAY;AAC5C,YAAM,SAAS,SAAS,KAAK;AAC7B,YAAM,cAAc,SAAS;AAK7B,YAAM,WAAW,aAAa,QAAQ;AACtC,YAAM,MAAM,KAAK,KAAK,KAAK,KAAK;AAGhC,WAAK,KACF,SAAS,EACT,IAAI,oBAAoB,MAAM,IAAI,SAAS,YAAY,GAAG,EAC1D,MAAM,CAAC,QAAQ;AACd,aAAK,OAAO,MAAM,8BAA8B;AAAA,UAC9C;AAAA,UACA,OAAO;AAAA,QACT,CAAC;AAAA,MACH,CAAC;AACH,UAAI,UAAU;AACZ,aAAK,KACF,SAAS,EACT,IAAI,kBAAkB,MAAM,IAAI,UAAU,GAAG,EAC7C,MAAM,CAAC,QAAQ;AACd,eAAK,OAAO,MAAM,4BAA4B;AAAA,YAC5C;AAAA,YACA,OAAO;AAAA,UACT,CAAC;AAAA,QACH,CAAC;AAAA,MACL;AAOA,YAAM,OAAO,aAAa;AAG1B,YAAM,iBAAiB,MAAM;AAC7B,YAAM,eAAe,MAAM;AAC3B,YAAM,iBAAiB,SAAS,cAAc,MAAM;AACpD,YAAM,gBAAgB,eAAe,QAAQ,kBAAkB,EAAE;AAEjE,UAAI,kBAAkB,aAAa,SAAS,MAAM,UAAU;AAE1D,cAAMA,WAA+B;AAAA,UACnC,QAAQ;AAAA;AAAA,UACR,WAAW,YAAY,QAAQ;AAAA,UAC/B;AAAA,QACF;AACA,cAAM,cAAc,KAAK,UAAUA,QAAO;AAG1C,aAAK,KACF,SAAS,EACT,IAAI,wBAAwB,aAAa,IAAI,aAAa,GAAG,EAC7D,MAAM,CAAC,QAAQ;AACd,eAAK,OAAO,MAAM,mCAAmC;AAAA,YACnD,gBAAgB;AAAA,YAChB,OAAO;AAAA,UACT,CAAC;AAAA,QACH,CAAC;AAIH,YAAI,cAAc;AAChB,eAAK,KACF,SAAS,EACT,IAAI,qBAAqB,YAAY,IAAI,aAAa,GAAG,EACzD,MAAM,CAAC,QAAQ;AACd,iBAAK,OAAO,MAAM,gCAAgC;AAAA,cAChD;AAAA,cACA,OAAO;AAAA,YACT,CAAC;AAAA,UACH,CAAC;AAAA,QACL;AAEA,aAAK,OAAO;AAAA,UACV;AAAA,UACA;AAAA,YACE,cAAc,SAAS;AAAA,YACvB,gBAAgB;AAAA,YAChB;AAAA,YACA,UAAUA,SAAQ;AAAA,YAClB,WAAWA,SAAQ;AAAA,UACrB;AAAA,QACF;AAAA,MACF,WAAW,gBAAgB,aAAa,SAAS,MAAM,UAAU;AAE/D,cAAM,oBAAoB,MAAM,KAAK,KAClC,SAAS,EACT,IAAY,qBAAqB,YAAY,EAAE;AAElD,YAAI,mBAAmB;AAErB,eAAK,KACF,SAAS,EACT;AAAA,YACC,wBAAwB,aAAa;AAAA,YACrC;AAAA,YACA;AAAA,UACF,EACC,MAAM,CAAC,QAAQ;AACd,iBAAK,OAAO,MAAM,6CAA6C;AAAA,cAC7D,gBAAgB;AAAA,cAChB,OAAO;AAAA,YACT,CAAC;AAAA,UACH,CAAC;AACH,eAAK,OAAO,KAAK,4CAA4C;AAAA,YAC3D,gBAAgB;AAAA,YAChB;AAAA,UACF,CAAC;AAAA,QACH,OAAO;AAGL,cAAI;AACF,kBAAM,cAAc,MAAM,UAAU,eAAe,OAAO;AAC1D,gBAAI,aAAa,YAAY;AAC3B,oBAAM,iBAAsC;AAAA,gBAC1C,QAAQ,YAAY;AAAA,gBACpB,WAAW,YAAY,QAAQ;AAAA,gBAC/B;AAAA,cACF;AACA,oBAAM,cAAc,KAAK,UAAU,cAAc;AAGjD,mBAAK,KACF,SAAS,EACT,IAAI,wBAAwB,aAAa,IAAI,aAAa,GAAG,EAC7D,MAAM,CAAC,QAAQ;AACd,qBAAK,OAAO,MAAM,2CAA2C;AAAA,kBAC3D,gBAAgB;AAAA,kBAChB,OAAO;AAAA,gBACT,CAAC;AAAA,cACH,CAAC;AAGH,mBAAK,KACF,SAAS,EACT,IAAI,qBAAqB,YAAY,IAAI,aAAa,GAAG,EACzD,MAAM,CAAC,QAAQ;AACd,qBAAK,OAAO,MAAM,wCAAwC;AAAA,kBACxD;AAAA,kBACA,OAAO;AAAA,gBACT,CAAC;AAAA,cACH,CAAC;AAEH,mBAAK,OAAO;AAAA,gBACV;AAAA,gBACA;AAAA,kBACE,gBAAgB;AAAA,kBAChB;AAAA,kBACA,UAAU,YAAY;AAAA,kBACtB,UAAU,YAAY;AAAA,gBACxB;AAAA,cACF;AAAA,YACF;AAAA,UACF,SAAS,OAAO;AAEd,iBAAK,OAAO;AAAA,cACV;AAAA,cACA,EAAE,cAAc,MAAM;AAAA,YACxB;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,QAAI,SAAS,SAAS,cAAc,iBAAiB;AACnD,WAAK,uBAAuB,UAAU,OAAO;AAC7C;AAAA,IACF;AAGA,QAAI,SAAS,SAAS,cAAc,QAAQ;AAC1C,YAAM,KAAK,qBAAqB,SAAS,OAAO;AAChD;AAAA,IACF;AAGA,QAAI,SAAS,SAAS,cAAc,SAAS;AAC3C,WAAK,OAAO,MAAM,iCAAiC;AAAA,QACjD,MAAM,SAAS;AAAA,MACjB,CAAC;AACD;AAAA,IACF;AAIA,UAAM,cAAc,SAAS;AAG7B,QAAI,aAAa,UAAU;AACzB,WAAK,oBAAoB,UAAU,aAAa,OAAO;AACvD;AAAA,IACF;AAEA,UAAM,WAAW,KAAK,eAAe;AAAA,MACnC,gBAAgB,SAAS,cAAc,MAAM;AAAA,MAC7C,YAAY,SAAS,cAAc;AAAA,MACnC,WAAW,SAAS;AAAA,IACtB,CAAC;AAGD,SAAK,KAAK;AAAA,MACR;AAAA,MACA;AAAA,MACA,KAAK,kBAAkB,UAAU,QAAQ;AAAA,MACzC;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,oBACN,UACA,aACA,SACM;AACN,QAAI,CAAC,KAAK,QAAQ,CAAC,YAAY,SAAU;AAEzC,UAAM,WAAW,KAAK,eAAe;AAAA,MACnC,gBAAgB,SAAS,cAAc,MAAM;AAAA,MAC7C,YAAY,SAAS,cAAc;AAAA,IACrC,CAAC;AAED,UAAM,cAEF;AAAA,MACF,UAAU,YAAY;AAAA,MACtB,OAAO,YAAY;AAAA,MACnB,MAAM;AAAA,QACJ,QAAQ,SAAS,MAAM,MAAM;AAAA,QAC7B,UAAU,SAAS,MAAM,QAAQ;AAAA,QACjC,UAAU,SAAS,MAAM,QAAQ;AAAA,QACjC,OAAO;AAAA,QACP,MAAM;AAAA,MACR;AAAA,MACA,WAAW,SAAS,aAAa,SAAS,MAAM;AAAA,MAChD;AAAA,MACA,SAAS;AAAA,MACT,KAAK;AAAA,IACP;AAEA,SAAK,OAAO,MAAM,mDAAmD;AAAA,MACnE,UAAU,YAAY;AAAA,MACtB,OAAO,YAAY;AAAA,MACnB,WAAW,YAAY;AAAA,MACvB;AAAA,IACF,CAAC;AAED,SAAK,KAAK,cAAc,aAAa,OAAO;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,qBACZ,SACA,SACe;AACf,UAAM,WAAW,QAAQ;AAGzB,QAAI,SAAS,SAAS,uBAAuB;AAC3C,YAAM,KAAK,yBAAyB,SAAS,UAAU,OAAO;AAC9D;AAAA,IACF;AAEA,SAAK,OAAO,MAAM,+BAA+B;AAAA,MAC/C,MAAM,SAAS;AAAA,IACjB,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,yBACZ,SACA,UACA,SACe;AACf,QAAI,CAAC,KAAK,KAAM;AAGhB,UAAM,aAAa,SAAS,OAAO,QAAQ;AAI3C,QAAI,CAAC,YAAY,UAAU;AACzB,WAAK,OAAO,MAAM,yCAAyC;AAAA,QACzD,OAAO,SAAS;AAAA,MAClB,CAAC;AAED,YAAM,QAAQ,aAAa;AAAA,QACzB,MAAM,cAAc;AAAA,QACpB,OAAO,EAAE,QAAQ,IAAI;AAAA,MACvB,CAAC;AACD;AAAA,IACF;AAEA,UAAM,WAAW,KAAK,eAAe;AAAA,MACnC,gBAAgB,SAAS,cAAc,MAAM;AAAA,MAC7C,YAAY,SAAS,cAAc;AAAA,IACrC,CAAC;AAED,UAAM,cAEF;AAAA,MACF,UAAU,WAAW;AAAA,MACrB,OAAO,WAAW;AAAA,MAClB,MAAM;AAAA,QACJ,QAAQ,SAAS,MAAM,MAAM;AAAA,QAC7B,UAAU,SAAS,MAAM,QAAQ;AAAA,QACjC,UAAU,SAAS,MAAM,QAAQ;AAAA,QACjC,OAAO;AAAA,QACP,MAAM;AAAA,MACR;AAAA,MACA,WAAW,SAAS,aAAa,SAAS,MAAM;AAAA,MAChD;AAAA,MACA,SAAS;AAAA,MACT,KAAK;AAAA,IACP;AAEA,SAAK,OAAO,MAAM,yCAAyC;AAAA,MACzD,UAAU,WAAW;AAAA,MACrB,OAAO,WAAW;AAAA,MAClB,WAAW,YAAY;AAAA,MACvB;AAAA,IACF,CAAC;AAED,SAAK,KAAK,cAAc,aAAa,OAAO;AAG5C,UAAM,QAAQ,aAAa;AAAA,MACzB,MAAM,cAAc;AAAA,MACpB,OAAO,EAAE,QAAQ,IAAI;AAAA,IACvB,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKQ,uBACN,UACA,SACM;AACN,QAAI,CAAC,KAAK,KAAM;AAIhB,UAAM,iBAAiB,SAAS,cAAc,MAAM;AACpD,UAAM,iBAAiB,eAAe,MAAM,iBAAiB;AAC7D,UAAM,YAAY,iBAAiB,CAAC,KAAK,SAAS,aAAa;AAI/D,UAAM,WAAW,KAAK,eAAe;AAAA,MACnC;AAAA,MACA,YAAY,SAAS,cAAc;AAAA,IACrC,CAAC;AAED,UAAM,OAAO;AAAA,MACX,QAAQ,SAAS,MAAM,MAAM;AAAA,MAC7B,UAAU,SAAS,MAAM,QAAQ;AAAA,MACjC,UAAU,SAAS,MAAM;AAAA,MACzB,OAAO;AAAA,MACP,MAAM,KAAK,kBAAkB,QAAQ;AAAA,IACvC;AAGA,UAAM,iBAAiB,SAAS,kBAAkB,CAAC;AACnD,eAAW,YAAY,gBAAgB;AACrC,YAAM,WAAW,SAAS,QAAQ;AAClC,YAAM,aAAa,qBAAqB,UAAU,QAAQ;AAE1D,YAAM,QAAmD;AAAA,QACvD,OAAO;AAAA,QACP;AAAA,QACA,OAAO;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA,KAAK;AAAA,MACP;AAEA,WAAK,OAAO,MAAM,mCAAmC;AAAA,QACnD,OAAO,WAAW;AAAA,QAClB;AAAA,QACA;AAAA,MACF,CAAC;AAED,WAAK,KAAK,gBAAgB,EAAE,GAAG,OAAO,SAAS,KAAK,GAAG,OAAO;AAAA,IAChE;AAGA,UAAM,mBAAmB,SAAS,oBAAoB,CAAC;AACvD,eAAW,YAAY,kBAAkB;AACvC,YAAM,WAAW,SAAS,QAAQ;AAClC,YAAM,aAAa,qBAAqB,UAAU,QAAQ;AAE1D,YAAM,QAAmD;AAAA,QACvD,OAAO;AAAA,QACP;AAAA,QACA,OAAO;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA,KAAK;AAAA,MACP;AAEA,WAAK,OAAO,MAAM,qCAAqC;AAAA,QACrD,OAAO,WAAW;AAAA,QAClB;AAAA,QACA;AAAA,MACF,CAAC;AAED,WAAK,KAAK,gBAAgB,EAAE,GAAG,OAAO,SAAS,KAAK,GAAG,OAAO;AAAA,IAChE;AAAA,EACF;AAAA,EAEQ,kBACN,UACA,UACkB;AAClB,UAAM,OAAO,SAAS,QAAQ;AAE9B,UAAM,iBAAiB,KAAK,kBAAkB,MAAM,QAAQ;AAE5D,UAAM,OAAO,KAAK,kBAAkB,QAAQ;AAE5C,WAAO,IAAI,QAAQ;AAAA,MACjB,IAAI,SAAS,MAAM;AAAA,MACnB;AAAA,MACA,MAAM,KAAK,gBAAgB,iBAAiB,cAAc;AAAA,MAC1D,WAAW,KAAK,gBAAgB,MAAM,cAAc;AAAA,MACpD,KAAK;AAAA,MACL,QAAQ;AAAA,QACN,QAAQ,SAAS,MAAM,MAAM;AAAA,QAC7B,UAAU,SAAS,MAAM,QAAQ;AAAA,QACjC,UAAU,SAAS,MAAM,QAAQ;AAAA,QACjC,OAAO,SAAS,MAAM,SAAS;AAAA,QAC/B;AAAA,MACF;AAAA,MACA,UAAU;AAAA,QACR,UAAU,SAAS,YACf,IAAI,KAAK,SAAS,SAAS,IAC3B,oBAAI,KAAK;AAAA,QACb,QAAQ;AAAA,MACV;AAAA,MACA,cAAc,SAAS,eAAe,CAAC,GACpC;AAAA,QACC,CAAC;AAAA;AAAA,UAEC,IAAI,gBAAgB;AAAA;AAAA;AAAA,UAIpB,EAAE,IAAI,gBAAgB,eAAe,CAAC,IAAI;AAAA;AAAA,MAC9C,EACC,IAAI,CAAC,QAAQ,KAAK,iBAAiB,GAAG,CAAC;AAAA,IAC5C,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKQ,iBAAiB,KAIV;AACb,UAAM,MAAM,IAAI;AAGhB,QAAI,OAA2B;AAC/B,QAAI,IAAI,aAAa,WAAW,QAAQ,GAAG;AACzC,aAAO;AAAA,IACT,WAAW,IAAI,aAAa,WAAW,QAAQ,GAAG;AAChD,aAAO;AAAA,IACT,WAAW,IAAI,aAAa,WAAW,QAAQ,GAAG;AAChD,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,MAAM,IAAI;AAAA,MACV,UAAU,IAAI;AAAA,MACd,WAAW,MACP,YAAY;AACV,cAAM,WAAW,MAAM,MAAM,GAAG;AAChC,YAAI,CAAC,SAAS,IAAI;AAChB,gBAAM,IAAI;AAAA,YACR;AAAA,YACA,yBAAyB,SAAS,MAAM,IAAI,SAAS,UAAU;AAAA,UACjE;AAAA,QACF;AACA,cAAM,cAAc,MAAM,SAAS,YAAY;AAC/C,eAAO,OAAO,KAAK,WAAW;AAAA,MAChC,IACA;AAAA,IACN;AAAA,EACF;AAAA,EAEQ,kBAAkB,MAAc,WAA6B;AAGnE,WAAO,KAAK,KAAK;AAAA,EACnB;AAAA,EAEA,MAAM,YACJ,UACA,SAC8B;AAC9B,UAAM,EAAE,gBAAgB,WAAW,IAAI,KAAK,eAAe,QAAQ;AAGnE,UAAM,QAAQ,aAAa,OAAO;AAClC,UAAM,kBACJ,MAAM,SAAS,IAAI,MAAM,KAAK,mBAAmB,KAAK,IAAI,CAAC;AAG7D,UAAM,OAAO,YAAY,OAAO;AAChC,QAAI;AAEJ,QAAI,MAAM;AAER,YAAM,eAAe,mBAAmB,IAAI;AAE5C,iBAAW;AAAA,QACT,MAAM,cAAc;AAAA;AAAA,QAEpB,aAAa;AAAA,UACX;AAAA,YACE,aAAa;AAAA,YACb,SAAS;AAAA,UACX;AAAA,UACA,GAAG;AAAA,QACL;AAAA,MACF;AAEA,WAAK,OAAO,MAAM,2CAA2C;AAAA,QAC3D;AAAA,QACA;AAAA,QACA,WAAW,gBAAgB;AAAA,MAC7B,CAAC;AAAA,IACH,OAAO;AAEL,YAAM,OAAO;AAAA,QACX,KAAK,gBAAgB,eAAe,OAAO;AAAA,QAC3C;AAAA,MACF;AAEA,iBAAW;AAAA,QACT,MAAM,cAAc;AAAA,QACpB;AAAA,QACA,YAAY;AAAA,QACZ,aAAa,gBAAgB,SAAS,IAAI,kBAAkB;AAAA,MAC9D;AAEA,WAAK,OAAO,MAAM,qCAAqC;AAAA,QACrD;AAAA,QACA;AAAA,QACA,YAAY,KAAK;AAAA,QACjB,WAAW,gBAAgB;AAAA,MAC7B,CAAC;AAAA,IACH;AAGA,UAAM,wBAAwB;AAAA,MAC5B,WAAW;AAAA,MACX;AAAA,MACA,cAAc,EAAE,IAAI,eAAe;AAAA,IACrC;AAEA,QAAI,YAAY;AAEhB,QAAI;AACF,YAAM,KAAK,WAAW;AAAA,QACpB,KAAK,OAAO;AAAA,QACZ;AAAA,QACA,OAAO,YAAY;AACjB,gBAAM,WAAW,MAAM,QAAQ,aAAa,QAAQ;AACpD,sBAAY,UAAU,MAAM;AAAA,QAC9B;AAAA,MACF;AAAA,IACF,SAAS,OAAO;AACd,WAAK,OAAO,MAAM,kCAAkC;AAAA,QAClD;AAAA,QACA;AAAA,MACF,CAAC;AACD,WAAK,iBAAiB,OAAO,aAAa;AAAA,IAC5C;AAEA,SAAK,OAAO,MAAM,oCAAoC,EAAE,UAAU,CAAC;AAEnE,WAAO;AAAA,MACL,IAAI;AAAA,MACJ;AAAA,MACA,KAAK;AAAA,IACP;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,mBACZ,OAC2E;AAC3E,UAAM,cAID,CAAC;AAEN,eAAW,QAAQ,OAAO;AAExB,YAAM,SAAS,MAAM,SAAS,KAAK,MAAM;AAAA,QACvC,UAAU;AAAA,QACV,oBAAoB;AAAA,MACtB,CAAC;AACD,UAAI,CAAC,QAAQ;AACX;AAAA,MACF;AAGA,YAAM,WAAW,KAAK,YAAY;AAClC,YAAM,UAAU,gBAAgB,QAAQ,QAAQ;AAEhD,kBAAY,KAAK;AAAA,QACf,aAAa;AAAA,QACb,YAAY;AAAA,QACZ,MAAM,KAAK;AAAA,MACb,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,YACJ,UACA,WACA,SAC8B;AAC9B,UAAM,EAAE,gBAAgB,WAAW,IAAI,KAAK,eAAe,QAAQ;AAGnE,UAAM,OAAO,YAAY,OAAO;AAChC,QAAI;AAEJ,QAAI,MAAM;AAER,YAAM,eAAe,mBAAmB,IAAI;AAE5C,iBAAW;AAAA,QACT,IAAI;AAAA,QACJ,MAAM,cAAc;AAAA;AAAA,QAEpB,aAAa;AAAA,UACX;AAAA,YACE,aAAa;AAAA,YACb,SAAS;AAAA,UACX;AAAA,QACF;AAAA,MACF;AAEA,WAAK,OAAO,MAAM,6CAA6C;AAAA,QAC7D;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH,OAAO;AAEL,YAAM,OAAO;AAAA,QACX,KAAK,gBAAgB,eAAe,OAAO;AAAA,QAC3C;AAAA,MACF;AAEA,iBAAW;AAAA,QACT,IAAI;AAAA,QACJ,MAAM,cAAc;AAAA,QACpB;AAAA,QACA,YAAY;AAAA,MACd;AAEA,WAAK,OAAO,MAAM,6BAA6B;AAAA,QAC7C;AAAA,QACA;AAAA,QACA,YAAY,KAAK;AAAA,MACnB,CAAC;AAAA,IACH;AAEA,UAAM,wBAAwB;AAAA,MAC5B,WAAW;AAAA,MACX;AAAA,MACA,cAAc,EAAE,IAAI,eAAe;AAAA,IACrC;AAEA,QAAI;AACF,YAAM,KAAK,WAAW;AAAA,QACpB,KAAK,OAAO;AAAA,QACZ;AAAA,QACA,OAAO,YAAY;AACjB,gBAAM,QAAQ,eAAe,QAAQ;AAAA,QACvC;AAAA,MACF;AAAA,IACF,SAAS,OAAO;AACd,WAAK,OAAO,MAAM,oCAAoC;AAAA,QACpD;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AACD,WAAK,iBAAiB,OAAO,aAAa;AAAA,IAC5C;AAEA,SAAK,OAAO,MAAM,sCAAsC,EAAE,IAAI,KAAK,CAAC;AAEpE,WAAO;AAAA,MACL,IAAI;AAAA,MACJ;AAAA,MACA,KAAK;AAAA,IACP;AAAA,EACF;AAAA,EAEA,MAAM,cAAc,UAAkB,WAAkC;AACtE,UAAM,EAAE,gBAAgB,WAAW,IAAI,KAAK,eAAe,QAAQ;AAEnE,UAAM,wBAAwB;AAAA,MAC5B,WAAW;AAAA,MACX;AAAA,MACA,cAAc,EAAE,IAAI,eAAe;AAAA,IACrC;AAEA,SAAK,OAAO,MAAM,6BAA6B;AAAA,MAC7C;AAAA,MACA;AAAA,IACF,CAAC;AAED,QAAI;AACF,YAAM,KAAK,WAAW;AAAA,QACpB,KAAK,OAAO;AAAA,QACZ;AAAA,QACA,OAAO,YAAY;AACjB,gBAAM,QAAQ,eAAe,SAAS;AAAA,QACxC;AAAA,MACF;AAAA,IACF,SAAS,OAAO;AACd,WAAK,OAAO,MAAM,oCAAoC;AAAA,QACpD;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AACD,WAAK,iBAAiB,OAAO,eAAe;AAAA,IAC9C;AAEA,SAAK,OAAO,MAAM,sCAAsC,EAAE,IAAI,KAAK,CAAC;AAAA,EACtE;AAAA,EAEA,MAAM,YACJ,WACA,YACA,QACe;AACf,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,eACJ,WACA,YACA,QACe;AACf,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,YAAY,UAAiC;AACjD,UAAM,EAAE,gBAAgB,WAAW,IAAI,KAAK,eAAe,QAAQ;AAEnE,UAAM,wBAAwB;AAAA,MAC5B,WAAW;AAAA,MACX;AAAA,MACA,cAAc,EAAE,IAAI,eAAe;AAAA,IACrC;AAEA,SAAK,OAAO,MAAM,oCAAoC,EAAE,eAAe,CAAC;AAExE,QAAI;AACF,YAAM,KAAK,WAAW;AAAA,QACpB,KAAK,OAAO;AAAA,QACZ;AAAA,QACA,OAAO,YAAY;AACjB,gBAAM,QAAQ,aAAa,EAAE,MAAM,cAAc,OAAO,CAAC;AAAA,QAC3D;AAAA,MACF;AAAA,IACF,SAAS,OAAO;AACd,WAAK,OAAO,MAAM,2CAA2C;AAAA,QAC3D;AAAA,QACA;AAAA,MACF,CAAC;AACD,WAAK,iBAAiB,OAAO,aAAa;AAAA,IAC5C;AAEA,SAAK,OAAO,MAAM,6CAA6C;AAAA,MAC7D,IAAI;AAAA,IACN,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,OAAO,QAAiC;AAE5C,UAAM,mBAAmB,MAAM,KAAK,MAChC,SAAS,EACV,IAAY,oBAAoB,MAAM,EAAE;AAC3C,UAAM,iBAAiB,MAAM,KAAK,MAC9B,SAAS,EACV,IAAY,kBAAkB,MAAM,EAAE;AAEzC,UAAM,aACJ,oBAAoB;AAEtB,UAAM,WAAW,kBAAkB,KAAK,OAAO;AAE/C,SAAK,OAAO,MAAM,oCAAoC;AAAA,MACpD;AAAA,MACA;AAAA,MACA;AAAA,MACA,kBAAkB,CAAC,CAAC;AAAA,MACpB,gBAAgB,CAAC,CAAC;AAAA,IACpB,CAAC;AAED,QAAI,CAAC,UAAU;AACb,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,QAAI,iBAAiB;AAKrB,UAAO,KAAK,WAAmB;AAAA,MAC7B,KAAK,OAAO;AAAA,MACZ;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MACA;AAAA,QACE,SAAS;AAAA,QACT,KAAK,EAAE,IAAI,KAAK,OAAO,OAAO,MAAM,KAAK,SAAS;AAAA,QAClD,SAAS,CAAC,EAAE,IAAI,OAAO,CAAC;AAAA,QACxB;AAAA,QACA,aAAa;AAAA,UACX,QAAQ,EAAE,IAAI,SAAS;AAAA,QACzB;AAAA,MACF;AAAA,MACA,OAAO,gBAA6B;AAElC,yBAAiB,aAAa,UAAU,cAAc,MAAM;AAC5D,aAAK,OAAO,MAAM,2CAA2C;AAAA,UAC3D;AAAA,UACA,YAAY,aAAa,UAAU;AAAA,QACrC,CAAC;AAAA,MACH;AAAA,IACF;AAEA,QAAI,CAAC,gBAAgB;AACnB,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,SAAK,OAAO,MAAM,mCAAmC,EAAE,eAAe,CAAC;AAEvE,WAAO,KAAK,eAAe;AAAA,MACzB;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,cACJ,UACA,UAAwB,CAAC,GACM;AAC/B,QAAI,CAAC,KAAK,aAAa;AACrB,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,UAAM,EAAE,eAAe,IAAI,KAAK,eAAe,QAAQ;AACvD,UAAM,QAAQ,QAAQ,SAAS;AAC/B,UAAM,SAAS,QAAQ;AACvB,UAAM,YAAY,QAAQ,aAAa;AAGvC,UAAM,iBAAiB,eAAe,MAAM,kBAAkB;AAC9D,UAAM,kBAAkB,iBAAiB,CAAC;AAG1C,UAAM,qBAAqB,eAAe,QAAQ,kBAAkB,EAAE;AAGtE,QAAI,iBAA6C;AACjD,QAAI,mBAAmB,KAAK,MAAM;AAChC,YAAM,gBAAgB,MAAM,KAAK,KAC9B,SAAS,EACT,IAAY,wBAAwB,kBAAkB,EAAE;AAC3D,UAAI,eAAe;AACjB,YAAI;AACF,2BAAiB,KAAK,MAAM,aAAa;AAAA,QAC3C,QAAQ;AAAA,QAER;AAAA,MACF;AAAA,IAIF;AAEA,QAAI;AACF,WAAK,OAAO,MAAM,sCAAsC;AAAA,QACtD,gBAAgB;AAAA,QAChB;AAAA,QACA,mBAAmB,CAAC,CAAC;AAAA,QACrB;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAID,UAAI,kBAAkB,iBAAiB;AACrC,eAAO,KAAK;AAAA,UACV;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAYA,UAAI;AACJ,UAAI,kBAAkB;AAEtB,UAAI,cAAc,WAAW;AAG3B,cAAM,cAAkC,CAAC;AACzC,YAAI;AACJ,cAAM,SAAS,UAAU,mBAAmB,kBAAkB,CAAC;AAE/D,WAAG;AACD,gBAAM,UAAU,WACZ,KAAK,YAAY,IAAI,QAAQ,IAC7B,KAAK,YACF,IAAI,MAAM,EACV,IAAI,EAAE,EACN,QAAQ,sBAAsB;AAErC,gBAAM,WAAW,MAAM,QAAQ,IAAI;AACnC,gBAAM,eAAgB,SAAS,SAAS,CAAC;AACzC,sBAAY,KAAK,GAAG,YAAY;AAChC,qBAAW,SAAS,iBAAiB;AAAA,QACvC,SAAS;AAGT,oBAAY,QAAQ;AAGpB,YAAI,aAAa;AACjB,YAAI,QAAQ;AACV,uBAAa,YAAY;AAAA,YACvB,CAAC,QAAQ,IAAI,mBAAmB,IAAI,kBAAkB;AAAA,UACxD;AACA,cAAI,eAAe,GAAI,cAAa,YAAY;AAAA,QAClD;AAGA,0BAAkB,aAAa,QAAQ,YAAY;AAEnD,wBAAgB,YAAY,MAAM,YAAY,aAAa,KAAK;AAAA,MAClE,OAAO;AAEL,YAAI,UAAU,KAAK,YAChB,IAAI,UAAU,mBAAmB,kBAAkB,CAAC,WAAW,EAC/D,IAAI,KAAK,EACT,QAAQ,sBAAsB;AAEjC,YAAI,QAAQ;AAEV,oBAAU,QAAQ,OAAO,sBAAsB,MAAM,EAAE;AAAA,QACzD;AAEA,cAAM,WAAW,MAAM,QAAQ,IAAI;AACnC,wBAAiB,SAAS,SAAS,CAAC;AAGpC,sBAAc,QAAQ;AAGtB,0BAAkB,cAAc,UAAU;AAAA,MAC5C;AAKA,UAAI,mBAAmB,CAAC,gBAAgB;AACtC,wBAAgB,cAAc,OAAO,CAAC,QAAQ;AAE5C,iBAAO,IAAI,MAAM,IAAI,MAAM;AAAA,QAC7B,CAAC;AACD,aAAK,OAAO,MAAM,0CAA0C;AAAA,UAC1D;AAAA,UACA,eAAe,cAAc;AAAA,QAC/B,CAAC;AAAA,MACH;AAEA,WAAK,OAAO,MAAM,qCAAqC;AAAA,QACrD,OAAO,cAAc;AAAA,QACrB;AAAA,QACA;AAAA,MACF,CAAC;AAED,YAAM,WAAW,cAAc,IAAI,CAAC,QAA0B;AAC5D,cAAM,YACJ,IAAI,MAAM,aAAa,OAAO,KAAK,OAAO,SAC1C,IAAI,MAAM,MAAM,OAAO,KAAK,OAAO;AAErC,eAAO,IAAI,QAAQ;AAAA,UACjB,IAAI,IAAI;AAAA,UACR;AAAA,UACA,MAAM,KAAK,4BAA4B,GAAG;AAAA,UAC1C,WAAW,KAAK,gBAAgB;AAAA,YAC9B,KAAK,4BAA4B,GAAG;AAAA,UACtC;AAAA,UACA,KAAK;AAAA,UACL,QAAQ;AAAA,YACN,QACE,IAAI,MAAM,MAAM,MAAM,IAAI,MAAM,aAAa,MAAM;AAAA,YACrD,UACE,IAAI,MAAM,MAAM,eAChB,IAAI,MAAM,aAAa,eACvB;AAAA,YACF,UACE,IAAI,MAAM,MAAM,eAChB,IAAI,MAAM,aAAa,eACvB;AAAA,YACF,OAAO,CAAC,CAAC,IAAI,MAAM;AAAA,YACnB,MAAM;AAAA,UACR;AAAA,UACA,UAAU;AAAA,YACR,UAAU,IAAI,kBACV,IAAI,KAAK,IAAI,eAAe,IAC5B,oBAAI,KAAK;AAAA,YACb,QAAQ,CAAC,CAAC,IAAI;AAAA,UAChB;AAAA,UACA,aAAa,KAAK,mCAAmC,GAAG;AAAA,QAC1D,CAAC;AAAA,MACH,CAAC;AAGD,UAAI;AACJ,UAAI,mBAAmB,cAAc,SAAS,GAAG;AAC/C,YAAI,cAAc,WAAW;AAE3B,gBAAM,UAAU,cAAc,cAAc,SAAS,CAAC;AACtD,cAAI,SAAS,iBAAiB;AAC5B,yBAAa,QAAQ;AAAA,UACvB;AAAA,QACF,OAAO;AAEL,gBAAM,YAAY,cAAc,CAAC;AACjC,cAAI,WAAW,iBAAiB;AAC9B,yBAAa,UAAU;AAAA,UACzB;AAAA,QACF;AAAA,MACF;AAEA,aAAO,EAAE,UAAU,WAAW;AAAA,IAChC,SAAS,OAAO;AACd,WAAK,OAAO,MAAM,wCAAwC,EAAE,MAAM,CAAC;AAGnE,UAAI,iBAAiB,SAAS,MAAM,SAAS,SAAS,KAAK,GAAG;AAC5D,cAAM,IAAI;AAAA,UACR;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAEA,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,2BACZ,SACA,iBACA,UACA,SAC+B;AAC/B,UAAM,QAAQ,QAAQ,SAAS;AAC/B,UAAM,SAAS,QAAQ;AACvB,UAAM,YAAY,QAAQ,aAAa;AAEvC,SAAK,OAAO,MAAM,qDAAqD;AAAA,MACrE,QAAQ,QAAQ;AAAA,MAChB,WAAW,QAAQ;AAAA,MACnB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAKD,UAAM,YAAY,UAAU,mBAAmB,QAAQ,MAAM,CAAC,aAAa,mBAAmB,QAAQ,SAAS,CAAC,aAAa,mBAAmB,eAAe,CAAC;AAChK,UAAM,aAAa,GAAG,SAAS;AAE/B,UAAM,cAAc,KAAK;AACzB,QAAI,CAAC,aAAa;AAChB,YAAM,IAAI,oBAAoB,SAAS,8BAA8B;AAAA,IACvE;AAGA,QAAI,gBAAyC;AAC7C,QAAI;AACF,sBAAiB,MAAM,YACpB,IAAI,SAAS,EACb,IAAI;AAAA,IACT,SAAS,KAAK;AACZ,WAAK,OAAO,KAAK,kCAAkC;AAAA,QACjD;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH;AAEA,QAAI;AACJ,QAAI,kBAAkB;AAEtB,QAAI,cAAc,WAAW;AAG3B,YAAM,aAAiC,CAAC;AACxC,UAAI;AAEJ,SAAG;AACD,cAAM,UAAU,WACZ,YAAY,IAAI,QAAQ,IACxB,YAAY,IAAI,UAAU,EAAE,IAAI,EAAE;AAEtC,cAAM,WAAW,MAAM,QAAQ,IAAI;AACnC,cAAM,eAAgB,SAAS,SAAS,CAAC;AACzC,mBAAW,KAAK,GAAG,YAAY;AAC/B,mBAAW,SAAS,iBAAiB;AAAA,MACvC,SAAS;AAGT,iBAAW,QAAQ;AAGnB,YAAM,cAAc,gBAChB,CAAC,eAAe,GAAG,UAAU,IAC7B;AAGJ,UAAI,aAAa;AACjB,UAAI,QAAQ;AACV,qBAAa,YAAY;AAAA,UACvB,CAAC,QAAQ,IAAI,mBAAmB,IAAI,kBAAkB;AAAA,QACxD;AACA,YAAI,eAAe,GAAI,cAAa,YAAY;AAAA,MAClD;AAEA,wBAAkB,aAAa,QAAQ,YAAY;AACnD,sBAAgB,YAAY,MAAM,YAAY,aAAa,KAAK;AAAA,IAClE,OAAO;AAGL,YAAM,aAAiC,CAAC;AACxC,UAAI;AAEJ,SAAG;AACD,cAAM,UAAU,WACZ,YAAY,IAAI,QAAQ,IACxB,YAAY,IAAI,UAAU,EAAE,IAAI,EAAE;AAEtC,cAAM,WAAW,MAAM,QAAQ,IAAI;AACnC,cAAM,eAAgB,SAAS,SAAS,CAAC;AACzC,mBAAW,KAAK,GAAG,YAAY;AAC/B,mBAAW,SAAS,iBAAiB;AAAA,MACvC,SAAS;AAGT,iBAAW,QAAQ;AAGnB,YAAM,cAAc,gBAChB,CAAC,eAAe,GAAG,UAAU,IAC7B;AAEJ,UAAI,QAAQ;AAGV,cAAM,cAAc,YAAY;AAAA,UAC9B,CAAC,QAAQ,IAAI,mBAAmB,IAAI,mBAAmB;AAAA,QACzD;AACA,YAAI,cAAc,GAAG;AAEnB,gBAAM,aAAa,KAAK,IAAI,GAAG,cAAc,KAAK;AAClD,0BAAgB,YAAY,MAAM,YAAY,WAAW;AACzD,4BAAkB,aAAa;AAAA,QACjC,OAAO;AAEL,0BAAgB,YAAY,MAAM,CAAC,KAAK;AACxC,4BAAkB,YAAY,SAAS;AAAA,QACzC;AAAA,MACF,OAAO;AAEL,wBAAgB,YAAY,MAAM,CAAC,KAAK;AACxC,0BAAkB,YAAY,SAAS;AAAA,MACzC;AAAA,IACF;AAEA,SAAK,OAAO,MAAM,oDAAoD;AAAA,MACpE,OAAO,cAAc;AAAA,MACrB;AAAA,MACA;AAAA,IACF,CAAC;AAED,UAAM,WAAW,cAAc,IAAI,CAAC,QAA0B;AAC5D,YAAM,YACJ,IAAI,MAAM,aAAa,OAAO,KAAK,OAAO,SAC1C,IAAI,MAAM,MAAM,OAAO,KAAK,OAAO;AAErC,aAAO,IAAI,QAAQ;AAAA,QACjB,IAAI,IAAI;AAAA,QACR;AAAA,QACA,MAAM,KAAK,4BAA4B,GAAG;AAAA,QAC1C,WAAW,KAAK,gBAAgB;AAAA,UAC9B,KAAK,4BAA4B,GAAG;AAAA,QACtC;AAAA,QACA,KAAK;AAAA,QACL,QAAQ;AAAA,UACN,QAAQ,IAAI,MAAM,MAAM,MAAM,IAAI,MAAM,aAAa,MAAM;AAAA,UAC3D,UACE,IAAI,MAAM,MAAM,eAChB,IAAI,MAAM,aAAa,eACvB;AAAA,UACF,UACE,IAAI,MAAM,MAAM,eAChB,IAAI,MAAM,aAAa,eACvB;AAAA,UACF,OAAO,CAAC,CAAC,IAAI,MAAM;AAAA,UACnB,MAAM;AAAA,QACR;AAAA,QACA,UAAU;AAAA,UACR,UAAU,IAAI,kBACV,IAAI,KAAK,IAAI,eAAe,IAC5B,oBAAI,KAAK;AAAA,UACb,QAAQ,CAAC,CAAC,IAAI;AAAA,QAChB;AAAA,QACA,aAAa,KAAK,mCAAmC,GAAG;AAAA,MAC1D,CAAC;AAAA,IACH,CAAC;AAGD,QAAI;AACJ,QAAI,mBAAmB,cAAc,SAAS,GAAG;AAC/C,UAAI,cAAc,WAAW;AAC3B,cAAM,UAAU,cAAc,cAAc,SAAS,CAAC;AACtD,YAAI,SAAS,iBAAiB;AAC5B,uBAAa,QAAQ;AAAA,QACvB;AAAA,MACF,OAAO;AACL,cAAM,YAAY,cAAc,CAAC;AACjC,YAAI,WAAW,iBAAiB;AAC9B,uBAAa,UAAU;AAAA,QACzB;AAAA,MACF;AAAA,IACF;AAEA,WAAO,EAAE,UAAU,WAAW;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA,EAKQ,4BAA4B,KAA+B;AAEjE,QAAI,IAAI,MAAM,gBAAgB,QAAQ;AACpC,aAAO,IAAI,KAAK,WAAW;AAAA,IAC7B;AAGA,QAAI,OAAO;AACX,QAAI,IAAI,MAAM,SAAS;AACrB,aAAO,IAAI,KAAK,QAAQ,QAAQ,YAAY,EAAE,EAAE,KAAK;AAAA,IACvD;AAGA,QAAI,CAAC,QAAQ,IAAI,aAAa,QAAQ;AACpC,iBAAW,OAAO,IAAI,aAAa;AACjC,YAAI,IAAI,gBAAgB,2CAA2C;AACjE,cAAI;AACF,kBAAM,OAAO,KAAK,MAAM,IAAI,WAAW,IAAI;AAE3C,kBAAM,QAAQ,KAAK,iBAAiB,IAAI;AACxC,gBAAI,OAAO;AACT,qBAAO;AAAA,YACT;AACA,mBAAO;AAAA,UACT,QAAQ;AACN,mBAAO;AAAA,UACT;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,iBAAiB,MAA8B;AACrD,QAAI,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO;AAE9C,UAAM,UAAU;AAGhB,QAAI,MAAM,QAAQ,QAAQ,IAAI,GAAG;AAC/B,iBAAW,WAAW,QAAQ,MAAM;AAClC,YACE,WACA,OAAO,YAAY,YAClB,QAAoC,SAAS,aAC9C;AACA,gBAAM,YAAY;AAElB,cACE,UAAU,WAAW,YACrB,UAAU,SAAS,WACnB,UAAU,SAAS,cACnB;AACA,kBAAM,OAAO,UAAU;AACvB,gBAAI,OAAO,SAAS,UAAU;AAC5B,qBAAO;AAAA,YACT;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAEA,iBAAW,WAAW,QAAQ,MAAM;AAClC,YACE,WACA,OAAO,YAAY,YAClB,QAAoC,SAAS,aAC9C;AACA,gBAAM,OAAQ,QAAoC;AAClD,cAAI,OAAO,SAAS,UAAU;AAC5B,mBAAO;AAAA,UACT;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,mCACN,KACc;AACd,QAAI,CAAC,IAAI,aAAa,QAAQ;AAC5B,aAAO,CAAC;AAAA,IACV;AAEA,WAAO,IAAI,YAAY,IAAI,CAAC,SAAS;AAAA,MACnC,MAAM,IAAI,aAAa,SAAS,OAAO,IAAI,UAAU;AAAA,MACrD,MAAM,IAAI,QAAQ;AAAA,MAClB,KAAK,IAAI,cAAc;AAAA,MACvB,UAAU,IAAI,eAAe;AAAA,IAC/B,EAAE;AAAA,EACJ;AAAA,EAEA,MAAM,YAAY,UAAuC;AACvD,UAAM,EAAE,eAAe,IAAI,KAAK,eAAe,QAAQ;AAEvD,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,WAAW;AAAA,MACX,UAAU,CAAC;AAAA,IACb;AAAA,EACF;AAAA,EAEA,eAAe,cAAqC;AAElD,UAAM,wBAAwB,OAAO;AAAA,MACnC,aAAa;AAAA,IACf,EAAE,SAAS,WAAW;AACtB,UAAM,oBAAoB,OAAO,KAAK,aAAa,UAAU,EAAE;AAAA,MAC7D;AAAA,IACF;AACA,WAAO,SAAS,qBAAqB,IAAI,iBAAiB;AAAA,EAC5D;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,KAAK,UAA2B;AAC9B,UAAM,EAAE,eAAe,IAAI,KAAK,eAAe,QAAQ;AAEvD,WAAO,CAAC,eAAe,WAAW,KAAK;AAAA,EACzC;AAAA,EAEA,eAAe,UAAiC;AAC9C,UAAM,QAAQ,SAAS,MAAM,GAAG;AAChC,QAAI,MAAM,WAAW,KAAK,MAAM,CAAC,MAAM,SAAS;AAC9C,YAAM,IAAI;AAAA,QACR;AAAA,QACA,4BAA4B,QAAQ;AAAA,MACtC;AAAA,IACF;AACA,UAAM,iBAAiB,OAAO;AAAA,MAC5B,MAAM,CAAC;AAAA,MACP;AAAA,IACF,EAAE,SAAS,OAAO;AAClB,UAAM,aAAa,OAAO,KAAK,MAAM,CAAC,GAAa,WAAW,EAAE;AAAA,MAC9D;AAAA,IACF;AACA,WAAO,EAAE,gBAAgB,WAAW;AAAA,EACtC;AAAA,EAEA,aAAa,KAAgC;AAC3C,UAAM,WAAW;AACjB,UAAM,WAAW,KAAK,eAAe;AAAA,MACnC,gBAAgB,SAAS,cAAc,MAAM;AAAA,MAC7C,YAAY,SAAS,cAAc;AAAA,IACrC,CAAC;AACD,WAAO,KAAK,kBAAkB,UAAU,QAAQ;AAAA,EAClD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYQ,kBAAkB,UAA6B;AACrD,UAAM,SAAS,SAAS,MAAM;AAC9B,QAAI,CAAC,UAAU,CAAC,KAAK,OAAO,OAAO;AACjC,aAAO;AAAA,IACT;AAGA,QAAI,WAAW,KAAK,OAAO,OAAO;AAChC,aAAO;AAAA,IACT;AAIA,QAAI,OAAO,SAAS,IAAI,KAAK,OAAO,KAAK,EAAE,GAAG;AAC5C,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,gBAAgB,SAAmC;AACjD,WAAO,KAAK,gBAAgB,QAAQ,OAAO;AAAA,EAC7C;AAAA;AAAA;AAAA;AAAA,EAKQ,iBAAiB,OAAgB,WAA0B;AAEjE,QAAI,SAAS,OAAO,UAAU,UAAU;AACtC,YAAM,MAAM;AAGZ,YAAM,aACH,IAAI,cACJ,IAAI,UACJ,IAAI;AAEP,UAAI,eAAe,OAAO,eAAe,KAAK;AAC5C,cAAM,IAAI;AAAA,UACR;AAAA,UACA,6BAA6B,SAAS,KAAK,IAAI,WAAW,cAAc;AAAA,QAC1E;AAAA,MACF;AAEA,UAAI,eAAe,KAAK;AACtB,cAAM,IAAI;AAAA,UACR;AAAA,UACA,6BAA6B,SAAS;AAAA,UACtC,iBAAiB,QAAQ,QAAQ;AAAA,QACnC;AAAA,MACF;AAEA,UAAI,eAAe,KAAK;AACtB,cAAM,aACJ,OAAO,IAAI,eAAe,WAAW,IAAI,aAAa;AACxD,cAAM,IAAI,sBAAsB,SAAS,UAAU;AAAA,MACrD;AAGA,UACE,eAAe,OACd,IAAI,WACH,OAAO,IAAI,YAAY,YACvB,IAAI,QAAQ,YAAY,EAAE,SAAS,YAAY,GACjD;AACA,cAAM,IAAI,gBAAgB,SAAS,SAAS;AAAA,MAC9C;AAGA,UAAI,IAAI,WAAW,OAAO,IAAI,YAAY,UAAU;AAClD,cAAM,IAAI;AAAA,UACR;AAAA,UACA,0BAA0B,SAAS,KAAK,IAAI,OAAO;AAAA,UACnD,iBAAiB,QAAQ,QAAQ;AAAA,QACnC;AAAA,MACF;AAAA,IACF;AAGA,UAAM,IAAI;AAAA,MACR;AAAA,MACA,0BAA0B,SAAS,KAAK,OAAO,KAAK,CAAC;AAAA,MACrD,iBAAiB,QAAQ,QAAQ;AAAA,IACnC;AAAA,EACF;AACF;AAEO,SAAS,mBAAmB,QAA0C;AAC3E,SAAO,IAAI,aAAa,MAAM;AAChC;","names":["context"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chat-adapter/teams",
3
- "version": "4.3.0",
3
+ "version": "4.4.1",
4
4
  "description": "Microsoft Teams adapter for chat",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -19,8 +19,8 @@
19
19
  "@azure/identity": "^4.13.0",
20
20
  "@microsoft/microsoft-graph-client": "^3.0.7",
21
21
  "botbuilder": "^4.23.1",
22
- "@chat-adapter/shared": "4.3.0",
23
- "chat": "4.3.0"
22
+ "@chat-adapter/shared": "4.4.1",
23
+ "chat": "4.4.1"
24
24
  },
25
25
  "devDependencies": {
26
26
  "@types/node": "^22.10.2",