@chat-adapter/teams 4.13.1 → 4.13.2

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/README.md CHANGED
@@ -1,6 +1,9 @@
1
1
  # @chat-adapter/teams
2
2
 
3
- Microsoft Teams adapter for the [chat](https://github.com/vercel-labs/chat) SDK.
3
+ [![npm version](https://img.shields.io/npm/v/@chat-adapter/teams)](https://www.npmjs.com/package/@chat-adapter/teams)
4
+ [![npm downloads](https://img.shields.io/npm/dm/@chat-adapter/teams)](https://www.npmjs.com/package/@chat-adapter/teams)
5
+
6
+ Microsoft Teams adapter for [Chat SDK](https://chat-sdk.dev/docs). Uses Azure Bot Service for webhook handling and message delivery.
4
7
 
5
8
  ## Installation
6
9
 
@@ -14,7 +17,7 @@ npm install chat @chat-adapter/teams
14
17
  import { Chat } from "chat";
15
18
  import { createTeamsAdapter } from "@chat-adapter/teams";
16
19
 
17
- const chat = new Chat({
20
+ const bot = new Chat({
18
21
  userName: "mybot",
19
22
  adapters: {
20
23
  teams: createTeamsAdapter({
@@ -26,175 +29,14 @@ const chat = new Chat({
26
29
  },
27
30
  });
28
31
 
29
- // Handle @mentions
30
- chat.onNewMention(async (thread, message) => {
32
+ bot.onNewMention(async (thread, message) => {
31
33
  await thread.post("Hello from Teams!");
32
34
  });
33
35
  ```
34
36
 
35
- ## Configuration
36
-
37
- | Option | Required | Description |
38
- |--------|----------|-------------|
39
- | `appId` | Yes | Azure Bot App ID |
40
- | `appPassword` | Yes | Azure Bot App Password |
41
- | `appType` | No | `"MultiTenant"` or `"SingleTenant"` (default: `"MultiTenant"`) |
42
- | `appTenantId` | For SingleTenant | Azure AD Tenant ID |
43
-
44
- ## Environment Variables
45
-
46
- ```bash
47
- TEAMS_APP_ID=...
48
- TEAMS_APP_PASSWORD=...
49
- TEAMS_APP_TENANT_ID=... # Required for SingleTenant
50
- ```
51
-
52
- ## Azure Bot Setup
53
-
54
- ### 1. Create Azure Bot Resource
55
-
56
- 1. Go to [portal.azure.com](https://portal.azure.com)
57
- 2. Click **Create a resource**
58
- 3. Search for **Azure Bot** and select it
59
- 4. Click **Create**
60
- 5. Fill in:
61
- - **Bot handle**: Unique identifier for your bot
62
- - **Subscription**: Your Azure subscription
63
- - **Resource group**: Create new or use existing
64
- - **Pricing tier**: F0 (free) for testing
65
- - **Type of App**: **Single Tenant** (recommended for enterprise)
66
- - **Creation type**: **Create new Microsoft App ID**
67
- 6. Click **Review + create** → **Create**
68
-
69
- ### 2. Get App Credentials
70
-
71
- 1. Go to your newly created Bot resource
72
- 2. Go to **Configuration**
73
- 3. Copy **Microsoft App ID** → `TEAMS_APP_ID`
74
- 4. Click **Manage Password** (next to Microsoft App ID)
75
- 5. In the App Registration page, go to **Certificates & secrets**
76
- 6. Click **New client secret**
77
- 7. Add description, select expiry, click **Add**
78
- 8. Copy the **Value** immediately (shown only once) → `TEAMS_APP_PASSWORD`
79
- 9. Go back to **Overview** and copy **Directory (tenant) ID** → `TEAMS_APP_TENANT_ID`
80
-
81
- ### 3. Configure Messaging Endpoint
82
-
83
- 1. In your Azure Bot resource, go to **Configuration**
84
- 2. Set **Messaging endpoint** to: `https://your-domain.com/api/webhooks/teams`
85
- 3. Click **Apply**
86
-
87
- ### 4. Enable Teams Channel
88
-
89
- 1. In your Azure Bot resource, go to **Channels**
90
- 2. Click **Microsoft Teams**
91
- 3. Accept the terms of service
92
- 4. Click **Apply**
93
-
94
- ### 5. Create Teams App Package
95
-
96
- Create a `manifest.json` file:
97
-
98
- ```json
99
- {
100
- "$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.16/MicrosoftTeams.schema.json",
101
- "manifestVersion": "1.16",
102
- "version": "1.0.0",
103
- "id": "YOUR_APP_ID_HERE",
104
- "packageName": "com.yourcompany.chatbot",
105
- "developer": {
106
- "name": "Your Company",
107
- "websiteUrl": "https://your-domain.com",
108
- "privacyUrl": "https://your-domain.com/privacy",
109
- "termsOfUseUrl": "https://your-domain.com/terms"
110
- },
111
- "name": {
112
- "short": "Chat Bot",
113
- "full": "Chat SDK Demo Bot"
114
- },
115
- "description": {
116
- "short": "A chat bot powered by Chat SDK",
117
- "full": "A chat bot powered by Chat SDK that can respond to messages and commands."
118
- },
119
- "icons": {
120
- "outline": "outline.png",
121
- "color": "color.png"
122
- },
123
- "accentColor": "#FFFFFF",
124
- "bots": [
125
- {
126
- "botId": "YOUR_APP_ID_HERE",
127
- "scopes": ["personal", "team", "groupchat"],
128
- "supportsFiles": false,
129
- "isNotificationOnly": false,
130
- "commandLists": [
131
- {
132
- "scopes": ["personal", "team", "groupchat"],
133
- "commands": [
134
- {
135
- "title": "help",
136
- "description": "Get help using this bot"
137
- }
138
- ]
139
- }
140
- ]
141
- }
142
- ],
143
- "permissions": ["identity", "messageTeamMembers"],
144
- "validDomains": ["your-domain.com"]
145
- }
146
- ```
147
-
148
- Create icon files (32x32 `outline.png` and 192x192 `color.png`), then zip all three files together.
149
-
150
- ### 6. Upload App to Teams
151
-
152
- **For testing (sideloading):**
153
- 1. In Teams, click **Apps** in the sidebar
154
- 2. Click **Manage your apps** → **Upload an app**
155
- 3. Click **Upload a custom app**
156
- 4. Select your zip file
157
-
158
- **For organization-wide deployment:**
159
- 1. Go to [Teams Admin Center](https://admin.teams.microsoft.com)
160
- 2. Go to **Teams apps** → **Manage apps**
161
- 3. Click **Upload new app**
162
- 4. Select your zip file
163
- 5. Go to **Setup policies** to control who can use the app
164
-
165
- ## Features
166
-
167
- - Message posting and editing
168
- - Thread subscriptions
169
- - Reaction events (receive only)
170
- - File attachments
171
- - Rich cards (Adaptive Cards)
172
- - Action callbacks (card actions)
173
- - Typing indicators
174
- - Direct messages
175
- - Proactive messaging
176
-
177
- ## Limitations
178
-
179
- - **Adding reactions**: Teams Bot Framework doesn't support bots adding reactions
180
- - **Message history**: No API to fetch message history
181
-
182
- ## Troubleshooting
183
-
184
- ### "Unauthorized" error
185
- - Verify `TEAMS_APP_ID` and `TEAMS_APP_PASSWORD` are correct
186
- - For SingleTenant apps, ensure `TEAMS_APP_TENANT_ID` is set
187
- - Check that the messaging endpoint URL is correct in Azure
188
-
189
- ### Bot not appearing in Teams
190
- - Verify the Teams channel is enabled in Azure Bot
191
- - Check that the app manifest is correctly configured
192
- - Ensure the app is installed in the workspace/team
37
+ ## Documentation
193
38
 
194
- ### Messages not being received
195
- - Verify the messaging endpoint URL is correct
196
- - Check that your server is accessible from the internet
197
- - Review Azure Bot logs for errors
39
+ Full setup instructions, configuration reference, and features at [chat-sdk.dev/docs/adapters/teams](https://chat-sdk.dev/docs/adapters/teams).
198
40
 
199
41
  ## License
200
42
 
package/dist/index.d.ts CHANGED
@@ -8,22 +8,22 @@ import { CardElement, BaseFormatConverter, AdapterPostableMessage, Root, Logger,
8
8
  */
9
9
 
10
10
  interface AdaptiveCard {
11
- type: "AdaptiveCard";
12
11
  $schema: string;
13
- version: string;
14
- body: AdaptiveCardElement[];
15
12
  actions?: AdaptiveCardAction[];
13
+ body: AdaptiveCardElement[];
14
+ type: "AdaptiveCard";
15
+ version: string;
16
16
  }
17
17
  interface AdaptiveCardElement {
18
18
  type: string;
19
19
  [key: string]: unknown;
20
20
  }
21
21
  interface AdaptiveCardAction {
22
- type: string;
23
- title: string;
24
22
  data?: Record<string, unknown>;
25
- url?: string;
26
23
  style?: string;
24
+ title: string;
25
+ type: string;
26
+ url?: string;
27
27
  }
28
28
  /**
29
29
  * Convert a CardElement to a Teams Adaptive Card.
@@ -76,31 +76,31 @@ interface TeamsAdapterConfig {
76
76
  appId: string;
77
77
  /** Microsoft App Password */
78
78
  appPassword: string;
79
- /** Logger instance for error reporting */
80
- logger: Logger;
81
- /** Microsoft App Type */
82
- appType?: "MultiTenant" | "SingleTenant";
83
79
  /** Microsoft App Tenant ID */
84
80
  appTenantId?: string;
81
+ /** Microsoft App Type */
82
+ appType?: "MultiTenant" | "SingleTenant";
83
+ /** Logger instance for error reporting */
84
+ logger: Logger;
85
85
  /** Override bot username (optional) */
86
86
  userName?: string;
87
87
  }
88
88
  /** Teams-specific thread ID data */
89
89
  interface TeamsThreadId {
90
90
  conversationId: string;
91
- serviceUrl: string;
92
91
  replyToId?: string;
92
+ serviceUrl: string;
93
93
  }
94
94
  declare class TeamsAdapter implements Adapter<TeamsThreadId, unknown> {
95
95
  readonly name = "teams";
96
96
  readonly userName: string;
97
97
  readonly botUserId?: string;
98
- private botAdapter;
99
- private graphClient;
98
+ private readonly botAdapter;
99
+ private readonly graphClient;
100
100
  private chat;
101
- private logger;
102
- private formatConverter;
103
- private config;
101
+ private readonly logger;
102
+ private readonly formatConverter;
103
+ private readonly config;
104
104
  constructor(config: TeamsAdapterConfig);
105
105
  initialize(chat: ChatInstance): Promise<void>;
106
106
  handleWebhook(request: Request, options?: WebhookOptions): Promise<Response>;
package/dist/index.js CHANGED
@@ -338,6 +338,9 @@ var ServerlessCloudAdapter = class extends CloudAdapter {
338
338
  return this.processActivity(authHeader, activity, logic);
339
339
  }
340
340
  };
341
+ var MESSAGEID_CAPTURE_PATTERN = /messageid=(\d+)/;
342
+ var MESSAGEID_STRIP_PATTERN = /;messageid=\d+/;
343
+ var SEMICOLON_MESSAGEID_CAPTURE_PATTERN = /;messageid=(\d+)/;
341
344
  var TeamsAdapter = class {
342
345
  name = "teams";
343
346
  userName;
@@ -443,7 +446,7 @@ var TeamsAdapter = class {
443
446
  const teamAadGroupId = team?.aadGroupId;
444
447
  const teamThreadId = team?.id;
445
448
  const conversationId = activity.conversation?.id || "";
446
- const baseChannelId = conversationId.replace(/;messageid=\d+/, "");
449
+ const baseChannelId = conversationId.replace(MESSAGEID_STRIP_PATTERN, "");
447
450
  if (teamAadGroupId && channelData?.channel?.id && tenantId) {
448
451
  const context2 = {
449
452
  teamId: teamAadGroupId,
@@ -570,7 +573,9 @@ var TeamsAdapter = class {
570
573
  * Teams sends these with type "message" and value.actionId.
571
574
  */
572
575
  handleMessageAction(activity, actionValue, options) {
573
- if (!this.chat || !actionValue.actionId) return;
576
+ if (!(this.chat && actionValue.actionId)) {
577
+ return;
578
+ }
574
579
  const threadId = this.encodeThreadId({
575
580
  conversationId: activity.conversation?.id || "",
576
581
  serviceUrl: activity.serviceUrl || ""
@@ -616,7 +621,9 @@ var TeamsAdapter = class {
616
621
  * The action data is in activity.value with our { actionId, value } structure.
617
622
  */
618
623
  async handleAdaptiveCardAction(context, activity, options) {
619
- if (!this.chat) return;
624
+ if (!this.chat) {
625
+ return;
626
+ }
620
627
  const actionData = activity.value?.action?.data;
621
628
  if (!actionData?.actionId) {
622
629
  this.logger.debug("Adaptive card action missing actionId", {
@@ -663,9 +670,11 @@ var TeamsAdapter = class {
663
670
  * Handle Teams reaction events (reactionsAdded/reactionsRemoved).
664
671
  */
665
672
  handleReactionActivity(activity, options) {
666
- if (!this.chat) return;
673
+ if (!this.chat) {
674
+ return;
675
+ }
667
676
  const conversationId = activity.conversation?.id || "";
668
- const messageIdMatch = conversationId.match(/messageid=(\d+)/);
677
+ const messageIdMatch = conversationId.match(MESSAGEID_CAPTURE_PATTERN);
669
678
  const messageId = messageIdMatch?.[1] || activity.replyToId || "";
670
679
  const threadId = this.encodeThreadId({
671
680
  conversationId,
@@ -1086,9 +1095,14 @@ var TeamsAdapter = class {
1086
1095
  const limit = options.limit || 50;
1087
1096
  const cursor = options.cursor;
1088
1097
  const direction = options.direction ?? "backward";
1089
- const messageIdMatch = conversationId.match(/;messageid=(\d+)/);
1098
+ const messageIdMatch = conversationId.match(
1099
+ SEMICOLON_MESSAGEID_CAPTURE_PATTERN
1100
+ );
1090
1101
  const threadMessageId = messageIdMatch?.[1];
1091
- const baseConversationId = conversationId.replace(/;messageid=\d+/, "");
1102
+ const baseConversationId = conversationId.replace(
1103
+ MESSAGEID_STRIP_PATTERN,
1104
+ ""
1105
+ );
1092
1106
  let channelContext = null;
1093
1107
  if (threadMessageId && this.chat) {
1094
1108
  const cachedContext = await this.chat.getState().get(`teams:channelContext:${baseConversationId}`);
@@ -1135,7 +1149,9 @@ var TeamsAdapter = class {
1135
1149
  startIndex = allMessages.findIndex(
1136
1150
  (msg) => msg.createdDateTime && msg.createdDateTime > cursor
1137
1151
  );
1138
- if (startIndex === -1) startIndex = allMessages.length;
1152
+ if (startIndex === -1) {
1153
+ startIndex = allMessages.length;
1154
+ }
1139
1155
  }
1140
1156
  hasMoreMessages = startIndex + limit < allMessages.length;
1141
1157
  graphMessages = allMessages.slice(startIndex, startIndex + limit);
@@ -1190,7 +1206,7 @@ var TeamsAdapter = class {
1190
1206
  let nextCursor;
1191
1207
  if (hasMoreMessages && graphMessages.length > 0) {
1192
1208
  if (direction === "forward") {
1193
- const lastMsg = graphMessages[graphMessages.length - 1];
1209
+ const lastMsg = graphMessages.at(-1);
1194
1210
  if (lastMsg?.createdDateTime) {
1195
1211
  nextCursor = lastMsg.createdDateTime;
1196
1212
  }
@@ -1265,7 +1281,9 @@ var TeamsAdapter = class {
1265
1281
  startIndex = allMessages.findIndex(
1266
1282
  (msg) => msg.createdDateTime && msg.createdDateTime > cursor
1267
1283
  );
1268
- if (startIndex === -1) startIndex = allMessages.length;
1284
+ if (startIndex === -1) {
1285
+ startIndex = allMessages.length;
1286
+ }
1269
1287
  }
1270
1288
  hasMoreMessages = startIndex + limit < allMessages.length;
1271
1289
  graphMessages = allMessages.slice(startIndex, startIndex + limit);
@@ -1330,7 +1348,7 @@ var TeamsAdapter = class {
1330
1348
  let nextCursor;
1331
1349
  if (hasMoreMessages && graphMessages.length > 0) {
1332
1350
  if (direction === "forward") {
1333
- const lastMsg = graphMessages[graphMessages.length - 1];
1351
+ const lastMsg = graphMessages.at(-1);
1334
1352
  if (lastMsg?.createdDateTime) {
1335
1353
  nextCursor = lastMsg.createdDateTime;
1336
1354
  }
@@ -1376,7 +1394,9 @@ var TeamsAdapter = class {
1376
1394
  * Extract a title/summary from an Adaptive Card structure.
1377
1395
  */
1378
1396
  extractCardTitle(card) {
1379
- if (!card || typeof card !== "object") return null;
1397
+ if (!card || typeof card !== "object") {
1398
+ return null;
1399
+ }
1380
1400
  const cardObj = card;
1381
1401
  if (Array.isArray(cardObj.body)) {
1382
1402
  for (const element of cardObj.body) {
@@ -1430,7 +1450,10 @@ var TeamsAdapter = class {
1430
1450
  */
1431
1451
  channelIdFromThreadId(threadId) {
1432
1452
  const { conversationId, serviceUrl } = this.decodeThreadId(threadId);
1433
- const baseConversationId = conversationId.replace(/;messageid=\d+/, "");
1453
+ const baseConversationId = conversationId.replace(
1454
+ MESSAGEID_STRIP_PATTERN,
1455
+ ""
1456
+ );
1434
1457
  return this.encodeThreadId({
1435
1458
  conversationId: baseConversationId,
1436
1459
  serviceUrl
@@ -1448,7 +1471,10 @@ var TeamsAdapter = class {
1448
1471
  );
1449
1472
  }
1450
1473
  const { conversationId } = this.decodeThreadId(channelId);
1451
- const baseConversationId = conversationId.replace(/;messageid=\d+/, "");
1474
+ const baseConversationId = conversationId.replace(
1475
+ MESSAGEID_STRIP_PATTERN,
1476
+ ""
1477
+ );
1452
1478
  const limit = options.limit || 50;
1453
1479
  const direction = options.direction ?? "backward";
1454
1480
  try {
@@ -1488,7 +1514,9 @@ var TeamsAdapter = class {
1488
1514
  startIndex = allMessages.findIndex(
1489
1515
  (msg) => msg.createdDateTime && msg.createdDateTime > cursor
1490
1516
  );
1491
- if (startIndex === -1) startIndex = allMessages.length;
1517
+ if (startIndex === -1) {
1518
+ startIndex = allMessages.length;
1519
+ }
1492
1520
  }
1493
1521
  hasMoreMessages = startIndex + limit < allMessages.length;
1494
1522
  graphMessages = allMessages.slice(startIndex, startIndex + limit);
@@ -1517,7 +1545,9 @@ var TeamsAdapter = class {
1517
1545
  startIndex = allMessages.findIndex(
1518
1546
  (msg) => msg.createdDateTime && msg.createdDateTime > cursor
1519
1547
  );
1520
- if (startIndex === -1) startIndex = allMessages.length;
1548
+ if (startIndex === -1) {
1549
+ startIndex = allMessages.length;
1550
+ }
1521
1551
  }
1522
1552
  hasMoreMessages = startIndex + limit < allMessages.length;
1523
1553
  graphMessages = allMessages.slice(startIndex, startIndex + limit);
@@ -1559,12 +1589,15 @@ var TeamsAdapter = class {
1559
1589
  let nextCursor;
1560
1590
  if (hasMoreMessages && graphMessages.length > 0) {
1561
1591
  if (direction === "forward") {
1562
- const lastMsg = graphMessages[graphMessages.length - 1];
1563
- if (lastMsg?.createdDateTime) nextCursor = lastMsg.createdDateTime;
1592
+ const lastMsg = graphMessages.at(-1);
1593
+ if (lastMsg?.createdDateTime) {
1594
+ nextCursor = lastMsg.createdDateTime;
1595
+ }
1564
1596
  } else {
1565
1597
  const oldestMsg = graphMessages[0];
1566
- if (oldestMsg?.createdDateTime)
1598
+ if (oldestMsg?.createdDateTime) {
1567
1599
  nextCursor = oldestMsg.createdDateTime;
1600
+ }
1568
1601
  }
1569
1602
  }
1570
1603
  return { messages, nextCursor };
@@ -1588,7 +1621,10 @@ var TeamsAdapter = class {
1588
1621
  );
1589
1622
  }
1590
1623
  const { conversationId, serviceUrl } = this.decodeThreadId(channelId);
1591
- const baseConversationId = conversationId.replace(/;messageid=\d+/, "");
1624
+ const baseConversationId = conversationId.replace(
1625
+ MESSAGEID_STRIP_PATTERN,
1626
+ ""
1627
+ );
1592
1628
  const limit = options.limit || 50;
1593
1629
  try {
1594
1630
  let channelContext = null;
@@ -1612,7 +1648,9 @@ var TeamsAdapter = class {
1612
1648
  const response = await this.graphClient.api(apiUrl).top(limit).get();
1613
1649
  const messages = response.value || [];
1614
1650
  for (const msg of messages) {
1615
- if (!msg.id) continue;
1651
+ if (!msg.id) {
1652
+ continue;
1653
+ }
1616
1654
  const threadId = this.encodeThreadId({
1617
1655
  conversationId: `${baseConversationId};messageid=${msg.id}`,
1618
1656
  serviceUrl
@@ -1649,7 +1687,9 @@ var TeamsAdapter = class {
1649
1687
  const response = await this.graphClient.api(apiUrl).top(limit).orderby("createdDateTime desc").get();
1650
1688
  const messages = response.value || [];
1651
1689
  for (const msg of messages) {
1652
- if (!msg.id) continue;
1690
+ if (!msg.id) {
1691
+ continue;
1692
+ }
1653
1693
  const threadId = this.encodeThreadId({
1654
1694
  conversationId: `${baseConversationId};messageid=${msg.id}`,
1655
1695
  serviceUrl
@@ -1695,7 +1735,10 @@ var TeamsAdapter = class {
1695
1735
  */
1696
1736
  async fetchChannelInfo(channelId) {
1697
1737
  const { conversationId } = this.decodeThreadId(channelId);
1698
- const baseConversationId = conversationId.replace(/;messageid=\d+/, "");
1738
+ const baseConversationId = conversationId.replace(
1739
+ MESSAGEID_STRIP_PATTERN,
1740
+ ""
1741
+ );
1699
1742
  let channelContext = null;
1700
1743
  if (this.chat) {
1701
1744
  const cachedContext = await this.chat.getState().get(`teams:channelContext:${baseConversationId}`);
@@ -1744,7 +1787,10 @@ var TeamsAdapter = class {
1744
1787
  */
1745
1788
  async postChannelMessage(channelId, message) {
1746
1789
  const { conversationId, serviceUrl } = this.decodeThreadId(channelId);
1747
- const baseConversationId = conversationId.replace(/;messageid=\d+/, "");
1790
+ const baseConversationId = conversationId.replace(
1791
+ MESSAGEID_STRIP_PATTERN,
1792
+ ""
1793
+ );
1748
1794
  const files = extractFiles(message);
1749
1795
  const fileAttachments = files.length > 0 ? await this.filesToAttachments(files) : [];
1750
1796
  const card = extractCard(message);
@@ -1856,7 +1902,7 @@ var TeamsAdapter = class {
1856
1902
  */
1857
1903
  isMessageFromSelf(activity) {
1858
1904
  const fromId = activity.from?.id;
1859
- if (!fromId || !this.config.appId) {
1905
+ if (!(fromId && this.config.appId)) {
1860
1906
  return false;
1861
1907
  }
1862
1908
  if (fromId === this.config.appId) {
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 ChannelInfo,\n ChatInstance,\n EmojiValue,\n FetchOptions,\n FetchResult,\n FileUpload,\n FormattedContent,\n ListThreadsOptions,\n ListThreadsResult,\n Logger,\n RawMessage,\n ReactionEvent,\n ThreadInfo,\n ThreadSummary,\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 /**\n * Derive channel ID from a Teams thread ID.\n * Teams conversation IDs may include \";messageid=XXX\" for threading.\n * Strip the messageid part to get the base channel/conversation.\n */\n channelIdFromThreadId(threadId: string): string {\n const { conversationId, serviceUrl } = this.decodeThreadId(threadId);\n // Strip ;messageid=XXX from conversation ID\n const baseConversationId = conversationId.replace(/;messageid=\\d+/, \"\");\n return this.encodeThreadId({\n conversationId: baseConversationId,\n serviceUrl,\n });\n }\n\n /**\n * Fetch channel-level messages (all messages in the conversation, not filtered by thread).\n * Uses the Graph API for chat messages.\n */\n async fetchChannelMessages(\n channelId: string,\n options: FetchOptions = {},\n ): Promise<FetchResult<unknown>> {\n if (!this.graphClient) {\n throw new NotImplementedError(\n \"Teams fetchChannelMessages requires appTenantId for Microsoft Graph API access.\",\n \"fetchChannelMessages\",\n );\n }\n\n const { conversationId } = this.decodeThreadId(channelId);\n const baseConversationId = conversationId.replace(/;messageid=\\d+/, \"\");\n const limit = options.limit || 50;\n const direction = options.direction ?? \"backward\";\n\n try {\n // Check if we have channel context (team channel vs group chat)\n let channelContext: TeamsChannelContext | null = null;\n if (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 // Ignore invalid cache\n }\n }\n }\n\n this.logger.debug(\"Teams Graph API: fetchChannelMessages\", {\n conversationId: baseConversationId,\n hasChannelContext: !!channelContext,\n limit,\n direction,\n });\n\n let graphMessages: GraphChatMessage[];\n let hasMoreMessages = false;\n\n if (channelContext) {\n // Team channel: use /teams/{teamId}/channels/{channelId}/messages\n const apiUrl = `/teams/${encodeURIComponent(channelContext.teamId)}/channels/${encodeURIComponent(channelContext.channelId)}/messages`;\n\n if (direction === \"forward\") {\n const allMessages: GraphChatMessage[] = [];\n let nextLink: string | undefined;\n do {\n const request = nextLink\n ? this.graphClient.api(nextLink)\n : this.graphClient.api(apiUrl).top(50);\n const response = await request.get();\n allMessages.push(...((response.value || []) as GraphChatMessage[]));\n nextLink = response[\"@odata.nextLink\"];\n } while (nextLink);\n\n allMessages.reverse();\n let startIndex = 0;\n if (options.cursor) {\n const cursor = options.cursor;\n startIndex = allMessages.findIndex(\n (msg) => msg.createdDateTime && msg.createdDateTime > cursor,\n );\n if (startIndex === -1) startIndex = allMessages.length;\n }\n hasMoreMessages = startIndex + limit < allMessages.length;\n graphMessages = allMessages.slice(startIndex, startIndex + limit);\n } else {\n const request = this.graphClient.api(apiUrl).top(limit);\n const response = await request.get();\n graphMessages = (response.value || []) as GraphChatMessage[];\n graphMessages.reverse();\n hasMoreMessages = graphMessages.length >= limit;\n }\n } else {\n // Group chat / 1:1: use /chats/{chatId}/messages\n const apiUrl = `/chats/${encodeURIComponent(baseConversationId)}/messages`;\n\n if (direction === \"forward\") {\n const allMessages: GraphChatMessage[] = [];\n let nextLink: string | undefined;\n do {\n const request = nextLink\n ? this.graphClient.api(nextLink)\n : this.graphClient\n .api(apiUrl)\n .top(50)\n .orderby(\"createdDateTime desc\");\n const response = await request.get();\n allMessages.push(...((response.value || []) as GraphChatMessage[]));\n nextLink = response[\"@odata.nextLink\"];\n } while (nextLink);\n\n allMessages.reverse();\n let startIndex = 0;\n if (options.cursor) {\n const cursor = options.cursor;\n startIndex = allMessages.findIndex(\n (msg) => msg.createdDateTime && msg.createdDateTime > cursor,\n );\n if (startIndex === -1) startIndex = allMessages.length;\n }\n hasMoreMessages = startIndex + limit < allMessages.length;\n graphMessages = allMessages.slice(startIndex, startIndex + limit);\n } else {\n let request = this.graphClient\n .api(apiUrl)\n .top(limit)\n .orderby(\"createdDateTime desc\");\n if (options.cursor) {\n request = request.filter(`createdDateTime lt ${options.cursor}`);\n }\n const response = await request.get();\n graphMessages = (response.value || []) as GraphChatMessage[];\n graphMessages.reverse();\n hasMoreMessages = graphMessages.length >= limit;\n }\n }\n\n const messages = graphMessages.map((msg) => {\n const isFromBot =\n msg.from?.application?.id === this.config.appId ||\n msg.from?.user?.id === this.config.appId;\n return new Message({\n id: msg.id,\n threadId: channelId,\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 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) nextCursor = lastMsg.createdDateTime;\n } else {\n const oldestMsg = graphMessages[0];\n if (oldestMsg?.createdDateTime)\n nextCursor = oldestMsg.createdDateTime;\n }\n }\n\n return { messages, nextCursor };\n } catch (error) {\n this.logger.error(\"Teams Graph API: fetchChannelMessages error\", {\n error,\n });\n throw error;\n }\n }\n\n /**\n * List threads in a Teams channel.\n * For team channels, fetches messages and filters for those with replies.\n * For group chats, threads are based on message IDs in conversation references.\n */\n async listThreads(\n channelId: string,\n options: ListThreadsOptions = {},\n ): Promise<ListThreadsResult<unknown>> {\n if (!this.graphClient) {\n throw new NotImplementedError(\n \"Teams listThreads requires appTenantId for Microsoft Graph API access.\",\n \"listThreads\",\n );\n }\n\n const { conversationId, serviceUrl } = this.decodeThreadId(channelId);\n const baseConversationId = conversationId.replace(/;messageid=\\d+/, \"\");\n const limit = options.limit || 50;\n\n try {\n // Check for channel context\n let channelContext: TeamsChannelContext | null = null;\n if (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 // Ignore\n }\n }\n }\n\n this.logger.debug(\"Teams Graph API: listThreads\", {\n conversationId: baseConversationId,\n hasChannelContext: !!channelContext,\n limit,\n });\n\n const threads: ThreadSummary[] = [];\n\n if (channelContext) {\n // Team channel: fetch messages and find those with replies\n const apiUrl = `/teams/${encodeURIComponent(channelContext.teamId)}/channels/${encodeURIComponent(channelContext.channelId)}/messages`;\n const response = await this.graphClient.api(apiUrl).top(limit).get();\n const messages = (response.value || []) as (GraphChatMessage & {\n replies?: GraphChatMessage[];\n })[];\n\n for (const msg of messages) {\n if (!msg.id) continue;\n const threadId = this.encodeThreadId({\n conversationId: `${baseConversationId};messageid=${msg.id}`,\n serviceUrl,\n });\n\n const isFromBot =\n msg.from?.application?.id === this.config.appId ||\n msg.from?.user?.id === this.config.appId;\n\n threads.push({\n id: threadId,\n rootMessage: 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 lastReplyAt: msg.lastModifiedDateTime\n ? new Date(msg.lastModifiedDateTime)\n : undefined,\n });\n }\n } else {\n // Group chat: list recent messages as \"threads\"\n const apiUrl = `/chats/${encodeURIComponent(baseConversationId)}/messages`;\n const response = await this.graphClient\n .api(apiUrl)\n .top(limit)\n .orderby(\"createdDateTime desc\")\n .get();\n\n const messages = (response.value || []) as GraphChatMessage[];\n\n for (const msg of messages) {\n if (!msg.id) continue;\n const threadId = this.encodeThreadId({\n conversationId: `${baseConversationId};messageid=${msg.id}`,\n serviceUrl,\n });\n\n const isFromBot =\n msg.from?.application?.id === this.config.appId ||\n msg.from?.user?.id === this.config.appId;\n\n threads.push({\n id: threadId,\n rootMessage: 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 }\n\n this.logger.debug(\"Teams Graph API: listThreads result\", {\n threadCount: threads.length,\n });\n\n return { threads };\n } catch (error) {\n this.logger.error(\"Teams Graph API: listThreads error\", { error });\n throw error;\n }\n }\n\n /**\n * Fetch Teams channel/conversation info.\n */\n async fetchChannelInfo(channelId: string): Promise<ChannelInfo> {\n const { conversationId } = this.decodeThreadId(channelId);\n const baseConversationId = conversationId.replace(/;messageid=\\d+/, \"\");\n\n // Check for channel context\n let channelContext: TeamsChannelContext | null = null;\n if (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 // Ignore\n }\n }\n }\n\n if (channelContext && this.graphClient) {\n try {\n this.logger.debug(\"Teams Graph API: GET channel info\", {\n teamId: channelContext.teamId,\n channelId: channelContext.channelId,\n });\n\n const response = await this.graphClient\n .api(\n `/teams/${encodeURIComponent(channelContext.teamId)}/channels/${encodeURIComponent(channelContext.channelId)}`,\n )\n .get();\n\n return {\n id: channelId,\n name: response.displayName,\n isDM: false,\n memberCount: response.memberCount,\n metadata: {\n membershipType: response.membershipType,\n description: response.description,\n raw: response,\n },\n };\n } catch (error) {\n this.logger.warn(\"Teams Graph API: channel info failed\", { error });\n }\n }\n\n // Fallback for group chats or when Graph API is not available\n return {\n id: channelId,\n isDM: this.isDM(channelId),\n metadata: {\n conversationId: baseConversationId,\n },\n };\n }\n\n /**\n * Post a message to the channel top-level (not in a thread).\n * Uses a conversation reference without ;messageid= to post at the top level.\n */\n async postChannelMessage(\n channelId: string,\n message: AdapterPostableMessage,\n ): Promise<RawMessage<unknown>> {\n const { conversationId, serviceUrl } = this.decodeThreadId(channelId);\n // Ensure we use the base conversation ID (no messageid)\n const baseConversationId = conversationId.replace(/;messageid=\\d+/, \"\");\n\n const files = extractFiles(message);\n const fileAttachments =\n files.length > 0 ? await this.filesToAttachments(files) : [];\n\n const card = extractCard(message);\n let activity: Partial<Activity>;\n\n if (card) {\n const adaptiveCard = cardToAdaptiveCard(card);\n activity = {\n type: ActivityTypes.Message,\n attachments: [\n {\n contentType: \"application/vnd.microsoft.card.adaptive\",\n content: adaptiveCard,\n },\n ...fileAttachments,\n ],\n };\n } else {\n const text = convertEmojiPlaceholders(\n this.formatConverter.renderPostable(message),\n \"teams\",\n );\n activity = {\n type: ActivityTypes.Message,\n text,\n textFormat: \"markdown\",\n attachments: fileAttachments.length > 0 ? fileAttachments : undefined,\n };\n }\n\n const conversationReference = {\n channelId: \"msteams\",\n serviceUrl,\n conversation: { id: baseConversationId },\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: postChannelMessage failed\", {\n conversationId: baseConversationId,\n error,\n });\n this.handleTeamsError(error, \"postChannelMessage\");\n }\n\n this.logger.debug(\"Teams API: postChannelMessage response\", { messageId });\n\n return {\n id: messageId,\n threadId: channelId,\n raw: activity,\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 LinkButtonElement,\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 url?: string;\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\n .filter((child) => child.type === \"button\" || child.type === \"link-button\")\n .map((button) => {\n if (button.type === \"link-button\") {\n return convertLinkButtonToAction(button);\n }\n return 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 convertLinkButtonToAction(\n button: LinkButtonElement,\n): AdaptiveCardAction {\n const action: AdaptiveCardAction = {\n type: \"Action.OpenUrl\",\n title: convertEmoji(button.label),\n url: button.url,\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 ChannelInfo,\n ChatInstance,\n EmojiValue,\n FetchOptions,\n FetchResult,\n FileUpload,\n FormattedContent,\n ListThreadsOptions,\n ListThreadsResult,\n Logger,\n RawMessage,\n ReactionEvent,\n ThreadInfo,\n ThreadSummary,\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\nconst MESSAGEID_CAPTURE_PATTERN = /messageid=(\\d+)/;\nconst MESSAGEID_STRIP_PATTERN = /;messageid=\\d+/;\nconst SEMICOLON_MESSAGEID_CAPTURE_PATTERN = /;messageid=(\\d+)/;\n\n/** Microsoft Graph API chat message type */\ninterface GraphChatMessage {\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 body?: {\n content?: string;\n contentType?: \"text\" | \"html\";\n };\n createdDateTime?: string;\n from?: {\n user?: {\n id?: string;\n displayName?: string;\n };\n application?: {\n id?: string;\n displayName?: string;\n };\n };\n id: string;\n lastModifiedDateTime?: string;\n replyToId?: string; // ID of parent message for channel threads\n}\n\nexport interface TeamsAdapterConfig {\n /** Microsoft App ID */\n appId: string;\n /** Microsoft App Password */\n appPassword: string;\n /** Microsoft App Tenant ID */\n appTenantId?: string;\n /** Microsoft App Type */\n appType?: \"MultiTenant\" | \"SingleTenant\";\n /** Logger instance for error reporting */\n logger: Logger;\n /** Override bot username (optional) */\n userName?: string;\n}\n\n/** Teams-specific thread ID data */\nexport interface TeamsThreadId {\n conversationId: string;\n replyToId?: string;\n serviceUrl: string;\n}\n\n/** Teams channel context extracted from activity.channelData */\ninterface TeamsChannelContext {\n channelId: string;\n teamId: 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 readonly botAdapter: ServerlessCloudAdapter;\n private readonly graphClient: Client | null = null;\n private chat: ChatInstance | null = null;\n private readonly logger: Logger;\n private readonly formatConverter = new TeamsFormatConverter();\n private readonly 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_STRIP_PATTERN, \"\");\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)) {\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: 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) {\n return;\n }\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) {\n return;\n }\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_CAPTURE_PATTERN);\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,\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(\n SEMICOLON_MESSAGEID_CAPTURE_PATTERN\n );\n const threadMessageId = messageIdMatch?.[1];\n\n // Strip ;messageid= from conversation ID\n const baseConversationId = conversationId.replace(\n MESSAGEID_STRIP_PATTERN,\n \"\"\n );\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) {\n startIndex = allMessages.length;\n }\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.at(-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) {\n startIndex = allMessages.length;\n }\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.at(-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\") {\n return null;\n }\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 /**\n * Derive channel ID from a Teams thread ID.\n * Teams conversation IDs may include \";messageid=XXX\" for threading.\n * Strip the messageid part to get the base channel/conversation.\n */\n channelIdFromThreadId(threadId: string): string {\n const { conversationId, serviceUrl } = this.decodeThreadId(threadId);\n // Strip ;messageid=XXX from conversation ID\n const baseConversationId = conversationId.replace(\n MESSAGEID_STRIP_PATTERN,\n \"\"\n );\n return this.encodeThreadId({\n conversationId: baseConversationId,\n serviceUrl,\n });\n }\n\n /**\n * Fetch channel-level messages (all messages in the conversation, not filtered by thread).\n * Uses the Graph API for chat messages.\n */\n async fetchChannelMessages(\n channelId: string,\n options: FetchOptions = {}\n ): Promise<FetchResult<unknown>> {\n if (!this.graphClient) {\n throw new NotImplementedError(\n \"Teams fetchChannelMessages requires appTenantId for Microsoft Graph API access.\",\n \"fetchChannelMessages\"\n );\n }\n\n const { conversationId } = this.decodeThreadId(channelId);\n const baseConversationId = conversationId.replace(\n MESSAGEID_STRIP_PATTERN,\n \"\"\n );\n const limit = options.limit || 50;\n const direction = options.direction ?? \"backward\";\n\n try {\n // Check if we have channel context (team channel vs group chat)\n let channelContext: TeamsChannelContext | null = null;\n if (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 // Ignore invalid cache\n }\n }\n }\n\n this.logger.debug(\"Teams Graph API: fetchChannelMessages\", {\n conversationId: baseConversationId,\n hasChannelContext: !!channelContext,\n limit,\n direction,\n });\n\n let graphMessages: GraphChatMessage[];\n let hasMoreMessages = false;\n\n if (channelContext) {\n // Team channel: use /teams/{teamId}/channels/{channelId}/messages\n const apiUrl = `/teams/${encodeURIComponent(channelContext.teamId)}/channels/${encodeURIComponent(channelContext.channelId)}/messages`;\n\n if (direction === \"forward\") {\n const allMessages: GraphChatMessage[] = [];\n let nextLink: string | undefined;\n do {\n const request = nextLink\n ? this.graphClient.api(nextLink)\n : this.graphClient.api(apiUrl).top(50);\n const response = await request.get();\n allMessages.push(...((response.value || []) as GraphChatMessage[]));\n nextLink = response[\"@odata.nextLink\"];\n } while (nextLink);\n\n allMessages.reverse();\n let startIndex = 0;\n if (options.cursor) {\n const cursor = options.cursor;\n startIndex = allMessages.findIndex(\n (msg) => msg.createdDateTime && msg.createdDateTime > cursor\n );\n if (startIndex === -1) {\n startIndex = allMessages.length;\n }\n }\n hasMoreMessages = startIndex + limit < allMessages.length;\n graphMessages = allMessages.slice(startIndex, startIndex + limit);\n } else {\n const request = this.graphClient.api(apiUrl).top(limit);\n const response = await request.get();\n graphMessages = (response.value || []) as GraphChatMessage[];\n graphMessages.reverse();\n hasMoreMessages = graphMessages.length >= limit;\n }\n } else {\n // Group chat / 1:1: use /chats/{chatId}/messages\n const apiUrl = `/chats/${encodeURIComponent(baseConversationId)}/messages`;\n\n if (direction === \"forward\") {\n const allMessages: GraphChatMessage[] = [];\n let nextLink: string | undefined;\n do {\n const request = nextLink\n ? this.graphClient.api(nextLink)\n : this.graphClient\n .api(apiUrl)\n .top(50)\n .orderby(\"createdDateTime desc\");\n const response = await request.get();\n allMessages.push(...((response.value || []) as GraphChatMessage[]));\n nextLink = response[\"@odata.nextLink\"];\n } while (nextLink);\n\n allMessages.reverse();\n let startIndex = 0;\n if (options.cursor) {\n const cursor = options.cursor;\n startIndex = allMessages.findIndex(\n (msg) => msg.createdDateTime && msg.createdDateTime > cursor\n );\n if (startIndex === -1) {\n startIndex = allMessages.length;\n }\n }\n hasMoreMessages = startIndex + limit < allMessages.length;\n graphMessages = allMessages.slice(startIndex, startIndex + limit);\n } else {\n let request = this.graphClient\n .api(apiUrl)\n .top(limit)\n .orderby(\"createdDateTime desc\");\n if (options.cursor) {\n request = request.filter(`createdDateTime lt ${options.cursor}`);\n }\n const response = await request.get();\n graphMessages = (response.value || []) as GraphChatMessage[];\n graphMessages.reverse();\n hasMoreMessages = graphMessages.length >= limit;\n }\n }\n\n const messages = graphMessages.map((msg) => {\n const isFromBot =\n msg.from?.application?.id === this.config.appId ||\n msg.from?.user?.id === this.config.appId;\n return new Message({\n id: msg.id,\n threadId: channelId,\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 let nextCursor: string | undefined;\n if (hasMoreMessages && graphMessages.length > 0) {\n if (direction === \"forward\") {\n const lastMsg = graphMessages.at(-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 } catch (error) {\n this.logger.error(\"Teams Graph API: fetchChannelMessages error\", {\n error,\n });\n throw error;\n }\n }\n\n /**\n * List threads in a Teams channel.\n * For team channels, fetches messages and filters for those with replies.\n * For group chats, threads are based on message IDs in conversation references.\n */\n async listThreads(\n channelId: string,\n options: ListThreadsOptions = {}\n ): Promise<ListThreadsResult<unknown>> {\n if (!this.graphClient) {\n throw new NotImplementedError(\n \"Teams listThreads requires appTenantId for Microsoft Graph API access.\",\n \"listThreads\"\n );\n }\n\n const { conversationId, serviceUrl } = this.decodeThreadId(channelId);\n const baseConversationId = conversationId.replace(\n MESSAGEID_STRIP_PATTERN,\n \"\"\n );\n const limit = options.limit || 50;\n\n try {\n // Check for channel context\n let channelContext: TeamsChannelContext | null = null;\n if (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 // Ignore\n }\n }\n }\n\n this.logger.debug(\"Teams Graph API: listThreads\", {\n conversationId: baseConversationId,\n hasChannelContext: !!channelContext,\n limit,\n });\n\n const threads: ThreadSummary[] = [];\n\n if (channelContext) {\n // Team channel: fetch messages and find those with replies\n const apiUrl = `/teams/${encodeURIComponent(channelContext.teamId)}/channels/${encodeURIComponent(channelContext.channelId)}/messages`;\n const response = await this.graphClient.api(apiUrl).top(limit).get();\n const messages = (response.value || []) as (GraphChatMessage & {\n replies?: GraphChatMessage[];\n })[];\n\n for (const msg of messages) {\n if (!msg.id) {\n continue;\n }\n const threadId = this.encodeThreadId({\n conversationId: `${baseConversationId};messageid=${msg.id}`,\n serviceUrl,\n });\n\n const isFromBot =\n msg.from?.application?.id === this.config.appId ||\n msg.from?.user?.id === this.config.appId;\n\n threads.push({\n id: threadId,\n rootMessage: 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 lastReplyAt: msg.lastModifiedDateTime\n ? new Date(msg.lastModifiedDateTime)\n : undefined,\n });\n }\n } else {\n // Group chat: list recent messages as \"threads\"\n const apiUrl = `/chats/${encodeURIComponent(baseConversationId)}/messages`;\n const response = await this.graphClient\n .api(apiUrl)\n .top(limit)\n .orderby(\"createdDateTime desc\")\n .get();\n\n const messages = (response.value || []) as GraphChatMessage[];\n\n for (const msg of messages) {\n if (!msg.id) {\n continue;\n }\n const threadId = this.encodeThreadId({\n conversationId: `${baseConversationId};messageid=${msg.id}`,\n serviceUrl,\n });\n\n const isFromBot =\n msg.from?.application?.id === this.config.appId ||\n msg.from?.user?.id === this.config.appId;\n\n threads.push({\n id: threadId,\n rootMessage: 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 }\n\n this.logger.debug(\"Teams Graph API: listThreads result\", {\n threadCount: threads.length,\n });\n\n return { threads };\n } catch (error) {\n this.logger.error(\"Teams Graph API: listThreads error\", { error });\n throw error;\n }\n }\n\n /**\n * Fetch Teams channel/conversation info.\n */\n async fetchChannelInfo(channelId: string): Promise<ChannelInfo> {\n const { conversationId } = this.decodeThreadId(channelId);\n const baseConversationId = conversationId.replace(\n MESSAGEID_STRIP_PATTERN,\n \"\"\n );\n\n // Check for channel context\n let channelContext: TeamsChannelContext | null = null;\n if (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 // Ignore\n }\n }\n }\n\n if (channelContext && this.graphClient) {\n try {\n this.logger.debug(\"Teams Graph API: GET channel info\", {\n teamId: channelContext.teamId,\n channelId: channelContext.channelId,\n });\n\n const response = await this.graphClient\n .api(\n `/teams/${encodeURIComponent(channelContext.teamId)}/channels/${encodeURIComponent(channelContext.channelId)}`\n )\n .get();\n\n return {\n id: channelId,\n name: response.displayName,\n isDM: false,\n memberCount: response.memberCount,\n metadata: {\n membershipType: response.membershipType,\n description: response.description,\n raw: response,\n },\n };\n } catch (error) {\n this.logger.warn(\"Teams Graph API: channel info failed\", { error });\n }\n }\n\n // Fallback for group chats or when Graph API is not available\n return {\n id: channelId,\n isDM: this.isDM(channelId),\n metadata: {\n conversationId: baseConversationId,\n },\n };\n }\n\n /**\n * Post a message to the channel top-level (not in a thread).\n * Uses a conversation reference without ;messageid= to post at the top level.\n */\n async postChannelMessage(\n channelId: string,\n message: AdapterPostableMessage\n ): Promise<RawMessage<unknown>> {\n const { conversationId, serviceUrl } = this.decodeThreadId(channelId);\n // Ensure we use the base conversation ID (no messageid)\n const baseConversationId = conversationId.replace(\n MESSAGEID_STRIP_PATTERN,\n \"\"\n );\n\n const files = extractFiles(message);\n const fileAttachments =\n files.length > 0 ? await this.filesToAttachments(files) : [];\n\n const card = extractCard(message);\n let activity: Partial<Activity>;\n\n if (card) {\n const adaptiveCard = cardToAdaptiveCard(card);\n activity = {\n type: ActivityTypes.Message,\n attachments: [\n {\n contentType: \"application/vnd.microsoft.card.adaptive\",\n content: adaptiveCard,\n },\n ...fileAttachments,\n ],\n };\n } else {\n const text = convertEmojiPlaceholders(\n this.formatConverter.renderPostable(message),\n \"teams\"\n );\n activity = {\n type: ActivityTypes.Message,\n text,\n textFormat: \"markdown\",\n attachments: fileAttachments.length > 0 ? fileAttachments : undefined,\n };\n }\n\n const conversationReference = {\n channelId: \"msteams\",\n serviceUrl,\n conversation: { id: baseConversationId },\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: postChannelMessage failed\", {\n conversationId: baseConversationId,\n error,\n });\n this.handleTeamsError(error, \"postChannelMessage\");\n }\n\n this.logger.debug(\"Teams API: postChannelMessage response\", { messageId });\n\n return {\n id: messageId,\n threadId: channelId,\n raw: activity,\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 LinkButtonElement,\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 $schema: string;\n actions?: AdaptiveCardAction[];\n body: AdaptiveCardElement[];\n type: \"AdaptiveCard\";\n version: string;\n}\n\nexport interface AdaptiveCardElement {\n type: string;\n [key: string]: unknown;\n}\n\nexport interface AdaptiveCardAction {\n data?: Record<string, unknown>;\n style?: string;\n title: string;\n type: string;\n url?: 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 actions: AdaptiveCardAction[];\n elements: AdaptiveCardElement[];\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\n .filter((child) => child.type === \"button\" || child.type === \"link-button\")\n .map((button) => {\n if (button.type === \"link-button\") {\n return convertLinkButtonToAction(button);\n }\n return 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 convertLinkButtonToAction(\n button: LinkButtonElement\n): AdaptiveCardAction {\n const action: AdaptiveCardAction = {\n type: \"Action.OpenUrl\",\n title: convertEmoji(button.label),\n url: button.url,\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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chat-adapter/teams",
3
- "version": "4.13.1",
3
+ "version": "4.13.2",
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.13.1",
23
- "chat": "4.13.1"
22
+ "@chat-adapter/shared": "4.13.2",
23
+ "chat": "4.13.2"
24
24
  },
25
25
  "devDependencies": {
26
26
  "@types/node": "^22.10.2",
@@ -30,12 +30,12 @@
30
30
  },
31
31
  "repository": {
32
32
  "type": "git",
33
- "url": "git+https://github.com/vercel-labs/chat.git",
33
+ "url": "git+https://github.com/vercel/chat.git",
34
34
  "directory": "packages/adapter-teams"
35
35
  },
36
- "homepage": "https://github.com/vercel-labs/chat#readme",
36
+ "homepage": "https://github.com/vercel/chat#readme",
37
37
  "bugs": {
38
- "url": "https://github.com/vercel-labs/chat/issues"
38
+ "url": "https://github.com/vercel/chat/issues"
39
39
  },
40
40
  "publishConfig": {
41
41
  "access": "public"
@@ -51,10 +51,9 @@
51
51
  "scripts": {
52
52
  "build": "tsup",
53
53
  "dev": "tsup --watch",
54
- "test": "vitest run",
54
+ "test": "vitest run --coverage",
55
55
  "test:watch": "vitest",
56
56
  "typecheck": "tsc --noEmit",
57
- "lint": "biome check src",
58
57
  "clean": "rm -rf dist"
59
58
  }
60
59
  }