@openclaw/msteams 2026.10.5-beta.4 → 2026.10.10-beta.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.
Files changed (35) hide show
  1. package/dist/.setup/{adapter.runtime-JoPydWqP.cjs → adapter.runtime-B-6Q3nMI.cjs} +6 -4
  2. package/dist/.setup/{approval-handler.runtime-bUN9icMr.cjs → approval-handler.runtime-DccVf5AY.cjs} +16 -6
  3. package/dist/.setup/{bot-framework-service-url-CPp4d-Q6.cjs → bot-framework-service-url-B3FWn2SV.cjs} +13 -0
  4. package/dist/.setup/{channel-Cifn9fsv.cjs → channel-B7Gy3QUW.cjs} +261 -175
  5. package/dist/.setup/{channel.runtime-DWRAolil.cjs → channel.runtime-DzKeQAqd.cjs} +84 -49
  6. package/dist/.setup/channel.setup-B5ClEcNI.cjs +990 -0
  7. package/dist/.setup/config-schema-CkKdphSp.cjs +293 -0
  8. package/dist/.setup/{conversation-store-state-DprmOfl7.cjs → conversation-store-state-C3__T9mK.cjs} +79 -91
  9. package/dist/.setup/delegated-state-Bv5cWuec.cjs +110 -0
  10. package/dist/.setup/{monitor-C9x_CJhY.cjs → monitor-CzRI-Hsc.cjs} +283 -240
  11. package/dist/.setup/{oauth-CWJ85AMw.cjs → oauth-D9u8sPCD.cjs} +1 -1
  12. package/dist/.setup/{reaction-types-Ig9sQJuR.cjs → reaction-types-DSQWVA4Z.cjs} +15 -9
  13. package/dist/.setup/{resolve-allowlist-DdDBVCzl.cjs → resolve-allowlist-tYgAHx0t.cjs} +252 -38
  14. package/dist/.setup/{secret-contract-api-ChQdBeU1.cjs → secret-contract-api-YY3v6MP8.cjs} +1 -1
  15. package/dist/.setup/secret-contract-api-wAyY5UHv.cjs +85 -0
  16. package/dist/.setup/{send-rJpcVtRp.cjs → send-Bgm8RYUa.cjs} +45 -24
  17. package/dist/.setup/{setup-plugin-api-DfoVOqMJ.cjs → setup-plugin-api-BACfeF7_.cjs} +1 -1
  18. package/dist/.setup/{thread-session-B74FC1Id.cjs → thread-session-C7KTMwLU.cjs} +11 -4
  19. package/dist/.setup/token-config-bEpua_Q7.cjs +93 -0
  20. package/dist/api.cjs +2 -2
  21. package/dist/channel-config-api.cjs +1 -1
  22. package/dist/channel-plugin-api.cjs +1 -1
  23. package/dist/directory-contract-api.cjs +30 -22
  24. package/dist/doctor-contract-api.cjs +11 -10
  25. package/dist/qa-runner-api.cjs +1 -1
  26. package/dist/runtime-api.cjs +2 -2
  27. package/dist/secret-contract-api.cjs +1 -1
  28. package/dist/setup-entry.cjs +2 -2
  29. package/dist/setup-plugin-api.cjs +1 -1
  30. package/openclaw.plugin.json +715 -0
  31. package/package.json +57 -6
  32. package/dist/.setup/channel.setup-BjbHugkL.cjs +0 -632
  33. package/dist/.setup/config-schema-Dk0mRIam.cjs +0 -160
  34. package/dist/.setup/secret-contract-api-ipiUsANg.cjs +0 -28
  35. package/dist/.setup/token-config-CqIh8z7Q.cjs +0 -138
@@ -1,20 +1,20 @@
1
1
  const require_rolldown_runtime = require("./rolldown-runtime-BloeesfJ.cjs");
2
+ const require_bot_framework_service_url = require("./bot-framework-service-url-B3FWn2SV.cjs");
3
+ const require_resolve_allowlist = require("./resolve-allowlist-tYgAHx0t.cjs");
2
4
  const require_runtime = require("./runtime-gZTAuFux.cjs");
3
5
  require("../runtime-api.cjs");
4
- const require_resolve_allowlist = require("./resolve-allowlist-DdDBVCzl.cjs");
5
- const require_bot_framework_service_url = require("./bot-framework-service-url-CPp4d-Q6.cjs");
6
- const require_token_config = require("./token-config-CqIh8z7Q.cjs");
7
- const require_channel = require("./channel-Cifn9fsv.cjs");
8
- const require_thread_session = require("./thread-session-B74FC1Id.cjs");
9
- const require_channel_setup = require("./channel.setup-BjbHugkL.cjs");
6
+ const require_delegated_state = require("./delegated-state-Bv5cWuec.cjs");
7
+ const require_channel = require("./channel-B7Gy3QUW.cjs");
8
+ const require_thread_session = require("./thread-session-C7KTMwLU.cjs");
9
+ const require_channel_setup = require("./channel.setup-B5ClEcNI.cjs");
10
10
  const require_approval_card = require("./approval-card-DWPDItvq.cjs");
11
- const require_conversation_store_state = require("./conversation-store-state-DprmOfl7.cjs");
12
- const require_reaction_types = require("./reaction-types-Ig9sQJuR.cjs");
11
+ const require_conversation_store_state = require("./conversation-store-state-C3__T9mK.cjs");
12
+ const require_reaction_types = require("./reaction-types-DSQWVA4Z.cjs");
13
13
  let openclaw_plugin_sdk_channel_outbound = require("openclaw/plugin-sdk/channel-outbound");
14
14
  let openclaw_plugin_sdk_string_coerce_runtime = require("openclaw/plugin-sdk/string-coerce-runtime");
15
+ let openclaw_plugin_sdk_account_id = require("openclaw/plugin-sdk/account-id");
15
16
  let openclaw_plugin_sdk_allow_from = require("openclaw/plugin-sdk/allow-from");
16
17
  let openclaw_plugin_sdk_provider_http = require("openclaw/plugin-sdk/provider-http");
17
- let openclaw_plugin_sdk_account_id = require("openclaw/plugin-sdk/account-id");
18
18
  let openclaw_plugin_sdk_media_runtime = require("openclaw/plugin-sdk/media-runtime");
19
19
  let openclaw_plugin_sdk_ssrf_runtime = require("openclaw/plugin-sdk/ssrf-runtime");
20
20
  let node_dns_promises = require("node:dns/promises");
@@ -22,12 +22,12 @@ let openclaw_plugin_sdk_ssrf_policy = require("openclaw/plugin-sdk/ssrf-policy")
22
22
  let openclaw_plugin_sdk_number_runtime = require("openclaw/plugin-sdk/number-runtime");
23
23
  let openclaw_plugin_sdk_text_utility_runtime = require("openclaw/plugin-sdk/text-utility-runtime");
24
24
  let openclaw_plugin_sdk_error_runtime = require("openclaw/plugin-sdk/error-runtime");
25
- let openclaw_plugin_sdk_routing = require("openclaw/plugin-sdk/routing");
26
- let openclaw_plugin_sdk_channel_inbound = require("openclaw/plugin-sdk/channel-inbound");
27
25
  let node_crypto = require("node:crypto");
28
26
  node_crypto = require_rolldown_runtime.__toESM(node_crypto, 1);
29
- let openclaw_plugin_sdk_security_runtime = require("openclaw/plugin-sdk/security-runtime");
30
27
  let openclaw_plugin_sdk_webhook_targets = require("openclaw/plugin-sdk/webhook-targets");
28
+ let openclaw_plugin_sdk_routing = require("openclaw/plugin-sdk/routing");
29
+ let openclaw_plugin_sdk_channel_inbound = require("openclaw/plugin-sdk/channel-inbound");
30
+ let openclaw_plugin_sdk_security_runtime = require("openclaw/plugin-sdk/security-runtime");
31
31
  let openclaw_plugin_sdk_approval_gateway_runtime = require("openclaw/plugin-sdk/approval-gateway-runtime");
32
32
  let openclaw_plugin_sdk_channel_ingress_runtime = require("openclaw/plugin-sdk/channel-ingress-runtime");
33
33
  let openclaw_plugin_sdk_context_visibility_runtime = require("openclaw/plugin-sdk/context-visibility-runtime");
@@ -86,7 +86,8 @@ async function storeSessionLearning(params) {
86
86
  */
87
87
  async function runFeedbackReflection(params) {
88
88
  const { cfg, log, sessionKey } = params;
89
- const cooldownMs = cfg.channels?.msteams?.feedbackReflectionCooldownMs ?? openclaw_plugin_sdk_channel_inbound.DEFAULT_CHANNEL_FEEDBACK_REFLECTION_COOLDOWN_MS;
89
+ const msteamsCfg = require_resolve_allowlist.resolveMSTeamsAccountConfig(cfg, params.accountId);
90
+ const cooldownMs = msteamsCfg.feedbackReflectionCooldownMs ?? openclaw_plugin_sdk_channel_inbound.DEFAULT_CHANNEL_FEEDBACK_REFLECTION_COOLDOWN_MS;
90
91
  let reflection;
91
92
  try {
92
93
  reflection = await (0, openclaw_plugin_sdk_channel_inbound.runChannelFeedbackReflection)({
@@ -140,7 +141,10 @@ async function runFeedbackReflection(params) {
140
141
  await require_conversation_store_state.sendMSTeamsActivityWithReference(params.app, require_conversation_store_state.buildConversationReference(params.conversationRef), {
141
142
  type: "message",
142
143
  text: reflection.userMessage
143
- }, { serviceUrlBoundary: require_resolve_allowlist.resolveMSTeamsSdkCloudOptions(cfg.channels?.msteams) });
144
+ }, {
145
+ accountId: params.accountId,
146
+ serviceUrlBoundary: require_resolve_allowlist.resolveMSTeamsSdkCloudOptions(msteamsCfg)
147
+ });
144
148
  log.info("sent reflection follow-up", { sessionKey });
145
149
  } catch (err) {
146
150
  log.debug?.("failed to send reflection follow-up", { error: require_channel_setup.formatUnknownError(err) });
@@ -197,7 +201,7 @@ async function maybeHandleMSTeamsApprovalCardSubmit(params) {
197
201
  ignored("unknown or expired card token");
198
202
  return true;
199
203
  }
200
- if (binding.accountId !== openclaw_plugin_sdk_account_id.DEFAULT_ACCOUNT_ID) {
204
+ if ((0, openclaw_plugin_sdk_account_id.normalizeAccountId)(binding.accountId) !== (0, openclaw_plugin_sdk_account_id.normalizeAccountId)(deps.accountId)) {
201
205
  ignored("card token account mismatch");
202
206
  return true;
203
207
  }
@@ -213,10 +217,16 @@ async function maybeHandleMSTeamsApprovalCardSubmit(params) {
213
217
  ignored("card token decision is no longer allowed");
214
218
  return true;
215
219
  }
220
+ const cfg = deps.readConfig?.() ?? deps.cfg;
221
+ const account = require_resolve_allowlist.resolveMSTeamsAccountConfig(cfg, binding.accountId);
222
+ if (account.appId && account.appId !== deps.appId) {
223
+ ignored("issuing account is no longer active");
224
+ return true;
225
+ }
216
226
  const senderId = context.activity.from?.aadObjectId;
217
227
  if (!(require_thread_session.msTeamsApprovalAuth.authorizeActorAction?.({
218
- cfg: deps.cfg,
219
- accountId: openclaw_plugin_sdk_account_id.DEFAULT_ACCOUNT_ID,
228
+ cfg,
229
+ accountId: binding.accountId,
220
230
  senderId,
221
231
  action: "approve",
222
232
  approvalKind: binding.approvalKind
@@ -226,12 +236,12 @@ async function maybeHandleMSTeamsApprovalCardSubmit(params) {
226
236
  }
227
237
  const outcome = await require_approval_card.msTeamsApprovalControls.settle(token, async (consumed) => {
228
238
  const result = await (0, openclaw_plugin_sdk_approval_gateway_runtime.resolveApprovalOverGateway)({
229
- cfg: deps.cfg,
239
+ cfg,
230
240
  approvalId: consumed.approvalId,
231
241
  approvalKind: consumed.approvalKind,
232
242
  decision: consumed.decision,
233
243
  channel: "msteams",
234
- accountId: openclaw_plugin_sdk_account_id.DEFAULT_ACCOUNT_ID,
244
+ accountId: consumed.accountId,
235
245
  senderId
236
246
  });
237
247
  await context.updateActivity({
@@ -306,7 +316,8 @@ function formatMSTeamsSenderReason(params) {
306
316
  }
307
317
  async function resolveMSTeamsSenderAccess(params) {
308
318
  const activity = params.activity;
309
- const msteamsCfg = params.cfg.channels?.msteams;
319
+ const accountId = params.accountId ?? openclaw_plugin_sdk_account_id.DEFAULT_ACCOUNT_ID;
320
+ const msteamsCfg = params.cfg.channels?.msteams ? require_resolve_allowlist.resolveMSTeamsAccountConfig(params.cfg, accountId) : void 0;
310
321
  const conversationId = require_resolve_allowlist.normalizeMSTeamsConversationId(activity.conversation?.id ?? "unknown");
311
322
  const convType = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(activity.conversation?.conversationType);
312
323
  const isDirectMessage = convType === "personal" || !convType && !activity.conversation?.isGroup;
@@ -317,7 +328,7 @@ async function resolveMSTeamsSenderAccess(params) {
317
328
  const pairing = (0, openclaw_plugin_sdk_channel_pairing.createChannelPairingController)({
318
329
  core,
319
330
  channel: "msteams",
320
- accountId: openclaw_plugin_sdk_account_id.DEFAULT_ACCOUNT_ID
331
+ accountId
321
332
  });
322
333
  const dmPolicy = msteamsCfg?.dmPolicy ?? "pairing";
323
334
  const configuredDmAllowFrom = msteamsCfg?.allowFrom ?? [];
@@ -418,6 +429,7 @@ async function admitMSTeamsMessage(params) {
418
429
  const isControlCommand = allowTextCommands && core.channel.commands.isControlCommandMessage(params.text, params.cfg);
419
430
  const access = await resolveMSTeamsSenderAccess({
420
431
  cfg: params.cfg,
432
+ accountId: params.accountId,
421
433
  activity: params.activity,
422
434
  hasControlCommand: isControlCommand
423
435
  });
@@ -543,6 +555,32 @@ function resolveBotThreadMentionPolicy(params) {
543
555
  };
544
556
  }
545
557
  //#endregion
558
+ //#region extensions/msteams/src/monitor-handler/config.ts
559
+ function createMSTeamsHandlerConfigReader(deps) {
560
+ const source = deps.accountPolicyCfg ?? deps.cfg;
561
+ const read = (0, openclaw_plugin_sdk_runtime_config_snapshot.createRuntimeConfigReader)(source);
562
+ const originallyConfigured = require_resolve_allowlist.listMSTeamsAccountIds(source).includes(deps.accountId);
563
+ let lastSource;
564
+ let lastProjection = deps.cfg;
565
+ return () => {
566
+ const current = read();
567
+ if (current === lastSource) return lastProjection;
568
+ lastSource = current;
569
+ if (current === source && deps.accountPolicyCfg) return lastProjection = deps.cfg;
570
+ const retired = originallyConfigured && !require_resolve_allowlist.listMSTeamsAccountIds(current).includes(deps.accountId);
571
+ if (deps.accountId === "default" && !current.channels?.msteams?.accounts && !retired) return lastProjection = current;
572
+ const accountConfig = require_resolve_allowlist.resolveMSTeamsAccountConfig(current, deps.accountId);
573
+ return lastProjection = require_resolve_allowlist.withAccountScopedMSTeamsConfig({
574
+ cfg: current,
575
+ accountId: deps.accountId,
576
+ accountConfig: {
577
+ ...accountConfig,
578
+ ...current.channels?.msteams?.enabled === false || retired ? { enabled: false } : {}
579
+ }
580
+ });
581
+ };
582
+ }
583
+ //#endregion
546
584
  //#region extensions/msteams/src/attachments/bot-framework.ts
547
585
  const BOT_FRAMEWORK_SCOPE = "https://api.botframework.com";
548
586
  /**
@@ -598,18 +636,6 @@ async function fetchBotFrameworkAttachmentInfo(params) {
598
636
  async function saveBotFrameworkAttachmentView(params) {
599
637
  const response = await fetchBotFrameworkAttachment(params, "attachmentView");
600
638
  if (!response) return;
601
- let contentLength;
602
- try {
603
- contentLength = (0, openclaw_plugin_sdk_media_runtime.parseMediaContentLength)(response.headers.get("content-length"));
604
- } catch (err) {
605
- await response.body?.cancel().catch(() => void 0);
606
- params.logger?.warn?.("msteams botFramework attachmentView invalid content-length", { error: (0, openclaw_plugin_sdk_error_runtime.coerceErrorMessage)(err) });
607
- return;
608
- }
609
- if (contentLength !== null && contentLength > params.maxBytes) {
610
- await response.body?.cancel().catch(() => void 0);
611
- return;
612
- }
613
639
  try {
614
640
  return await require_runtime.getMSTeamsRuntime().channel.media.saveResponseMedia(response, {
615
641
  sourceUrl: params.url,
@@ -845,8 +871,9 @@ async function downloadAndStoreMSTeamsRemoteMedia(params) {
845
871
  }
846
872
  //#endregion
847
873
  //#region extensions/msteams/src/attachments/download.ts
848
- function resolveDownloadCandidate(att) {
874
+ function resolveDownloadCandidate(att, graphReferenceToken) {
849
875
  const contentType = require_resolve_allowlist.normalizeContentType(att.contentType);
876
+ const graphReference = graphReferenceToken !== void 0 && contentType === "reference";
850
877
  const name = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalString)(att.name) ?? "";
851
878
  if (contentType === "application/vnd.microsoft.teams.file.download.info") {
852
879
  if (!(0, openclaw_plugin_sdk_string_coerce_runtime.isRecord)(att.content)) return null;
@@ -869,11 +896,11 @@ function resolveDownloadCandidate(att) {
869
896
  sourceId: att.id?.trim() || void 0
870
897
  };
871
898
  }
872
- const contentUrl = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalString)(att.contentUrl) ?? "";
899
+ const contentUrl = graphReference ? att.contentUrl ?? "" : (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalString)(att.contentUrl) ?? "";
873
900
  if (!contentUrl) return null;
874
- const sharesUrl = require_resolve_allowlist.tryBuildGraphSharesUrlForSharedLink(contentUrl);
901
+ const sharesUrl = graphReference ? `${require_resolve_allowlist.GRAPH_ROOT}/shares/${require_resolve_allowlist.encodeGraphShareId(contentUrl)}/driveItem/content` : require_resolve_allowlist.tryBuildGraphSharesUrlForSharedLink(contentUrl);
875
902
  const resolvedUrl = sharesUrl ?? contentUrl;
876
- const resolvedContentTypeHint = sharesUrl ? void 0 : contentType;
903
+ const resolvedContentTypeHint = graphReference ? "application/octet-stream" : sharesUrl ? void 0 : contentType;
877
904
  return {
878
905
  kind: "remote",
879
906
  mediaKind: require_resolve_allowlist.resolveMSTeamsMediaKind({
@@ -881,9 +908,10 @@ function resolveDownloadCandidate(att) {
881
908
  fileName: name
882
909
  }),
883
910
  url: resolvedUrl,
884
- fileHint: name || void 0,
911
+ fileHint: graphReference ? att.name ?? void 0 : name || void 0,
885
912
  contentTypeHint: resolvedContentTypeHint,
886
- sourceId: att.id?.trim() || void 0
913
+ sourceId: att.id?.trim() || void 0,
914
+ graphReference
887
915
  };
888
916
  }
889
917
  function scopeCandidatesForUrl(url) {
@@ -931,6 +959,19 @@ async function fetchWithAuthFallback(params) {
931
959
  resolveFn: params.resolveFn,
932
960
  timeoutMs: require_resolve_allowlist.resolveMSTeamsRequestTimeoutMs(params.deadline)
933
961
  });
962
+ if (params.bearerToken !== void 0) {
963
+ const headers = require_resolve_allowlist.ensureUserAgentHeader(params.requestInit?.headers);
964
+ require_resolve_allowlist.applyAuthorizationHeaderForUrl({
965
+ headers,
966
+ url: params.url,
967
+ authAllowHosts: params.policy.authAllowHosts,
968
+ bearerToken: params.bearerToken
969
+ });
970
+ return fetchAttempt(params.fetchFn, {
971
+ ...params.requestInit,
972
+ headers
973
+ });
974
+ }
934
975
  const firstAttempt = await fetchAttempt(params.fetchFn, params.requestInit);
935
976
  if (firstAttempt.ok) return firstAttempt;
936
977
  if (!params.tokenProvider) return firstAttempt;
@@ -967,7 +1008,7 @@ async function downloadMSTeamsAttachments(params) {
967
1008
  });
968
1009
  const allowHosts = policy.allowHosts;
969
1010
  const candidates = list.filter(require_resolve_allowlist.isAdvertisedFileAttachment).map((attachment) => {
970
- return (require_resolve_allowlist.isDownloadableAttachment(attachment) ? resolveDownloadCandidate(attachment) : null) ?? {
1011
+ return (require_resolve_allowlist.isDownloadableAttachment(attachment) ? resolveDownloadCandidate(attachment, params.graphReferenceToken) : null) ?? {
971
1012
  kind: "unavailable",
972
1013
  mediaKind: require_resolve_allowlist.resolveMSTeamsMediaKind({
973
1014
  contentType: require_resolve_allowlist.normalizeContentType(attachment.contentType),
@@ -1022,7 +1063,7 @@ async function downloadMSTeamsAttachments(params) {
1022
1063
  filePathHint: candidate.fileHint ?? candidate.url,
1023
1064
  maxBytes: params.maxBytes,
1024
1065
  contentTypeHint: candidate.contentTypeHint,
1025
- kind: candidate.mediaKind,
1066
+ kind: candidate.graphReference ? void 0 : candidate.mediaKind,
1026
1067
  preserveFilenames: params.preserveFilenames,
1027
1068
  fetchImpl: (input, init) => fetchWithAuthFallback({
1028
1069
  url: (0, openclaw_plugin_sdk_request_url.resolveRequestUrl)(input),
@@ -1031,10 +1072,18 @@ async function downloadMSTeamsAttachments(params) {
1031
1072
  requestInit: init,
1032
1073
  resolveFn: params.resolveFn,
1033
1074
  policy,
1034
- deadline: params.deadline
1075
+ deadline: params.deadline,
1076
+ bearerToken: candidate.graphReference ? params.graphReferenceToken : void 0
1035
1077
  })
1036
1078
  });
1037
1079
  } catch (err) {
1080
+ if (candidate.graphReference) {
1081
+ params.logger?.warn?.("msteams SharePoint reference download failed", {
1082
+ error: (0, openclaw_plugin_sdk_error_runtime.coerceErrorMessage)(err),
1083
+ name: candidate.fileHint
1084
+ });
1085
+ return;
1086
+ }
1038
1087
  const msg = (0, openclaw_plugin_sdk_error_runtime.coerceErrorMessage)(err);
1039
1088
  params.logger?.warn?.(`msteams attachment download failed host=${safeHostForLog(candidate.url)} error=${msg}`);
1040
1089
  }
@@ -1220,9 +1269,6 @@ async function downloadMSTeamsGraphMedia(params) {
1220
1269
  tokenError: true
1221
1270
  };
1222
1271
  }
1223
- const fetchFn = params.fetchFn ?? fetch;
1224
- const sharePointMedia = [];
1225
- const downloadedReferenceUrls = /* @__PURE__ */ new Set();
1226
1272
  let messageAttachments = [];
1227
1273
  let referenceAttachments = [];
1228
1274
  let messageStatus;
@@ -1251,72 +1297,13 @@ async function downloadMSTeamsGraphMedia(params) {
1251
1297
  } catch (err) {
1252
1298
  logGraphFailure("message fetch", err);
1253
1299
  }
1254
- for (const att of referenceAttachments) {
1255
- const name = att.name ?? "file";
1256
- const shareUrl = att.contentUrl ?? "";
1257
- const sourceId = att.id?.trim();
1258
- const unavailableMedia = {
1259
- kind: require_resolve_allowlist.resolveMSTeamsMediaKind({
1260
- contentType: att.contentType ?? void 0,
1261
- fileName: name
1262
- }),
1263
- ...sourceId ? { sourceId } : {}
1264
- };
1265
- if (!shareUrl) {
1266
- sharePointMedia.push(unavailableMedia);
1267
- continue;
1268
- }
1269
- downloadedReferenceUrls.add(shareUrl);
1270
- try {
1271
- const sharesUrl = `${require_resolve_allowlist.GRAPH_ROOT}/shares/${require_resolve_allowlist.encodeGraphShareId(shareUrl)}/driveItem/content`;
1272
- if (!(0, openclaw_plugin_sdk_ssrf_policy.isHttpsUrlAllowedByHostnameSuffixAllowlist)(sharesUrl, policy.allowHosts)) {
1273
- params.logger?.debug?.("graph media sharepoint url not in allowHosts", {
1274
- messageUrl,
1275
- sharesUrl
1276
- });
1277
- sharePointMedia.push(unavailableMedia);
1278
- continue;
1279
- }
1280
- const media = await downloadAndStoreMSTeamsRemoteMedia({
1281
- url: sharesUrl,
1282
- filePathHint: name,
1283
- maxBytes: params.maxBytes,
1284
- contentTypeHint: "application/octet-stream",
1285
- preserveFilenames: params.preserveFilenames,
1286
- fetchImpl: async (input, init) => {
1287
- const requestUrl = (0, openclaw_plugin_sdk_request_url.resolveRequestUrl)(input);
1288
- const headers = require_resolve_allowlist.ensureUserAgentHeader(init?.headers);
1289
- require_resolve_allowlist.applyAuthorizationHeaderForUrl({
1290
- headers,
1291
- url: requestUrl,
1292
- authAllowHosts: policy.authAllowHosts,
1293
- bearerToken: accessToken
1294
- });
1295
- return await require_resolve_allowlist.safeFetchWithPolicy({
1296
- url: requestUrl,
1297
- policy,
1298
- fetchFn,
1299
- requestInit: {
1300
- ...init,
1301
- headers
1302
- },
1303
- resolveFn: params.resolveFn,
1304
- timeoutMs: require_resolve_allowlist.resolveMSTeamsRequestTimeoutMs(params.deadline)
1305
- });
1306
- }
1307
- });
1308
- sharePointMedia.push(sourceId ? {
1309
- ...media,
1310
- sourceId
1311
- } : media);
1312
- } catch (err) {
1313
- sharePointMedia.push(unavailableMedia);
1314
- params.logger?.warn?.("msteams SharePoint reference download failed", {
1315
- error: (0, openclaw_plugin_sdk_error_runtime.coerceErrorMessage)(err),
1316
- name
1317
- });
1318
- }
1319
- }
1300
+ const sharePointMedia = await downloadMSTeamsAttachments({
1301
+ ...params,
1302
+ attachments: referenceAttachments,
1303
+ graphReferenceToken: accessToken,
1304
+ allowHosts: policy.allowHosts,
1305
+ authAllowHosts: policy.authAllowHosts
1306
+ });
1320
1307
  const hosted = await downloadGraphHostedContent({
1321
1308
  accessToken,
1322
1309
  messageUrl,
@@ -1327,7 +1314,8 @@ async function downloadMSTeamsGraphMedia(params) {
1327
1314
  deadline: params.deadline
1328
1315
  });
1329
1316
  const normalizedAttachments = messageAttachments.map(normalizeGraphAttachment);
1330
- const filteredAttachments = sharePointMedia.length > 0 ? normalizedAttachments.filter((att) => att.contentType !== "reference" || typeof att.contentUrl !== "string" || !att.contentUrl || !downloadedReferenceUrls.has(att.contentUrl)) : normalizedAttachments;
1317
+ const referenceUrls = new Set(referenceAttachments.map((att) => att.contentUrl));
1318
+ const filteredAttachments = normalizedAttachments.filter((att) => att.contentType !== "reference" || !referenceUrls.has(att.contentUrl));
1331
1319
  let attachmentMedia = [];
1332
1320
  try {
1333
1321
  attachmentMedia = await downloadMSTeamsAttachments({
@@ -1447,14 +1435,14 @@ async function resolveMSTeamsInboundMedia(params) {
1447
1435
  authAllowHosts: params.authAllowHosts,
1448
1436
  deadline: params.deadline
1449
1437
  });
1450
- if (bfMedia.media.length > 0) mediaList = mergeMSTeamsMediaFacts(mediaList, bfMedia.media, { positionallyAligned: false });
1438
+ mediaList = mergeMSTeamsMediaFacts(mediaList, bfMedia.media, { positionallyAligned: false });
1451
1439
  if (!hasMaterializedMedia(bfMedia.media)) log.debug?.("bot framework attachments fetch empty", {
1452
1440
  conversationType,
1453
1441
  attachmentCount: bfMedia.attachmentCount ?? attachmentIds.length
1454
1442
  });
1455
1443
  }
1456
1444
  }
1457
- if (shouldFetchGraphMessage && !hasMaterializedMedia(mediaList) && !isBotFrameworkPersonalChat) {
1445
+ if (shouldFetchGraphMessage && !isBotFrameworkPersonalChat) {
1458
1446
  const graphTeamAadGroupId = conversationType.trim().toLowerCase() === "channel" && !teamAadGroupId ? await params.resolveTeamAadGroupId?.() : teamAadGroupId;
1459
1447
  const messageUrl = buildMSTeamsGraphMessageUrl({
1460
1448
  conversationType,
@@ -1480,7 +1468,7 @@ async function resolveMSTeamsInboundMedia(params) {
1480
1468
  deadline: params.deadline,
1481
1469
  logger: log
1482
1470
  });
1483
- if (graphMedia.media.length > 0) mediaList = mergeMSTeamsMediaFacts(mediaList, graphMedia.media, { positionallyAligned: false });
1471
+ mediaList = mergeMSTeamsMediaFacts(mediaList, graphMedia.media, { positionallyAligned: false });
1484
1472
  if (!hasMaterializedMedia(mediaList)) log.debug?.("graph media fetch empty", {
1485
1473
  messageUrl,
1486
1474
  hostedStatus: graphMedia.hostedStatus,
@@ -1517,9 +1505,10 @@ async function prepareMSTeamsInboundContent(params) {
1517
1505
  return null;
1518
1506
  }
1519
1507
  let mediaList = [];
1508
+ const deadline = require_resolve_allowlist.createMSTeamsInboundMediaDeadline();
1520
1509
  try {
1521
1510
  mediaList = await require_resolve_allowlist.withMSTeamsRequestDeadline({
1522
- deadline: params.deadline,
1511
+ deadline,
1523
1512
  label: "MS Teams inbound media",
1524
1513
  work: () => resolveMSTeamsInboundMedia({
1525
1514
  attachments: params.entry.attachments,
@@ -1533,7 +1522,7 @@ async function prepareMSTeamsInboundContent(params) {
1533
1522
  conversationId: params.conversationId,
1534
1523
  conversationMessageId: params.conversationMessageId,
1535
1524
  teamAadGroupId: params.teamAadGroupId,
1536
- resolveTeamAadGroupId: params.resolveTeamAadGroupId,
1525
+ resolveTeamAadGroupId: () => params.resolveTeamAadGroupId(deadline),
1537
1526
  serviceUrl: activity.serviceUrl,
1538
1527
  activity: {
1539
1528
  id: activity.id,
@@ -1541,7 +1530,7 @@ async function prepareMSTeamsInboundContent(params) {
1541
1530
  channelData: activity.channelData
1542
1531
  },
1543
1532
  log: params.log,
1544
- deadline: params.deadline
1533
+ deadline
1545
1534
  })
1546
1535
  });
1547
1536
  } catch (err) {
@@ -1567,6 +1556,30 @@ async function prepareMSTeamsInboundContent(params) {
1567
1556
  };
1568
1557
  }
1569
1558
  //#endregion
1559
+ //#region extensions/msteams/src/informative-status.ts
1560
+ const INFORMATIVE_MAX_CHARS = 1e3;
1561
+ const INFORMATIVE_MAX_BYTES = 1024;
1562
+ const ELLIPSIS = "…";
1563
+ const segmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
1564
+ const utf8 = new TextEncoder();
1565
+ function flattenInformativeStatus(text) {
1566
+ const joined = text.split("\n").map((line) => line.trim().replace(/^[•-]\s+/u, "")).filter(Boolean).join(" · ");
1567
+ if (joined.length <= INFORMATIVE_MAX_CHARS && utf8.encode(joined).length <= INFORMATIVE_MAX_BYTES) return joined;
1568
+ const graphemes = Array.from(segmenter.segment(joined), (s) => s.segment);
1569
+ let chars = 1;
1570
+ let bytes = utf8.encode(ELLIPSIS).length;
1571
+ let start = graphemes.length;
1572
+ while (start > 0) {
1573
+ const next = graphemes[start - 1];
1574
+ const nextBytes = utf8.encode(next).length;
1575
+ if (chars + next.length > INFORMATIVE_MAX_CHARS || bytes + nextBytes > INFORMATIVE_MAX_BYTES) break;
1576
+ chars += next.length;
1577
+ bytes += nextBytes;
1578
+ start -= 1;
1579
+ }
1580
+ return ELLIPSIS + graphemes.slice(start).join("");
1581
+ }
1582
+ //#endregion
1570
1583
  //#region extensions/msteams/src/reply-stream-controller.ts
1571
1584
  function isStreamCancelledError(err) {
1572
1585
  return err instanceof Error && err.name === "StreamCancelledError";
@@ -1581,19 +1594,16 @@ function createTeamsReplyStreamController(params) {
1581
1594
  let nativeDeliveryClaimed = false;
1582
1595
  let streamFinalizationPending = false;
1583
1596
  let canceledLocally = false;
1584
- let streamFailed = false;
1585
1597
  let pendingFinalPayload;
1586
1598
  let emittedText = "";
1587
1599
  let acknowledgedText = "";
1588
1600
  let acknowledgedLogicalText = "";
1589
1601
  let acknowledgedStreamId;
1590
1602
  let replacementFinalPending = false;
1591
- let replacementEmitFailed = false;
1592
1603
  let replacementSettlementPending = false;
1593
1604
  let replacementTextAwaitingAcknowledgement;
1594
1605
  let deferredReplacementEntries = [];
1595
1606
  let queuedFinalActivity;
1596
- let failedSegmentFallbackPrepared = false;
1597
1607
  const streamEvents = stream?.events;
1598
1608
  let streamChunkSubscription;
1599
1609
  if (typeof streamEvents?.on === "function" && typeof streamEvents.off === "function") streamChunkSubscription = streamEvents.on("chunk", (activity) => {
@@ -1640,6 +1650,16 @@ function createTeamsReplyStreamController(params) {
1640
1650
  ...require_conversation_store_state.buildMSTeamsMessageActivity(text === void 0 ? void 0 : require_conversation_store_state.formatMSTeamsMarkdown(text, params.tableMode ?? "code")),
1641
1651
  channelData: params.feedbackLoopEnabled ? { feedbackLoopEnabled: true } : {}
1642
1652
  });
1653
+ const queueFinalReplacement = (activeStream, text) => {
1654
+ const activity = finalStreamActivity(text);
1655
+ replacementTextAwaitingAcknowledgement = {
1656
+ text: activity.text,
1657
+ logicalText: text
1658
+ };
1659
+ activeStream.emit(activity);
1660
+ queuedFinalActivity = activity;
1661
+ emittedText = text;
1662
+ };
1643
1663
  const deferredReplacementLogicalContent = () => {
1644
1664
  return deferredReplacementEntries.map((entry) => entry.payload.text).filter((text) => Boolean(text)).join("\n") || void 0;
1645
1665
  };
@@ -1673,7 +1693,7 @@ function createTeamsReplyStreamController(params) {
1673
1693
  update: (text) => {
1674
1694
  if (!stream || wasCanceled() || streamFinalizationPending) return false;
1675
1695
  try {
1676
- stream.update(text.replace(/^• /gmu, "- "));
1696
+ stream.update(flattenInformativeStatus(text));
1677
1697
  return true;
1678
1698
  } catch (err) {
1679
1699
  if (isStreamCancelledError(err)) canceledLocally = true;
@@ -1698,10 +1718,6 @@ function createTeamsReplyStreamController(params) {
1698
1718
  if (!delta) return;
1699
1719
  if (previousRemainder.trim()) {
1700
1720
  stream.clearText();
1701
- if (streamFailed) {
1702
- streamFinalizationPending = true;
1703
- return;
1704
- }
1705
1721
  replacementFinalPending = true;
1706
1722
  replacementSettlementPending = true;
1707
1723
  pendingFinalPayload = { text: fullText };
@@ -1718,8 +1734,7 @@ function createTeamsReplyStreamController(params) {
1718
1734
  canceledLocally = true;
1719
1735
  return;
1720
1736
  }
1721
- streamFailed = true;
1722
- params.log?.warn?.(`msteams stream emit failed, falling back to block delivery: ${(0, openclaw_plugin_sdk_error_runtime.coerceErrorMessage)(err)}`);
1737
+ throw err;
1723
1738
  }
1724
1739
  },
1725
1740
  pushItemEvent: progressDraft.pushItemEvent.bind(progressDraft),
@@ -1747,14 +1762,7 @@ function createTeamsReplyStreamController(params) {
1747
1762
  });
1748
1763
  pendingFinalPayload = fallbackPayloadForSuppressedFinal(payload);
1749
1764
  try {
1750
- const activity = finalStreamActivity(payload.text);
1751
- replacementTextAwaitingAcknowledgement = {
1752
- text: activity.text,
1753
- logicalText: payload.text
1754
- };
1755
- stream.emit(activity);
1756
- queuedFinalActivity = activity;
1757
- emittedText = payload.text;
1765
+ queueFinalReplacement(stream, payload.text);
1758
1766
  tokensEmitted = false;
1759
1767
  return;
1760
1768
  } catch (err) {
@@ -1766,18 +1774,9 @@ function createTeamsReplyStreamController(params) {
1766
1774
  deferredReplacementEntries = [];
1767
1775
  return;
1768
1776
  }
1769
- streamFailed = true;
1770
- replacementEmitFailed = true;
1771
- params.log?.warn?.(`msteams stream replacement failed, falling back to block delivery: ${(0, openclaw_plugin_sdk_error_runtime.coerceErrorMessage)(err)}`);
1772
- return;
1777
+ throw err;
1773
1778
  }
1774
1779
  }
1775
- if (streamFailed) {
1776
- const fallback = failedSegmentFallbackPrepared ? payload : fallbackPayloadAfterAcknowledgedText(payload);
1777
- failedSegmentFallbackPrepared = true;
1778
- pendingFinalPayload = void 0;
1779
- return fallback;
1780
- }
1781
1780
  if (streamMode === "progress" && payload.text && !nativeDispatchStarted) try {
1782
1781
  stream.emit(payload.text);
1783
1782
  emittedText = payload.text;
@@ -1788,7 +1787,7 @@ function createTeamsReplyStreamController(params) {
1788
1787
  canceledLocally = true;
1789
1788
  return;
1790
1789
  }
1791
- params.log?.debug?.(`progress-mode finalize failed: ${(0, openclaw_plugin_sdk_error_runtime.coerceErrorMessage)(err)}`);
1790
+ throw err;
1792
1791
  }
1793
1792
  if (tokensEmitted) {
1794
1793
  const hasMedia = Boolean(payload.mediaUrl || payload.mediaUrls?.length);
@@ -1822,15 +1821,8 @@ function createTeamsReplyStreamController(params) {
1822
1821
  kind: "replacement",
1823
1822
  payload: pendingFinalPayload
1824
1823
  });
1825
- const activity = finalStreamActivity(pendingFinalPayload.text);
1826
- replacementTextAwaitingAcknowledgement = {
1827
- text: activity.text,
1828
- logicalText: pendingFinalPayload.text
1829
- };
1830
1824
  logicalContent = deferredReplacementLogicalContent();
1831
- stream.emit(activity);
1832
- queuedFinalActivity = activity;
1833
- emittedText = pendingFinalPayload.text;
1825
+ queueFinalReplacement(stream, pendingFinalPayload.text);
1834
1826
  }
1835
1827
  logicalContent ??= replacementSettlementPending ? deferredReplacementLogicalContent() : void 0;
1836
1828
  const logicalText = pendingFinalPayload?.text ?? (emittedText || void 0);
@@ -1852,15 +1844,13 @@ function createTeamsReplyStreamController(params) {
1852
1844
  }
1853
1845
  const result = await stream.close();
1854
1846
  if (!result) return finalizeWithoutReceipt(logicalContent, wasCanceled());
1855
- const replacementFallback = replacementSettlementPending && replacementEmitFailed && pendingFinalPayload ? fallbackPayloadAfterAcknowledgedText(pendingFinalPayload) : void 0;
1856
1847
  const messageId = require_conversation_store_state.extractMessageId(result) ?? acknowledgedStreamId;
1857
- const postNativePayloads = replacementSettlementPending ? deferredReplacementPayloads(replacementFallback) : [];
1858
- const nativeContent = replacementEmitFailed ? acknowledgedText || void 0 : content;
1848
+ const postNativePayloads = replacementSettlementPending ? deferredReplacementPayloads(void 0) : [];
1859
1849
  return {
1860
- visibleReplySent: replacementEmitFailed ? Boolean(nativeContent) : true,
1861
- ...nativeContent === void 0 ? {} : { content: nativeContent },
1850
+ visibleReplySent: true,
1851
+ ...content === void 0 ? {} : { content },
1862
1852
  ...logicalContent ? { logicalContent } : {},
1863
- ...messageId && (!replacementEmitFailed || nativeContent) ? { messageId } : {},
1853
+ ...messageId ? { messageId } : {},
1864
1854
  ...postNativePayloads.length > 0 ? { postNativePayloads } : {}
1865
1855
  };
1866
1856
  } catch (err) {
@@ -1868,15 +1858,12 @@ function createTeamsReplyStreamController(params) {
1868
1858
  canceledLocally = true;
1869
1859
  return acknowledgedNativeDelivery();
1870
1860
  }
1871
- streamFailed = true;
1872
1861
  params.log?.warn?.(`msteams stream finalize failed: ${(0, openclaw_plugin_sdk_error_runtime.coerceErrorMessage)(err)}`);
1873
1862
  return finalizeWithoutReceipt(logicalContent);
1874
1863
  } finally {
1875
1864
  pendingFinalPayload = void 0;
1876
1865
  streamFinalizationPending = false;
1877
- failedSegmentFallbackPrepared = true;
1878
1866
  queuedFinalActivity = void 0;
1879
- replacementEmitFailed = false;
1880
1867
  replacementFinalPending = false;
1881
1868
  replacementSettlementPending = false;
1882
1869
  replacementTextAwaitingAcknowledgement = void 0;
@@ -1888,7 +1875,7 @@ function createTeamsReplyStreamController(params) {
1888
1875
  return Boolean(stream);
1889
1876
  },
1890
1877
  isStreamActive() {
1891
- return Boolean(stream) && tokensEmitted && !wasCanceled() && !streamFailed;
1878
+ return Boolean(stream) && tokensEmitted && !wasCanceled();
1892
1879
  },
1893
1880
  wasCanceled
1894
1881
  };
@@ -1897,7 +1884,12 @@ function createTeamsReplyStreamController(params) {
1897
1884
  //#region extensions/msteams/src/reply-dispatcher.ts
1898
1885
  function createMSTeamsReplyDispatcher(params) {
1899
1886
  const core = require_runtime.getMSTeamsRuntime();
1900
- const msteamsCfg = params.cfg.channels?.msteams;
1887
+ const msteamsCfg = require_resolve_allowlist.resolveMSTeamsAccountConfig(params.cfg, params.accountId);
1888
+ const mediaMaxBytes = (0, openclaw_plugin_sdk_account_helpers.resolveChannelMediaMaxBytes)({
1889
+ cfg: params.cfg,
1890
+ resolveChannelLimitMb: () => msteamsCfg.mediaMaxMb
1891
+ });
1892
+ const feedbackLoopEnabled = msteamsCfg.feedbackEnabled !== false;
1901
1893
  const conversationType = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(params.conversationRef.conversation?.conversationType);
1902
1894
  const isTypingSupported = conversationType === "personal" || conversationType === "groupchat";
1903
1895
  const TYPING_KEEPALIVE_INTERVAL_MS = 8e3;
@@ -1909,7 +1901,10 @@ function createMSTeamsReplyDispatcher(params) {
1909
1901
  await params.context.sendActivity({ type: "typing" });
1910
1902
  },
1911
1903
  onRevoked: async () => {
1912
- await require_conversation_store_state.sendMSTeamsActivityWithReference(params.app, require_conversation_store_state.buildConversationReference(params.conversationRef), { type: "typing" }, { serviceUrlBoundary: require_resolve_allowlist.resolveMSTeamsSdkCloudOptions(msteamsCfg) });
1904
+ await require_conversation_store_state.sendMSTeamsActivityWithReference(params.app, require_conversation_store_state.buildConversationReference(params.conversationRef), { type: "typing" }, {
1905
+ accountId: params.accountId,
1906
+ serviceUrlBoundary: require_resolve_allowlist.resolveMSTeamsSdkCloudOptions(msteamsCfg)
1907
+ });
1913
1908
  },
1914
1909
  onRevokedLog: () => {
1915
1910
  params.log.debug?.("turn context revoked, sending typing via proactive messaging");
@@ -1935,16 +1930,12 @@ function createMSTeamsReplyDispatcher(params) {
1935
1930
  }
1936
1931
  }
1937
1932
  });
1938
- const chunkMode = core.channel.text.resolveChunkMode(params.cfg, "msteams");
1933
+ const chunkMode = core.channel.text.resolveChunkMode(params.cfg, "msteams", params.accountId);
1939
1934
  const tableMode = core.channel.text.resolveMarkdownTableMode({
1940
1935
  cfg: params.cfg,
1941
- channel: "msteams"
1942
- });
1943
- const mediaMaxBytes = (0, openclaw_plugin_sdk_account_helpers.resolveChannelMediaMaxBytes)({
1944
- cfg: params.cfg,
1945
- resolveChannelLimitMb: ({ cfg }) => cfg.channels?.msteams?.mediaMaxMb
1936
+ channel: "msteams",
1937
+ accountId: params.accountId
1946
1938
  });
1947
- const feedbackLoopEnabled = params.cfg.channels?.msteams?.feedbackEnabled !== false;
1948
1939
  const hookRunner = (0, openclaw_plugin_sdk_plugin_runtime.getGlobalHookRunner)();
1949
1940
  const streamController = createTeamsReplyStreamController({
1950
1941
  tableMode,
@@ -2018,6 +2009,7 @@ function createMSTeamsReplyDispatcher(params) {
2018
2009
  const sentIds = [];
2019
2010
  for (const msg of toSend) try {
2020
2011
  const msgIds = await require_conversation_store_state.sendMSTeamsMessages({
2012
+ accountId: params.accountId,
2021
2013
  replyStyle: params.replyStyle,
2022
2014
  app: params.app,
2023
2015
  conversationRef: params.conversationRef,
@@ -2048,9 +2040,14 @@ function createMSTeamsReplyDispatcher(params) {
2048
2040
  params.log.debug?.("individual message send failed, continuing with remaining blocks");
2049
2041
  }
2050
2042
  if (failed > 0) {
2043
+ const classification = require_channel_setup.classifyMSTeamsSendError(lastFailedError);
2044
+ const hint = require_channel_setup.formatMSTeamsSendErrorHint(classification);
2051
2045
  params.log.warn?.(`failed to deliver ${failed} of ${total} message blocks`, {
2052
2046
  failed,
2053
- total
2047
+ total,
2048
+ error: require_channel_setup.formatUnknownError(lastFailedError),
2049
+ classification,
2050
+ ...hint ? { hint } : {}
2054
2051
  });
2055
2052
  queueDeliveryFailureSystemEvent({
2056
2053
  failed,
@@ -2205,7 +2202,7 @@ async function dispatchMSTeamsInboundTurn(params) {
2205
2202
  const core = require_runtime.getMSTeamsRuntime();
2206
2203
  const { cfg, runtime, app, tokenProvider, textLimit, log, logVerboseMessage, facts, admission, content, routing, thread, replyStyle, timestamp, contextVisibilityMode, conversationHistories, historyLimit } = params;
2207
2204
  const { context, activity, rawBody, text, quoteInfo, conversationRef } = facts;
2208
- const { senderId, senderName, isDirectMessage, allowNameMatching, groupPolicy, commandAuthorized, effectiveGroupAllowFrom } = admission;
2205
+ const { senderId, senderName, isDirectMessage, allowNameMatching, groupPolicy, commandAuthorized, effectiveGroupAllowFrom, msteamsCfg } = admission;
2209
2206
  const { route } = routing;
2210
2207
  const { agentBody, inboundMedia } = content;
2211
2208
  const { teamAadGroupId, quoteBodyFull, quoteSenderId, quoteSenderName, threadContext } = thread;
@@ -2255,7 +2252,8 @@ async function dispatchMSTeamsInboundTurn(params) {
2255
2252
  }) : true;
2256
2253
  const nativeChannelId = isChannel && teamAadGroupId ? `${teamAadGroupId}/${graphChannelId}` : void 0;
2257
2254
  const boundIngress = await resolveMSTeamsSenderAccess({
2258
- cfg,
2255
+ cfg: route.accountId === params.listenerAccountId ? params.readConfig() : params.accountPolicyCfg,
2256
+ accountId: route.accountId,
2259
2257
  activity,
2260
2258
  hasControlCommand: admission.isControlCommand,
2261
2259
  conversationThreadId: facts.threadId,
@@ -2267,6 +2265,13 @@ async function dispatchMSTeamsInboundTurn(params) {
2267
2265
  inboundEventKind: "user_request"
2268
2266
  }
2269
2267
  });
2268
+ if (!boundIngress.senderAccess.allowed || boundIngress.hasConflictingConversationScope || boundIngress.msteamsCfg?.enabled === false || boundIngress.commandAccess.shouldBlockControlCommand) {
2269
+ log.info("dropping message (dispatch-time account authorization changed)");
2270
+ return {
2271
+ kind: "completed",
2272
+ finalResponses: 0
2273
+ };
2274
+ }
2270
2275
  const ctxPayload = core.channel.inbound.buildContext({
2271
2276
  channelIngress: boundIngress.channelIngress,
2272
2277
  channel: "msteams",
@@ -2353,10 +2358,10 @@ async function dispatchMSTeamsInboundTurn(params) {
2353
2358
  replyStyle,
2354
2359
  textLimit,
2355
2360
  onSentMessageIds: (ids) => {
2356
- for (const id of ids) require_conversation_store_state.recordMSTeamsSentMessage(conversationId, id);
2361
+ for (const id of ids) require_conversation_store_state.recordMSTeamsSentMessage(conversationId, id, { accountId: route.accountId });
2357
2362
  },
2358
2363
  tokenProvider,
2359
- sharePointSiteId: cfg.channels?.msteams?.sharePointSiteId
2364
+ sharePointSiteId: msteamsCfg?.sharePointSiteId
2360
2365
  });
2361
2366
  const senderTimezone = (activity.entities?.find((entity) => entity.type === "clientInfo"))?.timezone || conversationRef.timezone;
2362
2367
  const turnConfig = senderTimezone && !cfg.agents?.defaults?.userTimezone ? {
@@ -2449,7 +2454,8 @@ async function prepareMSTeamsDebounceEntry(params) {
2449
2454
  const replyToId = activity.replyToId ?? void 0;
2450
2455
  const implicitMentionKinds = conversationId && replyToId && await require_conversation_store_state.wasMSTeamsMessageSentWithPersistence({
2451
2456
  conversationId,
2452
- messageId: replyToId
2457
+ messageId: replyToId,
2458
+ accountId: params.accountId
2453
2459
  }) ? ["reply_to_bot"] : [];
2454
2460
  return {
2455
2461
  context: params.context,
@@ -2574,26 +2580,19 @@ function touchLru(map, key, value, max) {
2574
2580
  function buildParentCacheKey(groupId, channelId, parentId) {
2575
2581
  return `${groupId}\u0000${channelId}\u0000${parentId}`;
2576
2582
  }
2577
- function resolveParentCacheExpiresAt(nowRaw) {
2578
- const nowMs = (0, openclaw_plugin_sdk_number_runtime.asDateTimestampMs)(nowRaw);
2579
- return nowMs === void 0 ? void 0 : (0, openclaw_plugin_sdk_number_runtime.resolveExpiresAtMsFromDurationMs)(PARENT_CACHE_TTL_MS, { nowMs });
2580
- }
2581
2583
  async function fetchParentMessageCached(token, groupId, channelId, parentId, deadline) {
2582
2584
  const key = buildParentCacheKey(groupId, channelId, parentId);
2583
- const now = (0, openclaw_plugin_sdk_number_runtime.asDateTimestampMs)(Date.now());
2584
2585
  const cached = parentCache.get(key);
2585
- const cachedExpiresAt = cached ? (0, openclaw_plugin_sdk_number_runtime.asDateTimestampMs)(cached.expiresAt) : void 0;
2586
- if (cached && now !== void 0 && cachedExpiresAt !== void 0 && cachedExpiresAt > now) {
2586
+ if (cached && cached.expiresAt > Date.now()) {
2587
2587
  parentCache.delete(key);
2588
2588
  parentCache.set(key, cached);
2589
2589
  return cached.message;
2590
2590
  }
2591
2591
  if (cached) parentCache.delete(key);
2592
2592
  const message = await require_reaction_types.fetchChannelMessage(token, groupId, channelId, parentId, deadline);
2593
- const expiresAt = resolveParentCacheExpiresAt(Date.now());
2594
- if (expiresAt !== void 0) touchLru(parentCache, key, {
2593
+ touchLru(parentCache, key, {
2595
2594
  message,
2596
- expiresAt
2595
+ expiresAt: Date.now() + PARENT_CACHE_TTL_MS
2597
2596
  }, PARENT_CACHE_MAX);
2598
2597
  return message;
2599
2598
  }
@@ -2625,6 +2624,7 @@ function prepareMSTeamsThreadRouting(params) {
2625
2624
  const route = require_runtime.getMSTeamsRuntime().channel.routing.resolveAgentRoute({
2626
2625
  cfg: params.cfg,
2627
2626
  channel: "msteams",
2627
+ accountId: params.accountId,
2628
2628
  teamId: params.teamId,
2629
2629
  peer: {
2630
2630
  kind: params.isDirectMessage ? "direct" : params.isChannel ? "channel" : "group",
@@ -2637,11 +2637,10 @@ function prepareMSTeamsThreadRouting(params) {
2637
2637
  conversationMessageId: params.conversationMessageId,
2638
2638
  replyToId: params.context.activity.replyToId
2639
2639
  });
2640
- const deadline = require_resolve_allowlist.createMSTeamsInboundDeadline();
2641
2640
  let teamAadGroupId = params.context.activity.channelData?.team?.aadGroupId?.trim() || void 0;
2642
2641
  const conversationTeamId = params.isChannel ? params.teamId : void 0;
2643
2642
  let teamGroupIdPromise;
2644
- const resolveTeamAadGroupId = async () => {
2643
+ const resolveTeamAadGroupId = async (deadline) => {
2645
2644
  if (!conversationTeamId) return;
2646
2645
  teamGroupIdPromise ??= resolveTeamGroupId({
2647
2646
  conversationTeamId,
@@ -2660,7 +2659,6 @@ function prepareMSTeamsThreadRouting(params) {
2660
2659
  return {
2661
2660
  route,
2662
2661
  threadRootId: params.conversationMessageId ?? params.context.activity.replyToId,
2663
- deadline,
2664
2662
  resolveTeamAadGroupId,
2665
2663
  getTeamAadGroupId: () => teamAadGroupId
2666
2664
  };
@@ -2668,8 +2666,9 @@ function prepareMSTeamsThreadRouting(params) {
2668
2666
  async function resolveMSTeamsThreadContext(params) {
2669
2667
  const core = require_runtime.getMSTeamsRuntime();
2670
2668
  const activity = params.context.activity;
2671
- const { route, deadline } = params.routing;
2672
- const teamAadGroupId = await params.routing.resolveTeamAadGroupId();
2669
+ const { route } = params.routing;
2670
+ const deadline = require_resolve_allowlist.createMSTeamsInboundEnrichmentDeadline();
2671
+ const teamAadGroupId = await params.routing.resolveTeamAadGroupId(deadline);
2673
2672
  let quoteBodyFull;
2674
2673
  let quoteSenderId;
2675
2674
  let quoteSenderName;
@@ -2755,19 +2754,21 @@ async function resolveMSTeamsThreadContext(params) {
2755
2754
  //#endregion
2756
2755
  //#region extensions/msteams/src/monitor-handler/message-handler.ts
2757
2756
  function createMSTeamsMessageHandler(deps) {
2758
- const { cfg, runtime, appId, app, tokenProvider, textLimit, mediaMaxBytes, conversationStore, pollStore, log } = deps;
2757
+ const { cfg, accountPolicyCfg = cfg, accountId, runtime, appId, app, tokenProvider, textLimit, mediaMaxBytes, conversationStore, pollStore, log } = deps;
2759
2758
  const core = require_runtime.getMSTeamsRuntime();
2760
2759
  const logVerboseMessage = (message) => {
2761
2760
  if (core.logging.shouldLogVerbose()) log.debug?.(message);
2762
2761
  };
2763
- const msteamsCfg = cfg.channels?.msteams;
2762
+ const msteamsCfg = cfg.channels?.msteams ? require_resolve_allowlist.resolveMSTeamsAccountConfig(cfg, accountId) : void 0;
2764
2763
  const contextVisibilityMode = (0, openclaw_plugin_sdk_context_visibility_runtime.resolveChannelContextVisibilityMode)({
2765
2764
  cfg,
2766
- channel: "msteams"
2765
+ channel: "msteams",
2766
+ accountId
2767
2767
  });
2768
2768
  const historyLimit = (0, openclaw_plugin_sdk_number_runtime.resolvePromptHistoryLimit)(msteamsCfg?.historyLimit ?? cfg.messages?.groupChat?.historyLimit);
2769
2769
  const conversationHistories = /* @__PURE__ */ new Map();
2770
- const readConfig = (0, openclaw_plugin_sdk_runtime_config_snapshot.createRuntimeConfigReader)(cfg);
2770
+ const readFullConfig = (0, openclaw_plugin_sdk_runtime_config_snapshot.createRuntimeConfigReader)(accountPolicyCfg);
2771
+ const readConfig = deps.readConfig ?? createMSTeamsHandlerConfigReader(deps);
2771
2772
  const resolveDebounceMs = () => core.channel.debounce.resolveInboundDebounceMs({
2772
2773
  cfg: readConfig(),
2773
2774
  channel: "msteams"
@@ -2809,7 +2810,8 @@ function createMSTeamsMessageHandler(deps) {
2809
2810
  isBotOwnedThread = await require_conversation_store_state.wasMSTeamsMessageSentWithPersistence({
2810
2811
  conversationId,
2811
2812
  messageId: facts.threadId,
2812
- botId: activity.recipient.id
2813
+ botId: activity.recipient.id,
2814
+ accountId
2813
2815
  });
2814
2816
  currentCfg = readConfig();
2815
2817
  }
@@ -2818,6 +2820,7 @@ function createMSTeamsMessageHandler(deps) {
2818
2820
  if (currentMSTeamsCfg?.enabled === false || currentMSTeamsCfg?.appId && currentMSTeamsCfg.appId !== appId) return;
2819
2821
  const admission = await admitMSTeamsMessage({
2820
2822
  cfg: currentCfg,
2823
+ accountId,
2821
2824
  activity,
2822
2825
  text,
2823
2826
  conversationId,
@@ -2852,6 +2855,7 @@ function createMSTeamsMessageHandler(deps) {
2852
2855
  }
2853
2856
  const threadRouting = prepareMSTeamsThreadRouting({
2854
2857
  cfg: currentCfg,
2858
+ accountId,
2855
2859
  context,
2856
2860
  isDirectMessage,
2857
2861
  isChannel,
@@ -2861,7 +2865,7 @@ function createMSTeamsMessageHandler(deps) {
2861
2865
  teamId,
2862
2866
  log
2863
2867
  });
2864
- const { route, deadline: preprocessingDeadline } = threadRouting;
2868
+ const { route } = threadRouting;
2865
2869
  const inboundLabel = isDirectMessage ? `Teams DM from ${senderName}` : `Teams message in ${conversationType} from ${senderName}`;
2866
2870
  const enqueuePrimaryMessageSystemEvent = () => core.system.enqueueSystemEvent(inboundLabel, {
2867
2871
  sessionKey: route.sessionKey,
@@ -2937,7 +2941,6 @@ function createMSTeamsMessageHandler(deps) {
2937
2941
  mediaAllowHosts: currentMSTeamsCfg?.mediaAllowHosts,
2938
2942
  mediaAuthAllowHosts: currentMSTeamsCfg?.mediaAuthAllowHosts,
2939
2943
  graphMediaFallback: currentMSTeamsCfg?.graphMediaFallback,
2940
- deadline: preprocessingDeadline,
2941
2944
  log
2942
2945
  });
2943
2946
  if (!content) return;
@@ -2958,6 +2961,9 @@ function createMSTeamsMessageHandler(deps) {
2958
2961
  });
2959
2962
  await dispatchMSTeamsInboundTurn({
2960
2963
  cfg: currentCfg,
2964
+ accountPolicyCfg: readFullConfig(),
2965
+ readConfig,
2966
+ listenerAccountId: accountId,
2961
2967
  runtime,
2962
2968
  app,
2963
2969
  tokenProvider,
@@ -3044,6 +3050,7 @@ function createMSTeamsMessageHandler(deps) {
3044
3050
  return async function handleTeamsMessage(context, turnAdoptionLifecycle) {
3045
3051
  const entry = await prepareMSTeamsDebounceEntry({
3046
3052
  context,
3053
+ accountId,
3047
3054
  turnAdoptionLifecycle
3048
3055
  });
3049
3056
  await inboundDebouncer.enqueue(entry);
@@ -3053,10 +3060,11 @@ function createMSTeamsMessageHandler(deps) {
3053
3060
  //#endregion
3054
3061
  //#region extensions/msteams/src/monitor-handler/reaction-handler.ts
3055
3062
  function createMSTeamsReactionHandler(deps) {
3056
- const { cfg, log } = deps;
3063
+ const { accountId, log } = deps;
3064
+ const readConfig = deps.readConfig ?? createMSTeamsHandlerConfigReader(deps);
3057
3065
  const core = require_runtime.getMSTeamsRuntime();
3058
- const msteamsCfg = cfg.channels?.msteams;
3059
3066
  return async function handleReaction(context, direction) {
3067
+ const cfg = readConfig();
3060
3068
  const activity = context.activity;
3061
3069
  const rawReactions = direction === "added" ? activity.reactionsAdded : activity.reactionsRemoved;
3062
3070
  const reactions = Array.isArray(rawReactions) ? rawReactions : [];
@@ -3076,9 +3084,11 @@ function createMSTeamsReactionHandler(deps) {
3076
3084
  const senderName = from.name ?? from.id;
3077
3085
  const access = await resolveMSTeamsSenderAccess({
3078
3086
  cfg,
3087
+ accountId,
3079
3088
  activity
3080
3089
  });
3081
- const { isDirectMessage, channelGate } = access;
3090
+ const { isDirectMessage, channelGate, msteamsCfg } = access;
3091
+ if (msteamsCfg?.enabled === false || msteamsCfg?.appId && msteamsCfg.appId !== deps.appId) return;
3082
3092
  if (access.hasConflictingConversationScope) {
3083
3093
  log.info("dropping reaction (conflicting conversation scope)", { conversationId });
3084
3094
  return;
@@ -3106,6 +3116,7 @@ function createMSTeamsReactionHandler(deps) {
3106
3116
  const route = core.channel.routing.resolveAgentRoute({
3107
3117
  cfg,
3108
3118
  channel: "msteams",
3119
+ accountId,
3109
3120
  peer: {
3110
3121
  kind: isDirectMessage ? "direct" : isChannel ? "channel" : "group",
3111
3122
  id: isDirectMessage ? senderId : conversationId
@@ -3177,8 +3188,12 @@ function buildGroupWelcomeText(botName) {
3177
3188
  //#region extensions/msteams/src/monitor-handler.ts
3178
3189
  async function isMSTeamsInvokeAuthorized(params) {
3179
3190
  const { context, deps, invokeKind } = params;
3191
+ const cfg = deps.readConfig?.() ?? deps.cfg;
3192
+ const account = require_resolve_allowlist.resolveMSTeamsAccountConfig(cfg, deps.accountId);
3193
+ if (account.enabled === false || account.appId && account.appId !== deps.appId) return false;
3180
3194
  const resolved = await resolveMSTeamsSenderAccess({
3181
- cfg: deps.cfg,
3195
+ cfg,
3196
+ accountId: deps.accountId,
3182
3197
  activity: context.activity
3183
3198
  });
3184
3199
  const { msteamsCfg, isDirectMessage, conversationId, senderId } = resolved;
@@ -3224,7 +3239,7 @@ function createMSTeamsActivityHandler(deps) {
3224
3239
  const handleMembersAdded = async (ctx) => {
3225
3240
  const membersAdded = ctx.activity?.membersAdded ?? [];
3226
3241
  const botId = ctx.activity?.recipient?.id;
3227
- const msteamsCfg = deps.cfg.channels?.msteams;
3242
+ const msteamsCfg = deps.cfg.channels?.msteams ? require_resolve_allowlist.resolveMSTeamsAccountConfig(deps.cfg, deps.accountId) : void 0;
3228
3243
  for (const member of membersAdded) if (member.id === botId) {
3229
3244
  const isPersonal = ((0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(ctx.activity?.conversation?.conversationType) ?? "personal") === "personal";
3230
3245
  if (isPersonal && msteamsCfg?.welcomeCard !== false) {
@@ -3301,7 +3316,8 @@ async function runMSTeamsFeedbackInvokeHandler(context, deps) {
3301
3316
  deps.log.debug?.("ignoring feedback with unknown reaction", { reaction });
3302
3317
  return false;
3303
3318
  }
3304
- const msteamsCfg = deps.cfg.channels?.msteams;
3319
+ const cfg = deps.readConfig?.() ?? deps.cfg;
3320
+ const msteamsCfg = require_resolve_allowlist.resolveMSTeamsAccountConfig(cfg, deps.accountId);
3305
3321
  if (msteamsCfg?.feedbackEnabled === false) {
3306
3322
  deps.log.debug?.("feedback handling disabled");
3307
3323
  return true;
@@ -3324,8 +3340,9 @@ async function runMSTeamsFeedbackInvokeHandler(context, deps) {
3324
3340
  const isDirectMessage = convType === "personal" || !convType && !activity.conversation?.isGroup;
3325
3341
  const isChannel = convType === "channel";
3326
3342
  const route = require_runtime.getMSTeamsRuntime().channel.routing.resolveAgentRoute({
3327
- cfg: deps.cfg,
3343
+ cfg,
3328
3344
  channel: "msteams",
3345
+ accountId: deps.accountId,
3329
3346
  peer: {
3330
3347
  kind: isDirectMessage ? "direct" : isChannel ? "channel" : "group",
3331
3348
  id: isDirectMessage ? senderId : conversationId
@@ -3356,7 +3373,7 @@ async function runMSTeamsFeedbackInvokeHandler(context, deps) {
3356
3373
  });
3357
3374
  try {
3358
3375
  await (0, openclaw_plugin_sdk_channel_inbound.recordChannelFeedbackEvent)({
3359
- cfg: deps.cfg,
3376
+ cfg,
3360
3377
  agentId: route.agentId,
3361
3378
  sessionKey: route.sessionKey,
3362
3379
  event: feedbackEvent
@@ -3383,8 +3400,9 @@ async function runMSTeamsFeedbackInvokeHandler(context, deps) {
3383
3400
  locale: activity.locale
3384
3401
  };
3385
3402
  if (isNegative && msteamsCfg?.feedbackReflection !== false) runFeedbackReflection({
3386
- cfg: deps.cfg,
3403
+ cfg,
3387
3404
  app: deps.app,
3405
+ accountId: deps.accountId,
3388
3406
  conversationRef,
3389
3407
  sessionKey: route.sessionKey,
3390
3408
  agentId: route.agentId,
@@ -3405,7 +3423,7 @@ async function handleMSTeamsFileConsentInvoke(context, log) {
3405
3423
  if (activity.type !== "invoke" || activity.name !== "fileConsent/invoke") return false;
3406
3424
  const consentResponse = require_conversation_store_state.parseFileConsentInvoke(activity);
3407
3425
  if (!consentResponse) {
3408
- log.debug?.("invalid file consent invoke", { value: activity.value });
3426
+ log.debug?.("invalid file consent invoke");
3409
3427
  return false;
3410
3428
  }
3411
3429
  const uploadId = typeof consentResponse.context?.uploadId === "string" ? consentResponse.context.uploadId : void 0;
@@ -3501,6 +3519,14 @@ async function runMSTeamsFileConsentInvokeHandler(context, log) {
3501
3519
  }
3502
3520
  }
3503
3521
  //#endregion
3522
+ //#region extensions/msteams/src/monitor-account-stores.ts
3523
+ function createMSTeamsMonitorStores(accountId, overrides) {
3524
+ return {
3525
+ conversationStore: overrides.conversationStore ?? require_conversation_store_state.createMSTeamsConversationStoreState({ accountId }),
3526
+ pollStore: overrides.pollStore ?? require_conversation_store_state.createMSTeamsPollStoreState({ accountId })
3527
+ };
3528
+ }
3529
+ //#endregion
3504
3530
  //#region extensions/msteams/src/monitor-status.ts
3505
3531
  function publishMSTeamsBlocked(statusSink, lastError) {
3506
3532
  statusSink?.((0, openclaw_plugin_sdk_gateway_runtime.channelBlockedPatch)(lastError, { running: true }));
@@ -3613,7 +3639,7 @@ function createMSTeamsIngress(options) {
3613
3639
  const installedLiveContext = Boolean(liveContext) && !liveContexts.has(facts.eventId);
3614
3640
  if (liveContext && installedLiveContext) liveContexts.set(facts.eventId, liveContext);
3615
3641
  const uninstallLiveContext = () => {
3616
- if (installedLiveContext && liveContexts.get(facts.eventId) === liveContext) liveContexts.delete(facts.eventId);
3642
+ if (installedLiveContext) liveContexts.delete(facts.eventId);
3617
3643
  };
3618
3644
  let result;
3619
3645
  try {
@@ -3643,7 +3669,7 @@ function createMSTeamsIngress(options) {
3643
3669
  }
3644
3670
  //#endregion
3645
3671
  //#region extensions/msteams/src/replay-context.ts
3646
- function createMSTeamsReplayContext(activity, app, serviceUrlBoundary) {
3672
+ function createMSTeamsReplayContext(activity, app, serviceUrlBoundary, accountId) {
3647
3673
  const rawConversationId = activity.conversation?.id ?? "";
3648
3674
  const conversationId = require_resolve_allowlist.normalizeMSTeamsConversationId(rawConversationId);
3649
3675
  const conversationType = activity.conversation?.conversationType ?? "personal";
@@ -3667,6 +3693,7 @@ function createMSTeamsReplayContext(activity, app, serviceUrlBoundary) {
3667
3693
  ...activity.from?.aadObjectId ? { aadObjectId: activity.from.aadObjectId } : {}
3668
3694
  };
3669
3695
  const proactiveOptions = {
3696
+ accountId,
3670
3697
  ...quoteActivityId ? { quoteActivityId } : {},
3671
3698
  ...threadActivityId ? { threadActivityId } : {},
3672
3699
  serviceUrlBoundary
@@ -3699,15 +3726,15 @@ function createMSTeamsReplayContext(activity, app, serviceUrlBoundary) {
3699
3726
  * falling back to the Bot Framework `activity.from.id`.
3700
3727
  */
3701
3728
  const MSTEAMS_SSO_TOKENS_NAMESPACE = "sso-tokens";
3702
- const SSO_TOKEN_MUTATION_KEY = "sso-tokens";
3703
3729
  const MSTEAMS_MAX_SSO_TOKENS = 5e3;
3704
3730
  const STORE_KEY_VERSION_PREFIX = "v2:";
3705
3731
  function makeMSTeamsSsoTokenStoreKey(connectionName, userId) {
3706
3732
  return `${STORE_KEY_VERSION_PREFIX}${(0, node_crypto.createHash)("sha256").update(JSON.stringify([connectionName, userId])).digest("hex")}`;
3707
3733
  }
3708
- function createMSTeamsSsoTokenStoreFs() {
3734
+ function createMSTeamsSsoTokenStoreFs(params) {
3735
+ const namespace = require_delegated_state.resolveMSTeamsAccountStateNamespace(MSTEAMS_SSO_TOKENS_NAMESPACE, params?.accountId);
3709
3736
  const tokenStore = require_runtime.getMSTeamsRuntime().state.openKeyedStore({
3710
- namespace: MSTEAMS_SSO_TOKENS_NAMESPACE,
3737
+ namespace,
3711
3738
  maxEntries: MSTEAMS_MAX_SSO_TOKENS
3712
3739
  });
3713
3740
  return {
@@ -3715,12 +3742,12 @@ function createMSTeamsSsoTokenStoreFs() {
3715
3742
  return await tokenStore.lookup(makeMSTeamsSsoTokenStoreKey(connectionName, userId)) ?? null;
3716
3743
  },
3717
3744
  async save(token) {
3718
- await require_conversation_store_state.withMSTeamsSqliteMutationLock(SSO_TOKEN_MUTATION_KEY, async () => {
3719
- await tokenStore.register(makeMSTeamsSsoTokenStoreKey(token.connectionName, token.userId), require_conversation_store_state.toPluginJsonValue({ ...token }));
3745
+ await require_delegated_state.withMSTeamsSqliteMutationLock(namespace, async () => {
3746
+ await tokenStore.register(makeMSTeamsSsoTokenStoreKey(token.connectionName, token.userId), require_delegated_state.toPluginJsonValue({ ...token }));
3720
3747
  });
3721
3748
  },
3722
3749
  async remove({ connectionName, userId }) {
3723
- return require_conversation_store_state.withMSTeamsSqliteMutationLock(SSO_TOKEN_MUTATION_KEY, () => tokenStore.delete(makeMSTeamsSsoTokenStoreKey(connectionName, userId)));
3750
+ return require_delegated_state.withMSTeamsSqliteMutationLock(namespace, () => tokenStore.delete(makeMSTeamsSsoTokenStoreKey(connectionName, userId)));
3724
3751
  }
3725
3752
  };
3726
3753
  }
@@ -3776,9 +3803,12 @@ function createMSTeamsWebhookHandler(expressApp, warn) {
3776
3803
  async function monitorMSTeamsProvider(opts) {
3777
3804
  const core = require_runtime.getMSTeamsRuntime();
3778
3805
  const log = core.logging.getChildLogger({ name: "msteams" });
3779
- let cfg = opts.cfg;
3780
- let msteamsCfg = cfg.channels?.msteams;
3781
- if (!msteamsCfg?.enabled) {
3806
+ const account = require_resolve_allowlist.resolveMSTeamsRuntimeAccount(opts);
3807
+ const { accountId, credentials: creds } = account;
3808
+ const accountPolicyCfg = opts.cfg;
3809
+ let cfg = accountPolicyCfg;
3810
+ let msteamsCfg = account.config;
3811
+ if (opts.cfg.channels?.msteams?.enabled !== true || msteamsCfg.enabled === false) {
3782
3812
  log.debug?.("msteams provider disabled");
3783
3813
  publishMSTeamsBlocked(opts.statusSink, "Microsoft Teams provider is disabled");
3784
3814
  return {
@@ -3786,7 +3816,6 @@ async function monitorMSTeamsProvider(opts) {
3786
3816
  shutdown: async () => {}
3787
3817
  };
3788
3818
  }
3789
- const creds = require_token_config.resolveMSTeamsCredentials(msteamsCfg);
3790
3819
  if (!creds) {
3791
3820
  log.error("msteams credentials not configured");
3792
3821
  publishMSTeamsBlocked(opts.statusSink, "Microsoft Teams credentials are not configured");
@@ -3795,6 +3824,8 @@ async function monitorMSTeamsProvider(opts) {
3795
3824
  shutdown: async () => {}
3796
3825
  };
3797
3826
  }
3827
+ const collisionIssue = require_channel.resolveMSTeamsWebhookCollisionIssue(accountPolicyCfg);
3828
+ if (collisionIssue) throw new Error(collisionIssue);
3798
3829
  const appId = creds.appId;
3799
3830
  const runtime = opts.runtime ?? {
3800
3831
  log: console.log,
@@ -3803,6 +3834,11 @@ async function monitorMSTeamsProvider(opts) {
3803
3834
  throw new Error(`exit ${code}`);
3804
3835
  }
3805
3836
  };
3837
+ cfg = require_resolve_allowlist.withAccountScopedMSTeamsConfig({
3838
+ cfg,
3839
+ accountId,
3840
+ accountConfig: msteamsCfg
3841
+ });
3806
3842
  const configuredAllowFrom = msteamsCfg.allowFrom;
3807
3843
  const configuredGroupAllowFrom = msteamsCfg.groupAllowFrom;
3808
3844
  let allowFrom = require_resolve_allowlist.projectStableMSTeamsUserAllowlist(configuredAllowFrom);
@@ -3816,6 +3852,7 @@ async function monitorMSTeamsProvider(opts) {
3816
3852
  const resolveAllowlistUsers = async (label, entries) => {
3817
3853
  const resolved = await require_resolve_allowlist.resolveMSTeamsUserAllowlist({
3818
3854
  cfg,
3855
+ accountId,
3819
3856
  entries
3820
3857
  });
3821
3858
  const additions = [];
@@ -3853,6 +3890,7 @@ async function monitorMSTeamsProvider(opts) {
3853
3890
  if (msteamsCfg.teams && Object.keys(msteamsCfg.teams).length > 0) {
3854
3891
  const resolved = await require_resolve_allowlist.resolveMSTeamsTeamsConfig({
3855
3892
  cfg,
3893
+ accountId,
3856
3894
  teamIdMode: "bot-framework",
3857
3895
  teams: msteamsCfg.teams
3858
3896
  });
@@ -3872,15 +3910,17 @@ async function monitorMSTeamsProvider(opts) {
3872
3910
  groupAllowFrom,
3873
3911
  teams: teamsConfig
3874
3912
  };
3875
- cfg = {
3876
- ...cfg,
3877
- channels: {
3878
- ...cfg.channels,
3879
- msteams: msteamsCfg
3880
- }
3881
- };
3913
+ cfg = require_resolve_allowlist.withAccountScopedMSTeamsConfig({
3914
+ cfg,
3915
+ accountId,
3916
+ accountConfig: msteamsCfg
3917
+ });
3882
3918
  const legacyListener = require_channel.resolveMSTeamsLegacyWebhook(msteamsCfg);
3883
- const pathIssue = require_channel.resolveMSTeamsWebhookPathIssue({ cfg });
3919
+ const pathIssue = require_channel.resolveMSTeamsWebhookPathIssue({
3920
+ cfg: accountPolicyCfg,
3921
+ accountId,
3922
+ accountConfig: msteamsCfg
3923
+ });
3884
3924
  if (pathIssue) {
3885
3925
  if (!legacyListener) throw new Error(pathIssue);
3886
3926
  log.warn?.(pathIssue);
@@ -3890,8 +3930,7 @@ async function monitorMSTeamsProvider(opts) {
3890
3930
  cfg,
3891
3931
  resolveChannelLimitMb: ({ cfg: channelCfg }) => channelCfg.channels?.msteams?.mediaMaxMb
3892
3932
  }) ?? 8388608;
3893
- const conversationStore = opts.conversationStore ?? require_conversation_store_state.createMSTeamsConversationStoreState();
3894
- const pollStore = opts.pollStore ?? require_conversation_store_state.createMSTeamsPollStoreState();
3933
+ const { conversationStore, pollStore } = createMSTeamsMonitorStores(accountId, opts);
3895
3934
  log.info("starting provider on Gateway HTTP routes");
3896
3935
  const express = await import("express");
3897
3936
  const expressApp = express.default();
@@ -3920,7 +3959,7 @@ async function monitorMSTeamsProvider(opts) {
3920
3959
  messagingEndpoint: configuredPath,
3921
3960
  ...ssoConnectionName ? { oauthDefaultConnectionName: ssoConnectionName } : {}
3922
3961
  });
3923
- if (configuredPath !== "/api/messages") {
3962
+ if (accountId === openclaw_plugin_sdk_account_id.DEFAULT_ACCOUNT_ID && configuredPath !== "/api/messages") {
3924
3963
  let warnedLegacyMessagesRoute = false;
3925
3964
  expressApp.post("/api/messages", (req, res, next) => {
3926
3965
  if (!warnedLegacyMessagesRoute) {
@@ -3933,12 +3972,14 @@ async function monitorMSTeamsProvider(opts) {
3933
3972
  }
3934
3973
  const tokenProvider = require_resolve_allowlist.createMSTeamsTokenProvider(app);
3935
3974
  const ssoDeps = ssoConnectionName ? {
3936
- tokenStore: createMSTeamsSsoTokenStoreFs(),
3975
+ tokenStore: createMSTeamsSsoTokenStoreFs({ accountId }),
3937
3976
  connectionName: ssoConnectionName
3938
3977
  } : void 0;
3939
3978
  if (ssoDeps) log.debug?.("msteams sso enabled", { connectionName: ssoDeps.connectionName });
3940
3979
  const handlerDeps = {
3941
3980
  cfg,
3981
+ accountPolicyCfg,
3982
+ accountId,
3942
3983
  runtime,
3943
3984
  appId,
3944
3985
  app,
@@ -3949,13 +3990,14 @@ async function monitorMSTeamsProvider(opts) {
3949
3990
  pollStore,
3950
3991
  log
3951
3992
  };
3993
+ handlerDeps.readConfig = createMSTeamsHandlerConfigReader(handlerDeps);
3952
3994
  const handleActivity = createMSTeamsActivityHandler(handlerDeps);
3953
3995
  const ingress = createMSTeamsIngress({
3954
3996
  accountId: appId,
3955
3997
  runtime,
3956
3998
  dispatch: async (activity, lifecycle, liveContext) => {
3957
3999
  if (liveContext) liveContext.activity = activity;
3958
- const context = liveContext ?? createMSTeamsReplayContext(activity, app, require_resolve_allowlist.resolveMSTeamsSdkCloudOptions(msteamsCfg));
4000
+ const context = liveContext ?? createMSTeamsReplayContext(activity, app, require_resolve_allowlist.resolveMSTeamsSdkCloudOptions(msteamsCfg), accountId);
3959
4001
  return await handleActivity(context, lifecycle);
3960
4002
  }
3961
4003
  });
@@ -4065,6 +4107,7 @@ async function monitorMSTeamsProvider(opts) {
4065
4107
  return;
4066
4108
  }
4067
4109
  await Promise.all(userIds.map((userId) => ssoDeps.tokenStore.save({
4110
+ ...accountId === openclaw_plugin_sdk_account_id.DEFAULT_ACCOUNT_ID ? {} : { accountId },
4068
4111
  connectionName,
4069
4112
  userId,
4070
4113
  token: ctx.token.token,
@@ -4111,7 +4154,7 @@ async function monitorMSTeamsProvider(opts) {
4111
4154
  auth: "plugin",
4112
4155
  pluginId: "msteams",
4113
4156
  source: "msteams-webhook",
4114
- accountId: appId,
4157
+ accountId,
4115
4158
  handler: webhook.handler,
4116
4159
  legacyListener: legacyListener ? {
4117
4160
  ...legacyListener,
@@ -4124,12 +4167,12 @@ async function monitorMSTeamsProvider(opts) {
4124
4167
  throwOnFailure: true,
4125
4168
  log: (message) => log.warn?.(message)
4126
4169
  }));
4127
- if (configuredPath !== "/api/messages") unregisterRoutes.push((0, openclaw_plugin_sdk_webhook_targets.registerPluginHttpRoute)({
4170
+ if (accountId === openclaw_plugin_sdk_account_id.DEFAULT_ACCOUNT_ID && configuredPath !== "/api/messages") unregisterRoutes.push((0, openclaw_plugin_sdk_webhook_targets.registerPluginHttpRoute)({
4128
4171
  path: "/api/messages",
4129
4172
  auth: "plugin",
4130
4173
  pluginId: "msteams",
4131
4174
  source: "msteams-webhook-alias",
4132
- accountId: appId,
4175
+ accountId,
4133
4176
  handler: webhook.handler,
4134
4177
  log: (message) => log.warn?.(message)
4135
4178
  }));